package qiyuesuosdk import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" ) type callbackMeta struct { CallbackType string `json:"callbackType"` CallbackTime string `json:"callbackTime"` CallbackBizType string `json:"callbackBizType"` } // ParseCallback 解密、验签并解析契约锁回调。 func (c *Client) ParseCallback(req CallbackRequest) (CallbackEvent, error) { plain, err := c.decryptCallback(req.Encrypted, c.cfg.CallbackToken, c.cfg.CallbackAESKey) if err != nil { return CallbackEvent{}, err } hash := sha256.Sum256([]byte(plain + req.Timestamp + req.Nonce + c.cfg.CallbackToken)) if hex.EncodeToString(hash[:]) != req.Signature { return CallbackEvent{}, fmt.Errorf("qiyuesuo: callback signature mismatch") } return parseCallbackPayload(plain) } func parseCallbackPayload(plain string) (CallbackEvent, error) { raw := []byte(plain) var meta callbackMeta if err := json.Unmarshal(raw, &meta); err != nil { return CallbackEvent{}, err } switch EventType(meta.CallbackType) { case EventCompanyAuthSuccess: var data CompanyCertificationData if err := json.Unmarshal(raw, &data); err != nil { return CallbackEvent{}, err } data.CallbackTime = meta.CallbackTime return CallbackEvent{Type: EventCompanyAuthSuccess, Data: data}, nil case EventCompanySealAuthSuccess: var data CompanySealAuthData if err := json.Unmarshal(raw, &data); err != nil { return CallbackEvent{}, err } data.CallbackTime = meta.CallbackTime return CallbackEvent{Type: EventCompanySealAuthSuccess, Data: data}, nil case EventPersonalSealAuthSuccess: var data PersonalSealAuthData if err := json.Unmarshal(raw, &data); err != nil { return CallbackEvent{}, err } data.CallbackTime = meta.CallbackTime return CallbackEvent{Type: EventPersonalSealAuthSuccess, Data: data}, nil case EventCallbackCheck: return CallbackEvent{Type: EventCallbackCheck, Data: nil}, nil default: return CallbackEvent{}, fmt.Errorf("qiyuesuo: unknown callback type %q", meta.CallbackType) } } func (c *Client) decryptCallback(encrypted, token, aesKey string) (string, error) { ciphertext, err := base64.StdEncoding.DecodeString(encrypted) if err != nil { return "", fmt.Errorf("qiyuesuo: base64 decode: %w", err) } key := []byte(aesKey) if len(key) < 16 { return "", fmt.Errorf("qiyuesuo: aes key too short") } key = key[:16] block, err := aes.NewCipher(key) if err != nil { return "", err } ivHash := sha256.Sum256([]byte(token)) iv := ivHash[:aes.BlockSize] if len(ciphertext)%aes.BlockSize != 0 { return "", fmt.Errorf("qiyuesuo: invalid ciphertext block size") } mode := cipher.NewCBCDecrypter(block, iv) plain := make([]byte, len(ciphertext)) mode.CryptBlocks(plain, ciphertext) if len(plain) == 0 { return "", fmt.Errorf("qiyuesuo: empty plaintext") } padding := int(plain[len(plain)-1]) if padding <= 0 || padding > len(plain) { return "", fmt.Errorf("qiyuesuo: invalid pkcs padding") } return string(plain[:len(plain)-padding]), nil }