Skip to content

Commit

Permalink
Merge pull request #101 from EasyAbp/wechatpay-v3-jspayment
Browse files Browse the repository at this point in the history
Reimplemented the signature interface for JS API payment (V3).
  • Loading branch information
real-zony authored Jan 7, 2024
2 parents a70e20d + 210eeb9 commit 5488a62
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;

namespace EasyAbp.Abp.WeChat.Pay.RequestHandling.Dtos;

[Serializable]
public class GetJsSdkWeChatPayParametersInput
{
[CanBeNull]
public string MchId { get; set; }

[Required]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using EasyAbp.Abp.WeChat.Common.Extensions;
using EasyAbp.Abp.WeChat.Common.Infrastructure;
using EasyAbp.Abp.WeChat.Common.Infrastructure.Signature;
using EasyAbp.Abp.WeChat.Pay.Options;
using EasyAbp.Abp.WeChat.Pay.RequestHandling.Dtos;
using EasyAbp.Abp.WeChat.Pay.Security;
using Volo.Abp.DependencyInjection;

namespace EasyAbp.Abp.WeChat.Pay.RequestHandling;

public class WeChatPayClientRequestHandlingService : IWeChatPayClientRequestHandlingService, ITransientDependency
{
private readonly ICertificatesManager _certificatesManager;
private readonly IAbpWeChatPayOptionsProvider _optionsProvider;
private readonly ISignatureGenerator _signatureGenerator;

public WeChatPayClientRequestHandlingService(
IAbpWeChatPayOptionsProvider optionsProvider,
ISignatureGenerator signatureGenerator)
public WeChatPayClientRequestHandlingService(ICertificatesManager certificatesManager,
IAbpWeChatPayOptionsProvider optionsProvider)
{
_certificatesManager = certificatesManager;
_optionsProvider = optionsProvider;
_signatureGenerator = signatureGenerator;
}

public virtual async Task<GetJsSdkWeChatPayParametersResult> GetJsSdkWeChatPayParametersAsync(
Expand All @@ -32,20 +30,18 @@ public virtual async Task<GetJsSdkWeChatPayParametersResult> GetJsSdkWeChatPayPa

var options = await _optionsProvider.GetAsync(input.MchId);

const string signType = "RSA";
var nonceStr = RandomStringHelper.GetRandomString();
var timeStamp = DateTimeHelper.GetNowTimeStamp();
var package = $"prepay_id={input.PrepayId}";

const string signType = "MD5";

var @params = new WeChatParameters();
@params.AddParameter("appId", input.AppId);
@params.AddParameter("nonceStr", nonceStr);
@params.AddParameter("timeStamp", timeStamp);
@params.AddParameter("package", package);
@params.AddParameter("signType", signType);

var paySign = _signatureGenerator.Generate(@params, MD5.Create(), options.ApiV3Key);
var waitSignString = new StringBuilder();
waitSignString.Append(input.AppId).Append('\n')
.Append(timeStamp).Append('\n')
.Append(nonceStr).Append('\n')
.Append("prepay_id=").Append(input.PrepayId).Append('\n');
var certificate = await _certificatesManager.GetCertificateAsync(options.MchId);
var paySign = _certificatesManager.GetSignature(waitSignString.ToString(), certificate);

return new GetJsSdkWeChatPayParametersResult(nonceStr, timeStamp, package, signType, paySign);
}
Expand Down
11 changes: 11 additions & 0 deletions src/Pay/EasyAbp.Abp.WeChat.Pay/Security/CertificatesManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using EasyAbp.Abp.WeChat.Pay.Options;
using Volo.Abp.BlobStoring;
Expand Down Expand Up @@ -36,6 +39,14 @@ public async Task<WeChatPayCertificate> GetCertificateAsync(string mchId)
new WeChatPayCertificate(options.MchId, certificateBytes, options.CertificateSecret))).Value;
}

public string GetSignature(string pendingSignature, WeChatPayCertificate certificate)
{
var privateKey = certificate.X509Certificate.GetRSAPrivateKey();
var signDataBytes = privateKey.SignData(Encoding.UTF8.GetBytes(pendingSignature), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

return Convert.ToBase64String(signDataBytes);
}

protected virtual async Task<byte[]> GetX509CertificateBytesAsync(AbpWeChatPayOptions options)
{
if (options.CertificateBlobName.IsNullOrEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ public interface ICertificatesManager
/// <exception cref="ArgumentNullException">当证书的 BLOB 不存在时抛出此异常。</exception>
/// <exception cref="NullReferenceException">无法获取证书时会抛出此异常。</exception>
Task<WeChatPayCertificate> GetCertificateAsync(string mchId);

/// <summary>
/// 使用微信支付证书对待签名字符串进行签名。
/// </summary>
/// <param name="pendingSignature">等待签名的字符串。</param>
/// <param name="certificate">用于签名的证书实例。</param>
/// <returns>具体的签名数据,使用 Base64 编码。</returns>
string GetSignature(string pendingSignature, WeChatPayCertificate certificate);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using EasyAbp.Abp.WeChat.Common.Extensions;
using EasyAbp.Abp.WeChat.Pay.ApiRequests;
Expand All @@ -21,7 +17,7 @@ public class WeChatPayAuthorizationGenerator : IWeChatPayAuthorizationGenerator,
/// 授权(Authorization)标头的认证类型。
/// </summary>
public const string AuthorizationScheme = "WECHATPAY2-SHA256-RSA2048";

private readonly IAbpWeChatPayOptionsProvider _weChatPayOptionsProvider;
private readonly ICertificatesManager _certificatesManager;

Expand All @@ -41,17 +37,9 @@ public async Task<string> GenerateAuthorizationAsync(HttpMethod method, string u
var requestModel = new WeChatPayApiRequestModel(method, url, body, timeStamp, nonceStr);
var pendingSignature = requestModel.GetPendingSignatureString();
var certificate = await _certificatesManager.GetCertificateAsync(mchId);
var signString = RsaSign(pendingSignature, certificate);
var signString = _certificatesManager.GetSignature(pendingSignature, certificate);

return
$"{AuthorizationScheme} mchid=\"{options.MchId}\",nonce_str=\"{nonceStr}\",timestamp=\"{timeStamp}\",serial_no=\"{certificate.X509Certificate.SerialNumber}\",signature=\"{signString}\"";
}

private string RsaSign(string pendingSignature, WeChatPayCertificate certificate)
{
var privateKey = certificate.X509Certificate.GetRSAPrivateKey();
var signDataBytes = privateKey.SignData(Encoding.UTF8.GetBytes(pendingSignature), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

return Convert.ToBase64String(signDataBytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public sealed class WeChatPayCertificate
/// </summary>
/// <param name="mchId">商户号。</param>
/// <param name="certificateBytes">X509 证书实例。</param>
/// <param name="password">X509 证书的哈希值,用于快速比对证书是否发生变化。</param>
/// <param name="password">X509 证书的密码。</param>
public WeChatPayCertificate(string mchId, byte[] certificateBytes, string password)
{
MchId = mchId;
Expand Down

0 comments on commit 5488a62

Please sign in to comment.