Net8 微信支付分相关接口(创建支付分订单,取消支付分订单,完结支付分订单,退款,支付,及对应的回调,验签(暂时没写))

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

我R它Ma的微信官方及微信官方文档撰写者,LJ腾讯从来不提供C#的SDK

R它奶奶的,算了,贴代码吧,搞了一天,弄了个支付分订单创建,不过这玩意是一通百通

,为了广大Net/NetCore开发者方便调用,奉献上简单的封装

 类1:相关DTO

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace swapExternal.WeScore
{
    public class WeScoreCreateOrderResponse
    {
        public string appid { get; set; }
        public string attach { get; set; }
        public string mchid { get; set; }
        public object location { get; set; }
        public string notify_url { get; set; }
        public string order_id { get; set; }
        public string out_order_no { get; set; }
        public string package { get; set; }
        public object post_discounts { get; set; }
        public object post_payments { get; set; }
        public risk_fundDto risk_fund { get; set; }
        public string service_id { get; set; }
        public string service_introduction { get; set; }
        public string state { get; set; }
        public time_rangeDto time_range { get; set; }
      
    }

    public class risk_fundDto
    {
        public int amount { get; set; }
        public string description { get; set; }
        public string name { get; set; }
    }
    public class time_rangeDto
    {
        public string start_time { get; set; }
        public string end_time { get; set; } 
        public string end_time_remark { get; set; }
    }


    /// <summary>
    /// 微信分确认订单回调对象
    /// </summary>
    public class WechatScoreConfigmOrderBackDto
    {
        public string id { get; set; }
        public string create_time { get; set; }
        public string resource_type { get; set; }
        public string event_type { get; set; }
        public string summary { get; set; }
        public WxPayResourceModel resource { get; set; }
    }
 

    public class WeScoreAesGcmDecryptDto
    {
        public string appid { get; set; }
        public string mchid { get; set; }
        public string out_order_no { get; set; }
        public string service_id { get; set; }
        public string openid { get; set; }
        public string state { get; set; }//DOING:服务订单进行中
        public string state_description { get; set; }
        public long? total_amount { get; set; }
        public string service_introduction { get; set; }
        public object post_payments { get; set; }//数组 参考:https://pay.weixin.qq.com/doc/v3/merchant/4012587953
        public object post_discounts { get; set; }//数组 参考:https://pay.weixin.qq.com/doc/v3/merchant/4012587953
        public string risk_fund { get; set; }
        public string time_range { get; set; }
        public object location { get; set; }
        public string attach { get; set; }
        public string order_id { get; set; }
        public bool need_collection { get; set; }
    }

    public class post_paymentsDto
    {
        public string name { get; set; }
        public string description { get; set; }
    }


    /// <summary>
    /// 微信支付结果回调通知实体
    /// </summary>
    public class WxPayNotifyModel
    {
        /// <summary>
        /// 通知的唯一ID
        /// </summary>
        public string id { set; get; }

        /// <summary>
        /// 通知创建时间,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒。
        /// </summary>
        public string create_time { set; get; }

        /// <summary>
        /// 通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS
        /// </summary>
        public string event_type { set; get; }

        /// <summary>
        /// 通知的资源数据类型,支付成功通知为encrypt-resource
        /// </summary>
        public string resource_type { set; get; }

        /// <summary>
        /// 通知资源数据,json格式
        /// </summary>
        public WxPayResourceModel resource { set; get; }

        /// <summary>
        /// 回调摘要
        /// </summary>
        public string summary { set; get; }
    }

    /// <summary>
    /// 微信支付回调通知结果resource实体
    /// </summary>
    public class WxPayResourceModel
    {
        /// <summary>
        /// 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM
        /// </summary>
        public string algorithm { set; get; }

        /// <summary>
        /// Base64编码后的开启/停用结果数据密文
        /// </summary>
        public string ciphertext { set; get; }

        /// <summary>
        /// 附加数据
        /// </summary>
        public string associated_data { set; get; }

        /// <summary>
        /// 原始回调类型,为transaction
        /// </summary>
        public string original_type { set; get; }

        /// <summary>
        /// 加密使用的随机串
        /// </summary>
        public string nonce { set; get; }
    }

    /// <summary>
    /// 微信支付回调通知结果解密实体
    /// </summary>
    public class WxPayResourceDecryptModel
    {
        /// <summary>
        /// 直连商户申请的公众号或移动应用appid
        /// </summary>
        public string appid { set; get; }

        /// <summary>
        /// 商户的商户号,由微信支付生成并下发。
        /// </summary>
        public string mchid { set; get; }

        /// <summary>
        /// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。特殊规则:最小字符长度为6
        /// </summary>
        public string out_trade_no { set; get; }

        /// <summary>
        /// 微信支付系统生成的订单号。
        /// </summary>
        public string transaction_id { set; get; }

        /// <summary>
        /// 交易状态,枚举值:
        /// SUCCESS:支付成功
        /// REFUND:转入退款
        /// NOTPAY:未支付
        /// CLOSED:已关闭
        /// REVOKED:已撤销(付款码支付)
        /// USERPAYING:用户支付中(付款码支付)
        /// PAYERROR:支付失败(其他原因,如银行返回失败)
        /// ACCEPT:已接收,等待扣款
        /// </summary>
        public string trade_state { get; set; }

        /// <summary>
        /// 交易状态描述
        /// </summary>
        public string trade_state_desc { get; set; }

        /// <summary>
        /// 支付者信息
        /// </summary>
        public WxPayerResourceDecryptModel payer { set; get; }

    }

    /// <summary>
    /// 支付用户信息实体
    /// </summary>
    public class WxPayerResourceDecryptModel
    {
        /// <summary>
        /// 用户在直连商户appid下的唯一标识。
        /// </summary>
        public string openid { get; set; }
    }


    public class RefundRequestModel
    {
        public RefundRequestModel()
        {
            this.notify_url = WeScoreHelper.RefundUrl;
        }
        public string transaction_id { get; set; }
        public string out_refund_no { get; set; }
        public string notify_url { get; set; }
    }

    public class RefundRequestAmountModel
    {
        public RefundRequestAmountModel()
        {
            this.currency = "CNY";
        }
        public long refund { get; set; }//申请退款时传的退款金额,整型,单位为分。
        public long total { get; set; }//原支付交易的订单总金额,单位为分,只能为整数。
        public string currency { get; set; }
    }


    public class RefundnotifyModel
    { 
        public string id { get; set; }
        public string create_time { get; set; }
        public string event_type { get; set; }
        public string summary { get; set; }
        public string resource_type { get; set; }
        public WxPayResourceModel resource { get; set; }
    }
    


}
View Code

类2:相关帮助类接口

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks; 
using WuAnExternal;
using System.Text.Json.Serialization;
using System.Net.Http;
using NPOI.SS.Formula.Functions;
using static NPOI.HSSF.Util.HSSFColor;
using System.Security.Cryptography.X509Certificates;
using swapCommon;
using System.Net;
using Org.BouncyCastle.Asn1.Ocsp;
using SQLitePCL;
using NPOI.XWPF.UserModel;
using System.Security.Policy;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;

namespace swapExternal.WeScore
{
    public class WeScoreHelper
    {   // 微信支付 API 接口地址
        /// <summary>
        /// 创建 、 查询 等 URL
        /// </summary>
        public const string ApiUrl = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder"; // 替换为实际的 API 接口地址

        // 微信支付凭据 (请替换为你的实际值)
        public const string MCH_ID = "17xxxxxx3"; // 商户号
        public const string API_KEY = "a0axxxxxx4bf"; // API 密钥
        public const string APPID = "wxxxxxxb9"; // 公众号/小程序 ID
        public const string SUB_MCH_ID = ""; // 子商户号 (可选)
        public const string OPENID = "or3xxxxxxdBg"; // 用户openid
        public const string SERVICE_ID = "00002xxxxxxxxxxx87"; // 服务ID
        public const string OUT_ORDER_NO = "123456789001"; // 商户订单号
        public const string SerialNo = "7ExxxxxxxA86092";
        /// <summary>
        /// 公钥
        /// </summary>
        public const string publicKey = @"MIIBIjANBgkqhkiG9w0BAQEFAxzDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxD2ZRK6u8z2i0C4gBuU3qmGiJ7qkn
rQIDAQAB";


        /// <summary>
        /// 创建支付分订单回调URL
        /// </summary>
        public const string WeScoreUrl = "https://xxxx.com/xxxpay/api/V1/WeScoreBack/WetScoreConfimOrderBack";
        /// <summary>
        /// 取消支付分订单
        /// </summary>
        public const string WeScoreCancelUrl = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{0}/cancel";
        /// <summary>
        /// 完结支付分订单
        /// </summary>
        public const string WeScoreCompleteUrl = "https://api.mch.weixin.qq.com//v3/payscore/serviceorder/{0}/complete";

        public const string RefundUrl = "https://xxxx.com/xxxpay/api/V1/WeScoreBack/RefundBack";


        public static async Task<string> PostWithToken(string url, string postData, string token)
        {
            using (var httpClient = new HttpClient())
            {
                // 设置请求头  
                httpClient.DefaultRequestHeaders.Add("Authorization", token);
                httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
                httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request)


                // 4. 发送 HTTP 请求
                var content = new StringContent(postData, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await httpClient.PostAsync(url, content);

                // 5. 处理响应
                string responseContent = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                { 
                    Console.WriteLine(responseContent);
                    // 解析响应,获取 order_id 等信息
                    return responseContent;
                }
                else
                { 
                    Console.WriteLine(responseContent);
                    // 处理错误信息
                    return null;
                }
            }

        }

        public static async Task<string> GetWithToken(string url, string token)
        {
            using (var httpClient = new HttpClient())
            {
                // 设置请求头  
                httpClient.DefaultRequestHeaders.Add("Authorization", token);
                httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
                httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");//如果缺少这句代码就会导致下单接口请求失败,报400错误(Bad Request)


                // 4. 发送 HTTP 请求 
                HttpResponseMessage response = await httpClient.GetAsync(url);

                // 5. 处理响应
                string responseContent = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                { 
                    Console.WriteLine(responseContent);
                    // 解析响应,获取 order_id 等信息
                    return responseContent;
                }
                else
                { 
                    Console.WriteLine(responseContent);
                    // 处理错误信息
                    return null;
                }
            }

        }


        /// <summary>
        /// 构造签名串 https://pay.weixin.qq.com/doc/v3/merchant/4012365336
        /// </summary>
        /// <param name="method">HTTP请求方式(全大写)</param>
        /// <param name="body">API接口请求参数的json字符串</param>
        /// <param name="uri">API接口的相对路径</param>
        /// <returns></returns>
        //protected static string BuildPostAuthAsync(string method, string body, string uri)
        //{
        //    var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
        //    string nonce = Path.GetRandomFileName();

        //    string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
        //    string signature = RequestSign(message);
        //    return $"mchid=\"{MCH_ID}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{SerialNo}\",signature=\"{signature}\"";
        //}

        /// <summary>
        /// 生成签名
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public static string RequestSign(string message)
        {
            //加载证书 _apiCertPath API证书物理路径 _certPwd API证书密码(默认是商户号)
            X509Certificate2 cer = new X509Certificate2(WechatConstants.WxPayCertPath, MCH_ID, X509KeyStorageFlags.Exportable);
            if (cer != null)
            {
                RSA rsa = cer.GetRSAPrivateKey();  //获取私钥
                //查看在不同平台上的具体类型
                byte[] data = Encoding.UTF8.GetBytes(message);
                return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
            }
            else
            {
                return "";
            }
        }

        /// <summary>
        /// 构建签名字符串
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static 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 = RequestSign(message);
            return $"mchid=\"{MCH_ID}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{SerialNo}\",signature=\"{signature}\"";
        }




        /// <summary>
        /// 最终提交请求时,需对敏感信息加密,如身份证、银行卡号。
        /// 加密算法是RSA,使用从接口下载到的公钥进行加密,非后台下载到的私钥。
        /// 感谢ISV(https://github.com/ndma-isv)提供示例
        /// 
        /// </summary>
        /// <param name="text">要加密的明文</param>
        /// <param name="publicKey"> -----BEGIN CERTIFICATE----- 开头的string,转为bytes </param>
        /// <returns></returns>
        public static string RSAEncrypt(string text, byte[] publicKey)
        {
            using (var x509 = new X509Certificate2(publicKey))
            {
                using (var rsa = (RSACryptoServiceProvider)x509.PublicKey.Key)
                {
                    var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true);

                    return Convert.ToBase64String(buff);
                }
            }
        }


       /// <summary>
       /// 用户确认订单 回调算法
       /// </summary>
        private static string ALGORITHM = "AES/GCM/NoPadding";
        private static int TAG_LENGTH_BIT = 128;
        private static int NONCE_LENGTH_BYTE = 12;
        private static string AES_KEY = "ChengXiangZhiNeng"; //youkeyhere
        public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
        {
            GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            AeadParameters aeadParameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)),
                128,
                Encoding.UTF8.GetBytes(nonce),
                Encoding.UTF8.GetBytes(associatedData));
            gcmBlockCipher.Init(false, aeadParameters);
            byte[] data = Convert.FromBase64String(ciphertext);
            byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
            int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
            gcmBlockCipher.DoFinal(plaintext, length);
            return Encoding.UTF8.GetString(plaintext);
        }

        

    }
}
View Code

类3:相关请求接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace swapExternal.WeScore
{
    public class WeScoreOrderHelper
    {
      
        /// <summary>
        /// 创建微信支付分订单
        /// </summary>
        /// <returns></returns>
        public static async Task<WeScoreCreateOrderResponse> CreateOrderAsync()
        {
            // 1. 构造请求参数
            var requestBody = new
            {
                appid = WeScoreHelper.APPID,
                service_id = WeScoreHelper.SERVICE_ID,
                out_order_no = DateTime.Now.ToString("yyyyMMddHHmmssfff"),//传递参数
                openid = WeScoreHelper.OPENID,//传递参数
                time_range = new
                {
                    start_time = DateTime.Now.AddSeconds(10).ToString("yyyyMMddHHmmss"), // //存入数据库 用于验证
                    start_time_remark = Guid.NewGuid().ToString() //存入数据库 用于验证
                },
                service_introduction = "充电宝免押",
                attach = "充电宝免押", // 附加信息 (可选)  
                risk_fund = new
                {
                    name = "DEPOSIT",
                    amount = 100,//冻结金额  实际支付金额需小于冻结金额  单位 分 传递参数
                    description = "租借充电宝免押金1元"
                },
                notify_url = WeScoreHelper.WeScoreUrl
            };

            string jsonRequestBody = JsonSerializer.Serialize(requestBody);

            string signature = await WeScoreHelper.BuildAuthAsync(new HttpRequestMessage() { Method = HttpMethod.Post, RequestUri = new Uri(WeScoreHelper.ApiUrl), Content = new StringContent(jsonRequestBody) });
            string token = $"WECHATPAY2-SHA256-RSA2048 {signature}";
            var result = await WeScoreHelper.PostWithToken(WeScoreHelper.ApiUrl, jsonRequestBody, token);
            return Newtonsoft.Json.JsonConvert.DeserializeObject<WeScoreCreateOrderResponse>(result);

        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="out_order_no">用户订单号</param>
        /// <returns></returns>
        public static async Task<string> SearchOrderAsync(string out_order_no)
        {
            var ApiUrl = string.Format("https://api.mch.weixin.qq.com/v3/payscore/serviceorder?out_order_no={0}&service_id={1}&appid={2}", out_order_no, WeScoreHelper.SERVICE_ID, WeScoreHelper.APPID);
            //var url = $"/v3/payscore/serviceorder?out_order_no={out_order_no}&service_id={SERVICE_ID}&appid={APPID}";
            var signature = await WeScoreHelper.BuildAuthAsync(new HttpRequestMessage() { Method = HttpMethod.Get, RequestUri = new Uri(ApiUrl) });
            string token = $"WECHATPAY2-SHA256-RSA2048 {signature}";

            var result = await WeScoreHelper.GetWithToken(ApiUrl, token);
            return result;
        }

        /// <summary>
        /// 取消支付分订单
        /// </summary>
        /// <returns></returns>
        public static async Task<string> CancelOrderAsync(string out_order_no)
        {
            string url = string.Format(WeScoreHelper.WeScoreCancelUrl, out_order_no);
            // 1. 构造请求参数
            var requestBody = new
            {
                appid = WeScoreHelper.APPID,
                service_id = WeScoreHelper.SERVICE_ID,
                reason = "测试",//传递参数
                
            };

            string jsonRequestBody = JsonSerializer.Serialize(requestBody);

            string signature = await WeScoreHelper.BuildAuthAsync(new HttpRequestMessage() { Method = HttpMethod.Post, RequestUri = new Uri(url), Content = new StringContent(jsonRequestBody) });
            string token = $"WECHATPAY2-SHA256-RSA2048 {signature}";
            var result = await WeScoreHelper.PostWithToken(url, jsonRequestBody, token);
            return result;

        }


        public static async Task<string> CompleteOrderAsync(string out_order_no)
        {
            string url = string.Format(WeScoreHelper.WeScoreCompleteUrl, out_order_no);
            // 1. 构造请求参数
            var requestBody = new
            {
                appid = WeScoreHelper.APPID,
                service_id = WeScoreHelper.SERVICE_ID,
                post_payments = new List<post_paymentsDto>() { new post_paymentsDto() { name="收费标准",description="不超过99元"} },//传递参数
                total_amount=10,
                time_range = new
                {
                    end_time = DateTime.Now.AddSeconds(10).ToString("yyyyMMddHHmmss"), // //存入数据库 用于验证
                    end_time_remark = Guid.NewGuid().ToString() //存入数据库 用于验证
                },

            };

            string jsonRequestBody = JsonSerializer.Serialize(requestBody);

            string signature = await WeScoreHelper.BuildAuthAsync(new HttpRequestMessage() { Method = HttpMethod.Post, RequestUri = new Uri(url), Content = new StringContent(jsonRequestBody) });
            string token = $"WECHATPAY2-SHA256-RSA2048 {signature}";
            var result = await WeScoreHelper.PostWithToken(url, jsonRequestBody, token);
            return result;
        }


        public static async Task<string> RefundOrderAsync(RefundRequestModel requestBody)
        { 
            string jsonRequestBody = JsonSerializer.Serialize(requestBody);

            string signature = await WeScoreHelper.BuildAuthAsync(new HttpRequestMessage() { Method = HttpMethod.Post, RequestUri = new Uri(WeScoreHelper.RefundUrl), Content = new StringContent(jsonRequestBody) });
            string token = $"WECHATPAY2-SHA256-RSA2048 {signature}";
            var result = await WeScoreHelper.PostWithToken(WeScoreHelper.RefundUrl, jsonRequestBody, token);
            return result;
        }
    }
}
View Code

类4:相关回调接口

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NPOI.XWPF.UserModel;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using swapCommon;
using swapCommon.Ext;
using swapExternal.WeScore;
using swapRepository.Repository.weUser;
using swapService.Interface.weUser;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using WuAnExternal;
using WuAnExternal.PayDto;

namespace xxxpay.Controllers.wechatPay
{
    [AllowAnonymous]
    public class WeScoreBackController : ControllerBase
    { 
        private readonly ILogger<WeScoreBackController> logger;
        public WeScoreBackController( ILogger<WeScoreBackController> logger)
        { 
            this.logger = logger;
        }
   
        /// <summary>
        /// 支付分订单创建通知
        /// {
        //    "id":"EV-2018022511223320873",
        //    "create_time":"2015-05-20T13:29:35+08:00",
        //    "resource_type":"encrypt-resource",
        //    "event_type":"PAYSCORE.USER_CONFIRM",
        //    "resource" : {
        //        "algorithm":"AEAD_AES_256_GCM",
        //        "ciphertext": "...",
        //        "nonce": "...",
        //        "associated_data": "",
        //          "original_type": "payscore"
        //    },
        //    "summary": "确认订单"
        //}
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost]
        [Route("WetScoreConfimOrderBack")]
        public IActionResult WetScoreConfimOrderBack()
        { 

            var buffer = new MemoryStream();
            Request.Body.CopyTo(buffer);
            var str = Encoding.UTF8.GetString(buffer.GetBuffer());

            var wxPayNotifyModel = str.ToObject<WechatScoreConfigmOrderBackDto>();
            var resource = wxPayNotifyModel?.resource ?? new WxPayResourceModel();
            var decryptStr = WeScoreHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext);
            var decryptModel = decryptStr.ToObject<WeScoreAesGcmDecryptDto>();
            if (decryptModel != null)
            {
                //验签
                //业务逻辑
                return Ok();
            } 
            return BadRequest(new { code= "FAIL", message ="失败"});
        }

        /// <summary>
        /// 退款回调
        /// </summary>
        /// <returns></returns>
        public IActionResult RefundBack()
        {
            var buffer = new MemoryStream();
            Request.Body.CopyTo(buffer);
            var str = Encoding.UTF8.GetString(buffer.GetBuffer());

            var wxPayNotifyModel = str.ToObject<RefundnotifyModel>();
            var resource = wxPayNotifyModel?.resource ?? new WxPayResourceModel();
            var decryptStr = WeScoreHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext);
            var decryptModel = decryptStr.ToObject<WxPayResourceDecryptModel>();

            if (decryptModel != null)
            {
                //验签
                //业务逻辑
                return Ok();
            }
            return BadRequest(new { code = "FAIL", message = "失败" });
        }

        /// <summary>
        /// 微信支付回调
        /// </summary>
        /// <returns></returns>
        public IActionResult WxPayCallback()
        {
            var buffer = new MemoryStream();
            Request.Body.CopyTo(buffer);
            var str = Encoding.UTF8.GetString(buffer.GetBuffer());

            var wxPayNotifyModel = str.ToObject<WxPayNotifyModel>();
            var resource = wxPayNotifyModel?.resource ?? new WxPayResourceModel();
            var decryptStr = WeScoreHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext);
            var decryptModel = decryptStr.ToObject<WxPayResourceDecryptModel>();

            if (decryptModel != null)
            {
                //验签
                //业务逻辑
                return Ok();
            }
            return BadRequest(new { code = "FAIL", message = "失败" });
        }

        /// <summary>
        /// 验证数字签名是否有效,公钥验签 https://www.cnblogs.com/dawenyang/p/14458773.html
        /// </summary>
        /// <param name="plaintext">明文</param>
        /// <param name="ciphertext">base64密文</param>
        /// <param name="publicKey">公钥,纯净格式</param>
        /// <returns>签名是否一致</returns>
        public static bool VerifySignByPublicKey(string plaintext, string ciphertext, string publicKey)
        {
            // 构建 X509Certificate 证书
            X509CertificateParser parser = new X509CertificateParser();
            // publicKey 公钥是删除了 -----BEGIN CERTIFICATE----- -----END CERTIFICATE----- \n 三种字符串
            X509Certificate certificate = parser.ReadCertificate(Convert.FromBase64String(publicKey));

            // 获取公钥
            AsymmetricKeyParameter pubKey = certificate.GetPublicKey();

            // 明文转 byte 数组
            byte[] msgBytes = Encoding.UTF8.GetBytes(plaintext);
            // 密文转 byte 数组
            byte[] sigBytes = Convert.FromBase64String(ciphertext);

            // 定义签名算法类型 SHA256WithRSA
            // 从证书描述里面获得
            ISigner signer = SignerUtilities.GetSigner(certificate.SigAlgName);

            // 签名类初始化
            // false表示不用于签名(此处是微信私钥签名,公钥验签,所以参数为false)
            signer.Init(false, pubKey);
            // 更新签名明文数据类容
            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
            // 验证密文与原文是否签名一致
            return signer.VerifySignature(sigBytes);

        }
    }

  
}
View Code

类5:控制器请求

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using swapExternal.WeScore;
using System.Threading.Tasks;

namespace xxxpay.Controllers.wechatPay
{
    [AllowAnonymous]
    public class WeScoreOrderController : BaseController
    {


        [HttpGet("CreateWeScoreOrder")]
        public async Task<IActionResult> CreateWeScoreOrder()
        {
            return Ok(await WeScoreOrderHelper.CreateOrderAsync());
        }


        [HttpGet("SearchOrder")]
        public async Task<IActionResult> SearchOrder()
        {
            return Ok(await WeScoreOrderHelper.SearchOrderAsync("20250726084054665"));
        }

        [HttpGet("CancelOrder")]
        public async Task<IActionResult> CancelOrder()
        {
            return Ok(await WeScoreOrderHelper.CancelOrderAsync("20250726084054665"));
        }


        [HttpGet("CompleteOrderAsync")]
        public async Task<IActionResult> CompleteOrderAsync()
        {
            return Ok(await WeScoreOrderHelper.CompleteOrderAsync("20250726084054665"));
        }
        

    }
}

 验签接口,参考:https://www.cnblogs.com/dawenyang/p/14458773.html

1

2

3

249616-20240926040833357-1012054575

 

结束之前,还是不解气,NN个腿的微信@腾讯

posted @ 2025-07-26 09:25  天才卧龙  阅读(4)  评论(0)    收藏  举报