用户国营 发表于 2024-12-3 06:37:08

.NET WebAPI+微信支付

简述

        .NET WebAPI+微信支付在网上资源较少疑抑或过于复杂对于小白不太友好,基于自己开辟故而撰写本篇文章。
        文末有少量代码不知道如何应用skit代码的可以找我拿类库,添加到你的项目即可食用。
        首先必要预备注册好以及通过认证的微信小步伐如下图
https://i-blog.csdnimg.cn/blog_migrate/c2c3e25f62527431976840cdb086755f.png
存案必要营业执照
https://i-blog.csdnimg.cn/blog_migrate/a5e4e706fea6971781245648468edcd6.png
微信认证是通过第三方认证,23年12月以后关闭以后只能通过官方认证。
 
https://i-blog.csdnimg.cn/blog_migrate/8afda525f603aad3e2cb62730376d3fd.png

以及商户号
https://i-blog.csdnimg.cn/blog_migrate/47141b5d08b1891628fb065650484632.png
https://i-blog.csdnimg.cn/blog_migrate/83b712c438ffd9b8d015563fc8f290a5.png
实在代码反倒更简朴,最紧张的是进行小步伐的设置以及商户号绑定,绑定以后不可解绑,留意坑,故需先商量好,好比为官方机构开辟,推荐直接开两个号,因为测试的时候利用官方的各人都懂的。
大致分为以下几部分

[*]注册小步伐账号
[*]小步伐认证
[*]存案
[*]开通支付
[*]开通商户号
[*]设置api v3
[*]商户号与小步伐绑定
[*]保存商户号,V3 API 密钥,商户证书序列号,商户证书文件内容,AppId,AppSecret
材料:营业执照,营业执照法人身份证,本人微信,电话邮箱等等(乐成办理以后可将管理员设置为自己),餐饮类必要食品安全许可证才能更新小步伐版本,更新版本也有坑
https://i-blog.csdnimg.cn/blog_migrate/b8bad21ba44b3f5602e816405a5aeb00.png
此中必要多张手机号,还有邮箱,验证,审核,过程相当,,,嗯 。
 
一.设置(官方扣的,但是禁绝)

 

1.设置API key

下载并设置商户证书
商户API证书具体利用说明可参见接口规则文档中私钥和证书https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml
商户可登录微信商户平台https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://pay.weixin.qq.com/


[*]微信开辟文档并未写完(现实还需解密获取商户证书序列号)这一步很关键啊,各人留意一下,这个序列号不是微信支付平台的,而是apiv3里面解析获得的
[*]https://i-blog.csdnimg.cn/blog_migrate/7ebf4765e3ad3d8812dfea1b7c02d032.png
[*]必要前去证书查看 (myssl.com)https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://myssl.com/cert_decode.html处解析获取
 
2.设置应用

账号申请指引
1、申请小步伐开辟者账号,进行微信认证,获取appid登录微信公众平台https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://mp.weixin.qq.com/      注册一个小步伐的开辟者账号。小步伐账号申请指引https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://mp.weixin.qq.com/debug/wxadoc/introduction/index.html
你看微信文档,存案那些都没做,就让你开支付
2、小步伐开通微信支付,即申请或复用微信支付商户号,申请完小步伐后,登录小步伐后台。点击左侧导航栏的微信支付,在页面中进行开通。
 
https://i-blog.csdnimg.cn/blog_migrate/360ab4b45d9903ada9a959fdde25046d.png
 
点击开通按钮后,有2种方式可以获取微信支付本领,新申请微信支付商户号或绑定一个已有的微信支付商户号,请根据你的业务必要和具体情况选择,只能二选一,肯定选新的呀,老表,你都来看我博客了。
 
https://i-blog.csdnimg.cn/blog_migrate/c57122422d71f1d11f7e047b42ac41d6.png
二.skit引用以及skit支付代码文件引入

地点为开源微信支付 v3 版 .Net SDK(支持 .NET Core / Framework,完整封装全部 v3 API) | 微信开放社区 (qq.com)https://i-blog.csdnimg.cn/blog_migrate/003a2ce7eb50c2e24a8c624c260c5930.pnghttps://developers.weixin.qq.com/community/develop/article/doc/00020aadc384a0a5f01c3526b56813
https://i-blog.csdnimg.cn/blog_migrate/a9f11083382a55cc797bb87935a13c04.png
三.设置

https://i-blog.csdnimg.cn/blog_migrate/78e2332065bb04ab98b28ab624f4fb73.png
控制器内引用
注:少数为编者数据操作层,留意甄别
using Framework.Wechat.HttpClients;
using Framework.Wechat.Repositories;
using Framework.WePay.Services.HttpClients;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SKIT.FlurlHttpClient.Wechat.Api.Models;
using SKIT.FlurlHttpClient.Wechat.Api;
using Framework.Wechat.Options;
using Framework.WePay.Options;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using Newtonsoft.Json;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.WxaSecOrderUploadCombinedShippingInfoRequest.Types.SubOrder.Types;
using Microsoft.AspNetCore.Authorization;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Events;
using System.Net;
using System.Text;
using NMemory.Data;
using CQIE.FC.NOS.DataAccess.Interfaces;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Bcpg;
using System.Net.Http;
using System.Threading.Tasks;
using System.Security.Cryptography;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.OpenSsl;
using System.Security.Cryptography.X509Certificates;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
using System.Globalization;

namespace WxPayDemo.Webapi.Controllers
{
    /")]
   
   
   
    public class PaymentController : ControllerBase
    {
      private readonly ILogger<PaymentController> _logger;
      private readonly IWechatTenpayHttpClientFactory _tenpayHttpClientFactory;
      private readonly IWechatApiHttpClientFactory _wechatApiHttpClientFactory;
      private readonly IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository;
      private readonly TenpayOptions _tenpayOptions;
      private readonly WechatOptions _wechatOptions;
      private IPaymentService _paymentService;
      private readonly HttpClient _httpClient;
      private readonly RSA _privateKey;
      private readonly string _merchantId;
      public PaymentController(ILogger<PaymentController> logger, IWechatTenpayHttpClientFactory wechatTenpayHttpClientFactory,
                                 IWechatApiHttpClientFactory wechatApiHttpClientFactory, IWechatAccessTokenEntityRepository wechatAccessTokenEntities,
                                 IOptions<TenpayOptions> tenpayOptions, IOptions<WechatOptions> wechatOptions,
                                 IPaymentService paymentService)
      {
            _logger = logger;
            _tenpayHttpClientFactory = wechatTenpayHttpClientFactory;
            _wechatApiHttpClientFactory = wechatApiHttpClientFactory;
            _wechatAccessTokenEntityRepository = wechatAccessTokenEntities;
            _tenpayOptions = tenpayOptions.Value;
            _wechatOptions = wechatOptions.Value;
            _paymentService = paymentService;
            _httpClient = new HttpClient();
            _merchantId = 配置中获取;
      } 其他内容稳定,如微信传入电话,openid,金额
#region 预支付生成
public record propay(string Phone,string Openid,int Total);

public async Task<IActionResult> CreatorPreOrder( propay propay)
{
    try
    {
      int total_fee = (int)(propay.Total);
      var client = _tenpayHttpClientFactory.Create();
      var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault();
      var request = new CreatePayTransactionJsapiRequest()
      {
            OutTradeNumber = OrderNumberGenerator.GenerateOrderNumber(long.Parse(propay.Phone)),
            AppId = wechatAccountOptions.AppId,
            Description = "创谷餐厅",
            NotifyUrl = _tenpayOptions.SucceedNotifyUrl,
            Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = total_fee },
            Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = propay.Openid }
      };
   
      var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken: HttpContext.RequestAborted);

      if (!response.IsSuccessful())
      {
         _logger.LogWarning("JSAPI 下单失败(状态码:{0},错误代码:{1},错误描述:{2})。", response.RawStatus, response.ErrorCode, response.ErrorMessage);
         return Ok("JSAPI 下单失败");
      }
         _logger.LogWarning("JSAPI 成功 预支付交易会话标识:{0}", response.PrepayId);

         List<IDictionary<string, string>> keyValuePairs = new List<IDictionary<string, string>>();
         var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
         keyValuePairs.Add(paramMap);
         IDictionary<string, string> orderdic = new Dictionary<string, string> { { "orderid", request.OutTradeNumber } };
         keyValuePairs.Add(orderdic);

      return Ok(keyValuePairs);
    }
    catch (Exception ex)
    {

      using (StreamWriter sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/error.txt"), true))
      {
             sw.WriteLine(ex.ToString());
      }
      return Ok(ex.ToString());
    }
}
#endregion 结果返回到前端
https://i-blog.csdnimg.cn/blog_migrate/e5ec42dd54794f0a100f8f35171af83c.png
前端进入支付
https://i-blog.csdnimg.cn/blog_migrate/f2c8c438c0afa6c454882626d4660dbb.png
 
skit未提供获取单号故无法退款,需自己封装
必要的订单号
#region 获取交易单号
public record data(string outTradeNo);

public async Task<IActionResult> Gettransaction_id( data data)
{
   // outTradeNo = "23122317444300007285";

   string asd = data.outTradeNo;
   try
   {
         var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{asd}?mchid={_merchantId}");

         var auth = await BuildAuthAsync1(request);
         string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
         request.Headers.Add("Authorization", value);
         request.Headers.Add("Accept", "application/json");
         request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

         var response = await _httpClient.SendAsync(request);

         if (response.IsSuccessStatusCode)
         {
             var content = await response.Content.ReadAsStringAsync();
             // _logger.LogInformation("订单查询成功:{0}", content);
             return Ok(content);
         }
         else
         {
             var errorContent = await response.Content.ReadAsStringAsync();
             //_logger.LogError("订单查询失败:{0}", errorContent);
             return BadRequest(errorContent);
         }
   }
   catch (Exception ex)
   {
         using (StreamWriter sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/error.txt"), true))
         {
             sw.WriteLine("newpay" + ex.Message);
         }
         // _logger.LogError("订单查询发生异常:{0}", ex.Message);
         return StatusCode(500);
   }
}

private async Task<string> BuildAuthAsync1(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 nonceStr = Guid.NewGuid().ToString("N");

   string message = BuildMessage1(method, uri, timestamp, nonceStr, body);
   string signature = Sign(message);
   //自己填写或注入时读取
   string serial_no = "";
   return $"mchid=\"{_merchantId}\",nonce_str=\"{nonceStr}\",timestamp=\"{timestamp}\",serial_no=\"{serial_no}\",signature=\"{signature}\"";
}


protected string Sign(string message)
{
   // 也可从配置文件读取
   // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
   //      亦不包括结尾的-----END PRIVATE KEY-----
   string privateKey = "";

   byte[] keyData = Convert.FromBase64String(privateKey);

   var rsa = RSA.Create();
   rsa.ImportPkcs8PrivateKey(keyData, out _);
   byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
   return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
private string BuildMessage1(string method, string uri, long timestamp, string nonceStr, string body)
{
   string canonicalUrl = uri;
   if (!string.IsNullOrEmpty(Request.QueryString.Value))
   {
         canonicalUrl += Request.QueryString.Value;
   }

   return $"{method}\n{canonicalUrl}\n{timestamp}\n{nonceStr}\n{body}\n";
}
#endregion
中间仍有很多细节,暂时无法补充。
 
 
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: .NET WebAPI+微信支付