mirror of
https://github.com/Varorbc/PaySharp.git
synced 2026-10-16 20:31:04 +08:00
添加银联协议支付 (#58)
* 优化测试环境签名失败问题 * 添加协议支付 * 修复清算金额字段错误 * 修改文件名 * 增加证书base64字符串支持 * 添加协议号为空判断
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user