添加文件: ymt_v3/client.go
This commit is contained in:
parent
9023f2bf2a
commit
09d4868e6a
|
|
@ -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)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue