🐹 Golang
声明
本 SDK 由 AI 生成,仅供参考使用。建议在实际生产环境使用前进行充分测试。
概述
平台 Go Server SDK 提供了便捷的服务端接入方式,支持签名生成、请求加密、响应验签和解密等核心功能。
功能特性
- 支持 SHA256withRSA 签名算法
- 支持 AES 对称加密/解密
- 灵活的 HTTP 客户端接口设计,支持多种 HTTP 客户端实现
- 自动处理签名和加密逻辑
- 完整的响应验签和解密
环境要求
- Go 1.16+
安装
go.mod 依赖
module your-module/pkg/kudian
go 1.21
require (
github.com/stretchr/testify v1.8.4 // 仅测试需要
)
SDK使用Go标准库,无需额外依赖。
快速开始
1. 创建配置
package main
import (
"your-module/pkg/kudian"
)
func main() {
// 创建配置
config := &kudian.ClientConfig{
GatewayURL: "https://pay.kudianvip.com", // 正式环境
// GatewayURL: "https://pay.test.kudianvip.com", // 测试环境
MchID: "your_mch_id",
AppID: "your_app_id",
AppPrivateKey: "your_app_private_key", // Base64格式
AppSecretKey: "your_secret_key", // AES加密密钥
APIPublicKey: "your_api_public_key", // Base64格式
}
// 创建客户端
client := kudian.NewClient(config)
}
2. 发起API请求
package main
import (
"encoding/json"
"fmt"
"your-module/pkg/kudian"
)
func main() {
// 创建配置
config := &kudian.ClientConfig{
GatewayURL: "https://pay.kudianvip.com",
MchID: "your_mch_id",
AppID: "your_app_id",
AppPrivateKey: "your_app_private_key",
AppSecretKey: "your_secret_key",
APIPublicKey: "your_api_public_key",
}
client := kudian.NewClient(config)
// 构造业务参数
bizParams := map[string]interface{}{
"param1": "value1",
"param2": "value2",
}
// 发送请求
response, err := client.Post("/api/path", bizParams)
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return
}
// 解析响应
var apiResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
err = json.Unmarshal([]byte(response), &apiResponse)
if err != nil {
fmt.Printf("解析响应失败: %v\n", err)
return
}
if apiResponse.Code == 0 {
fmt.Printf("业务数据: %v\n", apiResponse.Data)
} else {
fmt.Printf("请求失败: %s\n", apiResponse.Msg)
}
}
核心 API
Client
主客户端类,提供所有API调用功能。
配置参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| GatewayURL | string | 是 | 网关地址,正式环境: https://pay.kudianvip.com,测试环境: https://pay.test.kudianvip.com |
| MchID | string | 是 | 商户编号 |
| AppID | string | 是 | 应用ID |
| AppPrivateKey | string | 是 | 应用私钥(Base64编码格式),用于请求签名 |
| AppSecretKey | string | 是 | AES加密密钥,用于请求/响应内容加密 |
| APIPublicKey | string | 是 | API平台公钥(Base64编码格式),用于响应验签 |
主要方法
Post(apiPath string, bizParams map[string]interface{}) (string, error)
发送POST请求到指定API路径。
参数:
apiPath: API路径,如/api/pay/createbizParams: 业务参数Map
返回:
string: 自动解密后的业务数据JSON字符串error: 错误信息
示例:
package main
func postDemo() {
params := map[string]interface{}{
"order_no": "ORDER202401010001",
"amount": 10000,
"subject": "测试订单",
}
result, err := client.Post("/api/pay/create", params)
if err != nil {
log.Fatal(err)
}
}
VerifyNotify(notifyParams map[string]string) (bool, error)
验证回调通知的签名。
参数:
notifyParams: 回调参数Map
返回:
bool: true-验签成功,false-验签失败error: 错误信息
示例:
package main
import "net/http"
// 在你的HTTP handler中
func handleNotify(w http.ResponseWriter, r *http.Request) {
var params map[string]string
json.NewDecoder(r.Body).Decode(¶ms)
// 验证签名
valid, err := client.VerifyNotify(params)
if err != nil || !valid {
w.Write([]byte("FAIL"))
return
}
// 获取业务数据(已自动解密)
bizData, err := client.DecryptNotify(params["result"])
if err != nil {
w.Write([]byte("FAIL"))
return
}
fmt.Printf("回调数据: %s\n", bizData)
// 处理业务逻辑...
w.Write([]byte("SUCCESS"))
}
完整代码实现
1. config.go - 配置类
package kudian
// ClientConfig SDK配置
type ClientConfig struct {
// GatewayURL 网关地址
GatewayURL string
// MchID 商户编号
MchID string
// AppID 应用ID
AppID string
// AppPrivateKey 应用私钥(Base64格式)
AppPrivateKey string
// APIPublicKey API平台公钥(Base64格式)
APIPublicKey string
// AppSecretKey AES加密密钥
AppSecretKey string
// ConnectTimeout 连接超时时间(秒),默认30秒
ConnectTimeout int
// ReadTimeout 读取超时时间(秒),默认30秒
ReadTimeout int
}
// DefaultConfig 返回默认配置
func DefaultConfig() *ClientConfig {
return &ClientConfig{
ConnectTimeout: 30,
ReadTimeout: 30,
}
}
2. crypto.go - 加密工具类
package kudian
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
)
// CryptoUtils 加密工具类
type CryptoUtils struct{}
// AESEncrypt AES加密(ECB模式)
func (c *CryptoUtils) AESEncrypt(plainText string, key string) (string, error) {
// 确保密钥是32字节
keyBytes := []byte(key)
if len(keyBytes) > 32 {
keyBytes = keyBytes[:32]
}
for len(keyBytes) < 32 {
keyBytes = append(keyBytes, 0)
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// PKCS5填充
plainBytes := []byte(plainText)
padding := aes.BlockSize - len(plainBytes)%aes.BlockSize
padText := make([]byte, padding)
for i := range padText {
padText[i] = byte(padding)
}
plainBytes = append(plainBytes, padText...)
// ECB模式加密
ciphertext := make([]byte, len(plainBytes))
mode := newECBMode(block)
mode.CryptBlocks(ciphertext, plainBytes)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// AESDecrypt AES解密(ECB模式)
func (c *CryptoUtils) AESDecrypt(cipherText string, key string) (string, error) {
// 确保密钥是32字节
keyBytes := []byte(key)
if len(keyBytes) > 32 {
keyBytes = keyBytes[:32]
}
for len(keyBytes) < 32 {
keyBytes = append(keyBytes, 0)
}
ciphertext, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", err
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// ECB模式解密
plainText := make([]byte, len(ciphertext))
mode := newECBMode(block)
mode.CryptBlocks(plainText, ciphertext)
// 移除PKCS5填充
padding := int(plainText[len(plainText)-1])
if padding > aes.BlockSize || padding > len(plainText) {
return "", errors.New("invalid padding")
}
plainText = plainText[:len(plainText)-padding]
return string(plainText), nil
}
// ecb ECB模式实现
type ecb struct {
b cipher.Block
blockSize int
}
// newECBMode 创建ECB模式
func newECBMode(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
// CryptBlocks 加密或解密块(ECB模式)
func (m *ecb) CryptBlocks(dst, src []byte) {
if len(src)%m.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
m.b.Encrypt(dst, src)
dst = dst[m.blockSize:]
src = src[m.blockSize:]
}
}
// SignSHA256withRSA SHA256withRSA签名
func (c *CryptoUtils) SignSHA256withRSA(message []byte, privateKeyBytes []byte) (string, error) {
block, _ := pem.Decode(privateKeyBytes)
if block == nil {
return "", errors.New("failed to decode PEM block")
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
// 尝试PKCS1格式
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("failed to parse private key: %v", err)
}
}
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
if !ok {
return "", errors.New("not an RSA private key")
}
hashed := sha256.Sum256(message)
signature, err := rsa.SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA256, hashed[:])
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}
// VerifySHA256withRSA SHA256withRSA验签
func (c *CryptoUtils) VerifySHA256withRSA(message []byte, publicKeyBytes []byte, sign string) (bool, error) {
block, _ := pem.Decode(publicKeyBytes)
if block == nil {
return false, errors.New("failed to decode PEM block")
}
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return false, fmt.Errorf("failed to parse public key: %v", err)
}
rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
if !ok {
return false, errors.New("not an RSA public key")
}
signature, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
return false, err
}
hashed := sha256.Sum256(message)
err = rsa.VerifyPKCS1v15(rsaPublicKey, crypto.SHA256, hashed[:], signature)
if err != nil {
return false, fmt.Errorf("验签失败: %v", err)
}
return true, nil
}
3. sign.go - 签名工具类
package kudian
import (
"sort"
"strings"
)
// SignUtils 签名工具类
type SignUtils struct{}
// MakeSignString 生成签名字符串
// 将参数按ASCII码排序后拼接成 key1=value1&key2=value2 格式
func (s *SignUtils) MakeSignString(params map[string]string) string {
var keys []string
// 过滤sign字段和空值
for k, v := range params {
if k != "sign" && v != "" {
keys = append(keys, k)
}
}
// 按ASCII码排序
sort.Strings(keys)
// 拼接字符串
var pairs []string
for _, k := range keys {
pairs = append(pairs, fmt.Sprintf("%s=%s", k, params[k]))
}
return strings.Join(pairs, "&")
}
4. http_client.go - HTTP客户端接口
package kudian
// HttpClient HTTP客户端接口
type HttpClient interface {
// Post 发送POST请求
Post(url string, headers map[string]string, requestBody string) (string, error)
}
5. http_client_default.go - 默认HTTP客户端实现
package kudian
import (
"bytes"
"fmt"
"io"
"net/http"
"time"
)
// HttpClientDefault 默认HTTP客户端实现
type HttpClientDefault struct {
client *http.Client
connectTimeout time.Duration
readTimeout time.Duration
}
// NewHttpClientDefault 创建默认客户端
func NewHttpClientDefault() *HttpClientDefault {
return &HttpClientDefault{
connectTimeout: 30 * time.Second,
readTimeout: 30 * time.Second,
}
}
// NewHttpClientDefaultWithTimeout 创建带超时的客户端
func NewHttpClientDefaultWithTimeout(connectTimeout, readTimeout int) *HttpClientDefault {
return &HttpClientDefault{
connectTimeout: time.Duration(connectTimeout) * time.Second,
readTimeout: time.Duration(readTimeout) * time.Second,
}
}
// Post 发送POST请求
func (c *HttpClientDefault) Post(url string, headers map[string]string, requestBody string) (string, error) {
req, err := http.NewRequest("POST", url, bytes.NewBufferString(requestBody))
if err != nil {
return "", err
}
// 设置请求头
for k, v := range headers {
req.Header.Set(k, v)
}
// 设置超时
c.client = &http.Client{
Timeout: c.connectTimeout + c.readTimeout,
}
resp, err := c.client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("请求失败: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
6. client.go - 主客户端类
package kudian
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"strconv"
"time"
)
// Client 酷点支付SDK主客户端
type Client struct {
config *ClientConfig
httpClient HttpClient
cryptoUtils *CryptoUtils
signUtils *SignUtils
}
// NewClient 创建客户端(使用默认HTTP客户端)
func NewClient(config *ClientConfig) *Client {
return &Client{
config: config,
httpClient: NewHttpClientDefault(),
cryptoUtils: &CryptoUtils{},
signUtils: &SignUtils{},
}
}
// NewClientWithHTTPClient 创建客户端(使用自定义HTTP客户端)
func NewClientWithHTTPClient(config *ClientConfig, httpClient HttpClient) *Client {
return &Client{
config: config,
httpClient: httpClient,
cryptoUtils: &CryptoUtils{},
signUtils: &SignUtils{},
}
}
// Post 发送POST请求
func (c *Client) Post(apiPath string, bizParams map[string]interface{}) (string, error) {
// 1. 构造完整请求URL
url := c.config.GatewayURL + apiPath
// 2. 将业务参数转换为JSON并加密
bizJSON, err := json.Marshal(bizParams)
if err != nil {
return "", err
}
encryptedContent, err := c.cryptoUtils.AESEncrypt(string(bizJSON), c.config.AppSecretKey)
if err != nil {
return "", err
}
// 3. 构造公共参数
publicParams := map[string]string{
"mch_id": c.config.MchID,
"app_id": c.config.AppID,
"timestamp": strconv.FormatInt(time.Now().UnixMilli(), 10),
"nonce_str": c.generateNonceStr(),
"sign_type": "SHA",
"content": encryptedContent,
"version": "2.0",
}
// 4. 生成签名
signString := c.signUtils.MakeSignString(publicParams)
privateKeyBytes, err := base64.StdEncoding.DecodeString(c.config.AppPrivateKey)
if err != nil {
return "", err
}
sign, err := c.cryptoUtils.SignSHA256withRSA([]byte(signString), privateKeyBytes)
if err != nil {
return "", err
}
publicParams["sign"] = sign
// 5. 发送请求
requestBody, err := json.Marshal(publicParams)
if err != nil {
return "", err
}
log.Printf("请求URL: %s", url)
log.Printf("请求参数: %s", requestBody)
headers := map[string]string{
"Content-Type": "application/json",
}
responseBody, err := c.httpClient.Post(url, headers, string(requestBody))
if err != nil {
return "", err
}
log.Printf("响应结果: %s", responseBody)
// 6. 解析响应并验签
var response map[string]interface{}
err = json.Unmarshal([]byte(responseBody), &response)
if err != nil {
return "", err
}
code := int(response["code"].(float64))
if code == 0 {
// 成功响应,验证签名
responseSign := response["sign"].(string)
verifyParams := make(map[string]string)
for k, v := range response {
// 排除sign字段
if k != "sign" && v != nil {
verifyParams[k] = fmt.Sprintf("%v", v)
}
}
verifyString := c.signUtils.MakeSignString(verifyParams)
publicKeyBytes, err := base64.StdEncoding.DecodeString(c.config.APIPublicKey)
if err != nil {
return "", err
}
verifyResult, err := c.cryptoUtils.VerifySHA256withRSA(
[]byte(verifyString),
publicKeyBytes,
responseSign,
)
if err != nil || !verifyResult {
return "", fmt.Errorf("响应签名验证失败")
}
// 解密业务数据
encryptedResult := response["result"].(string)
decryptedData, err := c.cryptoUtils.AESDecrypt(encryptedResult, c.config.AppSecretKey)
if err != nil {
return "", err
}
log.Printf("解密后的业务数据: %s", decryptedData)
return decryptedData, nil
} else {
// 失败响应
msg := response["msg"].(string)
return "", fmt.Errorf("请求失败: code=%d, msg=%s", code, msg)
}
}
// VerifyNotify 验证回调通知签名
func (c *Client) VerifyNotify(notifyParams map[string]string) (bool, error) {
sign, exists := notifyParams["sign"]
if !exists || sign == "" {
return false, nil
}
verifyParams := make(map[string]string)
for k, v := range notifyParams {
verifyParams[k] = v
}
verifyString := c.signUtils.MakeSignString(verifyParams)
publicKeyBytes, err := base64.StdEncoding.DecodeString(c.config.APIPublicKey)
if err != nil {
return false, err
}
return c.cryptoUtils.VerifySHA256withRSA(
[]byte(verifyString),
publicKeyBytes,
sign,
)
}
// DecryptNotify 解密回调通知的业务数据
func (c *Client) DecryptNotify(encryptedResult string) (string, error) {
return c.cryptoUtils.AESDecrypt(encryptedResult, c.config.AppSecretKey)
}
// generateNonceStr 生成随机字符串
func (c *Client) generateNonceStr() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)[:32]
}
可选实现
http_client_resty.go - Resty 客户端实现
package kudian
import (
"fmt"
"github.com/go-resty/resty/v2"
"time"
)
// HttpClientResty 基于Resty的HTTP客户端实现
type HttpClientResty struct {
client *resty.Client
}
// NewHttpClientResty 创建Resty客户端
func NewHttpClientResty() *HttpClientResty {
client := resty.New().
SetTimeout(30*time.Second).
SetHeader("Content-Type", "application/json")
return &HttpClientResty{client: client}
}
// Post 发送POST请求
func (c *HttpClientResty) Post(url string, headers map[string]string, requestBody string) (string, error) {
resp, err := c.client.R().
SetHeaders(headers).
SetBody(requestBody).
Post(url)
if err != nil {
return "", err
}
if resp.StatusCode() != 200 {
return "", fmt.Errorf("请求失败: %d", resp.StatusCode())
}
return string(resp.Body()), nil
}
使用示例
完整示例:创建支付订单
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"your-module/pkg/kudian"
)
func main() {
// 1. 创建配置
config := &kudian.ClientConfig{
GatewayURL: "https://pay.test.kudianvip.com",
MchID: "your_mch_id",
AppID: "your_app_id",
AppPrivateKey: "MIIEvQIBADANBgkqhkiG9w0BAQE...", // Base64格式的私钥
APIPublicKey: "MIGfMA0GCSqGSIb3DQEBAQUAA...", // Base64格式的公钥
AppSecretKey: "your_secret_key_32_bytes", // 32字节的AES密钥
}
// 2. 创建客户端
client := kudian.NewClient(config)
// 3. 构造业务参数
bizParams := map[string]interface{}{
"order_no": fmt.Sprintf("ORDER%d", time.Now().UnixMilli()),
"amount": 10000,
"currency": "CNY",
"subject": "测试商品",
"notify_url": "https://your-domain.com/notify",
}
// 4. 发送请求
result, err := client.Post("/api/pay/create", bizParams)
if err != nil {
log.Fatalf("订单创建失败: %v", err)
}
// 5. 解析响应
var data map[string]interface{}
err = json.Unmarshal([]byte(result), &data)
if err != nil {
log.Fatalf("解析响应失败: %v", err)
}
fmt.Println("支付订单创建成功!")
fmt.Printf("订单号: %v\n", data["order_no"])
fmt.Printf("支付URL: %v\n", data["pay_url"])
}
Gin 回调通知处理示例
package main
import (
"your-module/pkg/kudian"
"net/http"
"github.com/gin-gonic/gin"
)
// 创建客户端(全局单例)
var client *kudian.Client
func init() {
config := &kudian.ClientConfig{
MchID: "your_mch_id",
AppID: "your_app_id",
AppPrivateKey: "your_app_private_key",
AppSecretKey: "your_secret_key",
APIPublicKey: "your_api_public_key",
}
client = kudian.NewClient(config)
}
func main() {
r := gin.Default()
r.POST("/notify/pay", handlePayNotify)
r.Run(":8080")
}
// handlePayNotify 处理支付回调
func handlePayNotify(c *gin.Context) {
var params map[string]string
if err := c.BindJSON(¶ms); err != nil {
c.String(http.StatusBadRequest, "FAIL")
return
}
// 1. 验证签名
valid, err := client.VerifyNotify(params)
if err != nil || !valid {
fmt.Println("签名验证失败")
c.String(http.StatusBadRequest, "FAIL")
return
}
// 2. 解密业务数据
bizData, err := client.DecryptNotify(params["result"])
if err != nil {
fmt.Printf("解密失败: %v\n", err)
c.String(http.StatusInternalServerError, "FAIL")
return
}
fmt.Printf("收到支付回调: %s\n", bizData)
// 3. 解析业务数据并处理
// TODO: 根据业务逻辑处理支付结果
// 更新订单状态、发货等
// 4. 返回成功
c.String(http.StatusOK, "SUCCESS")
}
标准库 HTTP 回调通知处理示例
package main
import (
"encoding/json"
"fmt"
"io"
"your-module/pkg/kudian"
"net/http"
)
// 创建客户端(全局单例)
var client *kudian.Client
func init() {
config := &kudian.ClientConfig{
MchID: "your_mch_id",
AppID: "your_app_id",
AppPrivateKey: "your_app_private_key",
AppSecretKey: "your_secret_key",
APIPublicKey: "your_api_public_key",
}
client = kudian.NewClient(config)
}
func main() {
http.HandleFunc("/notify/pay", handlePayNotify)
http.ListenAndServe(":8080", nil)
}
// handlePayNotify 处理支付回调
func handlePayNotify(w http.ResponseWriter, r *http.Request) {
// 1. 解析请求参数
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("FAIL"))
return
}
var params map[string]string
err = json.Unmarshal(body, ¶ms)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("FAIL"))
return
}
// 2. 验证签名
valid, err := client.VerifyNotify(params)
if err != nil || !valid {
fmt.Println("签名验证失败")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("FAIL"))
return
}
// 3. 解密业务数据
bizData, err := client.DecryptNotify(params["result"])
if err != nil {
fmt.Printf("解密失败: %v\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("FAIL"))
return
}
fmt.Printf("收到支付回调: %s\n", bizData)
// 4. 解析业务数据并处理
// TODO: 根据业务逻辑处理支付结果
// 更新订单状态、发货等
// 5. 返回成功
w.WriteHeader(http.StatusOK)
w.Write([]byte("SUCCESS"))
}
异常处理
SDK可能返回的错误:
| 错误类型 | 说明 | 处理建议 |
|---|---|---|
| 配置错误 | 配置参数缺失或无效 | 检查配置参数是否完整和正确 |
| 签名验签失败 | 密钥错误或签名算法错误 | 检查密钥是否正确 |
| 加解密失败 | 密钥错误或数据格式错误 | 检查AppSecretKey是否正确 |
| HTTP请求失败 | 网络错误或服务器错误 | 检查网络连接和网关地址 |
常见问题
1. 如何获取密钥?
- app_private_key: 商户自行生成RSA密钥对,将公钥上传到商户后台
- api_public_key: 从商户后台获取平台公钥
- secret_key: 从商户后台获取或设置
2. 如何生成RSA密钥对?
可以使用OpenSSL命令生成:
# 生成私钥
openssl genrsa -out app_private_key.pem 2048
# 提取公钥
openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem
# 转换为PKCS8格式(Go需要)
openssl pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem
# 转换为Base64格式
base64 -w 0 app_private_key_pkcs8.pem
base64 -w 0 app_public_key.pem
3. 如何使用自定义HTTP客户端?
SDK支持自定义HTTP客户端实现,只需实现 HttpClient 接口:
package kudian
// 自定义实现
type CustomClient struct{}
func (c *CustomClient) Post(url string, headers map[string]string, requestBody string) (string, error) {
// 自定义HTTP客户端实现
// 例如使用其他HTTP库或添加特殊逻辑
return "", nil
}
// 使用示例(在应用代码中)
// client3 := kudian.NewClientWithHTTPClient(config, &CustomClient{})
5. 项目目录结构
your-module/
├── go.mod # Go模块定义文件
├── pkg/
│ └── kudian/ # SDK主包
│ ├── config.go # 配置类
│ ├── crypto.go # 加密工具类
│ ├── sign.go # 签名工具类
│ ├── http_client.go # HTTP客户端接口
│ ├── http_client_default.go # 默认HTTP客户端实现
│ └── client.go # 主客户端类
├── examples/ # 使用示例
│ ├── basic.go # 基础使用示例
│ ├── gin_notify.go # Gin回调处理示例
│ └── http_notify.go # 标准库HTTP回调处理示例
└── tests/ # 测试代码
└── client_test.go # 单元测试
包路径说明:
- 主包路径:
your-module/pkg/kudian - HTTP子包路径:
your-module/pkg/kudian/http - 所有导入语句应使用完整包路径
4. 日志配置
SDK使用标准log包记录日志,可以这样配置:
package main
import (
"log"
"os"
)
func main() {
// 配置日志输出到文件
f, err := os.OpenFile("sdk.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
log.SetOutput(f)
// SDK会使用标准log记录日志
}