Exemplo n.º 1
0
        /// <summary>
        /// 单次分账请求按照传入的分账接收方账号和资金进行分账,同时会将订单剩余的待分账金额解冻给特约商户。故操作成功后,订单不能再进行分账,也不能进行分账完结。<br/><br/>
        /// 接口频率:30 QPS <br/>
        /// 是否需要证书:是
        /// </summary>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="appId">微信分配的公众账号 Id。</param>
        /// <param name="transactionId">微信支付订单号,不能传递商户系统自己生成的订单号。</param>
        /// <param name="outOrderNo">服务商系统内部的分账单号,在服务商系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。</param>
        /// <param name="receivers">分账接收方列表,不超过 50 个接收对象。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id,可为空。</param>
        /// <exception cref="ArgumentException">当参数校验出现错误时,会抛出本异常。</exception>
        public Task <XmlDocument> ProfitSharingAsync(string mchId, string subMchId, string appId, string transactionId, string outOrderNo,
                                                     IList <ProfitSharingReceiver> receivers, string subAppId = null)
        {
            if (receivers.Count > 50)
            {
                throw new ArgumentException("分账接收方最大不能超过 50 个。", nameof(receivers));
            }

            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("appid", appId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_order_no", outOrderNo);
            request.AddParameter("receivers", JsonConvert.SerializeObject(receivers));

            request.AddParameter("sub_appid", subAppId);

            var signStr = SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(AbpWeChatPayOptions.ApiKey)), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(ProfitSharingUrl, request));
        }
Exemplo n.º 2
0
        /// <summary>
        /// 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将
        /// 支付款按原路退到买家帐号上。
        /// </summary>
        /// <remarks>
        /// 注意:<br/>
        /// 1. 交易时间超过一年的订单无法提交退款。<br/>
        /// 2. 微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请
        /// 不要更换退款单号,请使用原商户退款单号。<br/>
        /// 3. 请求频率限制:150 QPS,即每秒钟正常的申请退款请求次数不超过 150 次。<br/>
        /// 错误或无效请求频率限制:6 QPS,即每秒钟异常或错误的退款申请请求不超过 6 次。<br/>
        /// 4. 每个支付订单的部分退款次数不能超过 50 次。
        /// </remarks>
        /// <param name="appId">服务商商户的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="transactionId">微信生成的订单号,在支付通知中有返回。</param>
        /// <param name="outTradeNo">
        /// 商户系统内部订单号,要求 32 个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。<br/>
        /// <paramref name="transactionId"/> 与 <paramref name="outTradeNo"/> 二选一,如果同时存在,优先级是:<paramref name="transactionId"/> 大于 <paramref name="outTradeNo"/>。
        /// </param>
        /// <param name="outRefundNo">商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。</param>
        /// <param name="totalFee">订单总金额,单位为分,只能为整数。</param>
        /// <param name="refundFee">退款总金额,单位为分,只能为整数,可部分退款。</param>
        /// <param name="refundFeeType">退款货币类型,需与支付一致,或者不填。</param>
        /// <param name="refundDesc">若商户传入,会在下发给用户的退款消息中体现退款原因,当订单退款金额 ≤1 元并且属于部分退款,则不会在退款消息中体现退款原因。</param>
        /// <param name="refundAccount">仅针对老资金流商户使用,具体参考 <see cref="RefundAccountType"/> 的定义。</param>
        /// <param name="notifyUrl">异步接收微信支付退款结果通知的回调地址,通知 Url 必须为外网可访问的 Url,不允许带参数。如果传递了该参数,则商户平台上配置的回调地址将不会生效。</param>
        public Task <XmlDocument> RefundAsync(string appId, string mchId, string subAppId, string subMchId, string transactionId, string outTradeNo, string outRefundNo,
                                              int totalFee, int refundFee, string refundFeeType, string refundDesc, string refundAccount, string notifyUrl)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_appid", subAppId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_trade_no", outTradeNo);
            request.AddParameter("out_refund_no", outRefundNo);
            request.AddParameter("total_fee", totalFee);
            request.AddParameter("refund_fee", refundFee);
            request.AddParameter("refund_fee_type", refundFeeType);
            request.AddParameter("refund_desc", refundDesc);
            request.AddParameter("refund_account", refundAccount);
            request.AddParameter("notify_url", notifyUrl);

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(RefundUrl, request));
        }
        /// <summary>
        /// 根据微信订单号或者商户订单号,查询订单的详细信息。如果两个参数都被填写,优先使用微信订单号进行查询。
        /// </summary>
        /// <remarks>
        /// 商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。一般来说,都是因为商户系统没有收到支付通知,需要主动
        /// 查询订单的状态才会调用本方法进行查询。
        /// </remarks>
        /// <param name="appId">微信小程序或公众号的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="weChatOrderNo">微信订单号。</param>
        /// <param name="orderNo">商户订单号,该订单号是商户系统内部生成的唯一编号。</param>
        /// <returns>请求的结果,会被转换为 <see cref="XmlDocument"/> 实例并返回。</returns>
        /// <exception cref="ArgumentException">当微信订单号和商户订单号都为 null 时,会抛出本异常。</exception>
        public Task <XmlDocument> OrderQueryAsync(string appId, string mchId, string weChatOrderNo = null, string orderNo = null)
        {
            if (string.IsNullOrEmpty(weChatOrderNo) && string.IsNullOrEmpty(orderNo))
            {
                throw new ArgumentException("微信订单号或商户订单号必须传递一个有效参数。");
            }

            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());

            if (!string.IsNullOrEmpty(weChatOrderNo))
            {
                request.AddParameter("transaction_id", weChatOrderNo);
            }
            else
            {
                request.AddParameter("out_trade_no", orderNo);
            }

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(OrderQueryUrl, request));
        }
Exemplo n.º 4
0
        /// <summary>
        /// 仅对订单进行退款时,如果订单已经分账,可以先调用此接口将指定的金额从分账接收方(仅限商户类型的分账接收方)回退给特约商户,然后再退款。<br/>
        /// 回退以原分账请求为依据,可以对分给分账接收方的金额进行多次回退,只要满足累计回退不超过该请求中分给接收方的金额。<br/>
        /// 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果。<br/><br/>
        /// 接口频率:30 QPS<br/>
        /// 是否需要证书:是
        /// </summary>
        /// <remarks>
        /// 此功能需要接收方在商户平台 - 交易中心 - 分账 - 分账接收设置下,开启同意分账回退后,才能使用。
        /// </remarks>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="appId">微信分配的公众账号 Id。</param>
        /// <param name="outOrderNo">原发起分账请求时使用的商户后台系统的分账单号。</param>
        /// <param name="outReturnNo">此回退单号是商户在自己后台生成的一个新的回退单号,在商户后台唯一。</param>
        /// <param name="returnAccountType">回退方类型(<see cref="ProfitSharingReceiverType"/>),暂时只支持从商户接收方回退分账金额。</param>
        /// <param name="returnAccount">回退方账号。</param>
        /// <param name="returnAmount">需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额。</param>
        /// <param name="description">分账回退的原因描述。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id,可空。</param>
        public async Task <XmlDocument> ProfitSharingReturnAsync(string mchId, string subMchId, string appId, string outOrderNo, string outReturnNo, string returnAccountType,
                                                                 string returnAccount, int returnAmount, string description, string subAppId = null)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("appid", appId);
            request.AddParameter("out_order_no", outOrderNo);
            request.AddParameter("out_return_no", outReturnNo);
            request.AddParameter("return_account_type", returnAccountType);
            request.AddParameter("return_account", returnAccount);
            request.AddParameter("return_amount", returnAmount);
            request.AddParameter("description", description);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());

            request.AddParameter("sub_appid", subAppId);

            var options = await GetAbpWeChatPayOptions();

            var signStr = SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(options.ApiKey)), options.ApiKey);

            request.AddParameter("sign", signStr);

            return(await RequestAndGetReturnValueAsync(ProfitSharingReturnUrl, request));
        }
Exemplo n.º 5
0
        /// <summary>
        /// 根据商户号获取平台证书。
        /// </summary>
        /// <param name="mchId">微信支付分配的商户号。</param>
        public Task <XmlDocument> GetCertificateAsync(string mchId)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("sign_type", "HMAC-SHA256");

            request.AddParameter("sign", SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(AbpWeChatPayOptions.ApiKey)), AbpWeChatPayOptions.ApiKey));

            return(RequestAndGetReturnValueAsync(GetCertificateUrl, request));
        }
        /// <summary>
        /// 除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按不同场景生成交易串调起支付。
        /// </summary>
        /// <param name="appId">服务商商户的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</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="TradeType"/> 的定义。</param>
        /// <param name="productId">当 <paramref name="tradeType"/> 参数为 <see cref="TradeType.Native"/> 时,此参数必填。</param>
        /// <param name="limitPay">指定支付方式,传递 no_credit 则说明不能使用信用卡支付。</param>
        /// <param name="openId">当 <paramref name="tradeType"/> 参数为 <see cref="TradeType.JsApi"/> 时,此参数必填。如果选择传 <paramref name="subOpenId"/>, 则必须传 <paramref name="subAppId"/>。</param>
        /// <param name="subOpenId">当 <paramref name="tradeType"/> 参数为 <see cref="TradeType.JsApi"/> 时,此参数必填。如果选择传 <paramref name="subOpenId"/>, 则必须传 <paramref name="subAppId"/>。</param>
        /// <param name="sceneInfo">该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。</param>
        public Task <XmlDocument> UnifiedOrderAsync(string appId, string mchId, string subAppId, string subMchId, string deviceInfo, string receipt,
                                                    string body, string detail, string attach, string outTradeNo, string feeType, int totalFee,
                                                    string billCreateIp, string timeStart, string timeExpire, string goodsTag, string notifyUrl, string tradeType, string productId,
                                                    string limitPay, string openId, string subOpenId, string sceneInfo)
        {
            if (tradeType == TradeType.JsApi && string.IsNullOrEmpty(openId))
            {
                throw new ArgumentException($"当交易类型为 JsApi 时,参数 {nameof(openId)} 必须传递有效值。");
            }

            if (tradeType == TradeType.Native && string.IsNullOrEmpty(productId))
            {
                throw new ArgumentException($"当交易类型为 Native 时,参数 {nameof(productId)} 必须传递有效值。");
            }

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

            var request = new WeChatPayParameters();

            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("receipt", receipt);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            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("sub_openid", subOpenId);
            request.AddParameter("scene_info", sceneInfo);

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(UnifiedOrderUrl, request));
        }
Exemplo n.º 7
0
        /// <summary>
        /// 使用申请入驻接口提交你的小微商户资料,申请后一般 5 分钟左右可以查询到具体的申请结果。
        /// </summary>
        /// <param name="version">接口版本号,固定版本号为 3.0。</param>
        /// <param name="certSn">平台证书序列号,通过 <see cref="GetCertificateAsync"/> 接口获取。</param>
        /// <param name="mchId">服务商的商户号。</param>
        /// <param name="businessCode">业务申请编号,服务商自定义的商户唯一编号。每个编号对应一个申请单,每个申请单审核通过后会生成一个微信支付商户号。</param>
        /// <param name="idCardCopy">身份证人像面照片,请使用 <see cref="UploadMediaAsync"/> 接口预先上传图片,传递其 media_id 。</param>
        /// <param name="idCardNational">身份证国徽面照片,请使用 <see cref="UploadMediaAsync"/> 接口预先上传图片,传递其 media_id。</param>
        /// <param name="idCardName">身份证姓名,请填写小微商户本人身份证上的姓名,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <param name="idCardNumber">身份证号码,15 位数字 或  17 位数字 + 1 位数字 | X ,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <param name="idCardValidTime">身份证有效期限,格式应该和 ["1970-01-01","长期"] 一致,结束时间需要大于开始时间,需要和上传的身份证内容一致。</param>
        /// <param name="accountName">开户名称,必须与身份证姓名一致,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <param name="accountBank">开户银行,请参考 <see cref="AccountBanks"/> 类型中定义的银行列表。</param>
        /// <param name="bankAddressCode">开户银行省市编码,至少精确到市,请参考 https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1 提供的对照表。</param>
        /// <param name="bankName">
        /// 开户银行全称(含支行),17 家直连银行无需填写(在 <see cref="AccountBanks"/> 定义的),其他银行请务必填写。<br/>
        /// 需填写银行全称,如 "深圳农村商业银行 XXX 支行",详细信息请参考 https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1。</param>
        /// <param name="accountNumber">银行账号,数字,长度遵循系统支持的对私卡号长度要求,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <param name="storeName">
        /// 门店名称,最长 50 个中文字符。<br/>
        /// 门店场所:填写门店名称<br/>
        /// 流动经营 / 便民服务:填写经营 / 服务名称<br/>
        /// 线上商品 / 服务交易:填写线上店铺名称<br/>
        /// </param>
        /// <param name="storeAddressCode">
        /// 门店省市编码,至少精确到市,参考 https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1 提供的对照表。<br/>
        /// 门店场所:填写门店省市编码<br/>
        /// 流动经营 / 便民服务:填写经营 / 服务所在地省市编码<br/>
        /// 线上商品 / 服务交易:填写卖家所在地省市编码<br/>
        /// </param>
        /// <param name="storeStreet">
        /// 门店街道名称,最长 500 个中文字符(无需填写省市信息)。<br/>
        /// 门店场所:填写店铺详细地址,具体区 / 县及街道门牌号或大厦楼层<br/>
        /// 流动经营 / 便民服务:填写 “无 "<br/>
        /// 线上商品 / 服务交易:填写电商平台名称<br/>
        /// </param>
        /// <param name="storeLongitude">门店经度。</param>
        /// <param name="storeLatitude">门店纬度。</param>
        /// <param name="storeEntrancePic">
        /// 门店门口照片,请使用 <see cref="UploadMediaAsync"/> 接口预先上传图片,传递其 media_id 。<br/>
        /// 门店场所:提交门店门口照片,要求招牌清晰可见<br/>
        /// 流动经营 / 便民服务:提交经营 / 服务现场照片<br/>
        /// 线上商品 / 服务交易:提交店铺首页截图<br/>
        /// </param>
        /// <param name="indoorPic">
        /// 店内环境照片,请使用 <see cref="UploadMediaAsync"/> 接口预先上传图片,传递其 media_id 。<br/>
        /// 门店场所:提交店内环境照片<br/>
        /// 流动经营 / 便民服务:可提交另一张经营 / 服务现场照片<br/>
        /// 线上商品 / 服务交易:提交店铺管理后台截图<br/>
        /// </param>
        /// <param name="addressCertification">经营场地证明,门面租赁合同扫描件或经营场地证明(需与身份证同名),请使用 <see cref="UploadMediaAsync"/> 接口预先上传图片,传递其 media_id 。</param>
        /// <param name="merchantShortName">商户简称,UTF-8 格式,中文占 3 个字节,即最多 16 个汉字长度。将在支付完成页向买家展示,需与商家的实际经营场景相符。</param>
        /// <param name="servicePhone">客服电话,UTF-8 格式,中文占 3 个字节,即最多 16 个汉字长度。在交易记录中向买家展示,请确保电话畅通以便平台回拨确认。</param>
        /// <param name="productDesc">
        /// 售卖商品 / 提供服务描述,请填写以下描述之一:<br/>
        /// 餐饮、线下零售、居民生活服务、休闲娱乐、交通出行、其他。
        /// </param>
        /// <param name="rate">费率,由服务商指定,具体有效费率枚举值,可以参考 https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1。</param>
        /// <param name="businessAdditionDesc">补充说明,可填写需要额外说明的文字。</param>
        /// <param name="businessAdditionPics">补充材料,最多可上传 5 张照片,请填写已预先上传图片生成好的 MediaID,例如 ["123","456"]。</param>
        /// <param name="contact">
        /// 超级管理员姓名,和身份证姓名一致。超级管理员需在开户后进行签约,并可接收日常重要管理信息和进行资金操作,请确定其为商户法定代表人或负责人。<br/>
        /// 使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。
        /// </param>
        /// <param name="contactPhone">手机号码,11 位数字,手机号码,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <param name="contactEmail">联系邮箱,需要带 @,遵循邮箱格式校验,使用 <see cref="WeChatPayToolUtility"/> 提供的加密方法进行加密。</param>
        /// <returns></returns>
        public async Task <XmlDocument> SubmitAsync(string version, string certSn, string mchId, string businessCode,
                                                    string idCardCopy, string idCardNational, string idCardName, string idCardNumber, string idCardValidTime,
                                                    string accountName, string accountBank, string bankAddressCode, [CanBeNull] string bankName, string accountNumber,
                                                    string storeName, string storeAddressCode, string storeStreet, [CanBeNull] string storeLongitude, [CanBeNull] string storeLatitude,
                                                    string storeEntrancePic, string indoorPic, [CanBeNull] string addressCertification, [CanBeNull] string merchantShortName,
                                                    string servicePhone, string productDesc, string rate, [CanBeNull] string businessAdditionDesc, [CanBeNull] string businessAdditionPics,
                                                    string contact, string contactPhone, [CanBeNull] string contactEmail)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("version", version);
            request.AddParameter("cert_sn", certSn);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("sign_type", "HMAC-SHA256");
            request.AddParameter("business_code", businessCode);
            request.AddParameter("id_card_copy", idCardCopy);
            request.AddParameter("id_card_national", idCardNational);
            request.AddParameter("id_card_name", idCardName);
            request.AddParameter("id_card_number", idCardNumber);
            request.AddParameter("id_card_valid_time", idCardValidTime);
            request.AddParameter("account_name", accountName);
            request.AddParameter("account_bank", accountBank);
            request.AddParameter("bank_address_code", bankAddressCode);
            request.AddParameter("bank_name", bankName);
            request.AddParameter("account_number", accountNumber);
            request.AddParameter("store_name", storeName);
            request.AddParameter("store_address_code", storeAddressCode);
            request.AddParameter("store_street", storeStreet);
            request.AddParameter("store_longitude", storeLongitude);
            request.AddParameter("store_latitude", storeLatitude);
            request.AddParameter("store_entrance_pic", storeEntrancePic);
            request.AddParameter("indoor_pic", indoorPic);
            request.AddParameter("address_certification", addressCertification);
            request.AddParameter("merchant_shortname", merchantShortName);
            request.AddParameter("service_phone", servicePhone);
            request.AddParameter("product_desc", productDesc);
            request.AddParameter("rate", rate);
            request.AddParameter("business_addition_desc", businessAdditionDesc);
            request.AddParameter("business_addition_pics", businessAdditionPics);
            request.AddParameter("contact", contact);
            request.AddParameter("contact_phone", contactPhone);
            request.AddParameter("contact_email", contactEmail);

            var options = await GetAbpWeChatPayOptions();

            request.AddParameter("sign", SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(options.ApiKey)), options.ApiKey));
            return(await RequestAndGetReturnValueAsync(SubmitUrl, request));
        }
Exemplo n.º 8
0
        /// <summary>
        /// 使用申请入驻接口提交小微商户资料后,一般 5 分钟左右可以通过该查询接口查询具体的申请结果。
        /// </summary>
        /// <param name="version">接口版本号,默认值 1.0。</param>
        /// <param name="mchId">服务商的商户号。</param>
        /// <param name="applyentId">商户申请单号,微信支付分配的申请单号。</param>
        /// <param name="businessCode">业务申请编号,服务商自定义的商户唯一编号,当 <paramref name="applyentId"/>  已填写时,此字段无效。</param>
        /// <returns></returns>
        public Task <XmlDocument> GetStateAsync(string version, string mchId, string applyentId, string businessCode)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("version", version);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("sign_type", "HMAC-SHA256");
            request.AddParameter("applyment_id", applyentId);
            request.AddParameter("business_code", businessCode);

            request.AddParameter("sign", SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(AbpWeChatPayOptions.ApiKey)), AbpWeChatPayOptions.ApiKey));

            return(RequestAndGetReturnValueAsync(GetStateUrl, request));
        }
        /// <summary>
        /// 发起分账请求后,可调用此接口查询分账结果;发起分账完结请求后,可调用此接口查询分账完结的执行结果。<br/><br/>
        /// 接口频率:80 QPS <br/>
        /// 是否需要证书:否
        /// </summary>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="transactionId">微信支付订单号。</param>
        /// <param name="outOrderNo">
        /// 查询分账结果,输入申请分账时的商户分账单号。 <br/>
        /// 查询分账完结的执行结果,输入发起分账完结时的商户分账单号。</param>
        public Task <XmlDocument> ProfitSharingQueryAsync(string mchId, string subMchId, string transactionId, string outOrderNo)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_order_no", outOrderNo);

            var signStr = SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(AbpWeChatPayOptions.ApiKey)), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(ProfitSharingQueryUrl, request));
        }
        public void Generate_Test()
        {
            AbpWeChatPayOptions.ApiKey = "192006250b4c09247ec02edce69f6a2d";

            var newParam = new WeChatPayParameters();

            newParam.AddParameter("appid", "wxd930ea5d5a258f4f");
            newParam.AddParameter("mch_id", "10000100");
            newParam.AddParameter("device_info", "1000");
            newParam.AddParameter("body", "test");
            newParam.AddParameter("nonce_str", "ibuaiVcKdpRxkhJA");

            var signStr = _signatureGenerator.Generate(newParam, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            signStr.ShouldBe("9A0A8659F005D6984697E2CA0A9CF3B7");
        }
Exemplo n.º 11
0
        /// <summary>
        /// 根据商户订单号,关闭指定的订单。
        /// </summary>
        /// <remarks>
        /// 以下情况需要调用关单接口:商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,
        /// 用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。订单生成后不能马上调用关单接口,最短调用时间间隔为 5 分钟。
        /// </remarks>
        /// <param name="appId">微信小程序或公众号的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="orderNo">商户订单号,该订单号是商户系统内部生成的唯一编号。</param>
        /// <returns>请求的结果,会被转换为 <see cref="XmlDocument"/> 实例并返回。</returns>
        public Task <XmlDocument> CloseOrderAsync(string appId, string mchId, string orderNo)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());

            request.AddParameter("out_trade_no", orderNo);

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(CloseOrderUrl, request));
        }
Exemplo n.º 12
0
        /// <summary>
        /// 图片上传功能,用于上传证件照片等数据。
        /// </summary>
        /// <param name="mchId">服务商商户号或渠道号。</param>
        /// <param name="imagePath">图片的文件路径。</param>
        /// <returns>图片关联的 Id。</returns>
        public async Task <string> UploadMediaAsync(string mchId, string imagePath)
        {
            if (new FileInfo(imagePath).Length > MaxMediaFileSize)
            {
                throw new ArgumentOutOfRangeException(nameof(imagePath), "指定的图像文件大小超过 2 M。");
            }

            var bytes     = File.ReadAllBytes(imagePath);
            var mediaHash = MD5.Create().ComputeHash(bytes).Select(b => b.ToString("X2")).JoinAsString("");

            // 构建并计算签名值。
            var parameters = new WeChatPayParameters();

            parameters.AddParameter("mch_id", mchId);
            parameters.AddParameter("media_hash", mediaHash);

            var options = await GetAbpWeChatPayOptions();

            var sign = SignatureGenerator.Generate(parameters, MD5.Create(), options.ApiKey);

            // 构建表单请求。
            var fileContent = new ByteArrayContent(bytes);

            fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");

            var form = new MultipartFormDataContent
            {
                { new StringContent(mchId), "\"mch_id\"" },
                { new ByteArrayContent(bytes), "\"media\"", $"\"{HttpUtility.UrlEncode(Path.GetFileName(imagePath))}\"" },
                { new StringContent(mediaHash), "\"media_hash\"" },
                { new StringContent(sign), "\"sign\"" }
            };

            // 处理 Boundary 蛋疼的引号问题,参考 https://developers.de/blogs/damir_dobric/archive/2013/09/10/problems-with-webapi-multipart-content-upload-and-boundary-quot-quotes.aspx 。
            var boundaryValue = form.Headers.ContentType.Parameters.Single(p => p.Name == "boundary");

            boundaryValue.Value = boundaryValue.Value.Replace("\"", String.Empty);

            using var client = HttpClientFactory.CreateClient("WeChatPay");
            using var resp   = await client.PostAsync(UploadMediaUrl, form);

            var xmlDocument = new XmlDocument();

            xmlDocument.LoadXml(await resp.Content.ReadAsStringAsync());
            return(xmlDocument.SelectSingleNode("/xml/media_id")?.InnerText);
        }
        /// <summary>
        /// 该接口提供所有微信支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。<br/>
        /// 需要调用查询接口的情况:<br/>
        /// 1. 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。<br/>
        /// 2. 调用支付接口后,返回系统错误或未知交易状态情况。<br/>
        /// 3. 调用被扫支付 API,返回 USERPAYING 的状态。<br/>
        /// 4. 调用关单或撤销接口 API 之前,需确认支付状态。
        /// </summary>
        /// <param name="appId">服务商商户的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="transactionId">微信生成的订单号,在支付通知中有返回。</param>
        /// <param name="outTradeNo">
        /// 商户系统内部订单号,要求 32 个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。<br/>
        /// <paramref name="transactionId"/> 与 <paramref name="outTradeNo"/> 二选一,如果同时存在,优先级是:<paramref name="transactionId"/> 大于 <paramref name="outTradeNo"/>。
        /// </param>
        public Task <XmlDocument> OrderQueryAsync(string appId, string mchId, string subAppId, string subMchId, string transactionId, string outTradeNo)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_appid", subAppId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_trade_no", outTradeNo);

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(OrderQueryUrl, request));
        }
Exemplo n.º 14
0
        /// <summary>
        /// 服务商代子商户发起删除分账接收方请求,删除后不支持将结算后的钱分到该分账接收方。<br/><br/>
        /// 是否需要证书:否
        /// </summary>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="appId">微信分配的公众账号 Id。</param>
        /// <param name="receiverType">分账接收方类型,参考 (<see cref="ProfitSharingReceiverType"/>)。</param>
        /// <param name="receiverAccount">分账接收方帐号。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id,可空。</param>
        public Task <XmlDocument> ProfitSharingRemoveReceiverAsync(string mchId, string subMchId, string appId, string receiverType, string receiverAccount, string subAppId = null)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("appid", appId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("receiver", JsonConvert.SerializeObject(new ProfitSharingReceiverDefinition(receiverType, receiverAccount, null)));

            request.AddParameter("sub_appid", subAppId);

            var signStr = SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(AbpWeChatPayOptions.ApiKey)), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(ProfitSharingRemoveReceiverUrl, request));
        }
Exemplo n.º 15
0
        /// <summary>
        /// 1. 不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户。<br/>
        /// 2. 调用多次分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户。<br/>
        /// 3. 已调用请求单次分账后,剩余待分账金额为零,不需要再调用此接口。<br/><br/>
        /// 接口频率:30 QPS<br/>
        /// 是否需要证书:是
        /// </summary>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="appId">微信分配的公众账号 Id。</param>
        /// <param name="transactionId">微信支付订单号,不能传递商户系统自己生成的订单号。</param>
        /// <param name="outOrderNo">服务商系统内部的分账单号,在服务商系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。</param>
        /// <param name="description">分账完结的原因描述。</param>
        public async Task <XmlDocument> ProfitSharingFinishAsync(string mchId, string subMchId, string appId, string transactionId, string outOrderNo, string description)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("appid", appId);
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_order_no", outOrderNo);
            request.AddParameter("description", description);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());

            var options = await GetAbpWeChatPayOptions();

            var signStr = SignatureGenerator.Generate(request, new HMACSHA256(Encoding.UTF8.GetBytes(options.ApiKey)), options.ApiKey);

            request.AddParameter("sign", signStr);

            return(await RequestAndGetReturnValueAsync(ProfitSharingFinishUrl, request));
        }
Exemplo n.º 16
0
        /// <summary>
        /// 申请退款功能,支持针对指定订单进行退款操作。
        /// </summary>
        /// <remarks>
        /// 注意交易时间超过一年的订单无法提交退款,并且如果一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。
        /// </remarks>
        /// <param name="appId">微信小程序或公众号的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="orderNo">商户订单号,该订单号是商户系统内部生成的唯一编号。</param>
        /// <param name="refundOrderNo">商户退款单号,该单号同 <paramref name="orderNo"/> 参数一样,都是通过商户系统自行生成。</param>
        /// <param name="orderTotalFee">原始订单的总金额,单位为分。</param>
        /// <param name="refundFee">本次需要进行退款的金额,单位为分。注意该金额不能大于原始订单的总金额。</param>
        /// <param name="refundDescription">退款说明,当订单退款金额小于 1 元时,不会在退款消息中体现。</param>
        /// <returns>请求的结果,会被转换为 <see cref="XmlDocument"/> 实例并返回。</returns>
        public Task <XmlDocument> RefundAsync(string appId, string mchId, string orderNo, string refundOrderNo,
                                              int orderTotalFee, int refundFee,
                                              string refundDescription = null)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("out_refund_no", refundOrderNo);
            request.AddParameter("out_trade_no", orderNo);
            request.AddParameter("total_fee", orderTotalFee);
            request.AddParameter("refund_fee", refundFee);
            request.AddParameter("notify_url", AbpWeChatPayOptions.RefundNotifyUrl);
            request.AddParameter("refund_desc", refundDescription);

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), AbpWeChatPayOptions.ApiKey);

            request.AddParameter("sign", signStr);

            return(RequestAndGetReturnValueAsync(RefundUrl, request));
        }
Exemplo n.º 17
0
        /// <summary>
        /// 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款 20 分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态。
        /// </summary>
        /// <remarks>
        /// 注意:如果单个支付订单部分退款次数超过 20 次请使用退款单号查询。<br/>
        /// 当一个订单部分退款超过 10 笔后,商户用微信订单号或商户订单号调退款查询 API 查询退款时,默认返回前 10 笔和 total_refund_count(退款单总笔数)。<br/>
        /// 商户需要查询同一订单下超过 10 笔的退款单时,可传入订单号及 offset 来查询,微信支付会返回 offset 及后面的 10 笔,以此类推。<br/>
        /// 当商户传入的 offset 超过 total_refund_count,则系统会返回报错 PARAM_ERROR。
        /// </remarks>
        /// <param name="appId">服务商商户的 AppId。</param>
        /// <param name="mchId">微信支付分配的商户号。</param>
        /// <param name="subAppId">微信分配的子商户公众账号 Id。</param>
        /// <param name="subMchId">微信支付分配的子商户号。</param>
        /// <param name="transactionId">微信订单号。</param>
        /// <param name="outTradeNo">商户系统内部订单号,要求 32 个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。</param>
        /// <param name="outRefundNo">商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。</param>
        /// <param name="refundId">微信退款单号。</param>
        /// <param name="offset">偏移量,当部分退款次数超过 10 次时可使用,表示返回的查询结果从这个偏移量开始取记录。</param>
        public async Task <XmlDocument> RefundQueryAsync(string appId, string mchId, string subAppId, string subMchId, string transactionId, string outTradeNo, string outRefundNo,
                                                         string refundId, int offset)
        {
            var request = new WeChatPayParameters();

            request.AddParameter("appid", appId);
            request.AddParameter("mch_id", mchId);
            request.AddParameter("sub_appid", subAppId);
            request.AddParameter("sub_mch_id", subMchId);
            request.AddParameter("nonce_str", RandomHelper.GetRandom());
            request.AddParameter("transaction_id", transactionId);
            request.AddParameter("out_trade_no", outTradeNo);
            request.AddParameter("out_refund_no", outRefundNo);
            request.AddParameter("refund_id", refundId);
            request.AddParameter("out_trade_no", offset);

            var options = await GetAbpWeChatPayOptions();

            var signStr = SignatureGenerator.Generate(request, MD5.Create(), options.ApiKey);

            request.AddParameter("sign", signStr);

            return(await RequestAndGetReturnValueAsync(RefundQueryUrl, request));
        }
Exemplo n.º 18
0
        protected virtual async Task <XmlDocument> RequestAndGetReturnValueAsync(string targetUrl, WeChatPayParameters requestParameters)
        {
            var result = await WeChatPayApiRequester.RequestAsync(targetUrl, requestParameters.ToXmlStr());

            if (result.SelectSingleNode("/xml/err_code") != null ||
                result.SelectSingleNode("/xml/return_code")?.InnerText != "SUCCESS" ||
                result.SelectSingleNode("/xml/return_msg")?.InnerText != "OK")
            {
                throw new UserFriendlyException($"调用微信支付接口失败,具体失败原因:{result.SelectSingleNode("/xml/err_code_des")?.InnerText}");
            }

            return(result);
        }
Exemplo n.º 19
0
        protected virtual async Task <XmlDocument> RequestAndGetReturnValueAsync(string targetUrl, WeChatPayParameters requestParameters)
        {
            var result = await WeChatPayApiRequester.RequestAsync(targetUrl, requestParameters.ToXmlStr());

            if (result.SelectSingleNode("/xml/return_code")?.InnerText != "SUCCESS" ||
                result.SelectSingleNode("/xml/return_code")?.InnerText != "SUCCESS" ||
                result.SelectSingleNode("/xml/return_msg")?.InnerText != "OK")
            {
                var errMsg = $"微信支付接口调用失败,具体失败原因:{result.SelectSingleNode("/xml/err_code_des")?.InnerText ?? result.SelectSingleNode("/xml/return_msg")?.InnerText}";
                Logger.Log(LogLevel.Error, errMsg, targetUrl, requestParameters);

                var exception = new CallWeChatPayApiException(errMsg);
                exception.Data.Add(nameof(targetUrl), targetUrl);
                exception.Data.Add(nameof(requestParameters), requestParameters);

                throw exception;
            }

            return(result);
        }