asp .net framework 微信支付

本文详细介绍了如何在.NET环境中实现微信支付的功能,包括初始化支付参数、使用HttpHandler处理签名、统一下单接口、查询订单、关闭订单和退款操作。同时,文中还涉及到回调通知的处理方法,确保支付流程的安全和正确性。
摘要由CSDN通过智能技术生成

前言

首先要开通小程序的支付能力(https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml),按要求提交审核材料,审核通过后,我们可以得到APPID、微信支付商户号mch_id、API密钥key、Appsecret。之后在商户后台绑定同一主体的APPID并授权,发起授权后,商户需要自行前往对应平台确认授权申请。最后在商户后台设置回调地址。

一、HttpHandler

public class HttpHandler : DelegatingHandler
    {
        private readonly string merchantId;
        private readonly string serialNo;

        public HttpHandler(string merchantId, string merchantSerialNo)
        {
            InnerHandler = new HttpClientHandler();

            this.merchantId = merchantId;
            this.serialNo = merchantSerialNo;
        }

        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("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36");
            request.Headers.Add("Accept", "application/json");
            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;
            DateTime DateStart = new DateTime(1970, 1, 1, 8, 0, 0);
            var timestamp = Convert.ToInt32((DateTime.Now - DateStart).TotalSeconds);
            string nonce = Path.GetRandomFileName();

            string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
            string signature = Sign(message);
            return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
        }
        protected string Sign(string message)
        {
            string privateKey = File.ReadAllText(@"" + PathHelper.AuthPath + this.merchantId.Trim() + ".txt", Encoding.UTF8).ToString().Trim();
            byte[] bt = Encoding.UTF8.GetBytes(message);
            var sha256 = new SHA256CryptoServiceProvider();
            byte[] rgbHash = sha256.ComputeHash(bt);

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            var _privateKey = RSAPrivateKeyJava2DotNet(privateKey);
            key.FromXmlString(_privateKey);
            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            formatter.SetHashAlgorithm("SHA256");//此处是你需要加签的hash算法,需要和上边你计算的hash值的算法一致,不然会报错。
            byte[] inArray = formatter.CreateSignature(rgbHash);
            var signature = Convert.ToBase64String(inArray);
            return signature;
        }
        public static string RSAPrivateKeyJava2DotNet(string privateKey)
        {
            var privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
            return
                string.Format(
                    "<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                    Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
        }
    }

二、jsapi统一下单

[HttpPost]
        [Route("jsapi")]
        public async Task<dynamic> JsapiPay(dynamic pay)
        {
            //string pay1 = "{\"appid\":\"\",\"mchid\":\"\",\"description\":\"\",\"out_trade_no\":\"\",\"notify_url\":\"\",\"amount\":{\"total\":,\"currency\":\"CNY\",},\"payer\":{\"openid\":\"\"}}\"";;
            var payPara = JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(pay));
            MerchantModel merchantModel = MerchantBLL.GetMerchant(null, null, null, Convert.ToString(payPara.mchid));
            var httpHandler = new HttpHandler(merchantModel.merchantNum.Trim(), merchantModel.merchantAuthNum.Trim());
            HttpClient client = new HttpClient(httpHandler);
            var bodyJson = new StringContent(JsonConvert.SerializeObject(pay), Encoding.UTF8, "application/json");
            var resp2 = await client.PostAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", bodyJson);
            var resp3 = await resp2.Content.ReadAsStringAsync();

            DateTime DateStart = new DateTime(1970, 1, 1, 8, 0, 0);
            var timestamp = Convert.ToInt64((DateTime.Now - DateStart).TotalSeconds).ToString();
            var nonceStr = Path.GetRandomFileName();
            string message = $"{payPara.appid}\n{timestamp}\n{nonceStr}\nprepay_id={JsonHelper.GetJsonValue(resp3, "prepay_id")}\n";
            string privateKey = File.ReadAllText(@"" + PathHelper.AuthPath + merchantModel.merchantNum.Trim() + ".txt", Encoding.UTF8).ToString().Trim();
            byte[] bt = Encoding.UTF8.GetBytes(message);
            var sha256 = new SHA256CryptoServiceProvider();
            byte[] rgbHash = sha256.ComputeHash(bt);
            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            var _privateKey = RSAPrivateKeyJava2DotNet(privateKey);
            key.FromXmlString(_privateKey);
            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            formatter.SetHashAlgorithm("SHA256");//此处是你需要加签的hash算法,需要和上边你计算的hash值的算法一致,不然会报错。
            byte[] inArray = formatter.CreateSignature(rgbHash);
            var signature = Convert.ToBase64String(inArray);

            return "{\"signature\":\""+ signature + "\",\"timestamp\":\"" + timestamp + "\",\"nonceStr\":\"" + nonceStr + "\",\"prepay_id\":\"" + JsonHelper.GetJsonValue(resp3, "prepay_id") + "\"}";
        }

二、查询订单

[HttpGet]
        [Route("get")]
        public async Task<dynamic> GetPay(string mchid, string transaction_id = null, string out_trade_no = null)
        {
            MerchantModel merchantModel = MerchantBLL.GetMerchant(null, null, null, Convert.ToString(mchid));
            HttpClient client = new HttpClient(new HttpHandler(merchantModel.merchantNum.Trim(), merchantModel.merchantAuthNum.Trim()));
            if (transaction_id != null)
            {
                //微信支付订单号查询
                var res1 = await client.GetAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/id/" + transaction_id + "?mchid=" + mchid);
                return res1;
            }
            else
            {
                //商户订单号查询
                var res2 = await client.GetAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no + "?mchid=" + mchid);
                return res2;
            }
        }

三、关闭订单

 [HttpPost]
        [Route("close")]
        public async Task<dynamic> ClosePay(dynamic pay, string out_trade_no)
        {
            //1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
            //2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
            var payPara = JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(pay));
            MerchantModel merchantModel = MerchantBLL.GetMerchant(null, null, null, Convert.ToString(payPara.mchid));
            HttpClient client = new HttpClient(new HttpHandler(merchantModel.merchantNum.Trim(), merchantModel.merchantAuthNum.Trim()));
            var bodyJson = new StringContent(JsonConvert.SerializeObject(pay), Encoding.UTF8, "application/json");
            var res = await client.PostAsync("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no + "/close", bodyJson);
            return res;//无数据(Http状态码为204)
        }

四、退款

[HttpPost]
        [Route("refunds")]
        public async Task<dynamic> RefundsPay(dynamic pay)
        {
            var payPara = JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(pay));
            MerchantModel merchantModel = MerchantBLL.GetMerchant(null, null, null, Convert.ToString(payPara.mchid));
            HttpClient client = new HttpClient(new HttpHandler(merchantModel.merchantNum.Trim(), merchantModel.merchantAuthNum.Trim()));
            var bodyJson = new StringContent(JsonConvert.SerializeObject(pay), Encoding.UTF8, "application/json");
            var res = await client.PostAsync("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds", bodyJson);
            return res;
        }

五、通知回调(注意解析参数格式)

[HttpPost]
        [Route("notify")]
        public dynamic NotifyPay()
        {
            HttpRequest httpRequest = HttpContext.Current.Request;
            string id = httpRequest.QueryString["id"].ToString().Trim();
            string create_time = httpRequest.QueryString["return_msg"].ToString().Trim();
            string resource_type = httpRequest.QueryString["resource_type"].ToString().Trim();
            string event_type = httpRequest.QueryString["event_type"].ToString().Trim();
            string summary = httpRequest.QueryString["summary"].ToString().Trim();
            string resource = httpRequest.QueryString["resource"].ToString().Trim();
            File.AppendAllText(@"" + PathHelper.LogPath + DateTime.Now.ToString("yyyyMMdd") + ".txt", DateTime.Now + " " + "id:" + id + ",create_time:" + create_time + ",resource_type:" + resource_type + ",event_type:" + event_type + ",summary:" + summary + ",resource:" + resource + "\n", Encoding.UTF8);
            if (summary == "支付成功")
            {
                return "{\"code\":200,\"message\":\"SUCCESS\"}";
            }
            else
            {
                return "{\"code\":200,\"message\":\"支付失败\"}";
            }
        }

注意:完全参考微信支付文档仅使用.net 7

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

策码奔腾R

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值