Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/alipay v3 #422

Merged
merged 13 commits into from
Oct 2, 2024
32 changes: 17 additions & 15 deletions alipay/cert/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package cert
var (
Appid = "2021000122672388"

// 私钥
PrivateKey = "MIIEpQIBAAKCAQEAw0x6QX0eeW+XRNh4u/BG1RsyuYYsHmqenk4GrPV8ElrUEN6nLRwSXGSIIuIuyCo0t4swCNp9Q54g+AUmpAddeBCYhHKrAYG4n11MnXUYosEe43wzUhd7PaXpxctFlSKhFgYBiX7cQg/O8ThHJJ0H0Qy+k9NNJ2gMnfPypk9/43DstHmKEVQvgQpcgnhlbK8X4thIK4zW0xwHgDhSAeZu9QLvf/cc2PdKmd5xiUUM7J7PtwT7VvbKI27fYOBe1hxxrvpY6vcGGGal8xhNOKD+eiqXAgPgIRYuXqLpPgaYOImjnnH9lIpzjrCO0nsba1T4tonDjp5jv0FaSKwOTAjO6QIDAQABAoIBAQCjxUUcF7zvXml+XPzZtQLg/97IfsAOfaAn5gxpC66QgcQlpWCRTmIDQnZ6sitCxUnRxJFySy4R/s9szHz7vgVegqQzJSlLqSlV0lpGDAStrr6lSuiKZZB+QNxJdk0SY6irnDu7vjsb1r/VvjjCdkAwyLwjoGSpr/Isnn4TgsUexoBJOVBRvfsVmzNq0oaD12jEHPLnOgyBOUxN54A64mz7H7VBnYhG2TOL2ECqiQ/bAD3hy4KGoU1y3uT5gcC1pOXTE1XP4d1LDt434G4nPUIMxkMLhotecviWsbJ4FocRcPXs8qVptgarj5h9IMrjyLXDm17hfUqTtImhMEDqE/JFAoGBAP5RWI9qhIzEAWxualhAc2IssQ8MPFGkXFWB4NWYmXVMxodVonyQp9P4AFdL3wsiVhvSmccxqCy5R6Mbv81rFnb29meL08eqAy3HAW/br3jcnbN/W799OGaMXBi+DDES1xBmQndFVGFfm09xcndyTrmGiMgHW1kGgz6WFEHuRXI/AoGBAMSXMMqHEZxD9T4tahFV4xlUP67mLDrETQNOT0vX4NrLS4CXZMkt+IVqy3Z0TRapyIrkBpEUoH3ScGzzbIOdLz57SS7D+ZbsU5kCTDXKfPVxww4RjZ92xNJiEHBMzta9Ku5+D3mBznBFm9dMO8+E+0PSKMxm8n4AovQ9SU/hGyTXAoGBALrU4+yoYixPqoQQMcwXvSx4jLLzWDTaPIMM4THJ46MK/iZaQP6l/sV4Qjffo0I4vW2/L/3oexYwH3KyZhvw+hX3pFm5naHnQmKU+ndEuwpdePVvMOXihla/8sCyjZ5Xqut/VIDuy+ilJiIcw+0Aatlc/ouE7BTg9fY6pzMwapBdAoGBAIkMvb7zGpvN5JJMJr2fGor16M+NNxhg8S900GMXRHJDd4dWA7UcjzyzjtQtj/BUvLHW9Zz+vEP7CNVrfiLi2aS9Xe90P/OvHTh2GZsGZsbVYB3WrtyUd/IS21LuuOOLTPqmdzNGAxzR6irVwnyRQHmvcTHOMw8UcoXCk/FUBRBRAoGAYx52azbgbw1dDoxgGWHWS31zpYdGdg2pxPkr1TInrKFbrQEwnNTqcnPIHqytkvrc9gpqW4XU0Ux7pU/twPO9JXHfXDNWXmJUHyYIeLwbeHrhJ+avtPDs8VwsI8zbBu84D13NJq7dWxPLEUYQNrCrAw4+ywvXmBzXkvEdLaQq1S4="
// 应用私钥
PrivateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3DCgphWsp4OyB5pxF7ImUHHC7luATNfACFiK9qCvMRX1Ot0j4Ll64HUwVWaTfYbS00QyWPZLxwmGIAvh0y+9odu699oAb047p1XG1Q5eQa4qYrlxKGDeGUdWjK0C8qqeVHMAa8FK3xGukPYNersUbQ5E/eQqD7o6m51xR8AzhZ1lwWdW6PPAN51sH9o/MSvGZTmYAOWsErhSKZ0Eb613oG/rfxrvuBH3tFAqHva/cjNPznukieWCHrD5OUXlM48MEK2Ht8rFXs2HG6ZDdMNUMJuz15H5HQ3Plt1KYLy6b6Y6a7NtE1VWGvlmhT1Quzeudkb4lyYMC7DBCE4cZtMBlAgMBAAECggEAAfjNjfTz/JWBtryHPnGX4dKUnFC5xTs1hLA8W5KuNhshiHGNBa9qMc2O/kPi9M0Wp0QBQLUVGimf51kw2AFCaPuvArjfFG2pAnfcK7m4rkOaUJ7AO8QbBQVoqcNThTTJbV/L6f5gxb4F8tT+z0xOr0aBEA/7vwQY4U9ovTE60krIDbd3hE2Eh8RdrKebLrUvvGZ2LPFF3suuGbHHhQwRIzF8Enfa5TjyEJxGz3C3cdCEDjhA/tbMh05egeDhu1qu+Nx1FXzcyc/bFTKF2qPXeE27rclw8U07wggiegHPntJJ9Fgso918f0n8URlzhwpRQ5EjbBNOiVjkKGxQD0rLGQKBgQDl81Jk1iw2WWMuBxZKtxy43sXKjWY8umhrhK0gDjfRXokppCf91+tC3t/7BEc+X8HuAEYCuPs+tnv0XYdhfOeBpy/GHB7NQqYqCiu3Ptm3KnIm/q+CnnPA1HY7222Sa/9NptbfOfrPIlINnglAwjpZ2tn5h0uNvZfMP4Yh+x6aTwKBgQDLyKADd6Iw5jshwVF2SIlv/qHH5w8UKFmkLDT5l6RAqIg0mqY5tPpOEm5JohfYRmp9+ny4U8+V0CNNehN7FLreLJm5ksDvcOuIlEXPCzuEzyO8tkHFuW1pXLcdNnzZmdQ3S8o0mDllegEbwV0OK10RcJ96NRJnngNYp7rohrUxCwKBgHAvHmJtjpJRV66r7Hc1EIEsxehExb0Y4DqcNu4toZO2gEpdeNlBztgH7dQDyVIn22/mmEFZx9FXq++S9apdCoDZdNg8/dX8Dx7xaVR0CDlxLtHRbQTXkvdfb/NR4QoPQDW9EJVFLxSA1PEpya/bUiZmO/OdywFynq0ZzLGlVs/HAoGBALyLV2Fd4vaTTcqhbpmd62tvkIfARLdFzgqTzD/SCeq5A6yIGZvy0lRBwUDndGBqZkVA6PwyrzHRbZhVhoiuWWcOpPyJx3DCbWnkpeI2Zk8ux8Xma1xhf9WoLtQcgc9jAHJY8TFQVmqJQ1VGxq5BeM6WATe0ut6b2ETCi7BwUlyZAoGAV4kL7eiV2rcHa8IROWh8oo+8fgPMb5MSmbU78n/o4tXRTdUmg/6ghE1t9dWK867/PEubVFEvO8F15oQvwLHYdD+SPeLmp/CLf20jzNSjkImFxMG7xmnpre5+sfgIGjioI1rFkwrVk7II7GfvmGpDTbOSLWeiymdFsmmPnjs8I5Q="

PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtwwoKYVrKeDsgeacReyJlBxwu5bgEzXwAhYivagrzEV9TrdI+C5euB1MFVmk32G0tNEMlj2S8cJhiAL4dMvvaHbuvfaAG9OO6dVxtUOXkGuKmK5cShg3hlHVoytAvKqnlRzAGvBSt8RrpD2DXq7FG0ORP3kKg+6OpudcUfAM4WdZcFnVujzwDedbB/aPzErxmU5mADlrBK4UimdBG+td6Bv638a77gR97RQKh72v3IzT857pInlgh6w+TlF5TOPDBCth7fKxV7NhxumQ3TDVDCbs9eR+R0Nz5bdSmC8um+mOmuzbRNVVhr5ZoU9ULs3rnZG+JcmDAuwwQhOHGbTAZQIDAQAB"

// 支付宝公钥证书
AlipayPublicContentRSA2 = []byte(`-----BEGIN CERTIFICATE-----
Expand Down Expand Up @@ -139,22 +141,22 @@ Yf4Zr0fJsGuv

// 应用公钥证书
AppPublicContent = []byte(`-----BEGIN CERTIFICATE-----
MIIDmTCCAoGgAwIBAgIQICMGCXK2Am4QGbc/5Yi2QjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
MIIDmTCCAoGgAwIBAgIQICQJJiIfTmxJDlWhVlLYPjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMwNjA5MTI1MjAyWhcNMjQwNjEzMTI1MjAyWjBr
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjQwOTI2MTU0ODExWhcNMjUxMDAxMTU0ODExWjBr
MQswCQYDVQQGEwJDTjEfMB0GA1UECgwWbGFnbWlzNjI1NEBzYW5kYm94LmNvbTEPMA0GA1UECwwG
QWxpcGF5MSowKAYDVQQDDCEyMDg4NzIxMDAzMjM2NDQyLTIwMjEwMDAxMjI2NzIzODgwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDTHpBfR55b5dE2Hi78EbVGzK5hiweap6eTgas9XwS
WtQQ3qctHBJcZIgi4i7IKjS3izAI2n1DniD4BSakB114EJiEcqsBgbifXUyddRiiwR7jfDNSF3s9
penFy0WVIqEWBgGJftxCD87xOEcknQfRDL6T000naAyd8/KmT3/jcOy0eYoRVC+BClyCeGVsrxfi
2EgrjNbTHAeAOFIB5m71Au9/9xzY90qZ3nGJRQzsns+3BPtW9sojbt9g4F7WHHGu+ljq9wYYZqXz
GE04oP56KpcCA+AhFi5eouk+Bpg4iaOecf2UinOOsI7SextrVPi2icOOnmO/QVpIrA5MCM7pAgMB
AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAZc2RyiGKftnKi/0zIUUl
xKDPyUdVBBYDagcraHROKXafsZEnhO/cdBPC3VAJEiygNvddB51l088cGb8pGS2VqTQqR/Ehmd1x
kLMleWlUocX0Mwctz/J6jNp9/JKcVe1jk492HR7Csqjf+hvsajIVliIWzuhzB7C0eNEb46Js/G8T
QmsX92eHI0r2pcmsVr+PmDioLu4H4miKrDRhXNXLQ2AQfxPOCOtxt0tSNrB97bDNSwB1O4OItmr/
Cimb7l1nqxY3BOY5o8iAWLv4wG4giYu9LSrEJW0nWa2JG059kMlWLSVe7Afx0GzMVQUP6NJNdulP
ddebveu2CNZcJ7ipbA==
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3DCgphWsp4OyB5pxF7ImUHHC7luATNfACFiK9qCvM
RX1Ot0j4Ll64HUwVWaTfYbS00QyWPZLxwmGIAvh0y+9odu699oAb047p1XG1Q5eQa4qYrlxKGDeG
UdWjK0C8qqeVHMAa8FK3xGukPYNersUbQ5E/eQqD7o6m51xR8AzhZ1lwWdW6PPAN51sH9o/MSvGZ
TmYAOWsErhSKZ0Eb613oG/rfxrvuBH3tFAqHva/cjNPznukieWCHrD5OUXlM48MEK2Ht8rFXs2HG
6ZDdMNUMJuz15H5HQ3Plt1KYLy6b6Y6a7NtE1VWGvlmhT1Quzeudkb4lyYMC7DBCE4cZtMBlAgMB
AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAeMqluNI07JunRvkeOdN3
u4l1EicBtaMA63h6Ico92YanIgAzvZY9etwBiCP9ULmqckw+jo6+taXERe3fXSoUqyCH+3Hnb30f
MHp+oHlmpxCG93GLYBe9QcugrbEKg7RJ1l8ikyXMt3nkxBcQochCuNxdhGGYM4rxlGtwU1j0v+CV
Ne4IsK8fbpMF8wfYGOwDb+P1Mqx2Hv1D0qLu4S2ztz+br8Opg5leGY8wMt9+mW5aKwEKX3FVDi5J
ijiKYWzTm718zrmMJ+sN2VrapDvtj92bibOt0PKXUtQ5I++RtjG//dRpjAg9aS5pjvui80tNtwAc
xaEqw58KZ41dIaw3Zw==
-----END CERTIFICATE-----`)
)
4 changes: 0 additions & 4 deletions alipay/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ var (
ctx = context.Background()
client *Client
err error
// 普通公钥模式时
//aliPayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3/P3KX7rqwVEMkbZlY80PqBKxNHsMB4q5wKbpKU2KZffDiilV3bPpHPm1wHaJHKHudmirVWjn65lIspqIMgA4RoU3Cf0sEs8Mf9aAzv0F47KAWb2oBhDyQcYGtHftLG7q2mGBalXK7TJWWA2+DB2UAtfZELBRaVhsaF7bCkGd3GjTSXBZcyXAShgnH94C7yTr2IRbv+SnwedTdCKHxXvoPRvABylO9Krx8MiyJECBBfQScA67SsL+E1MKiMVdMomzlTEQ7W0UEAtzmG4aRMzSp30Lggit1xH7HKyOWgSE3Xy0LT78VKbqGRuCAT6IT5AA9jqSAbOBft7igJFkz9swIDAQAB"
//appPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0x6QX0eeW+XRNh4u/BG1RsyuYYsHmqenk4GrPV8ElrUEN6nLRwSXGSIIuIuyCo0t4swCNp9Q54g+AUmpAddeBCYhHKrAYG4n11MnXUYosEe43wzUhd7PaXpxctFlSKhFgYBiX7cQg/O8ThHJJ0H0Qy+k9NNJ2gMnfPypk9/43DstHmKEVQvgQpcgnhlbK8X4thIK4zW0xwHgDhSAeZu9QLvf/cc2PdKmd5xiUUM7J7PtwT7VvbKI27fYOBe1hxxrvpY6vcGGGal8xhNOKD+eiqXAgPgIRYuXqLpPgaYOImjnnH9lIpzjrCO0nsba1T4tonDjp5jv0FaSKwOTAjO6QIDAQAB"
//appPrivateKey = "MIIEpQIBAAKCAQEAw0x6QX0eeW+XRNh4u/BG1RsyuYYsHmqenk4GrPV8ElrUEN6nLRwSXGSIIuIuyCo0t4swCNp9Q54g+AUmpAddeBCYhHKrAYG4n11MnXUYosEe43wzUhd7PaXpxctFlSKhFgYBiX7cQg/O8ThHJJ0H0Qy+k9NNJ2gMnfPypk9/43DstHmKEVQvgQpcgnhlbK8X4thIK4zW0xwHgDhSAeZu9QLvf/cc2PdKmd5xiUUM7J7PtwT7VvbKI27fYOBe1hxxrvpY6vcGGGal8xhNOKD+eiqXAgPgIRYuXqLpPgaYOImjnnH9lIpzjrCO0nsba1T4tonDjp5jv0FaSKwOTAjO6QIDAQABAoIBAQCjxUUcF7zvXml+XPzZtQLg/97IfsAOfaAn5gxpC66QgcQlpWCRTmIDQnZ6sitCxUnRxJFySy4R/s9szHz7vgVegqQzJSlLqSlV0lpGDAStrr6lSuiKZZB+QNxJdk0SY6irnDu7vjsb1r/VvjjCdkAwyLwjoGSpr/Isnn4TgsUexoBJOVBRvfsVmzNq0oaD12jEHPLnOgyBOUxN54A64mz7H7VBnYhG2TOL2ECqiQ/bAD3hy4KGoU1y3uT5gcC1pOXTE1XP4d1LDt434G4nPUIMxkMLhotecviWsbJ4FocRcPXs8qVptgarj5h9IMrjyLXDm17hfUqTtImhMEDqE/JFAoGBAP5RWI9qhIzEAWxualhAc2IssQ8MPFGkXFWB4NWYmXVMxodVonyQp9P4AFdL3wsiVhvSmccxqCy5R6Mbv81rFnb29meL08eqAy3HAW/br3jcnbN/W799OGaMXBi+DDES1xBmQndFVGFfm09xcndyTrmGiMgHW1kGgz6WFEHuRXI/AoGBAMSXMMqHEZxD9T4tahFV4xlUP67mLDrETQNOT0vX4NrLS4CXZMkt+IVqy3Z0TRapyIrkBpEUoH3ScGzzbIOdLz57SS7D+ZbsU5kCTDXKfPVxww4RjZ92xNJiEHBMzta9Ku5+D3mBznBFm9dMO8+E+0PSKMxm8n4AovQ9SU/hGyTXAoGBALrU4+yoYixPqoQQMcwXvSx4jLLzWDTaPIMM4THJ46MK/iZaQP6l/sV4Qjffo0I4vW2/L/3oexYwH3KyZhvw+hX3pFm5naHnQmKU+ndEuwpdePVvMOXihla/8sCyjZ5Xqut/VIDuy+ilJiIcw+0Aatlc/ouE7BTg9fY6pzMwapBdAoGBAIkMvb7zGpvN5JJMJr2fGor16M+NNxhg8S900GMXRHJDd4dWA7UcjzyzjtQtj/BUvLHW9Zz+vEP7CNVrfiLi2aS9Xe90P/OvHTh2GZsGZsbVYB3WrtyUd/IS21LuuOOLTPqmdzNGAxzR6irVwnyRQHmvcTHOMw8UcoXCk/FUBRBRAoGAYx52azbgbw1dDoxgGWHWS31zpYdGdg2pxPkr1TInrKFbrQEwnNTqcnPIHqytkvrc9gpqW4XU0Ux7pU/twPO9JXHfXDNWXmJUHyYIeLwbeHrhJ+avtPDs8VwsI8zbBu84D13NJq7dWxPLEUYQNrCrAw4+ywvXmBzXkvEdLaQq1S4="
)

func TestMain(m *testing.M) {
Expand Down
6 changes: 2 additions & 4 deletions alipay/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,8 @@ func (a *Client) autoVerifySignByCert(sign, signData string, signDataErr error)
}

signBytes, _ := base64.StdEncoding.DecodeString(sign)
hashs := crypto.SHA256
h := hashs.New()
h.Write([]byte(signData))
if err = rsa.VerifyPKCS1v15(a.aliPayPublicKey, hashs, h.Sum(nil), signBytes); err != nil {
sum256 := sha256.Sum256([]byte(signData))
if err = rsa.VerifyPKCS1v15(a.aliPayPublicKey, crypto.SHA256, sum256[:], signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
}
Expand Down
111 changes: 111 additions & 0 deletions alipay/v3/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package alipay

import (
"crypto/md5"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"os"
"strings"

"github.com/go-pay/gopay"
)

// 允许进行 sn 提取的证书签名算法
var allowSignatureAlgorithm = map[string]bool{
"MD2-RSA": true,
"MD5-RSA": true,
"SHA1-RSA": true,
"SHA256-RSA": true,
"SHA384-RSA": true,
"SHA512-RSA": true,
"SHA256-RSAPSS": true,
"SHA384-RSAPSS": true,
"SHA512-RSAPSS": true,
}

// GetCertSN 获取证书序列号SN
// certPathOrData x509证书文件路径(appPublicCert.crt、alipayPublicCert.crt) 或证书 buffer
// 返回 sn:证书序列号(app_cert_sn、alipay_cert_sn)
// 返回 err:error 信息
func GetCertSN(certPathOrData any) (sn string, err error) {
var certData []byte
switch pathOrData := certPathOrData.(type) {
case string:
certData, err = os.ReadFile(pathOrData)
if err != nil {
return gopay.NULL, err
}
case []byte:
certData = pathOrData
default:
return gopay.NULL, errors.New("certPathOrData 证书类型断言错误")
}

if block, _ := pem.Decode(certData); block != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return gopay.NULL, err
}
name := cert.Issuer.String()
serialNumber := cert.SerialNumber.String()
h := md5.New()
h.Write([]byte(name))
h.Write([]byte(serialNumber))
sn = hex.EncodeToString(h.Sum(nil))
}
if sn == gopay.NULL {
return gopay.NULL, errors.New("failed to get sn,please check your cert")
}
return sn, nil
}

// GetRootCertSN 获取root证书序列号SN
// rootCertPathOrData x509证书文件路径(alipayRootCert.crt) 或文件 buffer
// 返回 sn:证书序列号(alipay_root_cert_sn)
// 返回 err:error 信息
func GetRootCertSN(rootCertPathOrData any) (sn string, err error) {
var (
certData []byte
certEnd = `-----END CERTIFICATE-----`
)
switch pathOrData := rootCertPathOrData.(type) {
case string:
certData, err = os.ReadFile(pathOrData)
if err != nil {
return gopay.NULL, err
}
case []byte:
certData = pathOrData
default:
return gopay.NULL, errors.New("rootCertPathOrData 断言异常")
}

pems := strings.Split(string(certData), certEnd)
for _, c := range pems {
if block, _ := pem.Decode([]byte(c + certEnd)); block != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
continue
}
if !allowSignatureAlgorithm[cert.SignatureAlgorithm.String()] {
continue
}
name := cert.Issuer.String()
serialNumber := cert.SerialNumber.String()
h := md5.New()
h.Write([]byte(name))
h.Write([]byte(serialNumber))
if sn == gopay.NULL {
sn += hex.EncodeToString(h.Sum(nil))
} else {
sn += "_" + hex.EncodeToString(h.Sum(nil))
}
}
}
if sn == gopay.NULL {
return gopay.NULL, errors.New("failed to get sn,please check your cert")
}
return sn, nil
}
104 changes: 104 additions & 0 deletions alipay/v3/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package alipay

import (
"crypto/rsa"
"fmt"
"time"

"github.com/go-pay/crypto/xpem"
"github.com/go-pay/crypto/xrsa"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/xhttp"
"github.com/go-pay/xlog"
)

// ClientV3 支付宝 V3
type ClientV3 struct {
AppId string
AppCertSN string
AliPayPublicCertSN string
AliPayRootCertSN string
AppAuthToken string
IsProd bool
aesKey string // biz_content 加密的 AES KEY
ivKey []byte
privateKey *rsa.PrivateKey
aliPayPublicKey *rsa.PublicKey // 支付宝证书公钥内容 alipayPublicCert.crt
DebugSwitch gopay.DebugSwitch
logger xlog.XLogger
requestIdFunc xhttp.RequestIdHandler
location *time.Location
hc *xhttp.Client
}

// NewClientV3 初始化支付宝客户端 V3
// appid:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// isProd:是否是正式环境,沙箱环境请选择新版沙箱应用。
func NewClientV3(appid, privateKey string, isProd bool) (client *ClientV3, err error) {
if appid == gopay.NULL || privateKey == gopay.NULL {
return nil, gopay.MissAlipayInitParamErr
}
key := xrsa.FormatAlipayPrivateKey(privateKey)
priKey, err := xpem.DecodePrivateKey([]byte(key))
if err != nil {
return nil, err
}
logger := xlog.NewLogger()
logger.SetLevel(xlog.DebugLevel)
client = &ClientV3{
AppId: appid,
IsProd: isProd,
privateKey: priKey,
DebugSwitch: gopay.DebugOff,
logger: logger,
requestIdFunc: defaultRequestIdFunc,
hc: xhttp.NewClient(),
}
return client, nil
}

// 设置自定义RequestId生成函数
func (a *ClientV3) SetRequestIdFunc(requestIdFunc xhttp.RequestIdHandler) {
if requestIdFunc != nil {
a.requestIdFunc = requestIdFunc
}
}

// 应用公钥证书内容设置 app_cert_sn、alipay_root_cert_sn、alipay_cert_sn
// appCertContent:应用公钥证书文件内容
// alipayRootCertContent:支付宝根证书文件内容
// alipayPublicCertContent:支付宝公钥证书文件内容
func (a *ClientV3) SetCert(appCertContent, alipayRootCertContent, alipayPublicCertContent []byte) (err error) {
appCertSn, err := GetCertSN(appCertContent)
if err != nil {
return fmt.Errorf("get app_cert_sn return err, but alse return alipay client. err: %w", err)
}
rootCertSn, err := GetRootCertSN(alipayRootCertContent)
if err != nil {
return fmt.Errorf("get alipay_root_cert_sn return err, but alse return alipay client. err: %w", err)
}
publicCertSn, err := GetCertSN(alipayPublicCertContent)
if err != nil {
return fmt.Errorf("get alipay_cert_sn return err, but alse return alipay client. err: %w", err)
}

// alipay public key
pubKey, err := xpem.DecodePublicKey(alipayPublicCertContent)
if err != nil {
return fmt.Errorf("decode alipayPublicCertContent err: %w", err)
}

a.AppCertSN = appCertSn
a.AliPayRootCertSN = rootCertSn
a.AliPayPublicCertSN = publicCertSn
a.aliPayPublicKey = pubKey
return nil
}

// SetAESKey 设置 biz_content 的AES加密key,设置此参数默认开启 biz_content 参数加密
// 注意:目前不可用,设置后会报错
func (a *ClientV3) SetAESKey(aesKey string) {
a.aesKey = aesKey
a.ivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
}
45 changes: 45 additions & 0 deletions alipay/v3/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package alipay

import (
"context"
"os"
"testing"

"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay/cert"
"github.com/go-pay/xlog"
)

var (
ctx = context.Background()
client *ClientV3
err error
)

func TestMain(m *testing.M) {
xlog.SetLevel(xlog.DebugLevel)
// 初始化支付宝客户端
// appid:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// isProd:是否是正式环境,沙箱环境请选择新版沙箱应用。
client, err = NewClientV3(cert.Appid, cert.PrivateKey, false)
if err != nil {
xlog.Error(err)
return
}
// Debug开关,输出/关闭日志
client.DebugSwitch = gopay.DebugOn

// 设置biz_content加密KEY,设置此参数默认开启加密(目前不可用,设置后会报错)
//client.SetAESKey("KvKUTqSVZX2fUgmxnFyMaQ==")

// 传入证书内容
err := client.SetCert(cert.AppPublicContent, cert.AlipayRootContent, cert.AlipayPublicContentRSA2)
// 传入证书文件路径
//err := client.SetCertSnByPath("cert/appPublicCert.crt", "cert/alipayRootCert.crt", "cert/alipayPublicCert.crt")
if err != nil {
xlog.Debug("SetCert:", err)
return
}
os.Exit(m.Run())
}
Loading