添加文件: ymt_v3/client.go

This commit is contained in:
renzhiyuan 2026-07-23 18:14:01 +08:00
parent 9023f2bf2a
commit 09d4868e6a
1 changed files with 240 additions and 0 deletions

240
ymt_v3/client.go Normal file
View File

@ -0,0 +1,240 @@
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)
}