241 lines
6.1 KiB
Go
241 lines
6.1 KiB
Go
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)
|
||
}
|