Esempio n. 1
0
        /// <summary>
        ///     <see cref="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1" />
        ///     除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按不同场景生成交易串调起支付。
        /// </summary>
        /// <param name="appId">服务商商户的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="key">服务商商户的 key。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id。<br />如需在支付完成后获取 <paramref name="subOpenId" /> 则此参数必传。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="deviceInfo">终端设备号 (门店号或收银设备 Id),注意:PC 网页或 JSAPI 支付请传 "WEB"。</param>
        /// <param name="receipt">传入 Y 时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。</param>
        /// <param name="body">具体的商品描述信息,建议根据不同的场景传递不同的描述信息。</param>
        /// <param name="detail">商品详细描述,对于使用单品优惠的商户,该字段必须按照规范上传。</param>
        /// <param name="attach">附加数据,在查询 API 和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据。</param>
        /// <param name="outTradeNo">商户系统内部订单号,要求 32 个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。</param>
        /// <param name="feeType">符合 ISO 4217 标准的三位字母代码,默认人民币:CNY。</param>
        /// <param name="totalFee">订单总金额,只能为整数,单位是分。</param>
        /// <param name="billCreateIp">调用微信支付 API 的机器 IP,可以使用 IPv4 或 IPv6。</param>
        /// <param name="timeStart">订单生成时间,格式为 yyyyMMddHHmmss。</param>
        /// <param name="timeExpire">订单失效时间,格式为 yyyyMMddHHmmss。</param>
        /// <param name="goodsTag">订单优惠标记,代金券或立减优惠功能的参数。</param>
        /// <param name="notifyUrl">接收微信支付异步通知回调地址,通知 Url 必须为直接可访问的 Url,不能携带参数。</param>
        /// <param name="tradeType">交易类型,请参考 <see cref="Consts.TradeType" /> 的定义。</param>
        /// <param name="productId">当 <paramref name="tradeType" /> 参数为 <see cref="Consts.TradeType.Native" /> 时,此参数必填。</param>
        /// <param name="limitPay">指定支付方式,传递 no_credit 则说明不能使用信用卡支付。</param>
        /// <param name="openId">当 <paramref name="tradeType" /> 参数为 <see cref="Consts.TradeType.JsApi" /> 时,此参数必填。如果选择传 <paramref name="subOpenId" />, 则必须传 <paramref name="subAppId" />。</param>
        /// <param name="subOpenId">当 <paramref name="tradeType" /> 参数为 <see cref="Consts.TradeType.JsApi" /> 时,此参数必填。如果选择传 <paramref name="subOpenId" />, 则必须传 <paramref name="subAppId" />。</param>
        /// <param name="sceneInfo">该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。</param>
        public async Task <UnifiedorderResult> UnifiedOrderAsync(
            string appId,
            string mchId,
            string mchKey,
            string body, string outTradeNo, int totalFee, string notifyUrl, string tradeType,
            string openId       = "",
            string billCreateIp = "",
            string subAppId     = "",
            string subMchId     = "",
            string deviceInfo   = "", string receipt    = "",
            string detail       = "", string attach     = "", string feeType  = "",
            string timeStart    = "", string timeExpire = "", string goodsTag = "",
            string productId    = "",
            string limitPay     = "", string subOpenId = "", string sceneInfo = "")
        {
            if (tradeType == Consts.TradeType.JsApi && string.IsNullOrEmpty(openId))
            {
                throw new ArgumentException($"当交易类型为 JsApi 时,参数 {nameof(openId)} 必须传递有效值。");
            }

            if (!string.IsNullOrEmpty(subOpenId) && string.IsNullOrEmpty(subAppId))
            {
                throw new ArgumentException($"传递子商户的 OpenId 时,参数 {nameof(subAppId)} 必须传递有效值。");
            }

            var request = new PayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_appid", subAppId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("device_info", deviceInfo);
            request.AddParameter("nonce_str", RandomExt.GetRandom());


            request.AddParameter("sign_type", "MD5");
            request.AddParameter("body", body);
            request.AddParameter("detail", detail);
            request.AddParameter("attach", attach);
            request.AddParameter("out_trade_no", outTradeNo);
            request.AddParameter("fee_type", feeType);
            request.AddParameter("total_fee", totalFee);
            request.AddParameter("spbill_create_ip", billCreateIp);
            request.AddParameter("time_start", timeStart);
            request.AddParameter("time_expire", timeExpire);
            request.AddParameter("goods_tag", goodsTag);
            request.AddParameter("notify_url", notifyUrl);
            request.AddParameter("trade_type", tradeType);
            request.AddParameter("product_id", productId);
            request.AddParameter("limit_pay", limitPay);
            request.AddParameter("openid", openId);
            request.AddParameter("receipt", receipt);
            request.AddParameter("scene_info", sceneInfo);
            request.AddParameter("sub_openid", subOpenId);
            //request.AddParameter("key", mchKey);

            var signStr = _signatureGenerator.Generate(request, MD5.Create(), mchKey);

            request.AddParameter("sign", signStr);

            var xmlResult = await RequestAndGetReturnValueAsync("pay/unifiedorder", request);

            var result = new UnifiedorderResult(xmlResult);

            var package = $"prepay_id={result.prepay_id}";

            result.PaySign = _signatureGenerator.GetJsPaySign(
                subAppId.IsNullOrEmptyOrWhiteSpace() ? appId : subAppId, //如果是服务商模式,这里是SubAppId
                result.TimeStamp,
                result.nonce_str,
                package, mchKey);

            return(result);
        }