首页
视频
资源
登录
原
.net6开发企业微信小程序支付流程
189
人阅读
2024/4/11 15:21
总访问:
2637146
评论:
0
收藏:
1
手机
分类:
.net后台框架
![](https://img.tnblog.net/arcimg/hb/d0512362aed443efbb82e644f2d634e0.jpg) >#企业微信小程序支付流程 [TOC] 注册微信公众号 ------------ tn2>首先通过 https://mp.weixin.qq.com/ 链接进行企业级的小程序注册。 ![](https://img.tnblog.net/arcimg/hb/4ef7c3fe2a0e48128cd10383535494dd.png) ![](https://img.tnblog.net/arcimg/hb/b5b82fea445c423c81b3e5c25c00c57f.png) ![](https://img.tnblog.net/arcimg/hb/08fea189496a462f88a4f57b3194fe2a.png) ![](https://img.tnblog.net/arcimg/hb/a7add6d002774dd1bb0653f56e66ca40.png) ![](https://img.tnblog.net/arcimg/hb/666c48e169164b388f3f8cb59fa4f021.png) tn2>然后打开自己的邮箱,点击收到的邮件的链接进行继续注册。 需要填写如下信息: * 实体类型 * 公司类型 * 公司名称 * 营业执照注册号 * 身份证上的管理员姓名 * 身份证上号码 * 管理员的手机号码 * 短信验证码 * 小程序的付费验证300元 ![](https://img.tnblog.net/arcimg/hb/9eef39bafc0b44a8ba86422f7a3ab801.png) tn2>通过这些还点击继续,这里我就不继续了。 我已经注册好了 申请微信支付商户 ------------ tn2>我们登录好后点击微信支付-->接入微信支付-->点击申请接入(也就是这里:https://pay.weixin.qq.com/index.php/core/home/login ) ![](https://img.tnblog.net/arcimg/hb/06d67f62f8f54501b8314d0222455b1b.png) ![](https://img.tnblog.net/arcimg/hb/df1bae6c58fe4959a330a8e5dd0c04e0.png) tn2>点击成为微信支付商户号 ![](https://img.tnblog.net/arcimg/hb/b0c696b345534e19b598914aef326149.png) tn2>然后就开始填写商户信息,主要有下面: * 姓名 * 手机号 * 法人 * 营业执照 * 企业银行卡 ![](https://img.tnblog.net/arcimg/hb/b336e2e1583c47368ab6ae4fe285f183.png) tn2>这个只要慢慢弄一步步弄就好了。 然后关联我们的APP ID ![](https://img.tnblog.net/arcimg/hb/8e05740d11184fa09ac2ae136956de11.png) tn2>也就是我们小程序中的AppID,复制到这里并进行提交就可以了。 ![](https://img.tnblog.net/arcimg/hb/1b6b565a05ff445e9257ea90297a1acc.png) 开发配置 ------------ tn2>通过小程序登录后 https://mp.weixin.qq.com/ ,找到开发配置 ![](https://img.tnblog.net/arcimg/hb/0f764c33d3f34c7381d8005f485669e9.png) tn2>1.需要APPID 2.生成AppSecret 3.配置服务器合法域名(前提服务器有证书,并且域名可访问) 4.添加业务域名(添加时,通过保存微信提供的txt) 5.添加消息推送(获取token和EncodingAESKey,如果提交时有系统错误就算了,这个不太重要) 6.查看商户号https://pay.weixin.qq.com/index.php/extend/pay_setting 7.开通申请API证书和APIv3密钥https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ ![](https://img.tnblog.net/arcimg/hb/4ebb5458f773421f94b2512afa7c56d6.png) tn>主要开通证书的时候,具体可以看官网的流程:https://kf.qq.com/faq/161222NneAJf161222U7fARv.html (证书重要的在于证书序列号,和privatekey) 开通APIv3密钥的时候可以查看这个教程:https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html 创建项目 ------------ tn2>创建一个.net6的项目,添加相关的包。 ```bash <ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> <PackageReference Include="Autofac" Version="8.0.0" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.1.0" /> <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.0.0" /> <PackageReference Include="NMemory" Version="3.1.6" /> <PackageReference Include="DistributedLock.Core" Version="1.0.6" /> <PackageReference Include="DistributedLock.FileSystem" Version="1.0.2" /> </ItemGroup> ``` tn2>在`appsettings.json`添加相关配置。 ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "TenpayOptions": { "Merchants": [ { "MerchantId": "商户号", "SecretV3": "APIv3密钥", "CertificateSerialNumber": "微信API证书的序列号", "CertificatePrivateKey": "微信API证书的apiclient_key.pem文件" } ], "SucceedNotifyUrl": "https:/你的域名/api/order/ReceiveSucceedMessage", "FailNotifyUrl": "https://你的域名/api/order/RefundSucceedMessage" }, "WechatOptions": { "Accounts": [ { "AppId": "企业微信的AppID", "AppSecret": "企业微信的AppSecret" } ], "CallbackEncodingAESKey": "企业微信的消息推送EncodingAESKey", "CallbackToken": "企业微信的消息推送token" } } ``` tn2>接下来我们要做三件事: 1.通过openid获取AccessToken去获取用户的openid,让我们知道对谁发起支付 2.后台自动刷新相关AccessToken和支付需要的相关证书 3.创建相关控制器 ### 刷新AccessToken tn2>通过微信接收相关参数。 ![](https://img.tnblog.net/arcimg/hb/90405c3a803d4ad4a8b2dbd791d80abe.png) ```csharp namespace IPayAI.WeChat.MINIPay.OptionTenpays { public partial class WechatOptions : IOptions<WechatOptions> { WechatOptions IOptions<WechatOptions>.Value => this; public Types.WechatAccount[] Accounts { get; set; } = Array.Empty<Types.WechatAccount>(); public string CallbackEncodingAESKey { get; set; } = string.Empty; public string CallbackToken { get; set; } = string.Empty; } public partial class WechatOptions { public static class Types { public class WechatAccount { public string? GhId { get; set; } public string AppId { get; set; } = string.Empty; public string AppSecret { get; set; } = string.Empty; } } } } ``` ```csharp namespace IPayAI.WeChat.MINIPay.OptionTenpays { public partial class TenpayOptions : IOptions<TenpayOptions> { TenpayOptions IOptions<TenpayOptions>.Value => this; public Types.WechatMerchant[] Merchants { get; set; } = Array.Empty<Types.WechatMerchant>(); public string SucceedNotifyUrl { get; set; } = string.Empty; public string FailNotifyUrl { get; set; } = string.Empty; } public partial class TenpayOptions { public static class Types { public class WechatMerchant { public string MerchantId { get; set; } = string.Empty; public string SecretV3 { get; set; } = string.Empty; public string CertificateSerialNumber { get; set; } = string.Empty; public string CertificatePrivateKey { get; set; } = string.Empty; } } } } ``` ```csharp Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var builder = WebApplication .CreateBuilder(args); // Add services to the container. builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Services.AddControllers(); // 注入配置项(内容见 `appsettings.json` 文件) builder.Services.AddOptions(); builder.Services.Configure<TenpayOptions>(builder.Configuration.GetSection(nameof(TenpayOptions))); builder.Services.Configure<WechatOptions>(builder.Configuration.GetSection(nameof(WechatOptions))); ``` tn2>在`Models`文件夹下面创建`WechatAccessTokenEntity`类,用于装AccessToken的类。 ```csharp public class WechatAccessTokenEntity { /// <summary> /// 企业AppId /// </summary> public string AppId { get; set; } = string.Empty; /// <summary> /// AccessToken /// </summary> public string AccessToken { get; set; } = string.Empty; public long ExpireTimestamp { get; set; } public long UpdateTimestamp { get; set; } public long CreateTimestamp { get; set; } } ``` tn2>创建分布式锁,便于获取`AccessToken`时进行锁住其他请求来获取`AccessToken`。 ![](https://img.tnblog.net/arcimg/hb/e6c4a1c7ebe84deca5a8c96efa3662f4.png) ```csharp public interface IDistributedLockFactory { IDistributedLock Create(string lockName); } ``` ```csharp internal class DistributedLockFactory : IDistributedLockFactory { private readonly DirectoryInfo _lockFileDirectory = new DirectoryInfo(Environment.CurrentDirectory); public IDistributedLock Create(string lockName) { // NOTICE: // 单机演示基于文件实现分布式锁,生产项目请替换成其他实现。 return new FileDistributedLock(_lockFileDirectory, lockName); } } ``` ```csharp // 注入分布式锁 builder.Services.AddSingleton<IDistributedLockFactory, DistributedLockFactory>(); ``` tn2>写一个简单的内存仓储,用于存储`WechatAccessTokenEntity`。 ![](https://img.tnblog.net/arcimg/hb/53c52bc64b424720ab2e97591b0aede3.png) ```csharp internal class GlobalDatabase { static GlobalDatabase() { Database db = new Database(); TableWechatAccessTokenEntity = db.Tables.Create<Models.WechatAccessTokenEntity, string>(e => e.AppId); } public static Table<WechatAccessTokenEntity, string> TableWechatAccessTokenEntity { get; } } ``` ```csharp public interface IWechatAccessTokenEntityRepository : IEnumerable<Models.WechatAccessTokenEntity> { void Insert(Models.WechatAccessTokenEntity entity); void Update(Models.WechatAccessTokenEntity entity); void Delete(Models.WechatAccessTokenEntity entity); } ``` ```csharp public class WechatAccessTokenEntityRepository : IWechatAccessTokenEntityRepository { public void Insert(Models.WechatAccessTokenEntity entity) { entity.CreateTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); entity.UpdateTimestamp = entity.CreateTimestamp; GlobalDatabase.TableWechatAccessTokenEntity.Insert(entity); } public void Update(WechatAccessTokenEntity entity) { entity.UpdateTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); GlobalDatabase.TableWechatAccessTokenEntity.Update(entity); } public void Delete(WechatAccessTokenEntity entity) { GlobalDatabase.TableWechatAccessTokenEntity.Delete(entity); } IEnumerator<WechatAccessTokenEntity> IEnumerable<Models.WechatAccessTokenEntity>.GetEnumerator() { return GlobalDatabase.TableWechatAccessTokenEntity.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GlobalDatabase.TableWechatAccessTokenEntity.GetEnumerator(); } } ``` ```csharp // 注入仓储类 builder.Services.AddSingleton<IWechatAccessTokenEntityRepository, WechatAccessTokenEntityRepository>(); ``` tn2>创建相关微信API请求工厂接口并进行依赖注入。 ```csharp public interface IWechatApiClientFactory { WechatApiClient Create(string appId); } ``` ```csharp internal partial class WechatApiClientFactory : IWechatApiClientFactory { private readonly IHttpClientFactory _httpClientFactory; private readonly WechatOptions _wechatOptions; public WechatApiClientFactory( IHttpClientFactory httpClientFactory, IOptions<WechatOptions> wechatOptions) { _httpClientFactory = httpClientFactory; _wechatOptions = wechatOptions.Value; } public WechatApiClient Create(string appId) { // NOTICE: // 这里的工厂方法是为了演示多租户而存在的,可根据 AppId 生成不同的 API 客户端。 // 如果你的项目只存在唯一一个租户,那么直接注入 `WechatApiClient` 即可。 var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault(e => string.Equals(appId, e.AppId)); if (wechatAccountOptions == null) throw new Exception("未在配置项中找到该 AppId 对应的微信账号。"); var wechatApiClientOptions = new WechatApiClientOptions() { AppId = wechatAccountOptions.AppId, AppSecret = wechatAccountOptions.AppSecret, PushEncodingAESKey = _wechatOptions.CallbackEncodingAESKey, PushToken = _wechatOptions.CallbackToken }; var wechatApiClient = WechatApiClientBuilder.Create(wechatApiClientOptions) .UseHttpClient(_httpClientFactory.CreateClient(), disposeClient: false) .Build(); return wechatApiClient; } } ``` ```csharp // 注入工厂 HTTP 客户端 builder.Services.AddHttpClient(); builder.Services.AddSingleton<Services.HttpClients.IWechatApiClientFactory, Services.HttpClients.Implements.WechatApiClientFactory>(); ``` tn2>自动创建后台刷新AccessToken的服务 ```csharp internal class WechatAccessTokenRefreshingBackgroundService : BackgroundService { private readonly ILogger _logger; private readonly WechatOptions _wechatOptions; private readonly DistributedLock.IDistributedLockFactory _distributedLockFactory; private readonly HttpClients.IWechatApiClientFactory _wechatApiClientFactory; private readonly Repositories.IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository; public WechatAccessTokenRefreshingBackgroundService( ILoggerFactory loggerFactory, IOptions<WechatOptions> wechatOptions, DistributedLock.IDistributedLockFactory distributedLockFactory, HttpClients.IWechatApiClientFactory wechatApiClientFactory, Repositories.IWechatAccessTokenEntityRepository wechatAccessTokenEntityRepository) { _logger = loggerFactory.CreateLogger(GetType()); _wechatOptions = wechatOptions.Value; _distributedLockFactory = distributedLockFactory; _wechatApiClientFactory = wechatApiClientFactory; _wechatAccessTokenEntityRepository = wechatAccessTokenEntityRepository; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { IList<Task> tasks = new List<Task>(); foreach (var wechatAccount in _wechatOptions.Accounts) { Task task = TryRefreshWechatAccessTokenAsync(wechatAccount.AppId, stoppingToken); tasks.Add(task); } await Task.WhenAll(tasks); await Task.Delay(1 * 60 * 1000); // 每隔 1 分钟轮询刷新 } } private async Task TryRefreshWechatAccessTokenAsync(string appId, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(appId)) return; // 无效参数 var entity = _wechatAccessTokenEntityRepository.FirstOrDefault(e => e.AppId == appId); if (entity?.ExpireTimestamp > DateTimeOffset.Now.ToUnixTimeSeconds()) return; // AccessToken 未过期 var locker = _distributedLockFactory.Create("accessToken:" + appId); using var lockHandler = await locker.TryAcquireAsync(TimeSpan.FromSeconds(15), cancellationToken); if (lockHandler == null) return; // 未取得锁 var client = _wechatApiClientFactory.Create(appId); var request = new CgibinTokenRequest(); var response = await client.ExecuteCgibinTokenAsync(request, cancellationToken); if (!response.IsSuccessful()) { _logger.LogWarning( "刷新 AppId 为 {0} 微信 AccessToken 失败(状态码:{1},错误代码:{2},错误描述:{3})。", appId, response.GetRawStatus(), response.ErrorCode, response.ErrorMessage ); return; // 请求失败 } long nextExpireTimestamp = DateTimeOffset.Now .AddSeconds(response.ExpiresIn) .AddMinutes(-10) .ToUnixTimeSeconds(); // 提前十分钟过期,以便于系统能及时刷新,防止因在过期临界点时出现问题 if (entity == null) { entity = new Models.WechatAccessTokenEntity() { AppId = appId, AccessToken = response.AccessToken, ExpireTimestamp = nextExpireTimestamp }; _wechatAccessTokenEntityRepository.Insert(entity); } else { entity.AccessToken = response.AccessToken; entity.ExpireTimestamp = nextExpireTimestamp; _wechatAccessTokenEntityRepository.Update(entity); } _logger.LogInformation("刷新 AppId 为 {0} 的微信 AccessToken 成功。", appId); } } ``` ```csharp builder.Services.AddHostedService<Services.BackgroundServices.WechatAccessTokenRefreshingBackgroundService>(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); ``` tn2>创建获取微信用户OpenId的控制器 ```csharp [ApiController] [Route("api/wxuser")] public class WxUserController : ControllerBase { private readonly ILogger _logger; readonly IWechatApiClientFactory _httpfactory; private readonly WechatOptions _wechatOptions; private readonly IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository; public WxUserController( ILoggerFactory loggerFactory, IOptions<WechatOptions> wechatOptions, IWechatApiClientFactory httpfactory, IWechatAccessTokenEntityRepository wechatAccessTokenEntityRepository ) { _wechatOptions = wechatOptions.Value; _wechatAccessTokenEntityRepository = wechatAccessTokenEntityRepository; _httpfactory = httpfactory; _logger = loggerFactory.CreateLogger(GetType()); } [HttpGet("{code}")] public async Task<IActionResult> Get(string code) { //读取微信支付配置文件 var tenpayAccountOptions = _wechatOptions.Accounts?.FirstOrDefault(); //创建微信支付客户端 var client = _httpfactory.Create(tenpayAccountOptions.AppId); var entity = _wechatAccessTokenEntityRepository.FirstOrDefault(e => e.AppId == tenpayAccountOptions.AppId); var response = await client.ExecuteSnsJsCode2SessionAsync(new SnsJsCode2SessionRequest() { JsCode = code, AccessToken = entity.AccessToken }); if (!response.IsSuccessful()) { _logger.LogWarning( "获取用户基本信息失败(状态码:{0},错误代码:{1},错误描述:{2})。", response.GetRawStatus(), response.ErrorCode, response.ErrorMessage ); } return new JsonResult(response); } } ``` ### 微信小程序支付与退款 tn2>创建支付类和退款类。 ```csharp public class CreateOrderByJsapiRequest { /// <summary> /// 商户号 /// </summary> public string MerchantId { get; set; } = default!; /// <summary> /// 企业appid /// </summary> public string AppId { get; set; } = default!; /// <summary> /// 用户Openid /// </summary> public string OpenId { get; set; } = default!; // NOTICE: // 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。 public int Amount { get; set; } } ``` ```csharp public class CreateRefundRequest { /// <summary> /// 商户号 /// </summary> public string MerchantId { get; set; } = default!; /// <summary> /// 订单号,商家自定义的订单号 /// </summary> public string TransactionId { get; set; } = default!; // NOTICE: // 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。 public int OrderAmount { get; set; } /// <summary> /// 需要退还的金额 /// </summary> public int RefundAmount { get; set; } } ``` tn2>创建支付需要的证书管理 ```csharp public interface IWechatTenpayCertificateManagerFactory { ICertificateManager Create(string merchantId); } ``` ```csharp internal partial class WechatTenpayCertificateManagerFactory : IWechatTenpayCertificateManagerFactory { private readonly ConcurrentDictionary<string, ICertificateManager> _dict; public WechatTenpayCertificateManagerFactory() { _dict = new ConcurrentDictionary<string, ICertificateManager>(); } public ICertificateManager Create(string merchantId) { return _dict.GetOrAdd(merchantId, new InMemoryCertificateManager()); } } ``` tn2>创建支付需要的证书请求的请求工厂类。 ```csharp public interface IWechatTenpayClientFactory { WechatTenpayClient Create(string merchantId); } ``` ```csharp internal partial class WechatTenpayClientFactory : IWechatTenpayClientFactory { private readonly IHttpClientFactory _httpClientFactory; private readonly TenpayOptions _tenpayOptions; private readonly IWechatTenpayCertificateManagerFactory _tenpayCertificateManagerFactory; public WechatTenpayClientFactory( IHttpClientFactory httpClientFactory, IOptions<TenpayOptions> tenpayOptions, IWechatTenpayCertificateManagerFactory tenpayCertificateManagerFactory) { _httpClientFactory = httpClientFactory; _tenpayOptions = tenpayOptions.Value; _tenpayCertificateManagerFactory = tenpayCertificateManagerFactory; } public WechatTenpayClient Create(string merchantId) { var tenpayMerchantConfig = _tenpayOptions.Merchants?.FirstOrDefault(e => string.Equals(merchantId, e.MerchantId)); if (tenpayMerchantConfig == null) throw new Exception("未在配置项中找到该 MerchantId 对应的微信商户号。"); var wechatTenpayClientOptions = new WechatTenpayClientOptions() { MerchantId = tenpayMerchantConfig.MerchantId, MerchantV3Secret = tenpayMerchantConfig.SecretV3, MerchantCertificateSerialNumber = tenpayMerchantConfig.CertificateSerialNumber, MerchantCertificatePrivateKey = tenpayMerchantConfig.CertificatePrivateKey, PlatformCertificateManager = _tenpayCertificateManagerFactory.Create(tenpayMerchantConfig.MerchantId), AutoEncryptRequestSensitiveProperty = false, AutoDecryptResponseSensitiveProperty = false }; var wechatTenpayClient = WechatTenpayClientBuilder.Create(wechatTenpayClientOptions) .UseHttpClient(_httpClientFactory.CreateClient(), disposeClient: false) .Build(); return wechatTenpayClient; } } ``` ```csharp builder.Services.AddHostedService<Services.BackgroundServices.TenpayCertificateRefreshingBackgroundService>(); ``` tn2>创建跨域设置 ```csharp public static class NetCoreExtend { public static string corsname = "MyAllowSpecificOrigins"; public static void AddMyServiceCors(this IServiceCollection services) { services.AddCors(options => { options.AddPolicy(name: corsname, builder => { builder.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); } public static void UseMyServiceCors(this WebApplication app) { app.UseCors(corsname); } } ``` ```csharp // 跨域设置 builder.Services.AddMyServiceCors(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseMyServiceCors(); app.UseHttpsRedirection(); app.UseAuthorization(); var s = File.ReadAllText(Path.Combine(app.Environment.ContentRootPath, "z3TAYpMYCs.txt")); // 验证业务域名 app.MapGet("z3TAYpMYCs.txt",content => content.Response.WriteAsync(s)); app.MapControllers(); app.Run(); ``` tn2>创建支付控制器 ```csharp [ApiController] [Route("api/order")] public class TenpayOrderController : ControllerBase { private readonly ILogger _logger; private readonly TenpayOptions _tenpayOptions; private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory; public TenpayOrderController( ILoggerFactory loggerFactory, IOptions<TenpayOptions> tenpayOptions, Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory) { _logger = loggerFactory.CreateLogger(GetType()); _tenpayOptions = tenpayOptions.Value; _wechatTenpayClientFactory = wechatTenpayClientFactory; } /// <summary> /// 发起订单操作 /// </summary> /// <param name="requestModel"></param> /// <returns></returns> [HttpPost] [Route("jsapi")] public async Task<IActionResult> CreateOrderByJsapi([FromBody] Models.CreateOrderByJsapiRequest requestModel) { var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId); var request = new CreatePayTransactionJsapiRequest() { OutTradeNumber = "SAMPLE_OTN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"), AppId = requestModel.AppId, Description = "演示订单", NotifyUrl = _tenpayOptions.SucceedNotifyUrl, Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = requestModel.Amount }, Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = requestModel.OpenId } }; var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken: HttpContext.RequestAborted); if (!response.IsSuccessful()) { _logger.LogWarning( "JSAPI 下单失败(状态码:{0},错误代码:{1},错误描述:{2})。", response.GetRawStatus(), response.ErrorCode, response.ErrorMessage ); return new JsonResult(response); } //传入小程序的appid及微信返回的预支付ID获取想要返回给前端的数据 var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId); return Ok(new { orderrequest = request, wxparam = paramMap }); } /// <summary> /// 查询订单的支付状态根据支付单号查询 /// </summary> /// <returns></returns> [HttpGet("{OutTradeNumber}")] public async Task<IActionResult> QueryDisposeOrderAlipayState(string OutTradeNumber) { //读取微信支付配置文件 var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault(); //创建微信支付客户端 var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId); //创建请求数据 var request = new GetPayTransactionByOutTradeNumberRequest(); request.MerchantId = tenpayAccountOptions.MerchantId;//这里是商户号 request.OutTradeNumber = OutTradeNumber; //发起请求 var response = await client.ExecuteGetPayTransactionByOutTradeNumberAsync(request); if (response.IsSuccessful()) { var eventType = response.TradeState?.ToUpper(); //当支付支付成功 if (eventType == "SUCCESS") { //处理内部业务 //微信支付订单号 var payno = response.TransactionId; //支付时间 var paytime = ((DateTimeOffset)response.SuccessTime).DateTime; _logger.LogInformation("查询到微信支付推送的订单支付成功,支付时间:{0}", ((DateTimeOffset)response.SuccessTime).DateTime); //实际支付金额 var paymoney = (decimal)response.Amount.PayerTotal / 100; _logger.LogInformation("查询到微信支付推送的订单支付成功,支付金额 单位分:{0}", response.Amount.PayerTotal); return Ok("状态码:" + eventType + ",消息:" + response.TradeStateDescription); } else//当查询到的状态是支付不成功 { //处理系统内部业务 return Ok("状态码:" + eventType + ",消息:" + response.TradeStateDescription); } } else { return Ok("查询失败 状态码:" + response.GetRawStatus() + ",错误代码:" + response.ErrorCode + ",错误描述" + response.ErrorMessage); } } /// <summary> /// 支付成功回调接口 /// </summary> /// <param name="dto"></param> /// <returns></returns> [HttpPost("ReceiveSucceedMessage")] public async Task<IActionResult> ReceiveSucceedMessage( [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp, [FromHeader(Name = "Wechatpay-Nonce")] string nonce, [FromHeader(Name = "Wechatpay-Signature")] string signature, [FromHeader(Name = "Wechatpay-Serial")] string serialNumber) { //读取微信支付配置文件 var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault(); using var reader = new StreamReader(Request.Body, Encoding.UTF8); string content = await reader.ReadToEndAsync(); _logger.LogInformation("接收到微信支付推送的数据:{0}", content); var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId); bool valid = client.VerifyEventSignature( webhookTimestamp: timestamp, webhookNonce: nonce, webhookBody: content, webhookSignature: signature, webhookSerialNumber: serialNumber ); //验证签名 if (!valid) { // NOTICE: // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。 // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。 // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。 // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。 _logger.LogInformation("验签失败", content); return new JsonResult(new { code = "FAIL", message = "验签失败" }); } //解密数据 var callbackModel = client.DeserializeEvent(content); var eventType = callbackModel.EventType?.ToUpper(); //当支付成功 if (eventType == "TRANSACTION.SUCCESS") { //处理自己系统的业务 var callbackResource = client.DecryptEventResource<TransactionResource>(callbackModel); _logger.LogInformation("接收到微信支付推送的订单支付成功通知,商户订单号:{0}", callbackResource.OutTradeNumber); _logger.LogInformation("接收到微信支付推送的订单支付成功通知,微信支付订单号:{0}", callbackResource.TransactionId); //订单号 var OutTradeNumber = callbackResource.OutTradeNumber; //微信支付订单号 var Payno = callbackResource.TransactionId; //支付时间 var Paytime = callbackResource.SuccessTime.DateTime; _logger.LogInformation("接收到微信支付推送的订单支付成功通知,支付时间:{0}", callbackResource.SuccessTime.DateTime); //实际支付金额 var Paymoney = (decimal)callbackResource.Amount.PayerTotal;//单位是分 _logger.LogInformation("接收到微信支付推送的订单支付成功通知,支付金额 单位分:{0}", callbackResource.Amount.PayerTotal); return new JsonResult(new { code = "SUCCESS", message = "支付成功" }); } else { // 其他情况略 _logger.LogInformation("支付回调发生严重错误eventType不等于TRANSACTION.SUCCESS:{0}", eventType); return new JsonResult(new { code = "FAIL", message = "eventType不等于TRANSACTION.SUCCESS" }); } } /// <summary> /// 退款成功回调接口 /// </summary> /// <param name="dto"></param> /// <returns></returns> [HttpPost("RefundSucceedMessage")] public async Task<IActionResult> RefundSucceedMessage( [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp, [FromHeader(Name = "Wechatpay-Nonce")] string nonce, [FromHeader(Name = "Wechatpay-Signature")] string signature, [FromHeader(Name = "Wechatpay-Serial")] string serialNumber ) { //读取微信支付配置文件 var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault(); using var reader = new StreamReader(Request.Body, Encoding.UTF8); string content = await reader.ReadToEndAsync(); _logger.LogInformation("接收到微信支付推送的退款通知数据:{0}", content); var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId); bool valid = client.VerifyEventSignature( webhookTimestamp: timestamp, webhookNonce: nonce, webhookBody: content, webhookSignature: signature, webhookSerialNumber: serialNumber ); //验证签名 if (!valid) { // NOTICE: // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。 // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。 // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。 // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。 _logger.LogInformation("验签失败", content); return new JsonResult(new { code = "FAIL", message = "验签失败" }); } var callbackModel = client.DeserializeEvent(content); var eventType = callbackModel.EventType?.ToUpper(); //解密数据 var callbackResource = client.DecryptEventResource<RefundResource>(callbackModel); //退款成功通知 if (eventType == "REFUND.SUCCESS") { //获取状态码 var State = callbackResource.RefundStatus?.ToUpper(); //订单号 var OrderId = long.Parse(callbackResource.OutTradeNumber); //退款单号 var OrderRefId = long.Parse(callbackResource.OutRefundNumber); //微信支付的退款单号 var WxOrderRefId = callbackResource.RefundId; //当退款成功 if (State == "SUCCESS") { //处理自己系统内部业务 //退款成功时间 var SuccessTime = ((DateTimeOffset)callbackResource.SuccessTime).DateTime; //退款成功金额 单位分 var Refund = (decimal)callbackResource.Amount.Refund; _logger.LogInformation("退款成功通知:{0}", callbackResource); return new JsonResult(new { code = "SUCCESS", message = "成功" }); } else { //当不成功的时候处理业务 _logger.LogInformation("退款成功通知:{0}", callbackResource); return new JsonResult(new { code = "SUCCESS", message = "成功" }); } } else//其他情况重新查询退款单 { //其他情况自己处理内部系统业务 return new JsonResult(new { code = "SUCCESS", message = "成功" }); } } [HttpPost] [Route("getconfig")] public async Task<IActionResult> GetConfig() { return Ok(_tenpayOptions); } } ``` tn2>创建退款控制器 ```csharp [ApiController] [Route("api/refund")] public class TenpayRefundController : ControllerBase { private readonly ILogger _logger; private readonly TenpayOptions _tenpayOptions; private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory; public TenpayRefundController( ILoggerFactory loggerFactory, IOptions<TenpayOptions> tenpayOptions, Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory) { _logger = loggerFactory.CreateLogger(GetType()); _tenpayOptions = tenpayOptions.Value; _wechatTenpayClientFactory = wechatTenpayClientFactory; } [HttpPost] [Route("")] public async Task<IActionResult> CreateRefund([FromBody] Models.CreateRefundRequest requestModel) { var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId); var request = new CreateRefundDomesticRefundRequest() { // TransactionId = requestModel.TransactionId, OutTradeNumber = requestModel.TransactionId, OutRefundNumber = "SAMPLE_ORN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"), Amount = new CreateRefundDomesticRefundRequest.Types.Amount() { Total = requestModel.OrderAmount, Refund = requestModel.RefundAmount }, Reason = "示例退款", NotifyUrl = _tenpayOptions.FailNotifyUrl }; var response = await client.ExecuteCreateRefundDomesticRefundAsync(request, cancellationToken: HttpContext.RequestAborted); if (!response.IsSuccessful()) { _logger.LogWarning( "申请退款失败(状态码:{0},错误代码:{1},错误描述:{2})。", response.GetRawStatus(), response.ErrorCode, response.ErrorMessage ); } return new JsonResult(response); } } ``` tn2>创建接收通知的控制器 ```csharp [ApiController] [Route("api/notify")] public class TenpayNotifyController : ControllerBase { private readonly ILogger _logger; private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory; public TenpayNotifyController( ILoggerFactory loggerFactory, Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory) { _logger = loggerFactory.CreateLogger(GetType()); _wechatTenpayClientFactory = wechatTenpayClientFactory; } [HttpPost] [Route("m-{merchant_id}/message-push")] public async Task<IActionResult> ReceiveMessage( [FromRoute(Name = "merchant_id")] string merchantId, [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp, [FromHeader(Name = "Wechatpay-Nonce")] string nonce, [FromHeader(Name = "Wechatpay-Signature")] string signature, [FromHeader(Name = "Wechatpay-Serial")] string serialNumber) { using var reader = new StreamReader(Request.Body, Encoding.UTF8); string content = await reader.ReadToEndAsync(); _logger.LogInformation("接收到微信支付推送的数据:{0}", content); var client = _wechatTenpayClientFactory.Create(merchantId); bool valid = client.VerifyEventSignature( webhookTimestamp: timestamp, webhookNonce: nonce, webhookBody: content, webhookSignature: signature, webhookSerialNumber: serialNumber ); if (!valid) { // NOTICE: // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。 // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。 // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。 // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。 return new JsonResult(new { code = "FAIL", message = "验签失败" }); } var callbackModel = client.DeserializeEvent(content); var eventType = callbackModel.EventType?.ToUpper(); switch (eventType) { case "TRANSACTION.SUCCESS": { var callbackResource = client.DecryptEventResource<SKIT.FlurlHttpClient.Wechat.TenpayV3.Events.TransactionResource>(callbackModel); _logger.LogInformation("接收到微信支付推送的订单支付成功通知,商户订单号:{0}", callbackResource.OutTradeNumber); // 后续处理略 } break; default: { // 其他情况略 } break; } return new JsonResult(new { code = "SUCCESS", message = "成功" }); } } ``` tn2>可通过DockerFile进行发布与支持。 ```bash FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["IPayAI.WeChat.MINIPay/IPayAI.WeChat.MINIPay.csproj", "IPayAI.WeChat.MINIPay/"] RUN dotnet restore "IPayAI.WeChat.MINIPay/IPayAI.WeChat.MINIPay.csproj" COPY . . WORKDIR "/src/IPayAI.WeChat.MINIPay" RUN dotnet build "IPayAI.WeChat.MINIPay.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "IPayAI.WeChat.MINIPay.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . # 设置环境变量,指定证书和密码(如果需要) ENTRYPOINT ["dotnet", "IPayAI.WeChat.MINIPay.dll"] ``` tn2>创建`docker-compose.yml`,通过`docker-compose build`来进行生成容器 ```bash version: '3.4' services: ipayai.wechat.minipay: build: context: . dockerfile: IPayAI.WeChat.MINIPay/Dockerfile image: 127.0.0.1:4443/ipay/ipayaiwechat_mini_pay environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/certificate.pfx - ASPNETCORE_Kestrel__Certificates__Default__Password=123456 - ASPNETCORE_URLS=http://+:80;https://+:443; volumes: - /home/ubuntu/aidasitop_yuming/aidasi.top.pfx:/https/certificate.pfx ports: - "80:80" - "443:443" ``` tn2>并通过`docker-compse up -d`来运行代码 ### 前端验证代码 tn2>首先在新创建的`app.js`,这里写入自己网站相关配置。 ```javascript // app.js App({ onLaunch() { }, globalData: { //MY_Url: "https://localhost:5001/", MY_Url: "域名", MY_MerchantId: "商户号", MY_AppId: "企业Appid", MY_Secret: "企业Secret", } }) ``` tn2>然后我通过我使用vant框架,所以我们需要初始化npm并安装该插件。 ```javascript npm init npm i @vant/weapp -S --production ``` tn2>安装好后在`app.json`中添加我们的button组件信息。 ```javascript "usingComponents": { "van-button": "@vant/weapp/button/index" }, ``` tn2>修改`index.js` ```javascript // index.js Page({ data: { useropenid: "", appid: "", secret: "", merchantid: "", url: "", orderinfo: {}, orderrequestinfo: {}, orderAmount: 0, refundAmount: 0, paymessage: "", refundmessage: "", }, onLoad: function(params) { const app = getApp(); var MY_AppId = app.globalData.MY_AppId var MY_MerchantId = app.globalData.MY_MerchantId var MY_Secret = app.globalData.MY_Secret var MY_Url = app.globalData.MY_Url this.setData({ appid: MY_AppId, secret: MY_Secret, merchantid: MY_MerchantId, url: MY_Url, }) }, sendrefund: function(event){ var data = { MerchantId : this.data.merchantid, TransactionId : this.data.orderrequestinfo.out_trade_no, OrderAmount : this.data.orderAmount, RefundAmount : this.data.refundAmount, } var e = this var fullurl = this.data.url + "/api/refund" wx.request({ url: fullurl, // 后端创建订单的接口 method: 'POST', data, success(res) { e.setData({ refundmessage: "msg: 退款成功 result:"+JSON.stringify(res) }); }, fail(err) { e.setData({ refundmessage: "msg: 退款失败 result:"+JSON.stringify(err) }); } }); }, sendpay: function(event){ console.log("发起支付") var openid = this.data.useropenid var fullurl = this.data.url + "/api/order/jsapi" console.log(fullurl) // 金额0.01元 var amount = 1 var e = this //前端调用后端接口创建订单并获取支付参数 wx.request({ url: fullurl, // 后端创建订单的接口 method: 'POST', data: { // 这些数据应根据实际情况获取 MerchantId: this.data.merchantid, AppId: this.data.appid, OpenId: openid, Amount: amount // 例如,1.00元 }, success(res) { var item = res.data.wxparam if (item) { // 假设prepay_id在返回的data中 // 使用返回的支付参数发起支付 wx.requestPayment({ ...item, success(payRes) { e.setData({ orderinfo: item, orderAmount: amount, refundAmount: amount, paymessage: '支付成功', orderrequestinfo: res.data.orderrequest }) console.log('支付成功', payRes); }, fail(payErr) { e.setData({ paymessage: '支付失败' }) console.log('支付失败', payErr); } }); } else { console.log('创建订单失败', res); } }, fail(err) { console.log('请求后端接口失败', err); } }); }, sendlogin: function(event){ console.log("发起登陆") var e = this wx.login({ success: function(res) { if (res.code) { e.setData({ 'useropenid': res.code }); console.log("登录成功,临时登录凭证:" +e.data.useropenid); } else { console.log('登录失败!' + res.errMsg); } } }); }, info(){ var e = this wx.getUserInfo({ //成功后会返回 success:(res)=>{ console.log(res); // 把你的用户信息存到一个变量中方便下面使用 let userInfo= res.userInfo console.log("getUserInfo:",JSON.stringify(userInfo),e.data.appid,e.data.secret) //获取openId(需要code来换取)这是用户的唯一标识符 // 获取code值 wx.login({ //成功放回 success:(res)=>{ console.log(res); let code=res.code console.log("getCode:",code) // 通过code换取openId var fullurl = this.data.url + "/api/wxuser/"+code wx.request({ url: fullurl, success:(res)=>{ console.log(res); userInfo.openid=res.data.openid console.log("getOpenid:",userInfo) console.log(userInfo.openid); e.setData({ 'useropenid': res.data.openid }); } }) } }) } }) } }) ``` tn2>修改`index.wxml`文件,实现简单登陆、支付和退款功能。 ```javascript <van-button plain type="info" bind:tap="info">登录</van-button> <view>{{ useropenid }}</view> <van-button plain type="info" bind:tap="sendpay">发起支付</van-button> <view style="margin: 10px;"></view> <view>TransactionId or prepay_id(商户号id): {{ orderinfo.package }}</view> <view>OrderAmount(订单金额):{{ orderAmount }} 分</view> <view>RefundAmount(退款金额):{{ refundAmount }} 分</view> <view>Pay Message 支付消息:{{ paymessage }}</view> <view style="margin: 10px;"></view> <van-button plain type="info" bind:tap="sendrefund">发起退款</van-button> <view>Pay Refund 退款消息:{{ refundmessage }}</view> ``` ![](https://img.tnblog.net/arcimg/hb/84d0681f41074a6db3b615ab3b861b0e.png) ![](https://img.tnblog.net/arcimg/hb/e7348798225d45549d8ff1df9fe1d489.jpg)
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
171篇
linux
17篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
4篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
18篇
threejs
2篇
人物
1篇
嵌入式
3篇
python
13篇
HuggingFace
8篇
pytorch
9篇
opencv
6篇
Halcon
3篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术