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

Fearure 250106 #12

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.2.8 - 2025-01-06
* [#10](https://github.com/alipay/global-open-sdk-go/pull/10) feature-250106
- 订阅支付新增“更新接口”
- 增加验签

## 1.2.7 - 2024-12-16
* [#9](https://github.com/alipay/global-open-sdk-go/pull/9) feature-241216
- RDR拒付通知优化通用能力变更
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
```
Language:GO
GO version:1.22.5+
Tags:v1.2.7
Tags:v1.2.8
Copyright:Ant financial services group
```

Expand Down
82 changes: 18 additions & 64 deletions com/alipay/api/defaultAlipayClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@ package defaultAlipayClient

import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"github.com/alipay/global-open-sdk-go/com/alipay/api/exception"
"github.com/alipay/global-open-sdk-go/com/alipay/api/request"
"github.com/alipay/global-open-sdk-go/com/alipay/api/tools"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -103,8 +95,15 @@ func (alipayClient *DefaultAlipayClient) httpDo(url, method string, params, head
return nil, &exception.AlipayLibraryError{Message: "client.Do is fail " + err.Error()}
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
responseTime := resp.Header.Get("response-time")
sign, err := checkRspSign(resp.Request.Method, req.URL.Path, resp.Header.Get("Client-id"), responseTime, string(body), resp.Header.Get("Signature"), alipayClient.AlipayPublicKey)
if err != nil {
return nil, &exception.AlipayLibraryError{Message: "checkRspSign is fail " + err.Error()}
}
if !sign {
return nil, &exception.AlipayLibraryError{Message: "check signature fail"}
}
//通过指针将request的response值赋值,这样虽然是any类型,
//但是我们在上次必然已经定义了any的类型
err = json.Unmarshal(body, alipayResponse)
Expand All @@ -123,7 +122,7 @@ func (alipayClient *DefaultAlipayClient) Execute(alipayRequest *request.AlipayRe
path := alipayRequest.Path
httpMethod := alipayRequest.HttpMethod
reqTime := strconv.FormatInt(time.Now().UnixNano(), 10)
sign, err := genSign(fmt.Sprintf("%s", httpMethod), path, alipayClient.ClientId, reqTime, string(reqPayload), getPkcsKeu(alipayClient.MerchantPrivateKey))
sign, err := tools.GenSign(fmt.Sprintf("%s", httpMethod), path, alipayClient.ClientId, reqTime, string(reqPayload), alipayClient.MerchantPrivateKey)
if err != nil {
return nil, err
}
Expand All @@ -134,49 +133,18 @@ func (alipayClient *DefaultAlipayClient) Execute(alipayRequest *request.AlipayRe
}
return alipayResponse, nil
}
func getPkcsKeu(key string) string {
return "-----BEGIN PRIVATE KEY-----\n" + key + "\n-----END PRIVATE KEY-----"
}

func genSign(httpMethod string, path string, clientId string, reqTime string, reqBody string, merchantPrivateKey string) (string, error) {
block, _ := pem.Decode([]byte(merchantPrivateKey))
if block == nil {
return "", &exception.AlipayLibraryError{Message: "Failed to decode private key"}
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", &exception.AlipayLibraryError{Message: "Failed to parse private key " + err.Error()}
}
payload := genSignContent(httpMethod, path, clientId, reqTime, reqBody)
signature, err := Sign(privateKey.(*rsa.PrivateKey), []byte(payload))
if err != nil {
return "", &exception.AlipayLibraryError{Message: "Failed to sign data " + err.Error()}
}

return signature, nil

}
func checkRspSign(httpMethod string, path string, clientId string, responseTime string, rspBody string, rspSignValue string, alipayPublicKey string) (bool, error) {

func genSignContent(httpMethod string, path string, clientId string, reqTime string, reqBody string) string {
return httpMethod + " " + path + "\n" + clientId + "." + reqTime + "." + reqBody
}

func Sign(privateKey *rsa.PrivateKey, data []byte) (string, error) {
// 计算数据的SHA256哈希
hashed := sha256.Sum256(data)

// 使用私钥签名哈希值
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.Hash.HashFunc(crypto.SHA256), hashed[:])
signature, err := tools.CheckSignature(path, httpMethod, clientId, responseTime, rspBody, rspSignValue, alipayPublicKey)
if err != nil {
return "", err
return false, &exception.AlipayLibraryError{Message: "Failed to check signature " + err.Error()}
}
if !signature {
return false, &exception.AlipayLibraryError{Message: "check signature fail"}
} else {
return true, nil
}
//base64编码 如果signature直接传string会造成乱码
base64Signature := base64.StdEncoding.EncodeToString(signature)
return Encode(base64Signature)
}

func Encode(signature string) (string, error) {
return url.QueryEscape(signature), nil
}

func buildBaseHeader(reqTime string, clientId string, keyVersion string, signatureValue string) map[string]string {
Expand All @@ -192,17 +160,3 @@ func buildBaseHeader(reqTime string, clientId string, keyVersion string, signatu
"Signature": signatureValue,
}
}

func getJsonValue(jsonValue []byte, jsonField string) any {
var jsonMap map[string]any

if err := json.Unmarshal(jsonValue, &jsonMap); err != nil {
log.Fatalf("json.Unmarshal is fail: %v \n")
}

value, ok := jsonMap[jsonField]
if !ok {
log.Fatalf("jsonMap is not contain jsonField: %v \n")
}
return value
}
1 change: 1 addition & 0 deletions com/alipay/api/model/AntomPathConstants.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
SUBSCRIPTION_CREATE_PATH = "/ams/api/v1/subscriptions/create"
SUBSCRIPTION_CHANGE_PATH = "/ams/api/v1/subscriptions/change"
SUBSCRIPTION_CANCEL_PATH = "/ams/api/v1/subscriptions/cancel"
SUBSCRIPTION_UPDATE_PATH = "/ams/api/v1/subscriptions/update"
ACCEPT_DISPUTE_PATH = "/ams/api/v1/payments/acceptDispute"
SUPPLY_DEFENCE_DOC_PATH = "/ams/api/v1/payments/supplyDefenseDocument"
DOWNLOAD_DISPUTE_EVIDENCE_PATH = "/ams/api/v1/payments/downloadDisputeEvidence"
Expand Down
25 changes: 25 additions & 0 deletions com/alipay/api/request/auth/AlipayAuthCreateSessionRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package auth

import (
"github.com/alipay/global-open-sdk-go/com/alipay/api/model"
"github.com/alipay/global-open-sdk-go/com/alipay/api/request"
responseAuth "github.com/alipay/global-open-sdk-go/com/alipay/api/response/auth"
)

type AlipayAuthCreateSessionRequest struct {
ProductCode model.ProductCodeType `json:"productCode,omitempty"`
AgreementInfo *model.AgreementInfo `json:"agreementInfo,omitempty"`
Scopes []model.ScopeType `json:"scopes,omitempty"`
PaymentMethod *model.PaymentMethod `json:"paymentMethod,omitempty"`
PaymentNotifyUrl string `json:"paymentNotifyUrl,omitempty"`
}

func (alipayAuthCreateSessionRequest *AlipayAuthCreateSessionRequest) NewRequest() *request.AlipayRequest {
return request.NewAlipayRequest(&alipayAuthCreateSessionRequest, model.CREATE_SESSION_PATH, &responseAuth.AlipayAuthCreateSessionResponse{})
}

func NewAlipayAuthCreateSessionRequest() (*request.AlipayRequest, *AlipayAuthCreateSessionRequest) {
alipayAuthCreateSessionRequest := &AlipayAuthCreateSessionRequest{}
alipayRequest := request.NewAlipayRequest(alipayAuthCreateSessionRequest, model.CREATE_SESSION_PATH, &responseAuth.AlipayAuthCreateSessionResponse{})
return alipayRequest, alipayAuthCreateSessionRequest
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package subscription

import (
"github.com/alipay/global-open-sdk-go/com/alipay/api/model"
"github.com/alipay/global-open-sdk-go/com/alipay/api/request"
responseSubscription "github.com/alipay/global-open-sdk-go/com/alipay/api/response/subscription"
)

type AlipaySubscriptionUpdateRequest struct {
SubscriptionUpdateRequestId string `json:"subscriptionUpdateRequestId,omitempty"`
SubscriptionId string `json:"subscriptionId,omitempty"`
SubscriptionDescription string `json:"subscriptionDescription,omitempty"`
PeriodRule *model.PeriodRule `json:"periodRule,omitempty"`
PaymentAmount *model.Amount `json:"paymentAmount,omitempty"`
SubscriptionEndTime string `json:"subscriptionEndTime,omitempty"`
OrderInfo *model.OrderInfo `json:"orderInfo,omitempty"`
}

func (alipaySubscriptionUpdateRequest *AlipaySubscriptionUpdateRequest) NewRequest() *request.AlipayRequest {
return request.NewAlipayRequest(&alipaySubscriptionUpdateRequest, model.SUBSCRIPTION_UPDATE_PATH, &responseSubscription.AlipaySubscriptionUpdateResponse{})
}
10 changes: 10 additions & 0 deletions com/alipay/api/response/auth/AlipayAuthCreateSessionResponse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package responseAuth

import "github.com/alipay/global-open-sdk-go/com/alipay/api/response"

type AlipayAuthCreateSessionResponse struct {
response.AlipayResponse
PaymentSessionId string `json:"paymentSessionId,omitempty"`
PaymentSessionData string `json:"paymentSessionData,omitempty"`
PaymentSessionExpiryTime string `json:"paymentSessionExpiryTime,omitempty"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package responseSubscription

import "github.com/alipay/global-open-sdk-go/com/alipay/api/response"

type AlipaySubscriptionUpdateResponse struct {
response.AlipayResponse
}
114 changes: 114 additions & 0 deletions com/alipay/api/tools/SignatureTool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package tools

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"github.com/alipay/global-open-sdk-go/com/alipay/api/exception"
"net/url"
)

func GenSign(httpMethod string, path string, clientId string, reqTime string, reqBody string, merchantPrivateKey string) (string, error) {
block, _ := pem.Decode([]byte(getPkcsKey(merchantPrivateKey)))
if block == nil {
return "", &exception.AlipayLibraryError{Message: "Failed to decode private key"}
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", &exception.AlipayLibraryError{Message: "Failed to parse private key " + err.Error()}
}
payload := genSignContent(httpMethod, path, clientId, reqTime, reqBody)
signature, err := Sign(privateKey.(*rsa.PrivateKey), []byte(payload))
if err != nil {
return "", &exception.AlipayLibraryError{Message: "Failed to sign data " + err.Error()}
}

return signature, nil

}

func Sign(privateKey *rsa.PrivateKey, data []byte) (string, error) {
// 计算数据的SHA256哈希
hashed := sha256.Sum256(data)

// 使用私钥签名哈希值
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.Hash.HashFunc(crypto.SHA256), hashed[:])
if err != nil {
return "", err
}
//base64编码 如果signature直接传string会造成乱码
base64Signature := base64.StdEncoding.EncodeToString(signature)
return Encode(base64Signature)
}

func Verify(httpMethod string, path string, clientId string, rspTimeStr string, rspBody string, signature string, alipayPublicKey string) (bool, error) {
rspContent := genSignContent(httpMethod, path, clientId, rspTimeStr, rspBody)
return verifySignatureWithSHA256RSA(rspContent, Decode(signature), alipayPublicKey)
}

func verifySignatureWithSHA256RSA(rspContent string, signature string, strPk string) (bool, error) {
publicKey, err := getPublicKeyFromBase64String(strPk)
if err != nil {
return false, err
}

hash := sha256.New()
hash.Write([]byte(rspContent))
digest := hash.Sum(nil)

signatureBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return false, err
}

err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, digest, signatureBytes)
if err != nil {
return false, err
}

return true, nil
}

func getPublicKeyFromBase64String(publicKeyString string) (*rsa.PublicKey, error) {
keyBytes, err := base64.StdEncoding.DecodeString(publicKeyString)
if err != nil {
return nil, err
}

pub, err := x509.ParsePKIXPublicKey(keyBytes)
if err != nil {
return nil, err
}

switch pub := pub.(type) {
case *rsa.PublicKey:
return pub, nil
default:
return nil, errors.New("not an RSA public key")
}
}

func genSignContent(httpMethod string, path string, clientId string, reqTime string, reqBody string) string {
return httpMethod + " " + path + "\n" + clientId + "." + reqTime + "." + reqBody
}

func Encode(signature string) (string, error) {
return url.QueryEscape(signature), nil
}

func Decode(originalStr string) string {
unescape, err := url.QueryUnescape(originalStr)
if err != nil {
return ""
}
return unescape
}

func getPkcsKey(key string) string {
return "-----BEGIN PRIVATE KEY-----\n" + key + "\n-----END PRIVATE KEY-----"
}
29 changes: 29 additions & 0 deletions com/alipay/api/tools/WebhookTool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tools

import (
"errors"
"strings"
)

func CheckSignature(requestUri, httpMethod, clientId, requestTime, responseBody, signature, alipayPublicKey string) (bool, error) {
realSignature := ""

// Check if signature is nil or empty
if signature == "" {
return false, errors.New("empty notify signature")
}

// Get valid part from raw signature
parts := strings.Split(signature, "signature=")
if len(parts) > 1 {
realSignature = parts[1]
}

// Verify signature
isValid, _ := Verify(httpMethod, requestUri, clientId, requestTime, responseBody, realSignature, alipayPublicKey)
if !isValid {
return false, errors.New("signature verification failed")
}

return true, nil
}