package pay import ( "crypto/aes" "crypto/cipher" "encoding/base64" "encoding/json" "fmt" "io" "net/http" ) type NotifyRequest struct { ID string `json:"id"` CreateTime string `json:"create_time"` EventType string `json:"event_type"` ResourceType string `json:"resource_type"` Summary string `json:"summary"` Resource NotifyResource `json:"resource"` } type NotifyResource struct { Algorithm string `json:"algorithm"` Ciphertext string `json:"ciphertext"` AssociatedData string `json:"associated_data"` Nonce string `json:"nonce"` OriginalType string `json:"original_type"` } type TransactionNotify struct { AppID string `json:"appid"` MchID string `json:"mchid"` OutTradeNo string `json:"out_trade_no"` TransactionID string `json:"transaction_id"` TradeType string `json:"trade_type"` TradeState string `json:"trade_state"` TradeStateDesc string `json:"trade_state_desc"` BankType string `json:"bank_type"` Attach string `json:"attach"` SuccessTime string `json:"success_time"` Payer struct { OpenID string `json:"openid"` } `json:"payer"` Amount struct { Total int64 `json:"total"` PayerTotal int64 `json:"payer_total"` Currency string `json:"currency"` PayerCurrency string `json:"payer_currency"` } `json:"amount"` } func (c *Client) ParseNotify(r *http.Request) (*NotifyRequest, error) { body, err := io.ReadAll(r.Body) if err != nil { return nil, err } if err := c.validateResponse(r.Header, body); err != nil { return nil, err } var notify NotifyRequest if err := json.Unmarshal(body, ¬ify); err != nil { return nil, err } return ¬ify, nil } func (c *Client) DecryptNotify(notify *NotifyRequest) (*TransactionNotify, error) { if c.APIv3Key == "" { return nil, fmt.Errorf("apiv3 key is required for notify decryption") } plaintext, err := decryptAES256GCM( c.APIv3Key, notify.Resource.AssociatedData, notify.Resource.Nonce, notify.Resource.Ciphertext, ) if err != nil { return nil, err } var tx TransactionNotify if err := json.Unmarshal(plaintext, &tx); err != nil { return nil, err } return &tx, nil } func decryptAES256GCM(apiV3Key, associatedData, nonce, ciphertext string) ([]byte, error) { key := []byte(apiV3Key) cipherBytes, err := base64.StdEncoding.DecodeString(ciphertext) if err != nil { return nil, err } block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } return gcm.Open(nil, []byte(nonce), cipherBytes, []byte(associatedData)) }