tnblog
首页
视频
资源
登录

.net6开发企业微信小程序支付流程

2210人阅读 2024/4/11 15:21 总访问:3663696 评论:0 收藏:1 手机
分类: .net后台框架

企业微信小程序支付流程

注册微信公众号


首先通过 https://mp.weixin.qq.com/ 链接进行企业级的小程序注册。


然后打开自己的邮箱,点击收到的邮件的链接进行继续注册。
需要填写如下信息:

  • 实体类型
  • 公司类型
  • 公司名称
  • 营业执照注册号
  • 身份证上的管理员姓名
  • 身份证上号码
  • 管理员的手机号码
  • 短信验证码
  • 小程序的付费验证300元


通过这些还点击继续,这里我就不继续了。
我已经注册好了

申请微信支付商户


我们登录好后点击微信支付—>接入微信支付—>点击申请接入(也就是这里:https://pay.weixin.qq.com/index.php/core/home/login


点击成为微信支付商户号


然后就开始填写商户信息,主要有下面:

  • 姓名
  • 手机号
  • 法人
  • 营业执照
  • 企业银行卡


这个只要慢慢弄一步步弄就好了。
然后关联我们的APP ID


也就是我们小程序中的AppID,复制到这里并进行提交就可以了。

开发配置


通过小程序登录后 https://mp.weixin.qq.com/ ,找到开发配置


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://kf.qq.com/faq/161222NneAJf161222U7fARv.html (证书重要的在于证书序列号,和privatekey)
开通APIv3密钥的时候可以查看这个教程:https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html

创建项目


创建一个.net6的项目,添加相关的包。

  1. <ItemGroup>
  2. <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
  3. <PackageReference Include="Autofac" Version="8.0.0" />
  4. <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
  5. <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.1.0" />
  6. <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.0.0" />
  7. <PackageReference Include="NMemory" Version="3.1.6" />
  8. <PackageReference Include="DistributedLock.Core" Version="1.0.6" />
  9. <PackageReference Include="DistributedLock.FileSystem" Version="1.0.2" />
  10. </ItemGroup>


appsettings.json添加相关配置。

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft.AspNetCore": "Warning"
  6. }
  7. },
  8. "AllowedHosts": "*",
  9. "TenpayOptions": {
  10. "Merchants": [
  11. {
  12. "MerchantId": "商户号",
  13. "SecretV3": "APIv3密钥",
  14. "CertificateSerialNumber": "微信API证书的序列号",
  15. "CertificatePrivateKey": "微信API证书的apiclient_key.pem文件"
  16. }
  17. ],
  18. "SucceedNotifyUrl": "https:/你的域名/api/order/ReceiveSucceedMessage",
  19. "FailNotifyUrl": "https://你的域名/api/order/RefundSucceedMessage"
  20. },
  21. "WechatOptions": {
  22. "Accounts": [
  23. {
  24. "AppId": "企业微信的AppID",
  25. "AppSecret": "企业微信的AppSecret"
  26. }
  27. ],
  28. "CallbackEncodingAESKey": "企业微信的消息推送EncodingAESKey",
  29. "CallbackToken": "企业微信的消息推送token"
  30. }
  31. }


接下来我们要做三件事:
1.通过openid获取AccessToken去获取用户的openid,让我们知道对谁发起支付
2.后台自动刷新相关AccessToken和支付需要的相关证书
3.创建相关控制器

刷新AccessToken


通过微信接收相关参数。

  1. namespace IPayAI.WeChat.MINIPay.OptionTenpays
  2. {
  3. public partial class WechatOptions : IOptions<WechatOptions>
  4. {
  5. WechatOptions IOptions<WechatOptions>.Value => this;
  6. public Types.WechatAccount[] Accounts { get; set; } = Array.Empty<Types.WechatAccount>();
  7. public string CallbackEncodingAESKey { get; set; } = string.Empty;
  8. public string CallbackToken { get; set; } = string.Empty;
  9. }
  10. public partial class WechatOptions
  11. {
  12. public static class Types
  13. {
  14. public class WechatAccount
  15. {
  16. public string? GhId { get; set; }
  17. public string AppId { get; set; } = string.Empty;
  18. public string AppSecret { get; set; } = string.Empty;
  19. }
  20. }
  21. }
  22. }
  1. namespace IPayAI.WeChat.MINIPay.OptionTenpays
  2. {
  3. public partial class TenpayOptions : IOptions<TenpayOptions>
  4. {
  5. TenpayOptions IOptions<TenpayOptions>.Value => this;
  6. public Types.WechatMerchant[] Merchants { get; set; } = Array.Empty<Types.WechatMerchant>();
  7. public string SucceedNotifyUrl { get; set; } = string.Empty;
  8. public string FailNotifyUrl { get; set; } = string.Empty;
  9. }
  10. public partial class TenpayOptions
  11. {
  12. public static class Types
  13. {
  14. public class WechatMerchant
  15. {
  16. public string MerchantId { get; set; } = string.Empty;
  17. public string SecretV3 { get; set; } = string.Empty;
  18. public string CertificateSerialNumber { get; set; } = string.Empty;
  19. public string CertificatePrivateKey { get; set; } = string.Empty;
  20. }
  21. }
  22. }
  23. }
  1. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  2. var builder = WebApplication
  3. .CreateBuilder(args);
  4. // Add services to the container.
  5. builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
  6. builder.Services.AddControllers();
  7. // 注入配置项(内容见 `appsettings.json` 文件)
  8. builder.Services.AddOptions();
  9. builder.Services.Configure<TenpayOptions>(builder.Configuration.GetSection(nameof(TenpayOptions)));
  10. builder.Services.Configure<WechatOptions>(builder.Configuration.GetSection(nameof(WechatOptions)));


Models文件夹下面创建WechatAccessTokenEntity类,用于装AccessToken的类。

  1. public class WechatAccessTokenEntity
  2. {
  3. /// <summary>
  4. /// 企业AppId
  5. /// </summary>
  6. public string AppId { get; set; } = string.Empty;
  7. /// <summary>
  8. /// AccessToken
  9. /// </summary>
  10. public string AccessToken { get; set; } = string.Empty;
  11. public long ExpireTimestamp { get; set; }
  12. public long UpdateTimestamp { get; set; }
  13. public long CreateTimestamp { get; set; }
  14. }


创建分布式锁,便于获取AccessToken时进行锁住其他请求来获取AccessToken

  1. public interface IDistributedLockFactory
  2. {
  3. IDistributedLock Create(string lockName);
  4. }
  1. internal class DistributedLockFactory : IDistributedLockFactory
  2. {
  3. private readonly DirectoryInfo _lockFileDirectory = new DirectoryInfo(Environment.CurrentDirectory);
  4. public IDistributedLock Create(string lockName)
  5. {
  6. // NOTICE:
  7. // 单机演示基于文件实现分布式锁,生产项目请替换成其他实现。
  8. return new FileDistributedLock(_lockFileDirectory, lockName);
  9. }
  10. }
  1. // 注入分布式锁
  2. builder.Services.AddSingleton<IDistributedLockFactory, DistributedLockFactory>();


写一个简单的内存仓储,用于存储WechatAccessTokenEntity

  1. internal class GlobalDatabase
  2. {
  3. static GlobalDatabase()
  4. {
  5. Database db = new Database();
  6. TableWechatAccessTokenEntity = db.Tables.Create<Models.WechatAccessTokenEntity, string>(e => e.AppId);
  7. }
  8. public static Table<WechatAccessTokenEntity, string> TableWechatAccessTokenEntity { get; }
  9. }
  1. public interface IWechatAccessTokenEntityRepository : IEnumerable<Models.WechatAccessTokenEntity>
  2. {
  3. void Insert(Models.WechatAccessTokenEntity entity);
  4. void Update(Models.WechatAccessTokenEntity entity);
  5. void Delete(Models.WechatAccessTokenEntity entity);
  6. }
  1. public class WechatAccessTokenEntityRepository : IWechatAccessTokenEntityRepository
  2. {
  3. public void Insert(Models.WechatAccessTokenEntity entity)
  4. {
  5. entity.CreateTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
  6. entity.UpdateTimestamp = entity.CreateTimestamp;
  7. GlobalDatabase.TableWechatAccessTokenEntity.Insert(entity);
  8. }
  9. public void Update(WechatAccessTokenEntity entity)
  10. {
  11. entity.UpdateTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
  12. GlobalDatabase.TableWechatAccessTokenEntity.Update(entity);
  13. }
  14. public void Delete(WechatAccessTokenEntity entity)
  15. {
  16. GlobalDatabase.TableWechatAccessTokenEntity.Delete(entity);
  17. }
  18. IEnumerator<WechatAccessTokenEntity> IEnumerable<Models.WechatAccessTokenEntity>.GetEnumerator()
  19. {
  20. return GlobalDatabase.TableWechatAccessTokenEntity.GetEnumerator();
  21. }
  22. IEnumerator IEnumerable.GetEnumerator()
  23. {
  24. return GlobalDatabase.TableWechatAccessTokenEntity.GetEnumerator();
  25. }
  26. }
  1. // 注入仓储类
  2. builder.Services.AddSingleton<IWechatAccessTokenEntityRepository, WechatAccessTokenEntityRepository>();


创建相关微信API请求工厂接口并进行依赖注入。

  1. public interface IWechatApiClientFactory
  2. {
  3. WechatApiClient Create(string appId);
  4. }
  1. internal partial class WechatApiClientFactory : IWechatApiClientFactory
  2. {
  3. private readonly IHttpClientFactory _httpClientFactory;
  4. private readonly WechatOptions _wechatOptions;
  5. public WechatApiClientFactory(
  6. IHttpClientFactory httpClientFactory,
  7. IOptions<WechatOptions> wechatOptions)
  8. {
  9. _httpClientFactory = httpClientFactory;
  10. _wechatOptions = wechatOptions.Value;
  11. }
  12. public WechatApiClient Create(string appId)
  13. {
  14. // NOTICE:
  15. // 这里的工厂方法是为了演示多租户而存在的,可根据 AppId 生成不同的 API 客户端。
  16. // 如果你的项目只存在唯一一个租户,那么直接注入 `WechatApiClient` 即可。
  17. var wechatAccountOptions = _wechatOptions.Accounts?.FirstOrDefault(e => string.Equals(appId, e.AppId));
  18. if (wechatAccountOptions == null)
  19. throw new Exception("未在配置项中找到该 AppId 对应的微信账号。");
  20. var wechatApiClientOptions = new WechatApiClientOptions()
  21. {
  22. AppId = wechatAccountOptions.AppId,
  23. AppSecret = wechatAccountOptions.AppSecret,
  24. PushEncodingAESKey = _wechatOptions.CallbackEncodingAESKey,
  25. PushToken = _wechatOptions.CallbackToken
  26. };
  27. var wechatApiClient = WechatApiClientBuilder.Create(wechatApiClientOptions)
  28. .UseHttpClient(_httpClientFactory.CreateClient(), disposeClient: false)
  29. .Build();
  30. return wechatApiClient;
  31. }
  32. }
  1. // 注入工厂 HTTP 客户端
  2. builder.Services.AddHttpClient();
  3. builder.Services.AddSingleton<Services.HttpClients.IWechatApiClientFactory, Services.HttpClients.Implements.WechatApiClientFactory>();


自动创建后台刷新AccessToken的服务

  1. internal class WechatAccessTokenRefreshingBackgroundService : BackgroundService
  2. {
  3. private readonly ILogger _logger;
  4. private readonly WechatOptions _wechatOptions;
  5. private readonly DistributedLock.IDistributedLockFactory _distributedLockFactory;
  6. private readonly HttpClients.IWechatApiClientFactory _wechatApiClientFactory;
  7. private readonly Repositories.IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository;
  8. public WechatAccessTokenRefreshingBackgroundService(
  9. ILoggerFactory loggerFactory,
  10. IOptions<WechatOptions> wechatOptions,
  11. DistributedLock.IDistributedLockFactory distributedLockFactory,
  12. HttpClients.IWechatApiClientFactory wechatApiClientFactory,
  13. Repositories.IWechatAccessTokenEntityRepository wechatAccessTokenEntityRepository)
  14. {
  15. _logger = loggerFactory.CreateLogger(GetType());
  16. _wechatOptions = wechatOptions.Value;
  17. _distributedLockFactory = distributedLockFactory;
  18. _wechatApiClientFactory = wechatApiClientFactory;
  19. _wechatAccessTokenEntityRepository = wechatAccessTokenEntityRepository;
  20. }
  21. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  22. {
  23. while (!stoppingToken.IsCancellationRequested)
  24. {
  25. IList<Task> tasks = new List<Task>();
  26. foreach (var wechatAccount in _wechatOptions.Accounts)
  27. {
  28. Task task = TryRefreshWechatAccessTokenAsync(wechatAccount.AppId, stoppingToken);
  29. tasks.Add(task);
  30. }
  31. await Task.WhenAll(tasks);
  32. await Task.Delay(1 * 60 * 1000); // 每隔 1 分钟轮询刷新
  33. }
  34. }
  35. private async Task TryRefreshWechatAccessTokenAsync(string appId, CancellationToken cancellationToken = default)
  36. {
  37. if (string.IsNullOrEmpty(appId))
  38. return; // 无效参数
  39. var entity = _wechatAccessTokenEntityRepository.FirstOrDefault(e => e.AppId == appId);
  40. if (entity?.ExpireTimestamp > DateTimeOffset.Now.ToUnixTimeSeconds())
  41. return; // AccessToken 未过期
  42. var locker = _distributedLockFactory.Create("accessToken:" + appId);
  43. using var lockHandler = await locker.TryAcquireAsync(TimeSpan.FromSeconds(15), cancellationToken);
  44. if (lockHandler == null)
  45. return; // 未取得锁
  46. var client = _wechatApiClientFactory.Create(appId);
  47. var request = new CgibinTokenRequest();
  48. var response = await client.ExecuteCgibinTokenAsync(request, cancellationToken);
  49. if (!response.IsSuccessful())
  50. {
  51. _logger.LogWarning(
  52. "刷新 AppId 为 {0} 微信 AccessToken 失败(状态码:{1},错误代码:{2},错误描述:{3})。",
  53. appId, response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
  54. );
  55. return; // 请求失败
  56. }
  57. long nextExpireTimestamp = DateTimeOffset.Now
  58. .AddSeconds(response.ExpiresIn)
  59. .AddMinutes(-10)
  60. .ToUnixTimeSeconds(); // 提前十分钟过期,以便于系统能及时刷新,防止因在过期临界点时出现问题
  61. if (entity == null)
  62. {
  63. entity = new Models.WechatAccessTokenEntity()
  64. {
  65. AppId = appId,
  66. AccessToken = response.AccessToken,
  67. ExpireTimestamp = nextExpireTimestamp
  68. };
  69. _wechatAccessTokenEntityRepository.Insert(entity);
  70. }
  71. else
  72. {
  73. entity.AccessToken = response.AccessToken;
  74. entity.ExpireTimestamp = nextExpireTimestamp;
  75. _wechatAccessTokenEntityRepository.Update(entity);
  76. }
  77. _logger.LogInformation("刷新 AppId 为 {0} 的微信 AccessToken 成功。", appId);
  78. }
  79. }
  1. builder.Services.AddHostedService<Services.BackgroundServices.WechatAccessTokenRefreshingBackgroundService>();
  2. builder.Services.AddEndpointsApiExplorer();
  3. builder.Services.AddSwaggerGen();


创建获取微信用户OpenId的控制器

  1. [ApiController]
  2. [Route("api/wxuser")]
  3. public class WxUserController : ControllerBase
  4. {
  5. private readonly ILogger _logger;
  6. readonly IWechatApiClientFactory _httpfactory;
  7. private readonly WechatOptions _wechatOptions;
  8. private readonly IWechatAccessTokenEntityRepository _wechatAccessTokenEntityRepository;
  9. public WxUserController(
  10. ILoggerFactory loggerFactory,
  11. IOptions<WechatOptions> wechatOptions,
  12. IWechatApiClientFactory httpfactory,
  13. IWechatAccessTokenEntityRepository wechatAccessTokenEntityRepository
  14. )
  15. {
  16. _wechatOptions = wechatOptions.Value;
  17. _wechatAccessTokenEntityRepository = wechatAccessTokenEntityRepository;
  18. _httpfactory = httpfactory;
  19. _logger = loggerFactory.CreateLogger(GetType());
  20. }
  21. [HttpGet("{code}")]
  22. public async Task<IActionResult> Get(string code)
  23. {
  24. //读取微信支付配置文件
  25. var tenpayAccountOptions = _wechatOptions.Accounts?.FirstOrDefault();
  26. //创建微信支付客户端
  27. var client = _httpfactory.Create(tenpayAccountOptions.AppId);
  28. var entity = _wechatAccessTokenEntityRepository.FirstOrDefault(e => e.AppId == tenpayAccountOptions.AppId);
  29. var response = await client.ExecuteSnsJsCode2SessionAsync(new SnsJsCode2SessionRequest() { JsCode = code, AccessToken = entity.AccessToken });
  30. if (!response.IsSuccessful())
  31. {
  32. _logger.LogWarning(
  33. "获取用户基本信息失败(状态码:{0},错误代码:{1},错误描述:{2})。",
  34. response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
  35. );
  36. }
  37. return new JsonResult(response);
  38. }
  39. }

微信小程序支付与退款


创建支付类和退款类。

  1. public class CreateOrderByJsapiRequest
  2. {
  3. /// <summary>
  4. /// 商户号
  5. /// </summary>
  6. public string MerchantId { get; set; } = default!;
  7. /// <summary>
  8. /// 企业appid
  9. /// </summary>
  10. public string AppId { get; set; } = default!;
  11. /// <summary>
  12. /// 用户Openid
  13. /// </summary>
  14. public string OpenId { get; set; } = default!;
  15. // NOTICE:
  16. // 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。
  17. public int Amount { get; set; }
  18. }
  1. public class CreateRefundRequest
  2. {
  3. /// <summary>
  4. /// 商户号
  5. /// </summary>
  6. public string MerchantId { get; set; } = default!;
  7. /// <summary>
  8. /// 订单号,商家自定义的订单号
  9. /// </summary>
  10. public string TransactionId { get; set; } = default!;
  11. // NOTICE:
  12. // 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。
  13. public int OrderAmount { get; set; }
  14. /// <summary>
  15. /// 需要退还的金额
  16. /// </summary>
  17. public int RefundAmount { get; set; }
  18. }


创建支付需要的证书管理

  1. public interface IWechatTenpayCertificateManagerFactory
  2. {
  3. ICertificateManager Create(string merchantId);
  4. }
  1. internal partial class WechatTenpayCertificateManagerFactory : IWechatTenpayCertificateManagerFactory
  2. {
  3. private readonly ConcurrentDictionary<string, ICertificateManager> _dict;
  4. public WechatTenpayCertificateManagerFactory()
  5. {
  6. _dict = new ConcurrentDictionary<string, ICertificateManager>();
  7. }
  8. public ICertificateManager Create(string merchantId)
  9. {
  10. return _dict.GetOrAdd(merchantId, new InMemoryCertificateManager());
  11. }
  12. }


创建支付需要的证书请求的请求工厂类。

  1. public interface IWechatTenpayClientFactory
  2. {
  3. WechatTenpayClient Create(string merchantId);
  4. }
  1. internal partial class WechatTenpayClientFactory : IWechatTenpayClientFactory
  2. {
  3. private readonly IHttpClientFactory _httpClientFactory;
  4. private readonly TenpayOptions _tenpayOptions;
  5. private readonly IWechatTenpayCertificateManagerFactory _tenpayCertificateManagerFactory;
  6. public WechatTenpayClientFactory(
  7. IHttpClientFactory httpClientFactory,
  8. IOptions<TenpayOptions> tenpayOptions,
  9. IWechatTenpayCertificateManagerFactory tenpayCertificateManagerFactory)
  10. {
  11. _httpClientFactory = httpClientFactory;
  12. _tenpayOptions = tenpayOptions.Value;
  13. _tenpayCertificateManagerFactory = tenpayCertificateManagerFactory;
  14. }
  15. public WechatTenpayClient Create(string merchantId)
  16. {
  17. var tenpayMerchantConfig = _tenpayOptions.Merchants?.FirstOrDefault(e => string.Equals(merchantId, e.MerchantId));
  18. if (tenpayMerchantConfig == null)
  19. throw new Exception("未在配置项中找到该 MerchantId 对应的微信商户号。");
  20. var wechatTenpayClientOptions = new WechatTenpayClientOptions()
  21. {
  22. MerchantId = tenpayMerchantConfig.MerchantId,
  23. MerchantV3Secret = tenpayMerchantConfig.SecretV3,
  24. MerchantCertificateSerialNumber = tenpayMerchantConfig.CertificateSerialNumber,
  25. MerchantCertificatePrivateKey = tenpayMerchantConfig.CertificatePrivateKey,
  26. PlatformCertificateManager = _tenpayCertificateManagerFactory.Create(tenpayMerchantConfig.MerchantId),
  27. AutoEncryptRequestSensitiveProperty = false,
  28. AutoDecryptResponseSensitiveProperty = false
  29. };
  30. var wechatTenpayClient = WechatTenpayClientBuilder.Create(wechatTenpayClientOptions)
  31. .UseHttpClient(_httpClientFactory.CreateClient(), disposeClient: false)
  32. .Build();
  33. return wechatTenpayClient;
  34. }
  35. }
  1. builder.Services.AddHostedService<Services.BackgroundServices.TenpayCertificateRefreshingBackgroundService>();


创建跨域设置

  1. public static class NetCoreExtend
  2. {
  3. public static string corsname = "MyAllowSpecificOrigins";
  4. public static void AddMyServiceCors(this IServiceCollection services)
  5. {
  6. services.AddCors(options =>
  7. {
  8. options.AddPolicy(name: corsname,
  9. builder =>
  10. {
  11. builder.AllowAnyOrigin()
  12. .AllowAnyHeader()
  13. .AllowAnyMethod();
  14. });
  15. });
  16. }
  17. public static void UseMyServiceCors(this WebApplication app)
  18. {
  19. app.UseCors(corsname);
  20. }
  21. }
  1. // 跨域设置
  2. builder.Services.AddMyServiceCors();
  3. var app = builder.Build();
  4. app.UseSwagger();
  5. app.UseSwaggerUI();
  6. app.UseMyServiceCors();
  7. app.UseHttpsRedirection();
  8. app.UseAuthorization();
  9. var s = File.ReadAllText(Path.Combine(app.Environment.ContentRootPath, "z3TAYpMYCs.txt"));
  10. // 验证业务域名
  11. app.MapGet("z3TAYpMYCs.txt",content => content.Response.WriteAsync(s));
  12. app.MapControllers();
  13. app.Run();


创建支付控制器

  1. [ApiController]
  2. [Route("api/order")]
  3. public class TenpayOrderController : ControllerBase
  4. {
  5. private readonly ILogger _logger;
  6. private readonly TenpayOptions _tenpayOptions;
  7. private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
  8. public TenpayOrderController(
  9. ILoggerFactory loggerFactory,
  10. IOptions<TenpayOptions> tenpayOptions,
  11. Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
  12. {
  13. _logger = loggerFactory.CreateLogger(GetType());
  14. _tenpayOptions = tenpayOptions.Value;
  15. _wechatTenpayClientFactory = wechatTenpayClientFactory;
  16. }
  17. /// <summary>
  18. /// 发起订单操作
  19. /// </summary>
  20. /// <param name="requestModel"></param>
  21. /// <returns></returns>
  22. [HttpPost]
  23. [Route("jsapi")]
  24. public async Task<IActionResult> CreateOrderByJsapi([FromBody] Models.CreateOrderByJsapiRequest requestModel)
  25. {
  26. var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId);
  27. var request = new CreatePayTransactionJsapiRequest()
  28. {
  29. OutTradeNumber = "SAMPLE_OTN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
  30. AppId = requestModel.AppId,
  31. Description = "演示订单",
  32. NotifyUrl = _tenpayOptions.SucceedNotifyUrl,
  33. Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = requestModel.Amount },
  34. Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = requestModel.OpenId }
  35. };
  36. var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken: HttpContext.RequestAborted);
  37. if (!response.IsSuccessful())
  38. {
  39. _logger.LogWarning(
  40. "JSAPI 下单失败(状态码:{0},错误代码:{1},错误描述:{2})。",
  41. response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
  42. );
  43. return new JsonResult(response);
  44. }
  45. //传入小程序的appid及微信返回的预支付ID获取想要返回给前端的数据
  46. var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
  47. return Ok(new { orderrequest = request, wxparam = paramMap });
  48. }
  49. /// <summary>
  50. /// 查询订单的支付状态根据支付单号查询
  51. /// </summary>
  52. /// <returns></returns>
  53. [HttpGet("{OutTradeNumber}")]
  54. public async Task<IActionResult> QueryDisposeOrderAlipayState(string OutTradeNumber)
  55. {
  56. //读取微信支付配置文件
  57. var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault();
  58. //创建微信支付客户端
  59. var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId);
  60. //创建请求数据
  61. var request = new GetPayTransactionByOutTradeNumberRequest();
  62. request.MerchantId = tenpayAccountOptions.MerchantId;//这里是商户号
  63. request.OutTradeNumber = OutTradeNumber;
  64. //发起请求
  65. var response = await client.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
  66. if (response.IsSuccessful())
  67. {
  68. var eventType = response.TradeState?.ToUpper();
  69. //当支付支付成功
  70. if (eventType == "SUCCESS")
  71. {
  72. //处理内部业务
  73. //微信支付订单号
  74. var payno = response.TransactionId;
  75. //支付时间
  76. var paytime = ((DateTimeOffset)response.SuccessTime).DateTime;
  77. _logger.LogInformation("查询到微信支付推送的订单支付成功,支付时间:{0}", ((DateTimeOffset)response.SuccessTime).DateTime);
  78. //实际支付金额
  79. var paymoney = (decimal)response.Amount.PayerTotal / 100;
  80. _logger.LogInformation("查询到微信支付推送的订单支付成功,支付金额 单位分:{0}", response.Amount.PayerTotal);
  81. return Ok("状态码:" + eventType + ",消息:" + response.TradeStateDescription);
  82. }
  83. else//当查询到的状态是支付不成功
  84. {
  85. //处理系统内部业务
  86. return Ok("状态码:" + eventType + ",消息:" + response.TradeStateDescription);
  87. }
  88. }
  89. else
  90. {
  91. return Ok("查询失败 状态码:" + response.GetRawStatus() + ",错误代码:" + response.ErrorCode + ",错误描述" + response.ErrorMessage);
  92. }
  93. }
  94. /// <summary>
  95. /// 支付成功回调接口
  96. /// </summary>
  97. /// <param name="dto"></param>
  98. /// <returns></returns>
  99. [HttpPost("ReceiveSucceedMessage")]
  100. public async Task<IActionResult> ReceiveSucceedMessage(
  101. [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp,
  102. [FromHeader(Name = "Wechatpay-Nonce")] string nonce,
  103. [FromHeader(Name = "Wechatpay-Signature")] string signature,
  104. [FromHeader(Name = "Wechatpay-Serial")] string serialNumber)
  105. {
  106. //读取微信支付配置文件
  107. var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault();
  108. using var reader = new StreamReader(Request.Body, Encoding.UTF8);
  109. string content = await reader.ReadToEndAsync();
  110. _logger.LogInformation("接收到微信支付推送的数据:{0}", content);
  111. var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId);
  112. bool valid = client.VerifyEventSignature(
  113. webhookTimestamp: timestamp,
  114. webhookNonce: nonce,
  115. webhookBody: content,
  116. webhookSignature: signature,
  117. webhookSerialNumber: serialNumber
  118. );
  119. //验证签名
  120. if (!valid)
  121. {
  122. // NOTICE:
  123. // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。
  124. // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
  125. // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。
  126. // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。
  127. _logger.LogInformation("验签失败", content);
  128. return new JsonResult(new { code = "FAIL", message = "验签失败" });
  129. }
  130. //解密数据
  131. var callbackModel = client.DeserializeEvent(content);
  132. var eventType = callbackModel.EventType?.ToUpper();
  133. //当支付成功
  134. if (eventType == "TRANSACTION.SUCCESS")
  135. {
  136. //处理自己系统的业务
  137. var callbackResource = client.DecryptEventResource<TransactionResource>(callbackModel);
  138. _logger.LogInformation("接收到微信支付推送的订单支付成功通知,商户订单号:{0}", callbackResource.OutTradeNumber);
  139. _logger.LogInformation("接收到微信支付推送的订单支付成功通知,微信支付订单号:{0}", callbackResource.TransactionId);
  140. //订单号
  141. var OutTradeNumber = callbackResource.OutTradeNumber;
  142. //微信支付订单号
  143. var Payno = callbackResource.TransactionId;
  144. //支付时间
  145. var Paytime = callbackResource.SuccessTime.DateTime;
  146. _logger.LogInformation("接收到微信支付推送的订单支付成功通知,支付时间:{0}", callbackResource.SuccessTime.DateTime);
  147. //实际支付金额
  148. var Paymoney = (decimal)callbackResource.Amount.PayerTotal;//单位是分
  149. _logger.LogInformation("接收到微信支付推送的订单支付成功通知,支付金额 单位分:{0}", callbackResource.Amount.PayerTotal);
  150. return new JsonResult(new { code = "SUCCESS", message = "支付成功" });
  151. }
  152. else
  153. {
  154. // 其他情况略
  155. _logger.LogInformation("支付回调发生严重错误eventType不等于TRANSACTION.SUCCESS:{0}", eventType);
  156. return new JsonResult(new { code = "FAIL", message = "eventType不等于TRANSACTION.SUCCESS" });
  157. }
  158. }
  159. /// <summary>
  160. /// 退款成功回调接口
  161. /// </summary>
  162. /// <param name="dto"></param>
  163. /// <returns></returns>
  164. [HttpPost("RefundSucceedMessage")]
  165. public async Task<IActionResult> RefundSucceedMessage(
  166. [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp,
  167. [FromHeader(Name = "Wechatpay-Nonce")] string nonce,
  168. [FromHeader(Name = "Wechatpay-Signature")] string signature,
  169. [FromHeader(Name = "Wechatpay-Serial")] string serialNumber
  170. )
  171. {
  172. //读取微信支付配置文件
  173. var tenpayAccountOptions = _tenpayOptions.Merchants?.FirstOrDefault();
  174. using var reader = new StreamReader(Request.Body, Encoding.UTF8);
  175. string content = await reader.ReadToEndAsync();
  176. _logger.LogInformation("接收到微信支付推送的退款通知数据:{0}", content);
  177. var client = _wechatTenpayClientFactory.Create(tenpayAccountOptions.MerchantId);
  178. bool valid = client.VerifyEventSignature(
  179. webhookTimestamp: timestamp,
  180. webhookNonce: nonce,
  181. webhookBody: content,
  182. webhookSignature: signature,
  183. webhookSerialNumber: serialNumber
  184. );
  185. //验证签名
  186. if (!valid)
  187. {
  188. // NOTICE:
  189. // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。
  190. // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
  191. // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。
  192. // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。
  193. _logger.LogInformation("验签失败", content);
  194. return new JsonResult(new { code = "FAIL", message = "验签失败" });
  195. }
  196. var callbackModel = client.DeserializeEvent(content);
  197. var eventType = callbackModel.EventType?.ToUpper();
  198. //解密数据
  199. var callbackResource = client.DecryptEventResource<RefundResource>(callbackModel);
  200. //退款成功通知
  201. if (eventType == "REFUND.SUCCESS")
  202. {
  203. //获取状态码
  204. var State = callbackResource.RefundStatus?.ToUpper();
  205. //订单号
  206. var OrderId = long.Parse(callbackResource.OutTradeNumber);
  207. //退款单号
  208. var OrderRefId = long.Parse(callbackResource.OutRefundNumber);
  209. //微信支付的退款单号
  210. var WxOrderRefId = callbackResource.RefundId;
  211. //当退款成功
  212. if (State == "SUCCESS")
  213. {
  214. //处理自己系统内部业务
  215. //退款成功时间
  216. var SuccessTime = ((DateTimeOffset)callbackResource.SuccessTime).DateTime;
  217. //退款成功金额 单位分
  218. var Refund = (decimal)callbackResource.Amount.Refund;
  219. _logger.LogInformation("退款成功通知:{0}", callbackResource);
  220. return new JsonResult(new { code = "SUCCESS", message = "成功" });
  221. }
  222. else
  223. {
  224. //当不成功的时候处理业务
  225. _logger.LogInformation("退款成功通知:{0}", callbackResource);
  226. return new JsonResult(new { code = "SUCCESS", message = "成功" });
  227. }
  228. }
  229. else//其他情况重新查询退款单
  230. {
  231. //其他情况自己处理内部系统业务
  232. return new JsonResult(new { code = "SUCCESS", message = "成功" });
  233. }
  234. }
  235. [HttpPost]
  236. [Route("getconfig")]
  237. public async Task<IActionResult> GetConfig()
  238. {
  239. return Ok(_tenpayOptions);
  240. }
  241. }


创建退款控制器

  1. [ApiController]
  2. [Route("api/refund")]
  3. public class TenpayRefundController : ControllerBase
  4. {
  5. private readonly ILogger _logger;
  6. private readonly TenpayOptions _tenpayOptions;
  7. private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
  8. public TenpayRefundController(
  9. ILoggerFactory loggerFactory,
  10. IOptions<TenpayOptions> tenpayOptions,
  11. Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
  12. {
  13. _logger = loggerFactory.CreateLogger(GetType());
  14. _tenpayOptions = tenpayOptions.Value;
  15. _wechatTenpayClientFactory = wechatTenpayClientFactory;
  16. }
  17. [HttpPost]
  18. [Route("")]
  19. public async Task<IActionResult> CreateRefund([FromBody] Models.CreateRefundRequest requestModel)
  20. {
  21. var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId);
  22. var request = new CreateRefundDomesticRefundRequest()
  23. {
  24. // TransactionId = requestModel.TransactionId,
  25. OutTradeNumber = requestModel.TransactionId,
  26. OutRefundNumber = "SAMPLE_ORN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
  27. Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
  28. {
  29. Total = requestModel.OrderAmount,
  30. Refund = requestModel.RefundAmount
  31. },
  32. Reason = "示例退款",
  33. NotifyUrl = _tenpayOptions.FailNotifyUrl
  34. };
  35. var response = await client.ExecuteCreateRefundDomesticRefundAsync(request, cancellationToken: HttpContext.RequestAborted);
  36. if (!response.IsSuccessful())
  37. {
  38. _logger.LogWarning(
  39. "申请退款失败(状态码:{0},错误代码:{1},错误描述:{2})。",
  40. response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
  41. );
  42. }
  43. return new JsonResult(response);
  44. }
  45. }


创建接收通知的控制器

  1. [ApiController]
  2. [Route("api/notify")]
  3. public class TenpayNotifyController : ControllerBase
  4. {
  5. private readonly ILogger _logger;
  6. private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
  7. public TenpayNotifyController(
  8. ILoggerFactory loggerFactory,
  9. Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
  10. {
  11. _logger = loggerFactory.CreateLogger(GetType());
  12. _wechatTenpayClientFactory = wechatTenpayClientFactory;
  13. }
  14. [HttpPost]
  15. [Route("m-{merchant_id}/message-push")]
  16. public async Task<IActionResult> ReceiveMessage(
  17. [FromRoute(Name = "merchant_id")] string merchantId,
  18. [FromHeader(Name = "Wechatpay-Timestamp")] string timestamp,
  19. [FromHeader(Name = "Wechatpay-Nonce")] string nonce,
  20. [FromHeader(Name = "Wechatpay-Signature")] string signature,
  21. [FromHeader(Name = "Wechatpay-Serial")] string serialNumber)
  22. {
  23. using var reader = new StreamReader(Request.Body, Encoding.UTF8);
  24. string content = await reader.ReadToEndAsync();
  25. _logger.LogInformation("接收到微信支付推送的数据:{0}", content);
  26. var client = _wechatTenpayClientFactory.Create(merchantId);
  27. bool valid = client.VerifyEventSignature(
  28. webhookTimestamp: timestamp,
  29. webhookNonce: nonce,
  30. webhookBody: content,
  31. webhookSignature: signature,
  32. webhookSerialNumber: serialNumber
  33. );
  34. if (!valid)
  35. {
  36. // NOTICE:
  37. // 需提前注入 CertificateManager、并下载平台证书,才可以使用扩展方法执行验签操作。
  38. // 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
  39. // 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。
  40. // 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。
  41. return new JsonResult(new { code = "FAIL", message = "验签失败" });
  42. }
  43. var callbackModel = client.DeserializeEvent(content);
  44. var eventType = callbackModel.EventType?.ToUpper();
  45. switch (eventType)
  46. {
  47. case "TRANSACTION.SUCCESS":
  48. {
  49. var callbackResource = client.DecryptEventResource<SKIT.FlurlHttpClient.Wechat.TenpayV3.Events.TransactionResource>(callbackModel);
  50. _logger.LogInformation("接收到微信支付推送的订单支付成功通知,商户订单号:{0}", callbackResource.OutTradeNumber);
  51. // 后续处理略
  52. }
  53. break;
  54. default:
  55. {
  56. // 其他情况略
  57. }
  58. break;
  59. }
  60. return new JsonResult(new { code = "SUCCESS", message = "成功" });
  61. }
  62. }


可通过DockerFile进行发布与支持。

  1. FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
  2. WORKDIR /app
  3. EXPOSE 80
  4. FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
  5. WORKDIR /src
  6. COPY ["IPayAI.WeChat.MINIPay/IPayAI.WeChat.MINIPay.csproj", "IPayAI.WeChat.MINIPay/"]
  7. RUN dotnet restore "IPayAI.WeChat.MINIPay/IPayAI.WeChat.MINIPay.csproj"
  8. COPY . .
  9. WORKDIR "/src/IPayAI.WeChat.MINIPay"
  10. RUN dotnet build "IPayAI.WeChat.MINIPay.csproj" -c Release -o /app/build
  11. FROM build AS publish
  12. RUN dotnet publish "IPayAI.WeChat.MINIPay.csproj" -c Release -o /app/publish
  13. FROM base AS final
  14. WORKDIR /app
  15. COPY --from=publish /app/publish .
  16. # 设置环境变量,指定证书和密码(如果需要)
  17. ENTRYPOINT ["dotnet", "IPayAI.WeChat.MINIPay.dll"]


创建docker-compose.yml,通过docker-compose build来进行生成容器

  1. version: '3.4'
  2. services:
  3. ipayai.wechat.minipay:
  4. build:
  5. context: .
  6. dockerfile: IPayAI.WeChat.MINIPay/Dockerfile
  7. image: 127.0.0.1:4443/ipay/ipayaiwechat_mini_pay
  8. environment:
  9. - ASPNETCORE_ENVIRONMENT=Development
  10. - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/certificate.pfx
  11. - ASPNETCORE_Kestrel__Certificates__Default__Password=123456
  12. - ASPNETCORE_URLS=http://+:80;https://+:443;
  13. volumes:
  14. - /home/ubuntu/aidasitop_yuming/aidasi.top.pfx:/https/certificate.pfx
  15. ports:
  16. - "80:80"
  17. - "443:443"


并通过docker-compse up -d来运行代码

前端验证代码


首先在新创建的app.js,这里写入自己网站相关配置。

  1. // app.js
  2. App({
  3. onLaunch() {
  4. },
  5. globalData: {
  6. //MY_Url: "https://localhost:5001/",
  7. MY_Url: "域名",
  8. MY_MerchantId: "商户号",
  9. MY_AppId: "企业Appid",
  10. MY_Secret: "企业Secret",
  11. }
  12. })


然后我通过我使用vant框架,所以我们需要初始化npm并安装该插件。

  1. npm init
  2. npm i @vant/weapp -S --production


安装好后在app.json中添加我们的button组件信息。

  1. "usingComponents": {
  2. "van-button": "@vant/weapp/button/index"
  3. },


修改index.js

  1. // index.js
  2. Page({
  3. data: {
  4. useropenid: "",
  5. appid: "",
  6. secret: "",
  7. merchantid: "",
  8. url: "",
  9. orderinfo: {},
  10. orderrequestinfo: {},
  11. orderAmount: 0,
  12. refundAmount: 0,
  13. paymessage: "",
  14. refundmessage: "",
  15. },
  16. onLoad: function(params) {
  17. const app = getApp();
  18. var MY_AppId = app.globalData.MY_AppId
  19. var MY_MerchantId = app.globalData.MY_MerchantId
  20. var MY_Secret = app.globalData.MY_Secret
  21. var MY_Url = app.globalData.MY_Url
  22. this.setData({
  23. appid: MY_AppId,
  24. secret: MY_Secret,
  25. merchantid: MY_MerchantId,
  26. url: MY_Url,
  27. })
  28. },
  29. sendrefund: function(event){
  30. var data = {
  31. MerchantId : this.data.merchantid,
  32. TransactionId : this.data.orderrequestinfo.out_trade_no,
  33. OrderAmount : this.data.orderAmount,
  34. RefundAmount : this.data.refundAmount,
  35. }
  36. var e = this
  37. var fullurl = this.data.url + "/api/refund"
  38. wx.request({
  39. url: fullurl, // 后端创建订单的接口
  40. method: 'POST',
  41. data,
  42. success(res) {
  43. e.setData({
  44. refundmessage: "msg: 退款成功 result:"+JSON.stringify(res)
  45. });
  46. },
  47. fail(err) {
  48. e.setData({
  49. refundmessage: "msg: 退款失败 result:"+JSON.stringify(err)
  50. });
  51. }
  52. });
  53. },
  54. sendpay: function(event){
  55. console.log("发起支付")
  56. var openid = this.data.useropenid
  57. var fullurl = this.data.url + "/api/order/jsapi"
  58. console.log(fullurl)
  59. // 金额0.01元
  60. var amount = 1
  61. var e = this
  62. //前端调用后端接口创建订单并获取支付参数
  63. wx.request({
  64. url: fullurl, // 后端创建订单的接口
  65. method: 'POST',
  66. data: {
  67. // 这些数据应根据实际情况获取
  68. MerchantId: this.data.merchantid,
  69. AppId: this.data.appid,
  70. OpenId: openid,
  71. Amount: amount // 例如,1.00元
  72. },
  73. success(res) {
  74. var item = res.data.wxparam
  75. if (item) { // 假设prepay_id在返回的data中
  76. // 使用返回的支付参数发起支付
  77. wx.requestPayment({
  78. ...item,
  79. success(payRes) {
  80. e.setData({
  81. orderinfo: item,
  82. orderAmount: amount,
  83. refundAmount: amount,
  84. paymessage: '支付成功',
  85. orderrequestinfo: res.data.orderrequest
  86. })
  87. console.log('支付成功', payRes);
  88. },
  89. fail(payErr) {
  90. e.setData({
  91. paymessage: '支付失败'
  92. })
  93. console.log('支付失败', payErr);
  94. }
  95. });
  96. } else {
  97. console.log('创建订单失败', res);
  98. }
  99. },
  100. fail(err) {
  101. console.log('请求后端接口失败', err);
  102. }
  103. });
  104. },
  105. sendlogin: function(event){
  106. console.log("发起登陆")
  107. var e = this
  108. wx.login({
  109. success: function(res) {
  110. if (res.code) {
  111. e.setData({
  112. 'useropenid': res.code
  113. });
  114. console.log("登录成功,临时登录凭证:" +e.data.useropenid);
  115. } else {
  116. console.log('登录失败!' + res.errMsg);
  117. }
  118. }
  119. });
  120. },
  121. info(){
  122. var e = this
  123. wx.getUserInfo({
  124. //成功后会返回
  125. success:(res)=>{
  126. console.log(res);
  127. // 把你的用户信息存到一个变量中方便下面使用
  128. let userInfo= res.userInfo
  129. console.log("getUserInfo:",JSON.stringify(userInfo),e.data.appid,e.data.secret)
  130. //获取openId(需要code来换取)这是用户的唯一标识符
  131. // 获取code值
  132. wx.login({
  133. //成功放回
  134. success:(res)=>{
  135. console.log(res);
  136. let code=res.code
  137. console.log("getCode:",code)
  138. // 通过code换取openId
  139. var fullurl = this.data.url + "/api/wxuser/"+code
  140. wx.request({
  141. url: fullurl,
  142. success:(res)=>{
  143. console.log(res);
  144. userInfo.openid=res.data.openid
  145. console.log("getOpenid:",userInfo)
  146. console.log(userInfo.openid);
  147. e.setData({
  148. 'useropenid': res.data.openid
  149. });
  150. }
  151. })
  152. }
  153. })
  154. }
  155. })
  156. }
  157. })


修改index.wxml文件,实现简单登陆、支付和退款功能。

  1. <van-button plain type="info" bind:tap="info">登录</van-button>
  2. <view>{{ useropenid }}</view>
  3. <van-button plain type="info" bind:tap="sendpay">发起支付</van-button>
  4. <view style="margin: 10px;"></view>
  5. <view>TransactionId or prepay_id(商户号id): {{ orderinfo.package }}</view>
  6. <view>OrderAmount(订单金额):{{ orderAmount }} 分</view>
  7. <view>RefundAmount(退款金额):{{ refundAmount }} 分</view>
  8. <view>Pay Message 支付消息:{{ paymessage }}</view>
  9. <view style="margin: 10px;"></view>
  10. <van-button plain type="info" bind:tap="sendrefund">发起退款</van-button>
  11. <view>Pay Refund 退款消息:{{ refundmessage }}</view>


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

青春年华

2020/3/8 1:47:31

优秀

这一世以无限游戏为使命!
排名
11
文章
201
粉丝
10
评论
13
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术