微信支付之JSAPI支付(微信小程序和微信公众号支付都可以采用该方式,后端使用.Net6)
本实例使用了工具包SKIT.FlurlHttpClient.Wechat.TenpayV3(github:https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat)
示例中的_repositoryWrapper的相关使用是我们业务中的业务代码,使用者可以根据自己的业务自行删除。
1、生成预支付订单(供前端调用,返回的信息供小程序端或公众号端调起微信支付).
public async Task<PayTransactionDto> GeneratePrePaidOrder(PrePaidOrderRequestDto orderBasic) { string orderStatus = await _repositoryWrapper.OrderRepository.QueryOrderStatusAsync(orderBasic.OrderId); //订单已取消||订单已支付 if (orderStatus == OrderStatus.Cancel.ToString("D") || orderStatus == OrderStatus.PaySuccess.ToString("D")) { PayTransactionDto payTransaction = new() { OrderStatus = orderStatus, }; return payTransaction; } //string serialNumber = RSAUtility.ExportSerialNumber(@"D:\1630126864_20220905_cert\apiclient_cert.pem"); var manager = new InMemoryCertificateManager(); var options = new WechatTenpayClientOptions() { MerchantId = _config["MerchantId"],//商户号 MerchantV3Secret = _config["MerchantV3Secret"],//商户API v3密钥 MerchantCertificateSerialNumber = _config["MerchantCertificateSerialNumber"],//商户API证书序列号 MerchantCertificatePrivateKey = FileContentHelper.ReadFileContent("apiclient_key.pem", Path.Combine(AppContext.BaseDirectory, "apiclient_key.pem")),//商户API证书私钥 PlatformCertificateManager = manager // 证书管理器的具体用法请参阅下文的高级技巧与加密、验签有关的章节 }; var client = new WechatTenpayClient(options); /* 以 JSAPI 统一下单接口为例 */ //var userLogin = await _userService.UserLogin(orderBasic.JSCode); Log.Information("OpenId is " + orderBasic.OpenId); Log.Information("OrderId is " + orderBasic.OrderId); IEnumerable<OrderDetailEntity> orderList = await _repositoryWrapper.OrderRepository.GetOrderPriceAndQty(orderBasic.OrderId); int total = 0; foreach (OrderDetailEntity orderDetail in orderList) { total += orderDetail.TicketTypePrice * 100 * orderDetail.Qty; } total = (int)(total - orderBasic.UseWalletAmount * 100); long orderNumber = await _repositoryWrapper.OrderRepository.QueryOrderNumberByOrderIdAsync(orderBasic.OrderId); var request = new CreatePayTransactionJsapiRequest() { OutTradeNumber = orderNumber.ToString(), AppId = _config["EmscnplAppId"],//微信 AppId Description = $"订单号为{orderNumber}", ExpireTime = DateTimeOffset.Now.AddSeconds(200), NotifyUrl = _config["wechatPayNotifyUrl"],//回调地址 Amount = new() { Total = total //Total = 1 }, Payer = new() { OpenId = orderBasic.OpenId //OpenId = "oLS5G5C9C2KZuYo-Y9HhyyP-RiFs" }, Attachment = orderBasic.UseWalletAmount.ToString(), }; //var response = await client.ExecuteCreatePayTransactionH5Async(request); var response = await client.ExecuteCreatePayTransactionJsapiAsync(request); Log.Information("response ExecuteCreatePayTransactionJsapiAsync {@response}", response); if (response.IsSuccessful()) { //Console.WriteLine("PrepayId:" + response.PrepayId); //var collection = ExtractQueryParams(response.H5Url); //var prepayId = collection["prepay_id"]; //var package= collection["package"]; var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId); Log.Information("response paramMap {@paramMap}", paramMap); PayTransactionDto payTransaction = new() { WechatpayNonce = paramMap["nonceStr"], WechatpaySignature = paramMap["paySign"], WeChatPrepayId = response.PrepayId, TimeStamp = paramMap["timeStamp"], SignType = paramMap["signType"], Package = paramMap["package"], OrderStatus = orderStatus, }; Log.Information("payTransaction information {@payTransaction}", payTransaction); await _repositoryWrapper.OrderRepository.UpdateOrderStatusAsync(new Contract.OrderStatusDto { OrderId = orderBasic.OrderId, OrderStatus = OrderStatus.PrePay.ToString("D") }); await _repositoryWrapper.RedPackageRepository.BatchUpdateRedPackeStatus(orderBasic.VoucherId, orderBasic.WeChatId, orderBasic.OrderId, RedpackageUseEnum.Lock); await _repositoryWrapper.OrderRepository.BindWechatId(orderBasic.OrderId, orderBasic.WeChatId); return payTransaction; } else { throw new Exception("ExecuteCreatePayTransactionJsapiAsync call return fail"); } }
2、支付完成后的回调方法处理
Controller方法:
/// <summary> /// 支付成功后的回调函数 /// </summary> /// <param name="timestamp"></param> /// <param name="nonce"></param> /// <param name="signature"></param> /// <param name="serialNumber"></param> /// <returns></returns> [HttpPost("WeChatPayNotifyUrl", Name = "WeChatPayNotifyUrl")] public async Task WeChatPayNotifyUrl( [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp, [FromHeader(Name = "Wechatpay-Nonce")] string nonce, [FromHeader(Name = "Wechatpay-Signature")] string signature, [FromHeader(Name = "Wechatpay-Serial")] string serialNumber) { // 接收服务器推送 // 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml using var reader = new StreamReader(Request.Body, Encoding.UTF8); string content = await reader.ReadToEndAsync(); Log.Information("Wechatpay-Timestamp data is {@content}", content); Log.Information("Wechatpay-Nonce {@nonce}", nonce); Log.Information("Wechatpay-Signature {@signature}", signature); Log.Information("Wechatpay-Serial {@serialNumber}", serialNumber); _weChatAppService.ParseNotifyData(timestamp, nonce, content, signature, serialNumber); }
Service方法:
public async void ParseNotifyData(string timeStamp, string nonce, string content, string signature, string serialNumber) { var manager = new InMemoryCertificateManager(); var options = new WechatTenpayClientOptions() { MerchantId = _config["MerchantId"],//商户号 MerchantV3Secret = _config["MerchantV3Secret"],//商户API v3密钥 MerchantCertificateSerialNumber = _config["MerchantCertificateSerialNumber"],//商户API证书序列号 MerchantCertificatePrivateKey = FileContentHelper.ReadFileContent("apiclient_key.pem", Path.Combine(AppContext.BaseDirectory, "apiclient_key.pem")),//商户API证书私钥 PlatformCertificateManager = manager // 证书管理器的具体用法请参阅下文的高级技巧与加密、验签有关的章节 }; var client = new WechatTenpayClient(options); var request = new QueryCertificatesRequest(); var response = await client.ExecuteQueryCertificatesAsync(request); if (response.IsSuccessful()) { response = client.DecryptResponseSensitiveProperty(response); foreach (var certificateModel in response.CertificateList) { manager.AddEntry(new CertificateEntry(certificateModel)); } Log.Information("查询微信商户平台证书成功。"); } bool valid = client.VerifyEventSignature(timeStamp, nonce, content, signature, serialNumber, out Exception? error); if (valid) { /* 将 JSON 反序列化得到通知对象 */ /* 你也可以将 WechatTenpayEvent 类型直接绑定到 MVC 模型上,这样就不再需要手动反序列化 */ var callbackModel = client.DeserializeEvent(content); if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType)) { /* 根据事件类型,解密得到支付通知敏感数据 */ var callbackResource = client.DecryptEventResource<TransactionResource>(callbackModel); string outTradeNumber = callbackResource.OutTradeNumber; string transactionId = callbackResource.TransactionId; Log.Information("回调返回的解密数据为{@callbackResource}", callbackResource); Console.WriteLine("订单 {0} 已完成支付,交易单号为 {1}", outTradeNumber, transactionId); Log.Information("outTradeNumber is " + outTradeNumber); Log.Information("outTradeNumber is " + transactionId); #region[存取支付结果] string boxCode = await GetBoxCodeByOrderNumber(Convert.ToInt64(outTradeNumber)); //解绑机器; await _repositoryWrapper.UserBoxRepository.UnBindBox(boxCode); var orderId = await _repositoryWrapper.OrderRepository.QueryOrderIdByOrderNumberAsync(Convert.ToInt64(outTradeNumber)); Log.Information("Update order pay result"); await _repositoryWrapper.OrderRepository.SavePayResult(new OrderEntity() { OrderId = orderId, OutTradeNo = Int64.Parse(outTradeNumber), TradeState = callbackResource.TradeState, TradeStateDesc = callbackResource.TradeStateDescription, BankType = callbackResource.BankType, Total = callbackResource.Amount.Total, OpenId = callbackResource.Payer.OpenId, PayTotal = callbackResource.Amount.PayerTotal, TransactionId = callbackResource.TransactionId, SuccessTime = callbackResource.SuccessTime.ToString() }); #endregion Log.Information("Update order pay status"); if (!String.IsNullOrWhiteSpace(callbackResource.Attachment)) { decimal walletAmount = 0m; if (Decimal.TryParse(callbackResource.Attachment, out walletAmount) && walletAmount > 0) { string weChatId = await _repositoryWrapper.OrderRepository.QueryWeChatIdByOrderId(orderId); await _repositoryWrapper.WalletRepository.PayUseWallet(new List<long>(), orderId, weChatId, walletAmount, false, true); } } await _repositoryWrapper.OrderRepository.UpdateOrderStatusByOrderNumberAsync(Convert.ToInt64(outTradeNumber), OrderStatus.PaySuccess.ToString("D"), (decimal)callbackResource.Amount.PayerTotal / 100); await _repositoryWrapper.RedPackageRepository.BatchUpdateRedPackeStatus(orderId, RedpackageUseEnum.Used); } else { /* 根据事件类型,解密得到支付通知敏感数据 */ var callbackResource = client.DecryptEventResource<TransactionResource>(callbackModel); string outTradeNumber = callbackResource.OutTradeNumber; JObject obj = new(); obj.Add("action", "payFail"); var payFailStr = JsonConvert.SerializeObject(obj); await _repositoryWrapper.OrderRepository.UpdateOrderStatusByOrderNumberAsync(Convert.ToInt64(outTradeNumber), OrderStatus.PayFail.ToString("D"), (decimal)callbackResource.Amount.PayerTotal / 100); } } else { Log.Error("Verify fail"); Log.Error("Verify fail is {@error}", error); } }
3、查询支付结果
_orderService是我们业务中使用的service,使用方可自行根据自身业务删除。
public async Task<PayResultDto> QueryOrderPayStatus(OrderBaseDto orderBasic) { PayResultDto payResultDto = new PayResultDto(); string orderStatus = await _repositoryWrapper.OrderRepository.QueryOrderStatusAsync(orderBasic.OrderId); if (orderStatus != null) { OrderStatus orderEnumStatus = OrderStatus.UnDefine; if (System.Enum.TryParse(orderStatus, out orderEnumStatus) && orderEnumStatus == OrderStatus.PaySuccess) { payResultDto.PaySuccess = true; payResultDto.ContributionAmount = await _orderService.GetOrderContributionAsync(orderBasic.OrderId); ; return payResultDto; } } //string serialNumber = RSAUtility.ExportSerialNumber(@"D:\1630126864_20220905_cert\apiclient_cert.pem"); var manager = new InMemoryCertificateManager(); var options = new WechatTenpayClientOptions() { MerchantId = _config["MerchantId"],//商户号 MerchantV3Secret = _config["MerchantV3Secret"],//商户API v3密钥 MerchantCertificateSerialNumber = _config["MerchantCertificateSerialNumber"],//商户API证书序列号 MerchantCertificatePrivateKey = FileContentHelper.ReadFileContent("apiclient_key.pem", Path.Combine(AppContext.BaseDirectory, "apiclient_key.pem")),//商户API证书私钥 PlatformCertificateManager = manager // 证书管理器的具体用法请参阅下文的高级技巧与加密、验签有关的章节 }; var client = new WechatTenpayClient(options); /* 以 JSAPI 统一下单接口为例 */ //var userLogin = await _userService.UserLogin(orderBasic.JSCode); Log.Information("OrderId is " + orderBasic.OrderId); long orderNumber = await _repositoryWrapper.OrderRepository.QueryOrderNumberByOrderIdAsync(orderBasic.OrderId); var request = new GetPayTransactionByOutTradeNumberRequest() { OutTradeNumber = orderNumber.ToString(), MerchantId = _config["MerchantId"],//商户号 WechatpayCertificateSerialNumber = _config["MerchantCertificateSerialNumber"]//商户API证书序列号 }; var response = await client.ExecuteGetPayTransactionByOutTradeNumberAsync(request); Log.Information("response {@response}", response); if (response.IsSuccessful() && response.TradeState == "SUCCESS") { int payTotal = response.Amount.Total; Console.WriteLine("pay amount:" + payTotal); Log.Information($"QueryOrder order {orderNumber} payTotal is {payTotal}"); if (payTotal > 0) { await _repositoryWrapper.OrderRepository.UpdateOrderStatusAsync(new OrderStatusDto() { OrderId = orderBasic.OrderId, OrderStatus = OrderStatus.PaySuccess.ToString("D"), }); payResultDto.PaySuccess = true; payResultDto.ContributionAmount = await _orderService.GetOrderContributionAsync(orderBasic.OrderId); //payResultDto.Amount = payTotal/100m; } } else { Log.Information($"response.RawStatus is {response.RawStatus}"); Log.Information($"response.ErrorCode is {response.ErrorCode},response.ErrorMessage is {response.ErrorMessage}"); //throw new Exception($"QueryOrder call return fail,orderNumber is {orderNumber}"); } return payResultDto; }
作者:tuohaibei
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利.
如果您觉得文章对您有帮助,可以点击文章右下角"推荐".您的鼓励是作者坚持原创和持续写作的最大动力!