daShangDao_kfzgw-info/expressDeliveryOrder/expressDeliveryOrder.go
97694732@qq.com ac2a39742d 各种修改
2026-06-11 13:21:55 +08:00

2169 lines
65 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
/*
#include <stdlib.h>
*/
import "C"
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"sort"
"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时orderType1全网件 2预约件。partnerType为2时orderType1全网件 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"` // 网点codeorderVasList.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为2orderType传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"` // 取件开始时间传值此字段则需必传vasTypetwoHour
EndTime string `json:"endTime"` // 取件截止时间传值此字段则需必传vasTypetwoHour
}
// 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)
}
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 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, 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": "sto_oms",
"to_code": "sto_oms",
"data_digest": dataDigest,
}
return param
}
func requestSto(apiName string, appkey string, code string, secretKey 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, 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, requestJSON *C.char) *C.char {
apiNameStr := C.GoString(apiName)
appkeyStr := C.GoString(appkey)
codeStr := C.GoString(code)
secretKeyStr := C.GoString(secretKey)
requestJSONStr := C.GoString(requestJSON)
info, err := requestSto(apiNameStr, appkeyStr, codeStr, secretKeyStr, 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\"}"
//info, err := requestSto(apiName, appkey, code, secretKey, content)
//if err != nil {
// fmt.Println(err)
// return
//}
//fmt.Println(info)
}