添加银联协议支付 (#58)

* 优化测试环境签名失败问题

* 添加协议支付

* 修复清算金额字段错误

* 修改文件名

* 增加证书base64字符串支持

* 添加协议号为空判断
This commit is contained in:
Varorbc
2020-11-18 10:05:17 +08:00
committed by GitHub
parent b82cb29499
commit 12bcc619ee
9 changed files with 113 additions and 143 deletions
@@ -16,20 +16,16 @@ using PaySharp.Unionpay.Properties;
namespace PaySharp.Unionpay
{
internal static class Util
public static class CertUtil
{
#region 字段
private static Pkcs12Store _pkcs12Store;
private static string _aliase;
#endregion
#region 证书
private static X509Certificate ACPENCCER = null;
private static X509Certificate ACPROOTCER = null;
private static X509Certificate ACPMIDDLECER = null;
private static X509Certificate _acpEncCer;
private static X509Certificate _acpRootCer;
private static X509Certificate _acpMiddleCer;
internal static bool IsTest { get; set; }
#endregion
@@ -38,13 +34,23 @@ namespace PaySharp.Unionpay
/// <summary>
/// 获取Pkcs12Store
/// </summary>
/// <param name="path">证书路径</param>
/// <param name="cert">证书路径或base64字符串</param>
/// <param name="pwd">证书密码</param>
/// <returns></returns>
private static void GetPkcs12Store(string path, string pwd)
private static void GetPkcs12Store(string cert, string pwd)
{
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var pkcs12Store = new Pkcs12Store(fileStream, pwd.ToCharArray());
Stream stream;
if (File.Exists(cert))
{
stream = new FileStream(cert, FileMode.Open, FileAccess.Read, FileShare.Read);
}
else
{
var buffer = Convert.FromBase64String(cert);
stream = new MemoryStream(buffer);
}
var pkcs12Store = new Pkcs12Store(stream, pwd.ToCharArray());
foreach (string item in pkcs12Store.Aliases)
{
if (pkcs12Store.IsKeyEntry(item))
@@ -53,19 +59,21 @@ namespace PaySharp.Unionpay
_aliase = item;
}
}
stream.Dispose();
}
/// <summary>
/// 获取证书编号
/// </summary>
/// <param name="path">证书路径</param>
/// <param name="cert">证书路径或base64字符串</param>
/// <param name="pwd">证书密码</param>
/// <returns></returns>
public static string GetCertId(string path, string pwd)
public static string GetCertId(string cert, string pwd)
{
if (string.IsNullOrEmpty(_aliase))
{
GetPkcs12Store(path, pwd);
GetPkcs12Store(cert, pwd);
}
return _pkcs12Store
@@ -84,14 +92,14 @@ namespace PaySharp.Unionpay
/// <summary>
/// 获取证书编号
/// </summary>
/// <param name="path">证书路径</param>
/// <param name="cert">证书路径或base64字符串</param>
/// <param name="pwd">证书密码</param>
/// <returns></returns>
public static AsymmetricKeyParameter GetCertKey(string path, string pwd)
public static AsymmetricKeyParameter GetCertKey(string cert, string pwd)
{
if (string.IsNullOrEmpty(_aliase))
{
GetPkcs12Store(path, pwd);
GetPkcs12Store(cert, pwd);
}
return _pkcs12Store
@@ -136,7 +144,7 @@ namespace PaySharp.Unionpay
var signByte = Convert.FromBase64String(sign);
var x509Cert = VerifyAndGetPubKey(signPubKeyCert);
return x509Cert == null ? false : VerifySignature(x509Cert.GetPublicKey(), signByte, dataByte);
return x509Cert != null && VerifySignature(x509Cert.GetPublicKey(), signByte, dataByte);
}
private static bool VerifySignature(AsymmetricKeyParameter key, byte[] base64DecodingSignStr, byte[] srcByte)
@@ -156,15 +164,8 @@ namespace PaySharp.Unionpay
private static bool VerifyCertificate(X509Certificate x509Cert)
{
var cn = GetIdentitiesFromCertficate(x509Cert);
try
{
x509Cert.CheckValidity();//验证有效期
if (!VerifyCertificateChain(x509Cert))//验证书链
{
return false;
}
}
catch
x509Cert.CheckValidity();//验证有效期
if (!VerifyCertificateChain(x509Cert))//验证书链
{
return false;
}
@@ -192,117 +193,70 @@ namespace PaySharp.Unionpay
return false;
}
try
var selector = new X509CertStoreSelector
{
var selector = new X509CertStoreSelector
{
Subject = cert.SubjectDN
};
Subject = cert.SubjectDN
};
ISet trustAnchors = new HashSet
{
new TrustAnchor(rootCert, null)
};
var pkixParams = new PkixBuilderParameters(trustAnchors, selector);
IList intermediateCerts = new ArrayList
{
rootCert,
middleCert,
cert
};
pkixParams.IsRevocationEnabled = false;
var intermediateCertStore = X509StoreFactory.Create(
"Certificate/Collection",
new X509CollectionStoreParameters(intermediateCerts));
pkixParams.AddStore(intermediateCertStore);
var pathBuilder = new PkixCertPathBuilder();
var result = pathBuilder.Build(pkixParams);
var path = result.CertPath;
return true;
}
catch
ISet trustAnchors = new HashSet
{
return false;
}
new TrustAnchor(rootCert, null)
};
var pkixParams = new PkixBuilderParameters(trustAnchors, selector);
IList intermediateCerts = new ArrayList
{
rootCert,
middleCert,
cert
};
pkixParams.IsRevocationEnabled = false;
var intermediateCertStore = X509StoreFactory.Create(
"Certificate/Collection",
new X509CollectionStoreParameters(intermediateCerts));
pkixParams.AddStore(intermediateCertStore);
var pathBuilder = new PkixCertPathBuilder();
var result = pathBuilder.Build(pkixParams);
_ = result.CertPath;
return true;
}
private static X509Certificate GetCert(byte[] input)
{
try
{
return new X509CertificateParser().ReadCertificate(input);
}
catch
{
return null;
}
return new X509CertificateParser().ReadCertificate(input);
}
private static X509Certificate GetRootCert()
{
try
if (_acpRootCer is null)
{
if (ACPROOTCER is null)
{
#if DEBUG
ACPROOTCER = GetCert(Resources.acp_test_root);
#else
ACPROOTCER= GetCert(Resources.acp_prod_root);
#endif
}
_acpRootCer = IsTest ? GetCert(Resources.acp_test_root) : GetCert(Resources.acp_prod_root);
}
return ACPROOTCER;
}
catch
{
return null;
}
return _acpRootCer;
}
private static X509Certificate GetMiddleCert()
{
try
if (_acpMiddleCer is null)
{
if (ACPMIDDLECER is null)
{
#if DEBUG
ACPMIDDLECER = GetCert(Resources.acp_test_middle);
#else
ACPMIDDLECER= GetCert(Resources.acp_prod_middle);
#endif
}
_acpMiddleCer = IsTest ? GetCert(Resources.acp_test_middle) : GetCert(Resources.acp_prod_middle);
}
return ACPMIDDLECER;
}
catch
{
return null;
}
return _acpMiddleCer;
}
private static X509Certificate GetEncCert()
{
try
if (_acpEncCer is null)
{
if (ACPENCCER is null)
{
#if DEBUG
ACPENCCER = GetCert(Resources.acp_test_enc);
#else
ACPENCCER= GetCert(Resources.acp_prod_enc);
#endif
}
_acpEncCer = IsTest ? GetCert(Resources.acp_test_enc) : GetCert(Resources.acp_prod_enc);
}
return ACPENCCER;
}
catch
{
return null;
}
return _acpEncCer;
}
private static string GetIdentitiesFromCertficate(X509Certificate aCert)
@@ -322,20 +276,13 @@ namespace PaySharp.Unionpay
private static X509Certificate GetPubKeyCert(string pubKeyCert)
{
try
{
pubKeyCert = pubKeyCert
.Replace("-----END CERTIFICATE-----", "")
.Replace("-----BEGIN CERTIFICATE-----", "");
var x509CertBytes = Convert.FromBase64String(pubKeyCert);
var cf = new X509CertificateParser();
var x509Cert = cf.ReadCertificate(x509CertBytes);
return x509Cert;
}
catch
{
return null;
}
pubKeyCert = pubKeyCert
.Replace("-----END CERTIFICATE-----", "")
.Replace("-----BEGIN CERTIFICATE-----", "");
var x509CertBytes = Convert.FromBase64String(pubKeyCert);
var cf = new X509CertificateParser();
var x509Cert = cf.ReadCertificate(x509CertBytes);
return x509Cert;
}
#endregion
@@ -16,5 +16,22 @@ namespace PaySharp.Unionpay.Domain
/// 支付卡类型
/// </summary>
public string PayCardType { get; set; }
/// <summary>
/// 签约协议号
/// </summary>
public string ContractId
{
get => _contractId;
set
{
_contractId = value;
if (!string.IsNullOrEmpty(_contractId))
{
TxnSubType = "10";
}
}
}
private string _contractId;
}
}
@@ -59,6 +59,7 @@ namespace PaySharp.Unionpay.Domain
/// 交易子类
/// 01:自助消费,通过地址的方式区分前台消费和后台消费(含无跳转支付)
/// 03:分期付款
/// 10:签约支付(协议号支付)
/// </summary>
[Required(ErrorMessage = "请设置交易子类")]
public string TxnSubType { get; internal set; } = "01";
+2 -2
View File
@@ -60,10 +60,10 @@ namespace PaySharp.Unionpay
public string ReturnUrl { get; set; }
/// <summary>
/// 签名证书路径
/// 签名证书路径/base64字符串
/// </summary>
[Ignore]
[Required(ErrorMessage = "请设置签名证书路径")]
[Required(ErrorMessage = "请设置签名证书路径或base64字符串")]
public string CertPath { get; set; }
/// <summary>
@@ -7,11 +7,7 @@ namespace PaySharp.Unionpay.Request
{
public BillDownloadRequest()
{
#if DEBUG
RequestUrl = "https://filedownload.test.95516.com/";
#else
RequestUrl = "https://filedownload.95516.com/";
#endif
}
internal override void Execute(Merchant merchant)
@@ -20,4 +16,4 @@ namespace PaySharp.Unionpay.Request
GatewayData.Remove("frontUrl");
}
}
}
}
@@ -40,7 +40,7 @@ namespace PaySharp.Unionpay.Response
{
if (!string.IsNullOrEmpty(FileContent))
{
_billFile = Util.Inflater(FileContent);
_billFile = CertUtil.Inflater(FileContent);
}
}
}
@@ -217,7 +217,7 @@ namespace PaySharp.Unionpay.Response
/// <summary>
/// 清算金额
/// </summary>
[ReName(Name = "SettleAmt")]
[ReName(Name = "settleAmt")]
public string SettleAmount { get; set; }
/// <summary>
+2 -2
View File
@@ -75,14 +75,14 @@ namespace PaySharp.Unionpay
{
gatewayData.Remove("signature");
return Util.Sign(asymmetricKeyParameter, gatewayData.ToUrl(false));
return CertUtil.Sign(asymmetricKeyParameter, gatewayData.ToUrl(false));
}
internal static bool CheckSign(GatewayData gatewayData, string sign, string signPubKeyCert)
{
gatewayData.Remove("signature");
return Util.VerifyData(gatewayData.ToUrl(false), sign, signPubKeyCert);
return CertUtil.VerifyData(gatewayData.ToUrl(false), sign, signPubKeyCert);
}
}
}
+12 -3
View File
@@ -33,8 +33,8 @@ namespace PaySharp.Unionpay
{
_merchant = merchant;
_merchant.CertId = Util.GetCertId(merchant.CertPath, merchant.CertPwd);
_merchant.CertKey = Util.GetCertKey(merchant.CertPath, merchant.CertPwd);
_merchant.CertId = CertUtil.GetCertId(merchant.CertPath, merchant.CertPwd);
_merchant.CertKey = CertUtil.GetCertKey(merchant.CertPath, merchant.CertPwd);
}
#if NETCOREAPP3_1
@@ -54,7 +54,16 @@ namespace PaySharp.Unionpay
#region 属性
public override string GatewayUrl { get; set; } = "https://gateway.95516.com";
public override string GatewayUrl
{
get => _gatewayUrl;
set
{
_gatewayUrl = value;
CertUtil.IsTest = _gatewayUrl.Contains("test");
}
}
private string _gatewayUrl = "https://gateway.95516.com";
public new Merchant Merchant => _merchant;