小包专线

追踪轨迹

海外派

未出账单详情

更新时间:2025-01-26 16:04:07

-接口概要

根据运单号获取未出账单详情

此接口需要联系燕文销售申请权限;

-公共参数

*所有请求都需要提供公共参数

参数名 类型 是否必填 描述
data String 业务接口参数
-业务参数

API调用除了必须包含公共参数外,如果API本身有业务级的参数也必须传入,每个API的业务级参数请参考文档以下章节。

-关于加密规则

采用AES加密方式,实例方法参见下方附件 

测试环境加密KEY:开通权限至燕文客户中心【财务管理-账单接口-未出账单接口-配置接口】沙箱服务配置

正式环境加密KEY:开通权限至燕文客户中心【财务管理-账单接口-未出账单接口-配置接口】正式服务配置

-请求方式

请求方式具体参考接口,用POST方式发起请求。发送参数时请以json方式发送数据。

Content-Type:application/json

-请求参数
字段名 类型 是否必填 描述
waybillNumber String 运单号
exchangeNumber String 转单号
orderNumber String 订单号
pickupWarehouse String 发货仓
customerCode String 发货账号
merchantCode String 商户号
calcWeight Int 计费重量
weight Int 实重
weightUnit String 重量单位
expressLength int 长,默认单位cm
expressWidth int 宽,默认单位cm
expressHeight int 高,默认单位cm
timeOfExpress date 账单日期
timeOfCalc datetime 计费时间
deliveryChargeOfOriginal decimal 折前资费
deliveryCharge decimal 折后资费
linehaulCharge decimal 干线费
surcharge decimal 附加费
vatCharge decimal 税费
totalAmount decimal 合计金额
currency String 币种,默认CNY
remark String 备注
version int 版本号,默认1,账单每调整一次,版本号+1
regionName String 目的地
productName String 产品名称
merchantName String 商户名称
expenseType String 交易类型
-请求示例:
{"data":"BBj85GSylO5g0HFuHFJoLoWvH4h6/kIF0/AmBxh//GqvHLr+Mg1P4HKyp9//7GAwqnLSHDDgkO/neascvLoT34R16kZ84spPfCBo3a+ALewBMwro5mdoSIzf/sO5QM8CYVOZUP9fe1yrdk/hPHABaZmv3sSxwjhLeeU8CWuVXTrf5dhr1qiq6h5wz+6WHGXs+Z9x/1JH3057YSUEKsZw+/zFr2H99OBPtP1WyVahNJTsvsNU9ihl9QQiNe9Ue+g3ZGxa6XKKa0g01xSeKfSIDfPPdkMaQx9+KYJnubVLqTkUPypiL7HDbDVtp/a5nUrP2bhNB6on4AKM7PhU5qqv05Tp4OyjAWD7CDMTJjL+RMhwICpEBmHbCnlztp/Lk3CfH+gDdTx3qHeZNr5QudcRNwIBuAkRRzEyp/ktPjgHIuPU7rFc+5QCqyNvzM0PLkdryYjT0RHRl+Zf80kn2eJAsvFbcCS8UijP79pNhhaLrk3rrWq3wtowQFT62BK8cq3Jf2Tp5a8Du5hEqlUPo/n9j2WUMCF920VK8rsnzk/ZQf05xNOom++rc3xwEdnbqRvrU2PRLklUUO95WpHn/+1hHfQX9xlxNdJj/BmhnygonOA6uTe2L+U+XjtV9x9JsTDNG79x5b0ooWCHaIOtlZBhMe2BCPNjyfQADsBycxDq6bFr2XDS7cJPXd0UUeT3+yYNe1qN0Zan91s0RHM37Z9gMS8JHrfJlBG/q7xE+oLXY30wViiR8UUtOtyCFnClzlSFoxfaiUa2i+mK/QSrIKakmzyQ4GPD1L/ZOic8M1FIY/YeWIr36Nfqvx2kBO6bt7NYy+0sRGN9fd+dqF4OvRuSOc5h+7SptHP8vrIO5nU8i7dQr74M6IUO85XT4dl3ftFu"}

data格式详情:

{"waybillNumber":"UG307850645YP","exchangeNumber":"4205315092612902849687000000637032","orderNumber":"AMTULAD21122860849","pickupWarehouse":"义乌燕文","customerCode":"1234567890","merchantCode":"1234567890","calcWeight":616,"weight":616,"weightUnit":"g","expressLength":0,"expressWidth":0,"expressHeight":0,"expenseType":"TC01","timeOfExpress":"2021-12-29","timeOfCalc":"2021-12-29 09:57:26","deliveryChargeOfOriginal":92.30,"deliveryCharge":88.64,"linehaulCharge":0.29,"surcharge":0.00,"vatCharge":0.00,"totalAmount":88.93,"currency":"USD","remark":"重量调整","version":1,"regionName":"美国","productName":"燕文专线追踪-普货","merchantName":"YW-TEST"}
- 返回参数
字段名 类型 必填 描述
success Boolean 是否成功
code String 消息编码。成功为0,失败且需触发重推为1,失败并不用触发重推为2.
message String 消息描述
data object 接口返回的数据对象
-返回示例:
{
	"success": true,
	"code": "0",
	"message": "操作成功",
	"data": object
}
- 重推机制

若接口因网络或返回值success为false等导致数据未能推送成功,则该条数据五分钟后重试,若重试五次后依旧未成功则不再重试,需要人工干预方能重新推送。

-附件A-Java
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

public class AesUtils {
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    /**
     * AES加密
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的byte[]
     * @throws Exception
     */
    public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(encryptKey.getBytes("utf-8"));
        kgen.init(128, secureRandom);
        SecretKey secretKey = kgen.generateKey();
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance("AES");// 创建密码器
        byte[] byteContent = content.getBytes("utf-8");
        cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
        byte[] result = cipher.doFinal(byteContent);
        return result; // 加密
    }


    /**
     * AES加密为base 64 code
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的base 64 code
     * @throws Exception
     */
    public static String aesEncrypt(String content, String encryptKey) throws Exception {
        return encode(aesEncryptToBytes(content, encryptKey));
    }

    /**
     * AES解密
     *
     * @param encryptBytes 待解密的byte[]
     * @param decryptKey   解密密钥
     * @return 解密后的String
     * @throws Exception
     */
    public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG") ;
        secureRandom.setSeed(decryptKey.getBytes("utf-8"));
        kgen.init(128, secureRandom);
        SecretKey secretKey = kgen.generateKey();
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance("AES");// 创建密码器
        cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
        byte[] result = cipher.doFinal(encryptBytes);
        return new String(result,"utf-8"); // 解密
    }


    /**
     * 将base 64 code AES解密
     *
     * @param encryptStr 待解密的base 64 code
     * @param decryptKey 解密密钥
     * @return 解密后的string
     * @throws Exception
     */
    public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
        return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(decode(encryptStr), decryptKey);
    }

    /**
     * 解码
     *
     * @param base64
     * @return
     */
    public static byte[] decode(final String base64) {
        if (StringUtils.isEmpty(base64)) {
            return null;
        }
        return Base64.decodeBase64(base64);
    }

    /**
     * 加码
     *
     * @param binaryData
     * @return
     */
    public static String encode(byte[] binaryData) {
        return Base64.encodeBase64String(binaryData);
    }


    public static void main(String[] args) throws Exception {
        String content = "我是明文";
        String password = "12345678";
        // 加密
        System.out.println("加密前:" + content);
        String decode = aesEncrypt(content,password);
        System.out.println("密文字符串:" + decode);
        // 解密
        System.out.println("解密后:" + aesDecrypt(decode, password)); //不转码会乱码

    }
}
-附件B-PHP
<?php
echo "解密处理:";

$sKey = substr(openssl_digest(openssl_digest('1234567890', 'sha1', true), 'sha1', true), 0, 16);
$iv = '';
$decrypted = openssl_decrypt(base64_decode('BBj85GSylO5g0HFuHFJoLoWvH4h6/kIF0/AmBxh//GqvHLr+Mg1P4HKyp9//7GAwqnLSHDDgkO/neascvLoT34R16kZ84spPfCBo3a+ALewBMwro5mdoSIzf/sO5QM8CYVOZUP9fe1yrdk/hPHABaZmv3sSxwjhLeeU8CWuVXTrf5dhr1qiq6h5wz+6WHGXs+Z9x/1JH3057YSUEKsZw+4z4pl7mWRY7FrEmotRpXIkRurY3rgweW4W1cnGlROCY8670nWNhVhIyW4fhJurJJq66mhXKBLyawDJNR1b4nTJJlf+DLyApjwmYiQbcnFZNf7rRDV37yks6wmWHGS0prlrI1T3akrA5Q4mYDSmknfn170+j52Jp9JmSq4YNgZyAcKqYRZJLkgwFaDgd8ai26nVydVM8ovD0LBA4QWen0Lz2aiahP46ODFxDeeY8Y9FtXpW2AGuZyCkCh2xZr0VDmvQQdoLdfaWQfjsR5S8CyuNz0sAya6pH1ndPQYD/SDn66iPMkbG3/7wy37GZxL3ds4Pw9HQ6aGYrONjOGPOz/cgyT5wrPX4alsAz+WIc3lql+pvnM4Kbli0rHNy2SzoxMnJCu1LDjCN//l3PpCXz0LrEq2XKyZB3SxgeBgRJNsHC1tyxhATwUUvgV4bvcIV6kLPxIxatGkH3AmDoc4ZT8mtbSOQi33JQJsBCw7O3uYzKwkORhyQvp0L+Po7iuqF1urGxEJEzQal7jAT7Ps97uvK0tVZUqjlLvKV2YvE6m/v3OWwGBLS2VFrPsmFmo0jyDeJKSmFj36msuaWWJWtXerD53l8+HBDBAFVWPPYVbw7RSZ20fwElHeLu4VF5PU2GnY0SqfY2YqadD9H9bSSd5YmdHuUHL2bQjD3hz7WUX73+1J3KNDl0KhQiEe64XGBnwQ=='), 'AES-128-ECB', $sKey, OPENSSL_RAW_DATA, $iv);
echo $decrypted;
-附件C-Python
#! /usr/bin/python3
# -*- coding:utf-8 -*-

import base64
from Crypto.Cipher import AES
import hashlib


class AesEncryptDecrypt:
    def __init__(self, key: str):
        """
        Init AES object used by encrypt or decrypt.
        AES-ECB-PKCS5Padding,the same as AES in java.
        :param key: Original key.
        """
        self.aes = AES.new(self.get_sha1prng_key(key), AES.MODE_ECB)

    @staticmethod
    def get_sha1prng_key(key: str) -> bytes:
        """
        key encrypted with SHA1PRNG,same as Java AES/ECB/PKCS5Padding mode.
        :param key :Original key.
        :return: key encrypted with SHA1PRNG,which used in AES Object.128 bits(16 long bytes).
        """
        signature: bytes = hashlib.sha1(key.encode()).digest()
        signature: bytes = hashlib.sha1(signature).digest()
        return signature[:16]

    @staticmethod
    def pkcs5_padding(data: bytes) -> bytes:
        """
        Padding PKCS5
        When pieces of data are required for alignment, fill in bytes.
        needSize = 16-len(data) % 16
        if needSize == 0:
            needSize = 16
        return data + needSize.to_bytes(1, 'little')*needSize

        :param data:string data need padding.
        :return:
        """
        '''
        num: int = ord(data[-1])
        padding_data: str = data + num * chr(num)
        return padding_data
        '''
        needSize = 16 - len(data) % 16
        if needSize == 0:
            needSize = 16
        return data + needSize.to_bytes(1, 'little') * needSize

    @staticmethod
    def pkcs5_unpadding(data: str) -> str:
        """
        Unpadding PKCS5
        num: int = ord(data[-1])
        unpadding_data = data[: -1 * num]
        return unpadding_data

        paddingSize = data[-1]
        return data.rstrip(paddingSize.to_s(1, 'little'))

        :param data:
        :return:
        """
        num: int = ord(data[-1])
        unpadding_data = data[: -1 * num]
        return unpadding_data

    def aes_encrypt(self, data: str) -> str:
        """
        Encrypt string by AES.
        String to bytes-->padding PKCS5--> Encrypt by AES-->Encrypt by Base64-->Base64 to string.
        :param data:string needed encrypt.
        :return:String encrypted.
        """
        # String to bytes.
        data_bytes = data.encode(encoding='utf-8')
        # print(f'data_bytes:{data_bytes}')
        # Padding PKCS5
        data_padding = self.pkcs5_padding(data_bytes)
        # print(f'data_padding:{data_padding}')
        # Encrypt by AES.
        encrypt_aes = self.aes.encrypt(data_padding)
        # print(f'encrypt_aes:{encrypt_aes}')
        # Encrypt by Base64
        encrypt_base64 = base64.b64encode(encrypt_aes)
        # print(f'encrypt_base64:{encrypt_base64}')
        # Base64 to string
        encrypt_data = encrypt_base64.decode(encoding='utf-8')
        # print(f'encrypt_data:{encrypt_data}')
        return encrypt_data

    def aes_decrypt(self, data: str) -> str:
        """
        Encrypt string by AES.
        String to bytes-->Decrypt by base64-->Decrypt by AES-->bytes to string-->unpadding string
        :param data:String needed decrypted.
        :return:String decrypted.
        """
        # String to bytes.
        decrypt_bytes = data.encode(encoding='utf-8')
        # print(f'decrypt_bytes:{decrypt_bytes}')
        # Decrypt by base64.
        decrypt_base64 = base64.decodebytes(decrypt_bytes)
        # print(f'decrypt_base64:{decrypt_base64}')
        # Decrypt by AES
        decrypt_aes = self.aes.decrypt(decrypt_base64)
        # print(f'decrypt_aes:{decrypt_aes}')
        # bytes to string
        decrypt_string = decrypt_aes.decode(encoding='utf-8')
        # print(f'decrypt_string:{decrypt_string}')
        # unpadding string
        decrypt_data = self.pkcs5_unpadding(decrypt_string)
        # print(f'decrypt_data:{decrypt_data}')
        return decrypt_data


if __name__ == '__main__':
    key = '1234567890'
    aes = AesEncryptDecrypt(key)
    encrypt_data = 'I am a string that needed to be encrypted!'
   # data needed decrypted.
    decrypt_data = 'oDlBOiK6y4prLlWa0qAQnM0C67pgmUyRAPcHlfoYkHqLeXDxuS14yuCvM8G6dvmX'
    print(aes)
    print(aes.aes_encrypt(encrypt_data))
    print(aes.aes_decrypt(decrypt_data))