zl程序教程

您现在的位置是:首页 >  APP

当前栏目

微信支付之JSAPI支付(微信小程序和微信公众号支付都可以采用该方式,后端使用.Net6)

2023-03-31 10:55:49 时间

本实例使用了工具包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_certapiclient_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_certapiclient_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;
        }