xy_sh-20260727101126/xy_sh/ymt_v3/client.go

241 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ymt_v3
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
// Env 环境
type Env string
const (
EnvTest Env = "test"
EnvProduct Env = "product"
)
// 环境地址
const (
testBaseURL = "https://gateway.dev.cdlsxd.cn"
productBaseURL = "https://market.api.86698.cn"
)
// ClientConfig 客户端配置
type ClientConfig struct {
AppID string // 应用ID
PrivateKey string // 应用私钥PEM格式
PublicKey string // 平台公钥PEM格式
Key string // 业务参数加密key
EncryptType EncryptType // 加密类型aes 或 sm4
Env Env // 环境test 或 product
HTTPClient *http.Client // HTTP客户端可选
}
// Client SDK客户端
type Client struct {
config *ClientConfig
httpClient *http.Client
baseURL string
}
// NewClient 创建新的SDK客户端
func NewClient(config *ClientConfig) (*Client, error) {
if config.AppID == "" {
return nil, fmt.Errorf("AppID不能为空")
}
if config.PrivateKey == "" {
return nil, fmt.Errorf("PrivateKey不能为空")
}
if config.PublicKey == "" {
return nil, fmt.Errorf("PublicKey不能为空")
}
if config.Key == "" {
return nil, fmt.Errorf("Key不能为空")
}
if config.EncryptType == "" {
config.EncryptType = EncryptTypeAES
}
if config.Env == "" {
config.Env = EnvTest
}
var baseURL string
switch config.Env {
case EnvTest:
baseURL = testBaseURL
case EnvProduct:
baseURL = productBaseURL
default:
return nil, fmt.Errorf("不支持的环境: %s", config.Env)
}
httpClient := config.HTTPClient
if httpClient == nil {
httpClient = &http.Client{}
}
return &Client{
config: config,
httpClient: httpClient,
baseURL: baseURL,
}, nil
}
// doRequest 发送请求并处理响应
func (c *Client) doRequest(path string, bizParams interface{}, result interface{}) error {
// 1. 加密业务参数
ciphertext, err := EncryptBizParams(bizParams, []byte(c.config.Key), c.config.EncryptType)
if err != nil {
return NewSDKError("加密业务参数失败", err)
}
// 2. 生成时间戳
timestamp := GenerateTimestamp()
// 3. 构建签名字符串并签名
signStr := BuildSignStr(c.config.AppID, timestamp, ciphertext)
sign, err := SignWithRSA(signStr, c.config.PrivateKey)
if err != nil {
return NewSDKError("签名失败", err)
}
// 4. 构建请求体
reqBody := map[string]string{
"ciphertext": ciphertext,
}
reqBodyBytes, err := json.Marshal(reqBody)
if err != nil {
return NewSDKError("序列化请求体失败", err)
}
// 5. 创建HTTP请求
url := c.baseURL + path
req, err := http.NewRequest("POST", url, bytes.NewReader(reqBodyBytes))
if err != nil {
return NewSDKError("创建HTTP请求失败", err)
}
// 6. 设置Header
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Appid", c.config.AppID)
req.Header.Set("Timestamp", timestamp)
req.Header.Set("Sign", sign)
// 7. 发送请求
resp, err := c.httpClient.Do(req)
if err != nil {
return NewSDKError("发送HTTP请求失败", err)
}
defer resp.Body.Close()
// 8. 读取响应
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return NewSDKError("读取响应失败", err)
}
// 9. 解析公共响应
var commonResp CommonResponse
if err := json.Unmarshal(respBody, &commonResp); err != nil {
return NewSDKError("解析响应JSON失败", err)
}
// 10. 检查业务错误
if commonResp.Code != 200 {
return NewAPIError(commonResp.Code, commonResp.Message, commonResp.Reason)
}
// 11. 检查是否有加密数据
if commonResp.Data == nil || commonResp.Data.Ciphertext == "" {
return NewSDKError("响应中没有加密数据", nil)
}
// 12. 解密响应数据
decrypted, err := DecryptBizParams(commonResp.Data.Ciphertext, []byte(c.config.Key), c.config.EncryptType)
if err != nil {
return NewSDKError("解密响应数据失败", err)
}
// 13. 解析解密后的数据到结果
if err := json.Unmarshal(decrypted, result); err != nil {
return NewSDKError("解析解密后的响应数据失败", err)
}
return nil
}
// ==================== API方法 ====================
// KeyOrder 获取券码
// POST /openapi/v1/key/order
func (c *Client) KeyOrder(req *KeyOrderRequest) (*KeyOrderResponse, error) {
result := &KeyOrderResponse{}
if err := c.doRequest("/openapi/v1/key/order", req, result); err != nil {
return nil, err
}
return result, nil
}
// KeyQuery 券码查询
// POST /openapi/v1/key/query
func (c *Client) KeyQuery(req *KeyQueryRequest) (*KeyOrderResponse, error) {
result := &KeyOrderResponse{}
if err := c.doRequest("/openapi/v1/key/query", req, result); err != nil {
return nil, err
}
return result, nil
}
// KeyDiscard 券码作废
// POST /openapi/v1/key/discard
func (c *Client) KeyDiscard(req *KeyDiscardRequest) (*KeyDiscardResponse, error) {
result := &KeyDiscardResponse{}
if err := c.doRequest("/openapi/v1/key/discard", req, result); err != nil {
return nil, err
}
return result, nil
}
// BatchOrder 批量发卡
// POST /openapi/v1/key/batch_order
func (c *Client) BatchOrder(req *BatchOrderRequest) (*BatchOrderResponse, error) {
result := &BatchOrderResponse{}
if err := c.doRequest("/openapi/v1/key/batch_order", req, result); err != nil {
return nil, err
}
return result, nil
}
// BatchQuery 批量查询
// POST /openapi/v1/key/batch_query
func (c *Client) BatchQuery(req *BatchQueryRequest) (*BatchQueryResponse, error) {
result := &BatchQueryResponse{}
if err := c.doRequest("/openapi/v1/key/batch_query", req, result); err != nil {
return nil, err
}
return result, nil
}
// VerifyNotify 验证回调通知
func (c *Client) VerifyNotify(header *NotifyHeader, data interface{}) error {
return VerifyNotifySign(header, data, []byte(c.config.Key), c.config.PublicKey, c.config.EncryptType)
}
// GetBaseURL 获取当前环境的基础URL
func (c *Client) GetBaseURL() string {
return c.baseURL
}
// GetAppID 获取应用ID
func (c *Client) GetAppID() string {
return c.config.AppID
}
// String 返回客户端配置的字符串表示(隐藏敏感信息)
func (c *Client) String() string {
return fmt.Sprintf("Client{AppID: %s, Env: %s, BaseURL: %s, EncryptType: %s}",
c.config.AppID, c.config.Env, c.baseURL, c.config.EncryptType)
}