评论

ASP.NET 微信支付(安装版)

为了更方便大家的使用,我又封装了.NET Framework的API,直接把代码打包上传到nuget,大家可以直接在nuget里面搜索:AspNet.WeChatPayAPIv3 安装到项目中就可以

前言

    为了更方便大家的使用,我又封装了.NET Framework的API,直接把代码打包上传到nuget,大家可以直接在nuget里面搜索:AspNet.WeChatPayAPIv3安装到项目中就可以使用了。

代码实战


using System;
using System.Configuration;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using AspNetCore.WeChatPayAPIv3.Helper;
using AspNetCore.WeChatPayAPIv3.Models;
using AspNetCore.WeChatPayAPIv3.Models.ClostOrder;
using AspNetCore.WeChatPayAPIv3.Models.GenerateOrder;
using AspNetCore.WeChatPayAPIv3.Models.QueryOrder;
using AspNetCore.WeChatPayAPIv3.Models.QueryRefunds;
using AspNetCore.WeChatPayAPIv3.Models.Refunds;
using AspNetCore.WeChatPayAPIv3.Models.RefundsCallback;
using AspNetCore.WeChatPayAPIv3.Models.WxPayCallback;

namespace WeChatAPIv3Demo.Controllers
{
    /// <summary>
    /// 测试微信支付的控制器
    /// </summary>
    public class WeChatPayTestController : ApiController
    {
        /// <summary>
        /// 统一下单接口
        /// </summary>
        /// <returns></returns>
        [HttpGet, Route("api/WeChatPayTest/GenerateOrder")]
        public async Task<AppPayModel> GenerateOrder()
        {
            var orderNumber = $"{DateTime.Now:yyyyMMddHHmmssff}{CodeHelper.CreateNumCode(3)}";
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var notify_url = ConfigurationManager.AppSettings["notify_url"]; //这个放在配置文件,从配置文件读取比较灵活,或者写到数据库中
            var payodel = await helper.UnionGenerateOrder("好东西啊", 1, orderNumber, notify_url, "附加信息测试啊");

            #region 为APP生成下单所需的参数,看个人实际需求,也可以APP自己生成所需的参数

            var signModel = WxPayForAppHelper.GetSign(WxPayConst.appid, payodel.prepay_id, WxPayConst.privateKey);

            #endregion

            return signModel;
        }

        /// <summary>
        /// 查询订单接口 -- 测试数据:2021033119240567226
        /// </summary>
        [HttpGet, Route("api/WeChatPayTest/QueryOrder")]
        public async Task<WxPayStatusRespModel> QueryOrder(string orderNumber)
        {
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var payModel = await helper.QueryOrder(orderNumber);
            return payModel;
        }

        /// <summary>
        /// 查询订单接口 -- 测试数据:2021033119240567226
        /// </summary>
        [HttpGet, Route("api/WeChatPayTest/CloseOrder")]
        public async Task<ClostOrderRespModel> CloseOrder(string orderNumber)
        {
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var payModel = await helper.ClostOrder(orderNumber);
            return payModel;
        }

        /// <summary>
        /// 微信支付成功结果回调接口
        /// </summary>
        /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。</returns>
        [HttpPost, Route("api/WeChatPayTest/WxPayCallback")]
        [AllowAnonymous]
        public async Task<WxPayCallbackRespModel> WxPayCallback()
        {
            #region 获取字符串流

            System.IO.Stream s = HttpContext.Current.Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();

            //var buffer = new MemoryStream();
            //Request.Body.CopyTo(buffer);

            #endregion
            //我没有使用官方的那种验证数据安全性的方法,我解密出来数据之后,直接拿着订单号再去查询一下订单状态,然后再更新到数据库中。我嫌麻烦……

            var str = Encoding.UTF8.GetString(buffer);
            var wxPayNotifyModel = str.ToObject<WxPayNotifyModel>();
            var resource = wxPayNotifyModel?.resource ?? new WxPayResourceModel();
            var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WxPayConst.APIV3Key);
            var decryptModel = decryptStr.ToObject<WxPayResourceDecryptModel>();

            var viewModel = new WxPayCallbackRespModel();
            if (string.IsNullOrEmpty(decryptModel.out_trade_no))
            {
                viewModel.code = "FAIL";
                viewModel.message = "数据解密失败";
            }
            else
            {
                var resp = await QueryOrder(decryptModel.out_trade_no);
                //然后进行数据库更新处理……等等其他操作
            }

            return viewModel;
        }

        /// <summary>
        /// 退款接口 -- 测试数据:2021033119240567226
        /// </summary>
        [HttpGet, Route("api/WeChatPayTest/Refunds")]
        public async Task<RefundsRespModel> Refunds(string orderNumber)
        {
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var refundNumber = $"{DateTime.Now:yyyyMMddHHmmssff}{CodeHelper.CreateNumCode(3)}";
            var payModel = await helper.Refunds(orderNumber, refundNumber, "测试退款行不行", 1, 2, "https://xxxxx.top/api/WeChatPayTest/RefundsCallback");
            return payModel;
        }

        /// <summary>
        /// 退款通知回调接口
        /// </summary>
        /// <returns>退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。</returns>
        [HttpPost, Route("api/WeChatPayTest/RefundsCallback")]
        [AllowAnonymous]
        public async Task<RefundsCallbackRespModel> RefundsCallback()
        {
            #region 获取字符串流

            System.IO.Stream s = HttpContext.Current.Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();

            //var buffer = new MemoryStream();
            //Request.Body.CopyTo(buffer);

            #endregion
            //我没有使用官方的那种验证数据安全性的方法,我解密出来数据之后,直接拿着商户退款订单号再去查询一下订单状态,然后再更新到数据库中。我嫌麻烦……

            var str = Encoding.UTF8.GetString(buffer);
            var wxPayNotifyModel = str.ToObject<RefundsCallbackModel>();
            var resource = wxPayNotifyModel?.resource ?? new RefundsCallbackResourceModel();
            var decryptStr = AesGcmHelper.AesGcmDecrypt(resource.associated_data, resource.nonce, resource.ciphertext, WxPayConst.APIV3Key);
            var decryptModel = decryptStr.ToObject<RefundsCallbackDecryptModel>();

            var viewModel = new RefundsCallbackRespModel();
            if (string.IsNullOrEmpty(decryptModel.out_trade_no))
            {
                viewModel.code = "FAIL";
                viewModel.message = "数据解密失败";
            }
            else
            {
                var resp = await QueryRefunds(decryptModel.out_refund_no);
                //然后进行数据库更新处理……等等其他操作
            }

            return viewModel;
        }

        /// <summary>
        /// 查询退款结果接口
        /// </summary>
        /// <param name="refundNumber">商户系统内部的退款单号,商户系统内部唯一</param>
        /// <returns></returns>
        [HttpGet, Route("api/WeChatPayTest/QueryRefunds")]
        public async Task<QueryRefundsOrderRespModel> QueryRefunds(string refundNumber)
        {
            var helper = new WxPayHelper(WxPayConst.appid, WxPayConst.mchid, WxPayConst.serialNo, WxPayConst.privateKey);
            var payModel = await helper.QueryRefundsOrder(refundNumber);
            return payModel;
        }
    }
}

最后

    欢迎大家反馈问题给我,我的仓库地址:https://github.com/wolfsheep/WeChatAPIv3ForAppWithAspNet

最后一次编辑于  2021-04-09  
点赞 5
收藏
评论

5 个评论

  •  Q
    Q
    2023-02-07

    您好,我的程序运行到这里的时候报错。

    调用的地址是http://localhost:4469/api/WeChatPayTest/GenerateOrder

    已经安装了 apiclient_cert.p12 证书。

    .net环境是4.7.2

    我做的改动:WxPayConst.cs文件中的信息改成自己的信息。


    接口调试返回的错误:

    期待您的回复,谢谢

    2023-02-07
    赞同
    回复 1
    •  Q
      Q
      2023-02-07
      解决了。原来是用错了密钥文件了
      2023-02-07
      回复
  • 未来
    未来
    2022-03-19

    你好,我的程序运行到var resp = await client.PostAsync(url, bodyJson);这一步的时候就没有任何反反应了,也没有返回,也没有报错,程序不会往下走了,是咋回事呢

    2022-03-19
    赞同
    回复 4
    • 大稳·杨
      大稳·杨
      2022-03-20
      你的.NET环境确认一下版本,微信支付证书安装了吗?网络没有问题?肯定会有返回信息的
      2022-03-20
      回复
    • 未来
      未来
      2022-03-20回复大稳·杨
      我用的是.NET Framework 4.6.1
      2022-03-20
      回复
    • 未来
      未来
      2022-03-20回复大稳·杨
      您说的支付证书是不是apiclient_cert.p12,已安装
      2022-03-20
      回复
    • 未来
      未来
      2022-03-21回复未来
      问题已解决:我这边证书安装等了一会就有回调结果了,一定要安装证书,作者大大的代码没问题
      2022-03-21
      1
      回复
  • luckily
    luckily
    2021-10-27

    解码失败啊。我也安装了证书

    2021-10-27
    赞同
    回复 1
    • 大稳·杨
      大稳·杨
      2021-10-28
      提示什么?环境版本是什么?
      2021-10-28
      回复
  • 卯月IT星君🤩智月星软件
    卯月IT星君🤩智月星软件
    2021-07-27

    打Debug,运行到这里就跳出了。return await base.SendAsync(request, cancellationToken);

    2021-07-27
    赞同
    回复 1
    • 大稳·杨
      大稳·杨
      2021-08-12
      排查一下网络,支付安全证书是否安装?运行环境
      2021-08-12
      回复
  • 可
    2021-06-07

    部署到linux报错

    System.PlatformNotSupportedException: Operation is not supported on this platform.

    解决方案:

    protected string Sign(string message)
    {
        // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
        //        亦不包括结尾的-----END PRIVATE KEY-----
        string privateKey = "{你的私钥}";
        var rsa = RSA.Create();
        rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
        var signbytes = rsa.SignData(Encoding.UTF8.GetBytes(message), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        return Convert.ToBase64String(signbytes);
    }
    


    2021-06-07
    赞同
    回复 1
登录 后发表内容