jjzjj

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

随遇而安 2023-03-28 原文

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

 

有关微信支付之JSAPI支付(微信小程序和微信公众号支付都可以采用该方式,后端使用.Net6)的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  3. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  4. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  5. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  6. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  7. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  8. .net - .NET 将如何影响 Python 和 Ruby 应用程序? - 2

    我很好奇.NET将如何影响Python和Ruby应用程序。用IronPython/IronRuby编写的应用程序是否会非常特定于.NET环境,以至于它们实际上将变得特定于平台?如果他们不使用任何.NET功能,那么IronPython/IronRuby相对于非.NET同类产品的优势是什么? 最佳答案 我不能说任何关于IronRuby的东西,但是大多数Python实现(如IronPython、Jython和PyPy)都试图尽可能忠实于CPython实现。不过,IronPython正在迅速成为这方面的佼佼者之一,并且在PlanetPyth

  9. ruby-on-rails - 与 ActiveMerchant 一起使用的最佳支付网关是什么? - 2

    我需要使用ActiveMerchant库在我们的一个Rails应用程序中设置支付解决方案。尽管这个问题非常主观,但人们对主要网关(BrainTree、Authorize.net等)的体验如何?它必须:处理定期付款。有能力记入个人帐户。能够取消付款。有办法存储用户的付款详细信息(例如Authotize.netsCIM)。干杯 最佳答案 ActiveMerchant很棒,但在过去一年左右的时间里,我在使用它时发现了一些问题。首先,虽然某些网关可能会得到“支持”——但并非所有功能都包含在内。查看功能矩阵以确保完全支持您选择的网关-http

  10. ruby - 如何使用 Ruby HTTP::Net 处理 404 错误? - 2

    我正在尝试解析网页,但有时会收到404错误。这是我用来获取网页的代码:result=Net::HTTP::getURI.parse(URI.escape(url))如何测试result是否为404错误代码? 最佳答案 像这样重写你的代码:uri=URI.parse(url)result=Net::HTTP.start(uri.host,uri.port){|http|http.get(uri.path)}putsresult.codeputsresult.body这将打印状态码和正文。

随机推荐