package xyshanghai import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strconv" "time" ) // Client 供应商API客户端 type Client struct { baseURL string httpClient *http.Client apiKey *APIKey } // Option 客户端配置选项 type Option func(*Client) // WithBaseURL 设置基础URL func WithBaseURL(baseURL string) Option { return func(c *Client) { c.baseURL = baseURL } } // WithTimeout 设置HTTP超时时间 func WithTimeout(timeout time.Duration) Option { return func(c *Client) { c.httpClient.Timeout = timeout } } // WithHTTPClient 设置自定义HTTP客户端 func WithHTTPClient(httpClient *http.Client) Option { return func(c *Client) { c.httpClient = httpClient } } // NewClient 创建新的客户端 func NewClient(apiKey *APIKey, opts ...Option) *Client { c := &Client{ httpClient: &http.Client{Timeout: 30 * time.Second}, apiKey: apiKey, } for _, opt := range opts { opt(c) } return c } // doRequest 发送请求并处理响应 func (c *Client) doRequest(ctx context.Context, path string, reqBody interface{}) (*CommonResponse, error) { // 序列化业务数据 plaintext, err := json.Marshal(reqBody) if err != nil { return nil, fmt.Errorf("marshal request body: %w", err) } // SM4加密 encryptedData, err := SM4Encrypt(c.apiKey.SM4Key, plaintext) if err != nil { return nil, fmt.Errorf("encrypt data: %w", err) } // 生成时间戳 timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) // 生成签名 sign := SM3Sign(c.apiKey.SM3Salt, timestamp+encryptedData) // 构建请求体 commonReq := CommonRequestBody{EncryptedData: encryptedData} reqBytes, err := json.Marshal(commonReq) if err != nil { return nil, fmt.Errorf("marshal common request: %w", err) } // 创建HTTP请求 req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, bytes.NewReader(reqBytes)) if err != nil { return nil, fmt.Errorf("create request: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("timestamp", timestamp) req.Header.Set("sign", sign) // 发送请求 resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("do request: %w", err) } defer resp.Body.Close() // 读取响应 respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("read response body: %w", err) } // 解析公共响应 var commonResp CommonResponse if err := json.Unmarshal(respBody, &commonResp); err != nil { return nil, fmt.Errorf("unmarshal common response: %w", err) } return &commonResp, nil } // decryptData 解密响应中的data字段 func (c *Client) decryptData(encryptedData string, target interface{}) error { plaintext, err := SM4Decrypt(c.apiKey.SM4Key, encryptedData) if err != nil { return fmt.Errorf("decrypt data: %w", err) } if err := json.Unmarshal(plaintext, target); err != nil { return fmt.Errorf("unmarshal decrypted data: %w", err) } return nil } // CreateOrder 卡券/直充权益下单 func (c *Client) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*CreateOrderResponse, error) { commonResp, err := c.doRequest(ctx, "/order/create", req) if err != nil { return nil, err } if commonResp.Code != 0 { return nil, NewSDKError(ErrorCode(commonResp.Code), commonResp.Msg, nil) } if commonResp.Data == nil { return nil, NewSDKError(ErrCodeFailure, "response data is nil", nil) } var resp CreateOrderResponse if err := c.decryptData(*commonResp.Data, &resp); err != nil { return nil, err } return &resp, nil } // QueryOrder 卡券/直充/微信立减金订单查询 func (c *Client) QueryOrder(ctx context.Context, req *QueryOrderRequest) (*QueryOrderResponse, error) { commonResp, err := c.doRequest(ctx, "/order/query", req) if err != nil { return nil, err } if commonResp.Code != 0 { return nil, NewSDKError(ErrorCode(commonResp.Code), commonResp.Msg, nil) } if commonResp.Data == nil { return nil, NewSDKError(ErrCodeFailure, "response data is nil", nil) } var resp QueryOrderResponse if err := c.decryptData(*commonResp.Data, &resp); err != nil { return nil, err } return &resp, nil } // RechargeOrder 微信立减金订单充值 func (c *Client) RechargeOrder(ctx context.Context, req *RechargeOrderRequest) (*RechargeOrderResponse, error) { commonResp, err := c.doRequest(ctx, "/order/recharge", req) if err != nil { return nil, err } if commonResp.Code != 0 { return nil, NewSDKError(ErrorCode(commonResp.Code), commonResp.Msg, nil) } if commonResp.Data == nil { return nil, NewSDKError(ErrCodeFailure, "response data is nil", nil) } var resp RechargeOrderResponse if err := c.decryptData(*commonResp.Data, &resp); err != nil { return nil, err } return &resp, nil } // ParseCallbackRequest 解析回调请求,返回业务数据 func (c *Client) ParseCallbackRequest(r *http.Request) (*CallbackRequest, error) { // 读取请求体 body, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("read request body: %w", err) } defer r.Body.Close() // 解析通用请求体 var commonReq CommonRequestBody if err := json.Unmarshal(body, &commonReq); err != nil { return nil, fmt.Errorf("unmarshal common request: %w", err) } // 验证签名(可选) timestamp := r.Header.Get("timestamp") sign := r.Header.Get("sign") expectedSign := SM3Sign(c.apiKey.SM3Salt, timestamp+commonReq.EncryptedData) if sign != expectedSign { return nil, NewSDKError(ErrCodeFailure, "sign verification failed", nil) } // 解密业务数据 plaintext, err := SM4Decrypt(c.apiKey.SM4Key, commonReq.EncryptedData) if err != nil { return nil, fmt.Errorf("decrypt callback data: %w", err) } var callbackReq CallbackRequest if err := json.Unmarshal(plaintext, &callbackReq); err != nil { return nil, fmt.Errorf("unmarshal callback request: %w", err) } return &callbackReq, nil } // CallbackHandler 返回一个http.Handler,用于处理回调通知 func (c *Client) CallbackHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callbackReq, err := c.ParseCallbackRequest(r) if err != nil { http.Error(w, "invalid request", http.StatusBadRequest) return } // 用户需要实现自己的处理逻辑,这里仅返回成功 // 实际使用时,用户应通过闭包或回调函数注入业务处理 _ = callbackReq // 返回成功 w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }) }