Asp.Net Core3.0 微信小程序统一下单

微信统一下单开发文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

微信支付小程序支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml

简单记录一下net core3.0对接微信支付的相关代码,网上有很多微信支付的文章,但是一定要仔细看一遍微信的官方文档。

准备工作:

首先要开通小程序的支付能力,按要求提交审核材料,审核通过后,我们可以得到APPID、微信支付商户号mch_id、API密钥key、Appsecret,详见 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=3_1

之后在商户后台绑定同一主体的APPID并授权,发起授权后,商户需要自行前往对应平台确认授权申请。

最后在商户后台设置回调地址。

一、微信支付

1.支付V3

代码只是简单实现退款功能,仅供参考,请勿在生产环境中使用,否则可能造成资金风险。

//支付接口 
 public async Task<ResponseResult<dynamic>> unifiedorder(int money)
{
  var url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; var req = new GenerateOrderModelForWxPay { appid = _appid, mchid = _mchid, description = "XXXXX支付交易单", amount = new WxPayAmountModel { total = money, currency = "CNY" }, payer = new WxPayerModel { openid = "付款人的openid" }, out_trade_no = DateTime.Now.ToString("yyyyMMddhhmmss"), notify_url = "http://api.xxxx.com/api/pay/NotifyUrl" //支付回调,自己创建 }; _log.WriteLog(JsonConvert.SerializeObject(req)); var httpHandler = new HttpHandler(_mchid, _serialNo); HttpClient client = new HttpClient(httpHandler); var bodyJson = new StringContent(JsonConvert.SerializeObject(req), Encoding.UTF8, "application/json"); //一定要这样传递参数,不然在加密签名的时候获取到的参数就是\\u0这种形式的数据了,不是传递的这样的数据了,导致加密的结果不正确 var response = await client.PostAsync(url, bodyJson); var respStr = await response.Content.ReadAsStringAsync();//读取统一下单之后的返回结果,这样读取出来的直接就是结果,或者错误原因。这里面就包含prepay_id了

        var timeStamp = DateTimeOffset.Now.ToUnixTimeSeconds();
        var nonceStr = Path.GetRandomFileName();
        string message = $"{_appid}\n{timeStamp}\n{nonceStr}\nprepay_id={JsonConvert.DeserializeObject<dynamic>(respStr).prepay_id}\n";
        string signature = httpHandler.Sign(message);
        var newObj = new
        {
        timeStamp= timeStamp.ToString(),
        nonceStr = nonceStr,
        package= "prepay_id="+JsonConvert.DeserializeObject<dynamic>(respStr).prepay_id,
        paySign = signature
        };
        return new ResponseResult<dynamic>()
        {
        Code = Constant.Success,
        Data=JsonConvert.SerializeObject(newObj),
        };

}

//model

public class WxPayAmountModel

{
  public int total { get; set; }
  public string currency { get; set; }
}

public class WxPayerModel
{
  public string openid { get; set; }
}

/// <summary>
/// 支付resource
/// </summary>
public class GenerateOrderModelForWxPay
{
  public string appid { get; set; }
  public string mchid { get; set; }
  public string description { get; set; }
  public WxPayAmountModel amount { get; set; }
  public WxPayerModel payer { get; set; }
  public string out_trade_no { get; set; }
  public string notify_url { get; set; }
}

支付完成后,微信返回的结果格式如下:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

2.支付结果通知:

支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。

通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)这里通知发送可能会多台服务器进行发送,且发送时间可能会在几秒内,但微信不保证通知最终一定能成功。

支付成功后,微信会将结果通知商户后台,即支付代码里的notify_url链接,该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。

微信调用回调地址返回数据格式:
<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付测试]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
  <coupon_fee><![CDATA[10]]></coupon_fee>
  <coupon_count><![CDATA[1]]></coupon_count>
  <coupon_type><![CDATA[CASH]]></coupon_type>
  <coupon_id><![CDATA[10000]]></coupon_id>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>

获取到微信的通知后,商户处理业务逻辑后要同步返回给微信结果,SUCCESS表示商户接收通知成功并校验成功。

        public dynamic NotifyUrl(NotifyDto ret)
        {
            WxPayAPI.WxPayData res = new WxPayAPI.WxPayData();
            res.SetValue("return_code", "FAIL");
            res.SetValue("return_msg", "订单查询失败");

            try
            {
                if (ret.Event_type == "TRANSACTION.SUCCESS")//支付成功
                {
                    //解密数据报文
                    var dataJson = AesGcmHelper.AesGcmDecryptFromBase64(_key, ret.Resource.Nonce, ret.Resource.Ciphertext, ret.Resource.Associated_data);
                    //转换对象接受
                    var data = JsonConvert.DeserializeObject<PayCipherText>(dataJson);
                    //处理自己的业务逻辑
                     TODO...

                    res.SetValue("return_code", "SUCCESS");
                    res.SetValue("return_msg", "OK");
                    return Content(res.ToXml());
                }
                else
                {
                    _log.WriteLog("NotifyFaile:" + JsonConvert.SerializeObject(ret));

                }
                return Content(res.ToXml());
            }
            catch (Exception ex)
            {
                _log.WriteLog(ex.ToString());
                return Content(res.ToXml());
            }
        }

/// 支付结果回调接收参数
public class NotifyDto
{
/// 通知ID通知的唯一ID
/// 示例值:EV-2018022511223320873
public string Id { get; set; }
/// 通知创建时间 通知创建的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒。
/// 示例值:2015-05-20T13:29:35+08:00
public string Create_time { get; set; }
/// 通知类型 通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS
/// 示例值:TRANSACTION.SUCCESS
public string Event_type { get; set; }
/// 通知数据类型 通知的资源数据类型,支付成功通知为encrypt-resource
/// 示例值:encrypt-resource
public string Resource_type { get; set; }
/// 通知数据 通知资源数据
/// json格式,见示例
public Resource Resource { get; set; }
/// 回调摘要
/// 示例值:支付成功

public string Summary { get; set; }
}

//回报文model

public class PayCipherText
{
public string out_trade_no { get; set; }
public string transaction_id { get; set; }
public string trade_type { get; set; }
public string trade_state { get; set; }
public string trade_state_desc { get; set; }
public string bank_type { get; set; }
public string success_time { get; set; }
public WxPayAmountModel amount { get; set; }
}

这里用到证书和回调报文解密,参考https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi

posted @ 2022-03-31 18:12  小丑不戴面具  阅读(505)  评论(0编辑  收藏  举报