.net core 微信支付-----下单
支付这一块也算是项目开发常遇到的功能,尤其是电商这块,支付必不可少,而现在常用的两大支付:1,微信支付 2 ,支付宝支付。但是我这里我要说的就是这个坑爹的微信支付,因为整体来说他没有支付宝支付显得友好,为什么呢?因为他没有测试环境,如果要开发这块必须要在实际环境中进行,真他妈操蛋!而这一块支付宝就比较友好了,他给开发者提供了沙箱测试环境,是真的不错,至于怎末操作,这里就不多说了,后续会详细介绍支付宝的沙箱支付。
按理说在实际环境下测试开发也没啥问题,但是对于好多想学微信支付的同僚们来说就显得不友好了,因为不是所有的软件都会包含支付功能这一块的,有的可能做了几年软件开发都没有接触到支付这一块,别不相信,这种情况普遍存在。所以呀,微信支付这一块官网还是得上点心,搞个测试环境,照顾照顾开发者。哈哈哈哈
微信支付主要是商家申请账号的,要到微信商户平台上去注册账号,还有申请一个微信公众号平台账号。至于这些怎末操作,这里就不多说了,自己看官网,讲的还是比较细致的。看一下就了解了。
微信支付有很多中类型,这里就说native支付的方式:
我们看一下Native支付,官方给的接口:
本次就先看一下Native下单和支付成功之后的回调。
下面我们就根据官方的文档一步一步操作:
首先看接口:
请求示例:
返回参数:
从上面由请求接口,请求参数,返回参数,我们可以根据官方要求去做:
微信支付需要的基本配置信息:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace WeChat.Api.Common {
注意:appi appSecret是申请微信公众号平台获取的 后面的几个是微信商户平台获取的。 public class WeChatConfig { /// <summary> /// 直连商户申请的公众号或移动应用appid。 /// </summary> public static string appid => ""; /// <summary> /// AppSecret,app端加密解密使用 /// </summary> public static string AppSecret => ""; /// <summary> /// 密钥,用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key; /// </summary> public static string APIV3Key => ""; /// <summary> /// 直连商户的商户号,由微信支付生成并下发。 /// </summary> public static string mchid => ""; /// <summary> /// 证书序列号 /// 查看证书序列号:https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan#ru-he-cha-kan-zheng-shu-xu-lie-hao /// </summary> public static string serialNo => ""; /// <summary> /// key 私钥 /// 私钥和证书:https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu#sheng-ming-suo-shi-yong-de-zheng-shu /// </summary> public static string privateKey => FileHelper.ReadFile(Directory.GetCurrentDirectory()+"/UpFile/WeChatKey/key.txt", "utf-8"); } }
远程请求微信接口的通用类:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; namespace WeChat.Api.Common { public class HttpHandler : DelegatingHandler { public HttpHandler() { InnerHandler = new HttpClientHandler(); } protected async override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var auth = await BuildAuthAsync(request); string value = $"WECHATPAY2-SHA256-RSA2048 {auth}"; request.Headers.Add("Authorization", value); request.Headers.Add("Accept", "application/json"); request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); return await base.SendAsync(request, cancellationToken); } protected async Task<string> BuildAuthAsync(HttpRequestMessage request) { string method = request.Method.ToString(); string body = ""; if (method == "POST" || method == "PUT" || method == "PATCH") { var content = request.Content; body = await content.ReadAsStringAsync(); } string uri = request.RequestUri.PathAndQuery; var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); string nonce = Path.GetRandomFileName(); string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n"; string signature = Sign(message); return $"mchid=\"{WeChatConfig.mchid}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{WeChatConfig.serialNo}\",signature=\"{signature}\""; } protected string Sign(string message) { // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- // 亦不包括结尾的-----END PRIVATE KEY----- string privateKey = WeChatConfig.privateKey; byte[] keyData = Convert.FromBase64String(privateKey); var rsa = RSA.Create(); rsa.ImportPkcs8PrivateKey(keyData, out _); byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } } }
请求参数的Model:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace WeChat.Api.Model.CreateOrder { public class PayOrder { /// <summary> /// 直连商户申请的公众号或移动应用appid。 /// </summary> public string appid { set; get; } /// <summary> /// 直连商户的商户号,由微信支付生成并下发。 /// </summary> public string mchid { set; get; } /// <summary> /// 商品描述 /// </summary> public string description { set; get; } /// <summary> /// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 /// </summary> public string out_trade_no { set; get; } /// <summary> /// 订单支付回调接口 /// </summary> public string notify_url { set; get; } /// <summary> /// 订单金额信息 /// </summary> public Amount amount { set; get; } } /// <summary> /// 微信支付金额实体 /// </summary> public class Amount { /// <summary> /// 订单总金额,单位为分。 /// </summary> public int total { set; get; } /// <summary> /// 货币类型,CNY:人民币,境内商户号仅支持人民币。 /// </summary> public string currency { set; get; } = "CNY"; } }
返回参数Model:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace WeChat.Api.Model.CreateOrder { public class ReturnPayOrder { /// <summary> /// 生成二维码的链接 /// </summary> public string code_url { get; set; } } }
下单:
/// <summary> /// 统一下单接口 /// </summary> /// <param name="total">支付金额</param> /// <param name="OrderId">订单ID</param> /// <param name="pixel">像素大小</param> [HttpPost("PayOrder")] public async Task<ReturnPayOrder> PayOrder(int total,string OrderId) { var orderNumber = $"{DateTime.Now:yyyyMMddHHmmssff}{CodeHelper.CreateNumCode(3)}"; HttpClient client = new HttpClient(new HttpHandler()); var req = new PayOrder { appid = WeChatConfig.appid, mchid =WeChatConfig.mchid, description = "nihao", out_trade_no = orderNumber, notify_url = "http://...../api/wechat/WxPayCallback",//这个是支付成功后的回调方法 amount = new Amount { total = total, currency = "CNY" } }; var bodyJson = new StringContent(req.ToJson(), Encoding.UTF8, "application/json"); var resp = await client.PostAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/native", bodyJson); // 注意!!! 这个resp只是http的结果,需要把接口具体返回的值读取出来,如果接口报错的话,这地方可以看到具体的错误信息,我就是在这里入坑的。 var respStr = await resp.Content.ReadAsStringAsync(); var respos = respStr.ToObject<ReturnPayOrder>(); return respos; }
根据获取的回调的url动态生成支付二维码:
/// <summary> /// 动态生成二维码(http://) /// </summary> /// <param name="url">下载路径</param> /// <param name="pixel">像素大小</param> [HttpGet("GetQRCode")] public void GetQRCode(string url, int pixel) { Response.ContentType = "image/jpeg"; var bitmap = _qRCode.GetQRCode(url, pixel); MemoryStream ms = new MemoryStream(); bitmap.Save(ms, ImageFormat.Jpeg); Response.Body.WriteAsync(ms.GetBuffer(), 0, Convert.ToInt32(ms.Length)); Response.Body.Close(); }
支付成功回调方法:
/// <summary> /// 微信支付成功结果回调接口 /// </summary> /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。</returns> [HttpPost("WxPayCallback")] public async Task<ReturnNotifyModel> WxPayCallback() { FileHelper.AddLog("进入", "weixin1"); FileHelper.AddLog(HttpContext.Request.Path.ToString(), "weixin1"); try { using (StreamReader sr = new StreamReader(Request.Body, Encoding.UTF8)) { string strContent = sr.ReadToEndAsync().Result; FileHelper.AddLog(strContent, "weixin1"); var wxPayNotifyModel = strContent.ToObject<WxPayNotifyModel>(); var decryptStr = AesGcmHelper.AesGcmDecrypt(wxPayNotifyModel.resource.associated_data, wxPayNotifyModel.resource.nonce, wxPayNotifyModel.resource.ciphertext, "89chulmpeQlk568752345UIcd891512a"); return decryptStr.ToString().ToObject<ReturnNotifyModel>(); } } catch (Exception e) { FileHelper.AddLog("读取异常", "weixin1"); throw; }
将这个api发布到服务器中,注意:回调方法一定是可以通过外网访问的,并且还要在微信商户平台中添加这个访问地址,不然回调会失败!
测试下单接口:
然后通过code_url动态生成支付二维码:
然后将支付成功的回调方法发布到服务器上:
然后,通过api接口生成的二维码模拟前端扫码支付:
后台接口回调日志:
ciphertext字段解密后的数据:
源码:链接: https://pan.baidu.com/s/17QaZnIRrDCUoO2x4RvzhIA 提取码: zb44
微信支付这块其实gitee上有好多不错的开源的代码,比如paylink ,盛派,人家已经封装的很好了,但是对于初学者来说并不友好,还是自己根据官网一步一步的来清晰,不然过程云里雾里的。所以我做微信支付都是自己根据官网然后借鉴网上的一些理论代码,一步一步来实现的,这样自己对整个过程就比较清楚,后续自己也可以封装一下。