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