2258 lines
69 KiB
Go
2258 lines
69 KiB
Go
package main
|
||
|
||
/*
|
||
#include <stdlib.h>
|
||
*/
|
||
import "C"
|
||
import (
|
||
"bytes"
|
||
"crypto/md5"
|
||
"encoding/base64"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"net/url"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
"unsafe"
|
||
|
||
"github.com/parnurzeal/gorequest"
|
||
"github.com/tjfoc/gmsm/sm4"
|
||
)
|
||
|
||
// ZtoOrderRequest 中通订单请求结构体
|
||
type ZtoOrderRequest struct {
|
||
PartnerType string `json:"partnerType"` // 合作模式 ,1:集团客户;2:非集团客户
|
||
OrderType string `json:"orderType"` // partnerType为1时,orderType:1:全网件 2:预约件。partnerType为2时,orderType:1:全网件 2:预约件(返回运单号) 3:预约件(不返回运单号) 4:星联全网件
|
||
PartnerOrderCode string `json:"partnerOrderCode"` // 合作商订单号
|
||
AccountInfo AccountInfo `json:"accountInfo"` // 账号信息
|
||
BillCode string `json:"billCode"` // 运单号
|
||
SenderInfo SenderInfo `json:"senderInfo"` // 发件人信息
|
||
ReceiveInfo ReceiveInfo `json:"receiveInfo"` // 收件人信息
|
||
OrderVasList []OrderVas `json:"orderVasList"` // 增值服务信息
|
||
HallCode string `json:"hallCode"` // 门店/仓库编码(partnerType为1时可使用)
|
||
SiteCode string `json:"siteCode"` // 网点code(orderVasList.vasType为receiveReturnService必填)
|
||
SiteName string `json:"siteName"` // 网点名称(orderVasList.vasType为receiveReturnService必填)
|
||
SummaryInfo SummaryInfo `json:"summaryInfo"` // 汇总信息
|
||
Remark string `json:"remark"` // 备注
|
||
OrderItems []OrderItem `json:"orderItems"` // 物品信息
|
||
Cabinet Cabinet `json:"cabinet"` // 机柜信息
|
||
}
|
||
|
||
// AccountInfo 账号信息
|
||
type AccountInfo struct {
|
||
AccountId string `json:"accountId"` // 电子面单账号(partnerType为2,orderType传1,2,4时必传)
|
||
AccountPassword string `json:"accountPassword"` // 电子面单密码(测试环境传ZTO123)
|
||
Type int `json:"type"` // 单号类型:1.普通电子面单;74.星联电子面单;默认是1
|
||
CustomerId string `json:"customerId"` // 集团客户编码(partnerType传1时必传)
|
||
}
|
||
|
||
// SenderInfo 发件人信息
|
||
type SenderInfo struct {
|
||
SenderId string `json:"senderId"` // 发件人ID
|
||
SenderName string `json:"senderName"` // 发件人姓名
|
||
SenderPhone string `json:"senderPhone"` // 发件人座机(与senderMobile二者不能同时为空)
|
||
SenderMobile string `json:"senderMobile"` // 发件人手机号(与senderPhone二者不能同时为空)
|
||
SenderProvince string `json:"senderProvince"` // 发件人省
|
||
SenderCity string `json:"senderCity"` // 发件人市
|
||
SenderDistrict string `json:"senderDistrict"` // 发件人区
|
||
SenderAddress string `json:"senderAddress"` // 发件人详细地址
|
||
}
|
||
|
||
// ReceiveInfo 收件人信息
|
||
type ReceiveInfo struct {
|
||
ReceiverName string `json:"receiverName"` // 收件人姓名
|
||
ReceiverPhone string `json:"receiverPhone"` // 收件人座机(与receiverMobile二者不能同时为空)
|
||
ReceiverMobile string `json:"receiverMobile"` // 收件人手机号(与 receiverPhone二者不能同时为空)
|
||
ReceiverProvince string `json:"receiverProvince"` // 收件人省
|
||
ReceiverCity string `json:"receiverCity"` // 收件人市
|
||
ReceiverDistrict string `json:"receiverDistrict"` // 收件人区
|
||
ReceiverAddress string `json:"receiverAddress"` // 收件人详细地址
|
||
}
|
||
|
||
// OrderVas 增值服务
|
||
type OrderVas struct {
|
||
VasType string `json:"vasType"` // 增值类型(COD:代收;vip:中通标快;insured:保价;receiveReturnService:签单返回;twoHour:两小时;standardExpress:中通好快)
|
||
VasAmount int64 `json:"vasAmount"` // 增值价格,如果增值类型涉及金额会校验,vasType为COD、insured时不能为空,单位:分
|
||
VasPrice int64 `json:"vasPrice"` // 增值价格(暂时不用)
|
||
VasDetail string `json:"vasDetail"` // 增值详情
|
||
AccountNo string `json:"accountNo"` // 代收账号(有代收货款增值时必填)
|
||
}
|
||
|
||
// SummaryInfo 汇总信息
|
||
type SummaryInfo struct {
|
||
Size string `json:"size"` // 订单包裹大小(单位:厘米、格式:"长,宽,高",用半角的逗号来分隔)
|
||
Quantity int `json:"quantity"` // 订单包裹内货物总数量
|
||
Price int `json:"price"` // 商品总价值(单位:元)
|
||
Freight int `json:"freight"` // 运输费(单位:元)
|
||
Premium int `json:"premium"` // 险费(单位:元)
|
||
StartTime string `json:"startTime"` // 取件开始时间(传值此字段则需必传vasType:twoHour)
|
||
EndTime string `json:"endTime"` // 取件截止时间(传值此字段则需必传vasType:twoHour)
|
||
}
|
||
|
||
// OrderItem 物品信息
|
||
type OrderItem struct {
|
||
Name string `json:"name"` // 货品名称
|
||
Category string `json:"category"` // 商品分类
|
||
Material string `json:"material"` // 商品材质
|
||
Size string `json:"size"` // 大小(长,宽,高)(单位:厘米), 用半角的逗号来分隔长宽高
|
||
Weight int64 `json:"weight"` // 重量(单位:克)
|
||
Unitprice int `json:"unitprice"` // 单价(单位:元)
|
||
Quantity int `json:"quantity"` // 货品数量
|
||
Remark string `json:"remark"` // 货品备注
|
||
}
|
||
|
||
// Cabinet 机柜信息
|
||
type Cabinet struct {
|
||
Address string `json:"address"` // 地址
|
||
Specification int `json:"specification"` // 格口规格 格口大小(1 大 2 中 3 小)
|
||
Code string `json:"code"` // 开箱码
|
||
}
|
||
|
||
/*
|
||
中通快递--创建订单接口
|
||
请求参数:
|
||
requestJSON 创建订单参数JSON字符串
|
||
appKey app值
|
||
appSecret 密钥
|
||
*/
|
||
func ztoOpenCreateOrder(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.createOrder"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.createOrder"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通签名生成函数
|
||
func ztoGenerateSign(data, appSecret string) (string, error) {
|
||
// 中通签名生成函数(修正版)
|
||
// 根据文档,签名流程应该是:
|
||
// 1. 请求参数json字符串 + appSecret
|
||
// 2. 进行MD5计算
|
||
// 3. 对MD5结果进行Base64编码
|
||
|
||
// 注意:中通可能要求特定的格式,比如参数需要排序
|
||
var requestData map[string]interface{}
|
||
if err := json.Unmarshal([]byte(data), &requestData); err != nil {
|
||
return "", fmt.Errorf("解析请求参数失败: %w", err)
|
||
}
|
||
|
||
// 将map转换为排序后的JSON字符串(中通可能要求按key排序)
|
||
keys := make([]string, 0, len(requestData))
|
||
for k := range requestData {
|
||
keys = append(keys, k)
|
||
}
|
||
sort.Strings(keys)
|
||
|
||
var buf strings.Builder
|
||
buf.WriteString("{")
|
||
for i, k := range keys {
|
||
if i > 0 {
|
||
buf.WriteString(",")
|
||
}
|
||
buf.WriteString(fmt.Sprintf(`"%s":`, k))
|
||
|
||
// 根据值的类型进行格式化
|
||
switch v := requestData[k].(type) {
|
||
case string:
|
||
buf.WriteString(fmt.Sprintf(`"%s"`, v))
|
||
case float64:
|
||
buf.WriteString(fmt.Sprintf(`%.0f`, v)) // 整数可能以float64形式存在
|
||
default:
|
||
// 其他类型转换为JSON字符串
|
||
valBytes, _ := json.Marshal(v)
|
||
buf.Write(valBytes)
|
||
}
|
||
}
|
||
buf.WriteString("}")
|
||
|
||
sortedJSON := buf.String()
|
||
|
||
// 按文档要求的格式
|
||
signString := sortedJSON + appSecret
|
||
|
||
// 计算MD5
|
||
md5Sum := md5.Sum([]byte(signString))
|
||
|
||
// Base64编码
|
||
sign := base64.StdEncoding.EncodeToString(md5Sum[:])
|
||
return sign, nil
|
||
}
|
||
|
||
// 中通快递--取消订单接口
|
||
func ztoOpenCancelPreOrder(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.cancelPreOrder"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.cancelPreOrder"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通快递--查询订单接口
|
||
func ztoOpenGetOrderInfo(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.getOrderInfo"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.getOrderInfo"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通快递--获取打单余额
|
||
func ztoOpenQueryAvailableBalanceNew(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.queryAvailableBalanceNew"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.queryAvailableBalanceNew"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通快递--请求生成面单图片/PDF
|
||
func ztoOpenOrderPrint(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.order.print"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.order.print"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通快递--绑定电子面单
|
||
func ztoOpenBindingEaccount(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.open.bindingEaccount"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.open.bindingEaccount"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 中通快递--网点code查询网点信息
|
||
func ztoVipQuerySiteInfoByCode(requestJSON, appKey, appSecret string) (string, error) {
|
||
if appKey == "" || appSecret == "" {
|
||
return "", fmt.Errorf("中通API配置错误: appKey或appSecret未设置")
|
||
}
|
||
// 正式环境
|
||
ztoUrl := "https://japi.zto.com/zto.vip.querySiteInfoByCode"
|
||
// 测试环境
|
||
//ztoUrl := "https://japi-test.zto.com/zto.vip.querySiteInfoByCode"
|
||
|
||
// 生成签名
|
||
dataDigest, err := ztoGenerateSign(requestJSON, appSecret)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 创建请求实例
|
||
req := gorequest.New()
|
||
// 发送POST请求
|
||
resp, body, errs := req.Post(ztoUrl).
|
||
Set("Content-Type", "application/json").
|
||
Set("x-appKey", appKey).
|
||
Set("x-dataDigest", dataDigest).
|
||
Set("x-timestamp", fmt.Sprintf("%d", time.Now().Unix())).
|
||
Send(requestJSON).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 极兔快递--创建订单
|
||
func jtOrderAddOrder(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/order/addOrder"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/order/addOrder?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--创建订单(带运单号)
|
||
func jtOrderV2AddOrder(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/order/v2/addOrder"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/order/v2/addOrder?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--查询订单
|
||
func jtOrderGetOrders(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/order/getOrders"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/order/getOrders?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--取消订单
|
||
func jtOrderCancelOrder(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/order/cancelOrder"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/order/cancelOrder?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--电子面单账号检验
|
||
func jtVipCheckCusPwd(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/vip/checkCusPwd"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/vip/checkCusPwd?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--电子面单余额查询
|
||
func jtEssBalance(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/ess/balance"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/ess/balance?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--获取电子面单
|
||
func jtBillCodeGetBatchBillCode(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/billCode/getBatchBillCode"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/billCode/getBatchBillCode?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// 极兔快递--面单打印
|
||
func jtOrderPrintOrder(bizContentJSONStr, apiAccount, privateKey string) (string, error) {
|
||
// 正式环境
|
||
jtUrl := "https://openapi.bxexpress.com.cn/webopenplatformapi/api/order/printOrder"
|
||
// 测试环境
|
||
// jtUrl := "https://uat-openapi.bxexpress.com.cn/webopenplatformapi/api/order/printOrder?uuid=c71ba79a80fc4307ae81f09e65b89af8"
|
||
|
||
// 生成签名(传入bizContent的JSON字符串)
|
||
digest, err := jtGenerateDigest(bizContentJSONStr, privateKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("生成签名失败: %w", err)
|
||
}
|
||
|
||
// 当前时间戳
|
||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||
|
||
// 构造请求体
|
||
requestBody := map[string]string{
|
||
"bizContent": bizContentJSONStr,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
// 使用gorequest发送POST请求
|
||
resp, body, errs := req.Post(jtUrl).
|
||
Type("form").
|
||
Set("Content-Type", "application/x-www-form-urlencoded").
|
||
Set("apiAccount", apiAccount).
|
||
Set("digest", digest).
|
||
Set("timestamp", timestamp).
|
||
Send(requestBody).
|
||
End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d, 响应: %s", resp.StatusCode, body)
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// jtGenerateDigest 极兔生成Headers中的digest签名
|
||
// bizContentJSONStr: 业务参数的JSON字符串
|
||
// privateKey: 平台分配的私钥
|
||
func jtGenerateDigest(bizContentJSONStr, privateKey string) (string, error) {
|
||
// 1. 拼接JSON字符串和私钥
|
||
signStr := bizContentJSONStr + privateKey
|
||
|
||
// 2. 计算MD5
|
||
hash := md5.Sum([]byte(signStr))
|
||
|
||
// 3. Base64编码
|
||
digest := base64.StdEncoding.EncodeToString(hash[:])
|
||
|
||
return digest, nil
|
||
}
|
||
|
||
// GenerateBusinessDigest 极兔生成业务参数中的digest签名(用于bizContent内部)
|
||
// customerCode: 客户编码,如"J0086474299"
|
||
// plainPassword: 明文密码,如"H5CD3zE6"
|
||
// businessKey: 业务密钥(固定为"jadada236t2")
|
||
// privateKey: 平台分配的私钥
|
||
// 返回:加密后的密码和digest签名
|
||
func GenerateBusinessDigest(customerCode, plainPassword, businessKey, privateKey string) (encryptedPwd, digest string, err error) {
|
||
// 1. 计算加密后的密码 pwd = MD5(明文密码 + 业务密钥),32位大写
|
||
pwdStr := plainPassword + businessKey
|
||
pwdHash := md5.Sum([]byte(pwdStr))
|
||
encryptedPwd = strings.ToUpper(hex.EncodeToString(pwdHash[:]))
|
||
|
||
// 2. 拼接 customerCode + pwd + privateKey
|
||
signStr := customerCode + encryptedPwd + privateKey
|
||
|
||
// 3. 计算MD5
|
||
hash := md5.Sum([]byte(signStr))
|
||
|
||
// 4. Base64编码
|
||
digest = base64.StdEncoding.EncodeToString(hash[:])
|
||
|
||
return encryptedPwd, digest, nil
|
||
}
|
||
|
||
/*
|
||
申通快递--订单创建接口
|
||
请求参数:
|
||
requestJSON content参数JSON字符串
|
||
fromAppkey 订阅方/请求发起方的应用key
|
||
secretKey 密钥
|
||
fromCode 订阅方/请求发起方的应用资源code
|
||
*/
|
||
func stoOmsExpressOrderCreate(requestJSON, fromAppkey, secretKey, fromCode string) (string, error) {
|
||
// 正式环境
|
||
//ztoUrl := "https://cloudinter-linkgateway.sto.cn/gateway/link.do"
|
||
// 测试环境
|
||
stoUrl := "http://cloudinter-linkgatewaytest.sto.cn/gateway/link.do"
|
||
|
||
signature := stoGenerateSign(requestJSON, secretKey)
|
||
|
||
// 准备请求参数
|
||
params := map[string]string{
|
||
"api_name": "OMS_EXPRESS_ORDER_CREATE",
|
||
"content": requestJSON,
|
||
"from_appkey": fromAppkey,
|
||
"from_code": fromCode,
|
||
"to_appkey": "sto_oms",
|
||
"to_code": "sto_oms",
|
||
"data_digest": signature,
|
||
}
|
||
|
||
// 创建gorequest实例
|
||
req := gorequest.New()
|
||
|
||
// 设置超时和重试策略
|
||
req = req.Timeout(30*time.Second).
|
||
Retry(3, 5*time.Second, http.StatusInternalServerError, http.StatusBadGateway)
|
||
|
||
// 使用gorequest发送POST请求
|
||
req.Post(stoUrl).
|
||
Set("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
|
||
|
||
// 添加表单参数
|
||
for key, value := range params {
|
||
req.Send(fmt.Sprintf("%s=%s", key, value))
|
||
}
|
||
resp, body, errs := req.End()
|
||
|
||
// 处理错误
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 申通快递计算签名
|
||
func stoGenerateSign(content, secretKey string) string {
|
||
text := content + secretKey
|
||
// 计算 MD5
|
||
hash := md5.Sum([]byte(text))
|
||
// Base64 编码
|
||
return base64.StdEncoding.EncodeToString(hash[:])
|
||
}
|
||
|
||
// 韵达快递-电子面单取消
|
||
func ydCancelBmOrder(requestJSON, appKey, appSecret string) (string, error) {
|
||
// 确定环境
|
||
baseURL := "https://openapi.yundaex.com/openapi-api/v1/accountOrder/cancelBmOrder"
|
||
// 测试环境
|
||
//baseURL := "https://u-openapi.yundasys.com/openapi-api/v1/accountOrder/cancelBmOrder"
|
||
|
||
// 1. 构建基础参数(用于签名和请求头)
|
||
timestamp := time.Now().UnixMilli()
|
||
reqTime := fmt.Sprintf("%d", timestamp)
|
||
|
||
// 针对JSON字符串进行升序排序
|
||
key, err := reorderJSONFieldsByKey(requestJSON, true)
|
||
if err != nil {
|
||
return "", fmt.Errorf("排序失败: %v", err)
|
||
}
|
||
fmt.Println("排序后的JSON字符串:", key)
|
||
|
||
sign := ydGenerateSign(key, appSecret)
|
||
|
||
// 3. 构建 Headers(根据文档)
|
||
headers := map[string]string{
|
||
"Content-Type": "application/json; charset=UTF-8",
|
||
"app-key": appKey,
|
||
"req-time": reqTime,
|
||
"sign": sign, // 需要实现签名方法
|
||
}
|
||
|
||
// 4. 发送请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(baseURL).
|
||
Set("Content-Type", headers["Content-Type"]).
|
||
Set("app-key", headers["app-key"]).
|
||
Set("req-time", headers["req-time"]).
|
||
Set("sign", headers["sign"]).
|
||
Send(requestJSON).
|
||
End()
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 韵达快递--电子面单下单
|
||
// 参数:requestJSON:业务参数JSON字符串 appid:合作商appid(等同app-key) appSecret:签名 partnerID
|
||
func ydCreateBmOrder(requestJSON, appKey, appSecret string) (string, error) {
|
||
// 确定环境
|
||
baseURL := "https://openapi.yundaex.com/openapi-api/v1/accountOrder/createBmOrder"
|
||
// 测试环境
|
||
//baseURL := "https://u-openapi.yundasys.com/openapi-api/v1/accountOrder/createBmOrder"
|
||
|
||
// 1. 构建基础参数(用于签名和请求头)
|
||
timestamp := time.Now().UnixMilli()
|
||
reqTime := fmt.Sprintf("%d", timestamp)
|
||
|
||
// 针对JSON字符串进行升序排序
|
||
key, err := reorderJSONFieldsByKey(requestJSON, true)
|
||
if err != nil {
|
||
return "", fmt.Errorf("排序失败: %v", err)
|
||
}
|
||
fmt.Println("排序后的JSON字符串:", key)
|
||
|
||
sign := ydGenerateSign(key, appSecret)
|
||
|
||
// 3. 构建 Headers(根据文档)
|
||
headers := map[string]string{
|
||
"Content-Type": "application/json; charset=UTF-8",
|
||
"app-key": appKey,
|
||
"req-time": reqTime,
|
||
"sign": sign, // 需要实现签名方法
|
||
}
|
||
|
||
// 4. 发送请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(baseURL).
|
||
Set("Content-Type", headers["Content-Type"]).
|
||
Set("app-key", headers["app-key"]).
|
||
Set("req-time", headers["req-time"]).
|
||
Set("sign", headers["sign"]).
|
||
Send(requestJSON).
|
||
End()
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 按键名排序JSON字段
|
||
func reorderJSONFieldsByKey(requestJSON string, sortAscending bool) (string, error) {
|
||
// 解析JSON
|
||
var obj map[string]interface{}
|
||
if err := json.Unmarshal([]byte(requestJSON), &obj); err != nil {
|
||
return "", fmt.Errorf("JSON解析失败: %v", err)
|
||
}
|
||
|
||
// 获取所有键并排序
|
||
keys := make([]string, 0, len(obj))
|
||
for k := range obj {
|
||
keys = append(keys, k)
|
||
}
|
||
|
||
if sortAscending {
|
||
sort.Strings(keys) // 升序
|
||
} else {
|
||
sort.Sort(sort.Reverse(sort.StringSlice(keys))) // 降序
|
||
}
|
||
|
||
// 创建有序结果
|
||
orderedObj := make(map[string]interface{})
|
||
for _, key := range keys {
|
||
orderedObj[key] = obj[key]
|
||
}
|
||
|
||
marshal, _ := json.Marshal(orderedObj)
|
||
return string(marshal), nil
|
||
}
|
||
|
||
// 韵达快递计算签名
|
||
func ydGenerateSign(content, secretKey string) string {
|
||
text := content + "_" + secretKey
|
||
// 计算 MD5
|
||
hash := md5.Sum([]byte(text))
|
||
|
||
return fmt.Sprintf("%x", hash[:])
|
||
}
|
||
|
||
// 韵达快递-电子面单余量查询接口
|
||
func ydSearchCount(requestJSON, appKey, appSecret string) (string, error) {
|
||
// 确定环境
|
||
baseURL := "https://openapi.yundaex.com/openapi-api/v1/accountOrder/searchCount"
|
||
// 测试环境
|
||
//baseURL := "https://u-openapi.yundasys.com/openapi-api/v1/accountOrder/searchCount"
|
||
// 排序
|
||
key, err := reorderJSONFieldsByKey(requestJSON, true)
|
||
if err != nil {
|
||
return "", fmt.Errorf("排序失败: %v", err)
|
||
}
|
||
|
||
sign := ydGenerateSign(key, appSecret)
|
||
|
||
fmt.Println("appKey=", appKey, " appSecret=", appSecret, " requestJSON=", requestJSON, " sign=", sign)
|
||
|
||
// 3. 构建 Headers(根据文档)
|
||
timestamp := time.Now().UnixMilli()
|
||
headers := map[string]string{
|
||
"Content-Type": "application/json; charset=UTF-8",
|
||
"app-key": appKey,
|
||
"req-time": fmt.Sprintf("%d", timestamp),
|
||
"sign": sign, // 需要实现签名方法
|
||
}
|
||
|
||
// 4. 发送请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(baseURL).
|
||
Set("Content-Type", headers["Content-Type"]).
|
||
Set("app-key", headers["app-key"]).
|
||
Set("req-time", headers["req-time"]).
|
||
Set("sign", headers["sign"]).
|
||
Send(requestJSON).
|
||
End()
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 韵达快递--电子面单打印
|
||
func ydBmGetPdfInfo(requestJSON, appKey, appSecret string) (string, error) {
|
||
// 确定环境
|
||
baseURL := "https://openapi.yundaex.com/openapi/outer/v1/bm/getPdfInfo"
|
||
// 测试环境
|
||
//baseURL := "https://u-openapi.yundasys.com/openapi/outer/v1/bm/getPdfInfo"
|
||
|
||
// 排序
|
||
key, err := reorderJSONFieldsByKey(requestJSON, true)
|
||
if err != nil {
|
||
return "", fmt.Errorf("排序失败: %v", err)
|
||
}
|
||
fmt.Println("排序后的JSON字符串:", key)
|
||
|
||
// 3. 构建 Headers(根据文档)
|
||
timestamp := time.Now().UnixMilli()
|
||
headers := map[string]string{
|
||
"Content-Type": "application/json; charset=UTF-8",
|
||
"app-key": appKey,
|
||
"req-time": fmt.Sprintf("%d", timestamp),
|
||
"sign": ydGenerateSign(key, appSecret), // 需要实现签名方法
|
||
}
|
||
|
||
// 4. 发送请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(baseURL).
|
||
Set("Content-Type", headers["Content-Type"]).
|
||
Set("app-key", headers["app-key"]).
|
||
Set("req-time", headers["req-time"]).
|
||
Set("sign", headers["sign"]).
|
||
Send(requestJSON).
|
||
End()
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// 韵达快递-电子面单更新接口
|
||
func ydUpdateBmOrder(requestJSON, appKey, appSecret string) (string, error) {
|
||
// 确定环境
|
||
baseURL := "https://openapi.yundaex.com/openapi-api/v1/accountOrder/updateBmOrder"
|
||
// 测试环境
|
||
//baseURL := "https://u-openapi.yundasys.com/openapi-api/v1/accountOrder/updateBmOrder"
|
||
|
||
// 1. 构建基础参数(用于签名和请求头)
|
||
timestamp := time.Now().UnixMilli()
|
||
reqTime := fmt.Sprintf("%d", timestamp)
|
||
|
||
// 针对JSON字符串进行升序排序
|
||
key, err := reorderJSONFieldsByKey(requestJSON, true)
|
||
if err != nil {
|
||
return "", fmt.Errorf("排序失败: %v", err)
|
||
}
|
||
fmt.Println("排序后的JSON字符串:", key)
|
||
|
||
sign := ydGenerateSign(key, appSecret)
|
||
|
||
// 3. 构建 Headers(根据文档)
|
||
headers := map[string]string{
|
||
"Content-Type": "application/json; charset=UTF-8",
|
||
"app-key": appKey,
|
||
"req-time": reqTime,
|
||
"sign": sign, // 需要实现签名方法
|
||
}
|
||
|
||
// 4. 发送请求
|
||
request := gorequest.New()
|
||
resp, body, errs := request.Post(baseURL).
|
||
Set("Content-Type", headers["Content-Type"]).
|
||
Set("app-key", headers["app-key"]).
|
||
Set("req-time", headers["req-time"]).
|
||
Set("sign", headers["sign"]).
|
||
Send(requestJSON).
|
||
End()
|
||
if len(errs) > 0 {
|
||
errStr := ""
|
||
for _, e := range errs {
|
||
errStr += e.Error() + "; "
|
||
}
|
||
return "", fmt.Errorf("发送HTTP请求失败: %s", errStr)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode >= 400 {
|
||
return "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 解析响应体
|
||
var response map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &response); err != nil {
|
||
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, body)
|
||
}
|
||
|
||
// 转换成json字符串
|
||
responseJSON, err := json.Marshal(response)
|
||
if err != nil {
|
||
return "", fmt.Errorf("JSON序列化失败: %v", err)
|
||
}
|
||
return string(responseJSON), nil
|
||
}
|
||
|
||
// ========================== C 导入函数 ===================
|
||
|
||
// ZtoOpenCreateOrder 中通快递--创建订单接口
|
||
//
|
||
//export ZtoOpenCreateOrder
|
||
func ZtoOpenCreateOrder(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenCreateOrder(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoOpenCancelPreOrder 中通快递--取消订单接口
|
||
//
|
||
//export ZtoOpenCancelPreOrder
|
||
func ZtoOpenCancelPreOrder(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenCancelPreOrder(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoOpenGetOrderInfo 中通快递--查询订单接口
|
||
//
|
||
//export ZtoOpenGetOrderInfo
|
||
func ZtoOpenGetOrderInfo(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenGetOrderInfo(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoOpenQueryAvailableBalanceNew 中通快递--获取打单余额
|
||
//
|
||
//export ZtoOpenQueryAvailableBalanceNew
|
||
func ZtoOpenQueryAvailableBalanceNew(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenQueryAvailableBalanceNew(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoOpenOrderPrint 中通快递--请求生成面单图片/PDF
|
||
//
|
||
//export ZtoOpenOrderPrint
|
||
func ZtoOpenOrderPrint(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenOrderPrint(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoOpenBindingEaccount 中通快递--绑定电子面单
|
||
//
|
||
//export ZtoOpenBindingEaccount
|
||
func ZtoOpenBindingEaccount(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoOpenBindingEaccount(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// ZtoVipQuerySiteInfoByCode 中通快递--网点code查询网点信息
|
||
//
|
||
//export ZtoVipQuerySiteInfoByCode
|
||
func ZtoVipQuerySiteInfoByCode(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ztoVipQuerySiteInfoByCode(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtOrderAddOrder 极兔快递--创建订单接口
|
||
//
|
||
//export JtOrderAddOrder
|
||
func JtOrderAddOrder(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtOrderAddOrder(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtOrderV2AddOrder 极兔快递--创建订单(带运单号)
|
||
//
|
||
//export JtOrderV2AddOrder
|
||
func JtOrderV2AddOrder(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtOrderV2AddOrder(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtOrderGetOrders 极兔快递--查询订单
|
||
//
|
||
//export JtOrderGetOrders
|
||
func JtOrderGetOrders(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtOrderGetOrders(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtOrderCancelOrder 极兔快递--取消订单
|
||
//
|
||
//export JtOrderCancelOrder
|
||
func JtOrderCancelOrder(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtOrderCancelOrder(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtVipCheckCusPwd 极兔快递--电子面单账号检验
|
||
//
|
||
//export JtVipCheckCusPwd
|
||
func JtVipCheckCusPwd(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtVipCheckCusPwd(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtEssBalance 极兔快递--电子面单余额查询
|
||
//
|
||
//export JtEssBalance
|
||
func JtEssBalance(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtEssBalance(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtBillCodeGetBatchBillCode 极兔快递--获取电子面单
|
||
//
|
||
//export JtBillCodeGetBatchBillCode
|
||
func JtBillCodeGetBatchBillCode(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtBillCodeGetBatchBillCode(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// JtOrderPrintOrder 极兔快递--面单打印
|
||
//
|
||
//export JtOrderPrintOrder
|
||
func JtOrderPrintOrder(bizContentJSON, apiAccount, privateKey *C.char) *C.char {
|
||
bizContentJSONStr := C.GoString(bizContentJSON)
|
||
apiAccountStr := C.GoString(apiAccount)
|
||
privateKeyStr := C.GoString(privateKey)
|
||
info, err := jtOrderPrintOrder(bizContentJSONStr, apiAccountStr, privateKeyStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// EmsAmpApiOpen 邮政快递--订单接入接口
|
||
//
|
||
//export EmsAmpApiOpen
|
||
func EmsAmpApiOpen(apiCode, senderNo, authorization, secretKey, requestJSON *C.char) *C.char {
|
||
apiCodeStr := C.GoString(apiCode)
|
||
senderNoStr := C.GoString(senderNo)
|
||
authorizationStr := C.GoString(authorization)
|
||
secretKeyStr := C.GoString(secretKey)
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
info, err := emsAmpApiOpen(apiCodeStr, senderNoStr, authorizationStr, secretKeyStr, requestJSONStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// StoOmsExpressOrderCreate 申通快递--订单接入接口
|
||
//
|
||
//export StoOmsExpressOrderCreate
|
||
func StoOmsExpressOrderCreate(requestJSON, fromAppkey, secretKey, fromCode *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
fromAppkeyStr := C.GoString(fromAppkey)
|
||
secretKeyStr := C.GoString(secretKey)
|
||
fromCodeStr := C.GoString(fromCode)
|
||
|
||
info, err := stoOmsExpressOrderCreate(requestJSONStr, fromAppkeyStr, secretKeyStr, fromCodeStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// YdCancelBmOrder 韵达快递-电子面单取消
|
||
//
|
||
//export YdCancelBmOrder
|
||
func YdCancelBmOrder(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ydCancelBmOrder(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// YdCreateBmOrder 韵达快递--电子面单下单
|
||
//
|
||
//export YdCreateBmOrder
|
||
func YdCreateBmOrder(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ydCreateBmOrder(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// YdSearchCount 韵达快递-电子面单余量查询接口
|
||
//
|
||
//export YdSearchCount
|
||
func YdSearchCount(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ydSearchCount(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// YdBmGetPdfInfo 韵达快递--电子面单打印
|
||
//
|
||
//export YdBmGetPdfInfo
|
||
func YdBmGetPdfInfo(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ydBmGetPdfInfo(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// YdUpdateBmOrder 韵达快递-电子面单更新接口
|
||
//
|
||
//export YdUpdateBmOrder
|
||
func YdUpdateBmOrder(requestJSON, appKey, appSecret *C.char) *C.char {
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
appKeyStr := C.GoString(appKey)
|
||
appSecretStr := C.GoString(appSecret)
|
||
info, err := ydUpdateBmOrder(requestJSONStr, appKeyStr, appSecretStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
// FreeCString 释放C字符串内存
|
||
//
|
||
//export FreeCString
|
||
func FreeCString(str *C.char) {
|
||
C.free(unsafe.Pointer(str))
|
||
}
|
||
|
||
///**********************************************************邮政开始***************************************************************//
|
||
|
||
// MultipartFormData 存储multipart表单数据
|
||
type MultipartFormData struct {
|
||
fields map[string]string
|
||
files map[string]FileInfo
|
||
}
|
||
|
||
// FileInfo 文件信息结构
|
||
type FileInfo struct {
|
||
FieldName string
|
||
FileName string
|
||
FileData []byte
|
||
}
|
||
|
||
// NewMultipartFormData 创建新的multipart表单数据对象
|
||
func NewMultipartFormData() *MultipartFormData {
|
||
return &MultipartFormData{
|
||
fields: make(map[string]string),
|
||
files: make(map[string]FileInfo),
|
||
}
|
||
}
|
||
|
||
/*
|
||
邮政快递--订单接入接口
|
||
请求参数:
|
||
apiCode 接口代码
|
||
senderNo 生产协议客户号
|
||
authorization 授权码
|
||
secretKey 密钥
|
||
requestJSON 业务报文JSON字符串
|
||
*/
|
||
func emsAmpApiOpen(apiCode, senderNo, authorization, secretKey, requestJSON string) (string, error) {
|
||
|
||
url := "https://api.ems.com.cn/amp-prod-api/f/amp/api/open"
|
||
|
||
contentToEncrypt := requestJSON + secretKey
|
||
signature, err := GenerateSM4Signature(contentToEncrypt, secretKey)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
formData := NewMultipartFormData()
|
||
formData.AddField("apiCode", apiCode)
|
||
formData.AddField("senderNo", senderNo)
|
||
formData.AddField("authorization", authorization)
|
||
formData.AddField("timeStamp", time.Now().Format("2006-01-02 15:04:05"))
|
||
formData.AddField("logitcsInterface", signature)
|
||
|
||
headers := map[string]string{
|
||
"User-Agent": "ZTO-API-Client/1.0",
|
||
}
|
||
|
||
response, err := PostMultipartRequest(url, formData, headers, 30*time.Second)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(response), nil
|
||
}
|
||
|
||
const (
|
||
CIPHERTEXT_PREFIX_THIRD_SM4ECB = "|$4|"
|
||
)
|
||
|
||
// GenerateSM4Signature 生成SM4签名
|
||
// params: 业务报文JSON字符串
|
||
// key: 从页面生成的秘钥(base64格式)
|
||
// 返回值: 签名后的字符串,格式为 |$4| + base64(SM4加密结果)
|
||
func GenerateSM4Signature(params, key string) (string, error) {
|
||
// 检查输入参数
|
||
if params == "" || key == "" {
|
||
return params, nil
|
||
}
|
||
|
||
// 检查是否已经加密
|
||
if strings.HasPrefix(params, CIPHERTEXT_PREFIX_THIRD_SM4ECB) {
|
||
return params, nil
|
||
}
|
||
|
||
// 拼接待签名的内容:业务报文 + key
|
||
content := params + key
|
||
|
||
// 解码base64格式的密钥
|
||
keyBytes, err := base64.StdEncoding.DecodeString(key)
|
||
if err != nil {
|
||
return "", fmt.Errorf("failed to decode base64 key: %v", err)
|
||
}
|
||
|
||
// SM4 ECB模式加密
|
||
encryptedData, err := sm4ECBEncrypt([]byte(content), keyBytes)
|
||
if err != nil {
|
||
return "", fmt.Errorf("failed to encrypt with SM4: %v", err)
|
||
}
|
||
|
||
// 对加密结果进行base64编码
|
||
encryptedBase64 := base64.StdEncoding.EncodeToString(encryptedData)
|
||
|
||
// 添加前缀
|
||
result := CIPHERTEXT_PREFIX_THIRD_SM4ECB + encryptedBase64
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// SM4ECBDecrypt 验证签名(解密)
|
||
func SM4ECBDecrypt(ciphertext, key string) (string, error) {
|
||
if ciphertext == "" || key == "" {
|
||
return ciphertext, nil
|
||
}
|
||
|
||
// 检查是否包含SM4前缀
|
||
if !strings.HasPrefix(ciphertext, CIPHERTEXT_PREFIX_THIRD_SM4ECB) {
|
||
return ciphertext, nil
|
||
}
|
||
|
||
// 移除前缀
|
||
encryptedBase64 := strings.TrimPrefix(ciphertext, CIPHERTEXT_PREFIX_THIRD_SM4ECB)
|
||
|
||
// 解码base64密文
|
||
encryptedData, err := base64.StdEncoding.DecodeString(encryptedBase64)
|
||
if err != nil {
|
||
return "", fmt.Errorf("failed to decode base64 ciphertext: %v", err)
|
||
}
|
||
|
||
// 解码base64密钥
|
||
keyBytes, err := base64.StdEncoding.DecodeString(key)
|
||
if err != nil {
|
||
return "", fmt.Errorf("failed to decode base64 key: %v", err)
|
||
}
|
||
|
||
// SM4 ECB模式解密
|
||
decryptedData, err := sm4ECBDecrypt(encryptedData, keyBytes)
|
||
if err != nil {
|
||
return "", fmt.Errorf("failed to decrypt with SM4: %v", err)
|
||
}
|
||
|
||
return string(decryptedData), nil
|
||
}
|
||
|
||
// sm4ECBEncrypt SM4 ECB模式加密
|
||
func sm4ECBEncrypt(plaintext, key []byte) ([]byte, error) {
|
||
block, err := sm4.NewCipher(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 使用PKCS5Padding填充
|
||
plaintext = pkcs5Padding(plaintext, block.BlockSize())
|
||
|
||
// ECB模式不需要IV
|
||
blockSize := block.BlockSize()
|
||
ciphertext := make([]byte, len(plaintext))
|
||
|
||
// 分块加密
|
||
for start := 0; start < len(plaintext); start += blockSize {
|
||
end := start + blockSize
|
||
block.Encrypt(ciphertext[start:end], plaintext[start:end])
|
||
}
|
||
|
||
return ciphertext, nil
|
||
}
|
||
|
||
// sm4ECBDecrypt SM4 ECB模式解密
|
||
func sm4ECBDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||
block, err := sm4.NewCipher(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 检查密文长度
|
||
if len(ciphertext)%block.BlockSize() != 0 {
|
||
return nil, fmt.Errorf("ciphertext length is not a multiple of block size")
|
||
}
|
||
|
||
plaintext := make([]byte, len(ciphertext))
|
||
blockSize := block.BlockSize()
|
||
|
||
// 分块解密
|
||
for start := 0; start < len(ciphertext); start += blockSize {
|
||
end := start + blockSize
|
||
block.Decrypt(plaintext[start:end], ciphertext[start:end])
|
||
}
|
||
|
||
// 去除PKCS5Padding填充
|
||
plaintext = pkcs5UnPadding(plaintext)
|
||
|
||
return plaintext, nil
|
||
}
|
||
|
||
// pkcs5Padding PKCS5填充
|
||
func pkcs5Padding(src []byte, blockSize int) []byte {
|
||
padding := blockSize - len(src)%blockSize
|
||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||
return append(src, padtext...)
|
||
}
|
||
|
||
// pkcs5UnPadding 去除PKCS5填充
|
||
func pkcs5UnPadding(src []byte) []byte {
|
||
length := len(src)
|
||
if length == 0 {
|
||
return src
|
||
}
|
||
|
||
unpadding := int(src[length-1])
|
||
if unpadding > length {
|
||
return src
|
||
}
|
||
|
||
return src[:(length - unpadding)]
|
||
}
|
||
|
||
// VerifySignature 验证签名是否正确
|
||
func VerifySignature(ciphertext, params, key string) (bool, error) {
|
||
// 解密签名
|
||
decrypted, err := SM4ECBDecrypt(ciphertext, key)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
// 验证解密后的内容是否等于 params + key
|
||
expected := params + key
|
||
return decrypted == expected, nil
|
||
}
|
||
|
||
// PostMultipartRequest POST multipart/form-data请求
|
||
func PostMultipartRequest(url string, formData *MultipartFormData, headers map[string]string, timeout time.Duration) ([]byte, error) {
|
||
// 创建payload缓冲区
|
||
payload := &bytes.Buffer{}
|
||
writer := multipart.NewWriter(payload)
|
||
|
||
// 添加表单字段
|
||
for key, value := range formData.fields {
|
||
if err := writer.WriteField(key, value); err != nil {
|
||
return nil, fmt.Errorf("写入字段 %s 失败: %v", key, err)
|
||
}
|
||
}
|
||
|
||
// 添加文件
|
||
for _, fileInfo := range formData.files {
|
||
part, err := writer.CreateFormFile(fileInfo.FieldName, fileInfo.FileName)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建文件字段 %s 失败: %v", fileInfo.FieldName, err)
|
||
}
|
||
if _, err := part.Write(fileInfo.FileData); err != nil {
|
||
return nil, fmt.Errorf("写入文件数据失败: %v", err)
|
||
}
|
||
}
|
||
|
||
// 关闭writer
|
||
if err := writer.Close(); err != nil {
|
||
return nil, fmt.Errorf("关闭writer失败: %v", err)
|
||
}
|
||
|
||
// 创建HTTP客户端
|
||
client := &http.Client{
|
||
Timeout: timeout,
|
||
}
|
||
|
||
// 创建请求
|
||
req, err := http.NewRequest("POST", url, payload)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||
}
|
||
|
||
// 设置Content-Type
|
||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||
|
||
// 设置自定义请求头
|
||
for key, value := range headers {
|
||
req.Header.Set(key, value)
|
||
}
|
||
|
||
// 发送请求
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取响应体
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return body, fmt.Errorf("请求返回非200状态码: %d, 响应: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// AddField 添加表单字段
|
||
func (m *MultipartFormData) AddField(key, value string) {
|
||
m.fields[key] = value
|
||
}
|
||
|
||
// AddFile 添加文件
|
||
func (m *MultipartFormData) AddFile(fieldName, fileName string, fileData []byte) {
|
||
m.files[fieldName] = FileInfo{
|
||
FieldName: fieldName,
|
||
FileName: fileName,
|
||
FileData: fileData,
|
||
}
|
||
}
|
||
|
||
///**********************************************************邮政结束***************************************************************//
|
||
|
||
///**********************************************************申通开始***************************************************************//
|
||
|
||
// 构建请求数据
|
||
func getStoHeader(apiName string, appkey string, code string, secretKey string, toKey string, content string) map[string]string {
|
||
dataDigest := getStoSignature(content, secretKey)
|
||
var param = map[string]string{
|
||
"api_name": apiName,
|
||
"content": content,
|
||
"from_appkey": appkey,
|
||
"from_code": code,
|
||
"to_appkey": toKey,
|
||
"to_code": toKey,
|
||
"data_digest": dataDigest,
|
||
}
|
||
return param
|
||
}
|
||
|
||
func requestSto(apiName string, appkey string, code string, secretKey string, toKey string, content string) (string, error) {
|
||
//var url = "http://cloudinter-linkgatewaytest.sto.cn/gateway/link.do"
|
||
url := "https://cloudinter-linkgateway.sto.cn/gateway/link.do"
|
||
|
||
// 获取请求参数
|
||
params := getStoHeader(apiName, appkey, code, secretKey, toKey, content)
|
||
|
||
// 创建 multipart form-data 请求体
|
||
body := &bytes.Buffer{}
|
||
writer := multipart.NewWriter(body)
|
||
|
||
// 添加所有参数到 form-data
|
||
for key, value := range params {
|
||
err := writer.WriteField(key, value)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
}
|
||
|
||
// 关闭 writer 以完成 multipart 数据
|
||
err := writer.Close()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 创建 HTTP 请求
|
||
req, err := http.NewRequest("POST", url, body)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 设置 Content-Type 头,包含 boundary
|
||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||
|
||
// 发送请求
|
||
client := &http.Client{}
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取响应
|
||
responseBody, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return string(responseBody), nil
|
||
}
|
||
|
||
// 生成签名
|
||
func getStoSignature(content, secretKey string) string {
|
||
text := content + secretKey
|
||
hash := md5.Sum([]byte(text))
|
||
return base64.StdEncoding.EncodeToString(hash[:])
|
||
}
|
||
|
||
// RequestSto 申通快递统一请求为入口
|
||
//
|
||
//export RequestSto
|
||
func RequestSto(apiName, appkey, code, secretKey, toKey, requestJSON *C.char) *C.char {
|
||
apiNameStr := C.GoString(apiName)
|
||
appkeyStr := C.GoString(appkey)
|
||
codeStr := C.GoString(code)
|
||
secretKeyStr := C.GoString(secretKey)
|
||
toKeyStr := C.GoString(toKey)
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
info, err := requestSto(apiNameStr, appkeyStr, codeStr, secretKeyStr, toKeyStr, requestJSONStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
///**********************************************************申通结束***************************************************************//
|
||
|
||
///**********************************************************圆通开始***************************************************************//
|
||
|
||
func requestYto(apiName, customerCode, secretKey, requestJSON string) (string, error) {
|
||
// 构建URL
|
||
//urlStr := fmt.Sprintf("https://openuat.yto56test.com:6443/open/%s/v1/zc0IzW/%s", apiName, customerCode)
|
||
urlStr := fmt.Sprintf("https://openapi.yto.net.cn:11443/open/%s/v1/zc0IzW/%s", apiName, customerCode)
|
||
|
||
// 当前毫秒级时间戳(13位)
|
||
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||
|
||
// 构建待签名字符串: param + method + v
|
||
// 注意:method应该是apiName,版本号根据URL中的v1确定
|
||
dataToSign := requestJSON + apiName + "v1"
|
||
|
||
// 生成签名
|
||
sign := EncryptSignForOpen(dataToSign, secretKey)
|
||
|
||
// 构建请求参数
|
||
params := url.Values{}
|
||
params.Set("timestamp", timestamp)
|
||
params.Set("param", requestJSON)
|
||
params.Set("sign", sign)
|
||
params.Set("format", "JSON")
|
||
|
||
// 发送POST请求
|
||
resp, err := http.PostForm(urlStr, params)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取响应
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(body), nil
|
||
}
|
||
|
||
// EncryptSignForOpen 开放平台公共加密方法
|
||
func EncryptSignForOpen(data string, secret string) string {
|
||
combined := data + secret
|
||
hash := md5.Sum([]byte(combined))
|
||
sign := base64.StdEncoding.EncodeToString(hash[:])
|
||
return sign
|
||
}
|
||
|
||
// RequestYto 申通快递统一请求为入口
|
||
//
|
||
//export RequestYto
|
||
func RequestYto(apiName, customerCode, secretKey, requestJSON *C.char) *C.char {
|
||
apiNameStr := C.GoString(apiName)
|
||
customerCodeStr := C.GoString(customerCode)
|
||
secretKeytr := C.GoString(secretKey)
|
||
requestJSONStr := C.GoString(requestJSON)
|
||
info, err := requestYto(apiNameStr, customerCodeStr, secretKeytr, requestJSONStr)
|
||
if err != nil {
|
||
return C.CString(err.Error())
|
||
}
|
||
return C.CString(info)
|
||
}
|
||
|
||
///**********************************************************圆通结束***************************************************************//
|
||
|
||
// 主函数
|
||
func main() {
|
||
//apiName := "OMS_EXPRESS_ORDER_CREATE"
|
||
//appkey := "CAKgwDceADOybmU"
|
||
//code := "CAKgwDceADOybmU"
|
||
//secretKey := "2kLMr75pURbVh3FnWzY6SCr7SM6qsoO3"
|
||
//content := "{\"orderNo\":\"8885452262\",\"orderSource\":\"****\",\"billType\":\"00\",\"orderType\":\"01\",\"sender\":{\"name\":\"测试名称\",\"tel\":\"0558-45778586\",\"mobile\":\"18775487548\",\"postCode\":\"100001\",\"country\":\"中国\",\"province\":\"安徽\",\"city\":\"合肥\",\"area\":\"泸州\",\"town\":\"测试镇\",\"address\":\"XX街道XX小区XX楼888\"},\"receiver\":{\"name\":\"测试名称\",\"tel\":\"0556-45778586\",\"mobile\":\"15575487548\",\"postCode\":\"100001\",\"country\":\"中国\",\"province\":\"河北\",\"city\":\"湖州\",\"area\":\"江汉\",\"town\":\"收件镇\",\"address\":\"XX街道XX小区XX楼666\",\"safeNo\":\"13466666632-0011\"},\"cargo\":{\"battery\":\"10\",\"goodsType\":\"大件\",\"goodsName\":\"XX物\",\"goodsCount\":10,\"spaceX\":10,\"spaceY\":10,\"spaceZ\":10,\"weight\":10,\"goodsAmount\":\"100\",\"cargoItemList\":[{\"serialNumber\":\"8451234\",\"referenceNumber\":\"88838783634\",\"productId\":\"001\",\"name\":\"小商品\",\"qty\":10,\"unitPrice\":1,\"amount\":10,\"currency\":\"美元\",\"weight\":10,\"remark\":\"无\"}]},\"customer\":{\"siteCode\":\"666666\",\"customerName\":\"666666000001\",\"sitePwd\":\"***\",\"monthCustomerCode\":\"9000000\"},\"internationalAnnex\":{\"internationalProductType\":\"01\",\"customsDeclaration\":false,\"senderCountry\":\"中国\",\"receiverCountry\":\"俄罗斯\"},\"waybillNo\":\"59635456632\",\"assignAnnex\":{\"takeCompanyCode\":\"862456565466\",\"takeUserCode\":\"9000000007\"},\"codValue\":\"2000\",\"freightCollectValue\":\"20\",\"timelessType\":\"01\",\"productType\":\"01\",\"serviceTypeList\":[\"***\"],\"extendFieldMap\":{\"mapValue\":\"***\"},\"remark\":\"无备注\",\"expressDirection\":\"01\",\"createChannel\":\"01\",\"regionType\":\"01\",\"insuredAnnex\":{\"insuredValue\":\"6.66\",\"goodsValue\":\"6.66\"},\"expectValue\":\"10\",\"payModel\":\"1\"}"
|
||
|
||
//apiName := "BILL_CODE_QUERY_USER_STOCK"
|
||
//appkey := "CAKDVcNVcjgqoKH"
|
||
//code := "CAKDVcNVcjgqoKH"
|
||
//secretKey := "3ySS5owwn6ENOSespB2c9fROojSAllA0"
|
||
//toKey := "billcode_open"
|
||
//content := "{\"siteCode\":\"130370\",\"userCode\":\"130370000295\",\"timestamp\":\"1781318163\",\"userType\":\"0\",\"password\":\"123456\",\"billSource\":\"0\"}"
|
||
//
|
||
//info, err := requestSto(apiName, appkey, code, secretKey, toKey, content)
|
||
//if err != nil {
|
||
// fmt.Println(err)
|
||
// return
|
||
//}
|
||
|
||
//fmt.Println(info)
|
||
//apiName := "privacy_create_adapter"
|
||
//customerCode := "K24079264"
|
||
//secretKey := "XII4ecRl"
|
||
//requestJSON := "{\"logisticsNo\":\"o362rAIY6DO\",\"senderName\":\"测试1\",\"senderProvinceName\":\"上海\",\"senderCityName\":\"上海市\",\"senderCountyName\":\"青浦区\",\"senderAddress\":\"汇金路100号\",\"senderMobile\":\"15900521555\",\"recipientName\":\"测试\",\"recipientProvinceName\":\"重庆\",\"recipientCityName\":\"重庆市\",\"recipientCountyName\":\"万州区\",\"recipientAddress\":\"汇金路100好\",\"recipientMobile\":\"021-59815121\",\"remark\":\"remark-test\",\"gotCode\":\"123\",\"increments\":[{\"type\":4,\"amount\":888}],\"goods\":[{\"name\":\"mobile\",\"weight\":5,\"length\":10,\"width\":20,\"height\":5,\"price\":100,\"quantity\":1},{\"name\":\"mobile1\",\"weight\":1,\"length\":1,\"width\":1,\"height\":1,\"price\":1,\"quantity\":1}],\"startTime\":\"2022-09-07 17:25:01\",\"endTime\":\"2022-09-07 17:25:01\",\"cstOrderNo\":\"csorderno\",\"weight\":5,\"productCode\":\"PK\"}"
|
||
//yto, err := requestYto(apiName, customerCode, secretKey, requestJSON)
|
||
//if err != nil {
|
||
// fmt.Println(err)
|
||
// return
|
||
//}
|
||
//fmt.Println(yto)
|
||
}
|