.NET WebAPI+微信支付

打印 上一主题 下一主题

主题 863|帖子 863|积分 2589

简述

        .NET WebAPI+微信支付在网上资源较少疑抑或过于复杂对于小白不太友好,基于自己开辟故而撰写本篇文章。
        文末有少量代码不知道如何应用skit代码的可以找我拿类库,添加到你的项目即可食用。
        首先必要预备注册好以及通过认证的微信小步伐如下图

存案必要营业执照

微信认证是通过第三方认证,23年12月以后关闭以后只能通过官方认证。
 


以及商户号


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

  • 注册小步伐账号
  • 小步伐认证
  • 存案
  • 开通支付
  • 开通商户号
  • 设置api v3
  • 商户号与小步伐绑定
  • 保存商户号,V3 API 密钥,商户证书序列号,商户证书文件内容,AppId,AppSecret
材料:营业执照,营业执照法人身份证,本人微信,电话邮箱等等(乐成办理以后可将管理员设置为自己),餐饮类必要食品安全许可证才能更新小步伐版本,更新版本也有坑

此中必要多张手机号,还有邮箱,验证,审核,过程相当,,,嗯 。
 
一.设置(官方扣的,但是禁绝)

 

1.设置API key

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


  • 微信开辟文档并未写完(现实还需解密获取商户证书序列号)这一步很关键啊,各人留意一下,这个序列号不是微信支付平台的,而是apiv3里面解析获得的

  • 必要前去证书查看 (myssl.com)
    https://myssl.com/cert_decode.html处解析获取
 
2.设置应用

账号申请指引
1、申请小步伐开辟者账号,进行微信认证,获取appid登录微信公众平台
https://mp.weixin.qq.com/      注册一个小步伐的开辟者账号。小步伐账号申请指引
https://mp.weixin.qq.com/debug/wxadoc/introduction/index.html
你看微信文档,存案那些都没做,就让你开支付
2、小步伐开通微信支付,即申请或复用微信支付商户号,申请完小步伐后,登录小步伐后台。点击左侧导航栏的微信支付,在页面中进行开通。
 

 
点击开通按钮后,有2种方式可以获取微信支付本领,新申请微信支付商户号或绑定一个已有的微信支付商户号,请根据你的业务必要和具体情况选择,只能二选一,肯定选新的呀,老表,你都来看我博客了。
 

二.skit引用以及skit支付代码文件引入

地点为开源微信支付 v3 版 .Net SDK(支持 .NET Core / Framework,完整封装全部 v3 API) | 微信开放社区 (qq.com)
https://developers.weixin.qq.com/community/develop/article/doc/00020aadc384a0a5f01c3526b56813

三.设置


控制器内引用
注:少数为编者数据操作层,留意甄别
  1. using Framework.Wechat.HttpClients;
  2. using Framework.Wechat.Repositories;
  3. using Framework.WePay.Services.HttpClients;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Mvc;
  6. using SKIT.FlurlHttpClient.Wechat.Api.Models;
  7. using SKIT.FlurlHttpClient.Wechat.Api;
  8. using Framework.Wechat.Options;
  9. using Framework.WePay.Options;
  10. using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
  11. using SKIT.FlurlHttpClient.Wechat.TenpayV3;
  12. using Microsoft.Extensions.Options;
  13. using System.Collections.Generic;
  14. using Newtonsoft.Json;
  15. using static SKIT.FlurlHttpClient.Wechat.Api.Models.WxaSecOrderUploadCombinedShippingInfoRequest.Types.SubOrder.Types;
  16. using Microsoft.AspNetCore.Authorization;
  17. using SKIT.FlurlHttpClient.Wechat.TenpayV3.Events;
  18. using System.Net;
  19. using System.Text;
  20. using NMemory.Data;
  21. using CQIE.FC.NOS.DataAccess.Interfaces;
  22. using Microsoft.Extensions.Logging;
  23. using Org.BouncyCastle.Bcpg;
  24. using System.Net.Http;
  25. using System.Threading.Tasks;
  26. using System.Security.Cryptography;
  27. using Newtonsoft.Json.Linq;
  28. using Org.BouncyCastle.Crypto.Parameters;
  29. using Org.BouncyCastle.Crypto;
  30. using Org.BouncyCastle.Security;
  31. using Org.BouncyCastle.OpenSsl;
  32. using System.Security.Cryptography.X509Certificates;
  33. using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
  34. using System.Globalization;
  35. namespace WxPayDemo.Webapi.Controllers
  36. {
  37.     [Route("api/[controller]/[action]")]
  38.     [ApiController]
  39.     [Authorize]
  40.     [Authorize(Roles = "用户")]
  41.     public class PaymentController : ControllerBase
  42.     {
  43.         private readonly ILogger<PaymentController> _logger;
  44.         private readonly IWechatTenpayHttpClientFactory _tenpayHttpClientFactory;
  45.         private readonly IWechatApiHttpClientFactory _wechatApiHttpClientFactory;
  46.         private readonly IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository;
  47.         private readonly TenpayOptions _tenpayOptions;
  48.         private readonly WechatOptions _wechatOptions;
  49.         private IPaymentService _paymentService;
  50.         private readonly HttpClient _httpClient;
  51.         private readonly RSA _privateKey;
  52.         private readonly string _merchantId;
  53.         public PaymentController(ILogger<PaymentController> logger, IWechatTenpayHttpClientFactory wechatTenpayHttpClientFactory,
  54.                                  IWechatApiHttpClientFactory wechatApiHttpClientFactory, IWechatAccessTokenEntityRepository wechatAccessTokenEntities,
  55.                                  IOptions<TenpayOptions> tenpayOptions, IOptions<WechatOptions> wechatOptions,
  56.                                  IPaymentService paymentService)
  57.         {
  58.             _logger = logger;
  59.             _tenpayHttpClientFactory = wechatTenpayHttpClientFactory;
  60.             _wechatApiHttpClientFactory = wechatApiHttpClientFactory;
  61.             _wechatAccessTokenEntityRepository = wechatAccessTokenEntities;
  62.             _tenpayOptions = tenpayOptions.Value;
  63.             _wechatOptions = wechatOptions.Value;
  64.             _paymentService = paymentService;
  65.             _httpClient = new HttpClient();
  66.             _merchantId = 配置中获取;
  67.         }
复制代码
其他内容稳定,如微信传入电话,openid,金额
  1. #region 预支付生成
  2. public record propay(string Phone,string Openid,int Total);
  3. [HttpPost]
  4. public async Task<IActionResult> CreatorPreOrder([FromBody] propay propay)
  5. {
  6.     try
  7.     {
  8.         int total_fee = (int)(propay.Total);
  9.         var client = _tenpayHttpClientFactory.Create();
  10.         var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault();
  11.         var request = new CreatePayTransactionJsapiRequest()
  12.         {
  13.             OutTradeNumber = OrderNumberGenerator.GenerateOrderNumber(long.Parse(propay.Phone)),
  14.             AppId = wechatAccountOptions.AppId,
  15.             Description = "创谷餐厅",
  16.             NotifyUrl = _tenpayOptions.SucceedNotifyUrl,
  17.             Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = total_fee },
  18.             Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = propay.Openid }
  19.         };
  20.    
  21.         var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken: HttpContext.RequestAborted);
  22.         if (!response.IsSuccessful())
  23.         {
  24.            _logger.LogWarning("JSAPI 下单失败(状态码:{0},错误代码:{1},错误描述:{2})。", response.RawStatus, response.ErrorCode, response.ErrorMessage);
  25.            return Ok("JSAPI 下单失败");
  26.         }
  27.          _logger.LogWarning("JSAPI 成功 预支付交易会话标识:{0}", response.PrepayId);
  28.          List<IDictionary<string, string>> keyValuePairs = new List<IDictionary<string, string>>();
  29.          var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
  30.          keyValuePairs.Add(paramMap);
  31.          IDictionary<string, string> orderdic = new Dictionary<string, string> { { "orderid", request.OutTradeNumber } };
  32.          keyValuePairs.Add(orderdic);
  33.         return Ok(keyValuePairs);
  34.     }
  35.     catch (Exception ex)
  36.     {
  37.         using (StreamWriter sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/error.txt"), true))
  38.         {
  39.              sw.WriteLine(ex.ToString());
  40.         }
  41.         return Ok(ex.ToString());
  42.     }
  43. }
  44. #endregion
复制代码
结果返回到前端

前端进入支付

 
skit未提供获取单号故无法退款,需自己封装
必要的订单号
  1. #region 获取交易单号
  2. public record data(string outTradeNo);
  3. [HttpPost]
  4. public async Task<IActionResult> Gettransaction_id([FromBody] data data)
  5. {
  6.      // outTradeNo = "23122317444300007285";
  7.      string asd = data.outTradeNo;
  8.      try
  9.      {
  10.          var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{asd}?mchid={_merchantId}");
  11.          var auth = await BuildAuthAsync1(request);
  12.          string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
  13.          request.Headers.Add("Authorization", value);
  14.          request.Headers.Add("Accept", "application/json");
  15.          request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
  16.          var response = await _httpClient.SendAsync(request);
  17.          if (response.IsSuccessStatusCode)
  18.          {
  19.              var content = await response.Content.ReadAsStringAsync();
  20.              // _logger.LogInformation("订单查询成功:{0}", content);
  21.              return Ok(content);
  22.          }
  23.          else
  24.          {
  25.              var errorContent = await response.Content.ReadAsStringAsync();
  26.              //_logger.LogError("订单查询失败:{0}", errorContent);
  27.              return BadRequest(errorContent);
  28.          }
  29.      }
  30.      catch (Exception ex)
  31.      {
  32.          using (StreamWriter sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/error.txt"), true))
  33.          {
  34.              sw.WriteLine("newpay" + ex.Message);
  35.          }
  36.          // _logger.LogError("订单查询发生异常:{0}", ex.Message);
  37.          return StatusCode(500);
  38.      }
  39. }
  40. private async Task<string> BuildAuthAsync1(HttpRequestMessage request)
  41. {
  42.      string method = request.Method.ToString();
  43.      string body = "";
  44.      if (method == "POST" || method == "PUT" || method == "PATCH")
  45.      {
  46.          var content = request.Content;
  47.          body = await content.ReadAsStringAsync();
  48.      }
  49.      string uri = request.RequestUri.PathAndQuery;
  50.      var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
  51.      string nonceStr = Guid.NewGuid().ToString("N");
  52.      string message = BuildMessage1(method, uri, timestamp, nonceStr, body);
  53.      string signature = Sign(message);
  54.      //自己填写或注入时读取
  55.      string serial_no = "";
  56.      return $"mchid="{_merchantId}",nonce_str="{nonceStr}",timestamp="{timestamp}",serial_no="{serial_no}",signature="{signature}"";
  57. }
  58. protected string Sign(string message)
  59. {
  60.      // 也可从配置文件读取
  61.      // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
  62.      //        亦不包括结尾的-----END PRIVATE KEY-----
  63.      string privateKey = "";
  64.      byte[] keyData = Convert.FromBase64String(privateKey);
  65.      var rsa = RSA.Create();
  66.      rsa.ImportPkcs8PrivateKey(keyData, out _);
  67.      byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
  68.      return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
  69. }
  70. private string BuildMessage1(string method, string uri, long timestamp, string nonceStr, string body)
  71. {
  72.      string canonicalUrl = uri;
  73.      if (!string.IsNullOrEmpty(Request.QueryString.Value))
  74.      {
  75.          canonicalUrl += Request.QueryString.Value;
  76.      }
  77.      return $"{method}\n{canonicalUrl}\n{timestamp}\n{nonceStr}\n{body}\n";
  78. }
  79. #endregion
复制代码
中间仍有很多细节,暂时无法补充。
 
 
 

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户国营

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表