diff --git a/src/PaySharp.Unionpay/Util.cs b/src/PaySharp.Unionpay/CertUtil.cs similarity index 65% rename from src/PaySharp.Unionpay/Util.cs rename to src/PaySharp.Unionpay/CertUtil.cs index 01170b3..c449330 100644 --- a/src/PaySharp.Unionpay/Util.cs +++ b/src/PaySharp.Unionpay/CertUtil.cs @@ -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 /// /// 获取Pkcs12Store /// - /// 证书路径 + /// 证书路径或base64字符串 /// 证书密码 /// - 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(); } /// /// 获取证书编号 /// - /// 证书路径 + /// 证书路径或base64字符串 /// 证书密码 /// - 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 /// /// 获取证书编号 /// - /// 证书路径 + /// 证书路径或base64字符串 /// 证书密码 /// - 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 diff --git a/src/PaySharp.Unionpay/Domain/AppPayModel.cs b/src/PaySharp.Unionpay/Domain/AppPayModel.cs index bbdedc8..6ba225c 100644 --- a/src/PaySharp.Unionpay/Domain/AppPayModel.cs +++ b/src/PaySharp.Unionpay/Domain/AppPayModel.cs @@ -16,5 +16,22 @@ namespace PaySharp.Unionpay.Domain /// 支付卡类型 /// public string PayCardType { get; set; } + + /// + /// 签约协议号 + /// + public string ContractId + { + get => _contractId; + set + { + _contractId = value; + if (!string.IsNullOrEmpty(_contractId)) + { + TxnSubType = "10"; + } + } + } + private string _contractId; } } diff --git a/src/PaySharp.Unionpay/Domain/BaseModel.cs b/src/PaySharp.Unionpay/Domain/BaseModel.cs index 446810c..7b3df9d 100644 --- a/src/PaySharp.Unionpay/Domain/BaseModel.cs +++ b/src/PaySharp.Unionpay/Domain/BaseModel.cs @@ -59,6 +59,7 @@ namespace PaySharp.Unionpay.Domain /// 交易子类 /// 01:自助消费,通过地址的方式区分前台消费和后台消费(含无跳转支付) /// 03:分期付款 + /// 10:签约支付(协议号支付) /// [Required(ErrorMessage = "请设置交易子类")] public string TxnSubType { get; internal set; } = "01"; diff --git a/src/PaySharp.Unionpay/Merchant.cs b/src/PaySharp.Unionpay/Merchant.cs index 8a527f5..6be071d 100644 --- a/src/PaySharp.Unionpay/Merchant.cs +++ b/src/PaySharp.Unionpay/Merchant.cs @@ -60,10 +60,10 @@ namespace PaySharp.Unionpay public string ReturnUrl { get; set; } /// - /// 签名证书路径 + /// 签名证书路径/base64字符串 /// [Ignore] - [Required(ErrorMessage = "请设置签名证书路径")] + [Required(ErrorMessage = "请设置签名证书路径或base64字符串")] public string CertPath { get; set; } /// diff --git a/src/PaySharp.Unionpay/Request/BillDownloadRequest.cs b/src/PaySharp.Unionpay/Request/BillDownloadRequest.cs index 5d7ba16..c66f87a 100644 --- a/src/PaySharp.Unionpay/Request/BillDownloadRequest.cs +++ b/src/PaySharp.Unionpay/Request/BillDownloadRequest.cs @@ -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"); } } -} \ No newline at end of file +} diff --git a/src/PaySharp.Unionpay/Response/BillDownloadResponse.cs b/src/PaySharp.Unionpay/Response/BillDownloadResponse.cs index dd15b00..e110452 100644 --- a/src/PaySharp.Unionpay/Response/BillDownloadResponse.cs +++ b/src/PaySharp.Unionpay/Response/BillDownloadResponse.cs @@ -40,7 +40,7 @@ namespace PaySharp.Unionpay.Response { if (!string.IsNullOrEmpty(FileContent)) { - _billFile = Util.Inflater(FileContent); + _billFile = CertUtil.Inflater(FileContent); } } } diff --git a/src/PaySharp.Unionpay/Response/NotifyResponse.cs b/src/PaySharp.Unionpay/Response/NotifyResponse.cs index b159b51..072d75d 100644 --- a/src/PaySharp.Unionpay/Response/NotifyResponse.cs +++ b/src/PaySharp.Unionpay/Response/NotifyResponse.cs @@ -217,7 +217,7 @@ namespace PaySharp.Unionpay.Response /// /// 清算金额 /// - [ReName(Name = "SettleAmt")] + [ReName(Name = "settleAmt")] public string SettleAmount { get; set; } /// diff --git a/src/PaySharp.Unionpay/SubmitProcess.cs b/src/PaySharp.Unionpay/SubmitProcess.cs index 3bd304a..5ffca67 100644 --- a/src/PaySharp.Unionpay/SubmitProcess.cs +++ b/src/PaySharp.Unionpay/SubmitProcess.cs @@ -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); } } } diff --git a/src/PaySharp.Unionpay/UnionpayGateway.cs b/src/PaySharp.Unionpay/UnionpayGateway.cs index 2c708fa..dc3ce5e 100644 --- a/src/PaySharp.Unionpay/UnionpayGateway.cs +++ b/src/PaySharp.Unionpay/UnionpayGateway.cs @@ -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;