根据运单号获取未出账单详情
此接口需要联系燕文销售申请权限;
*所有请求都需要提供公共参数
| 参数名 | 类型 | 是否必填 | 描述 |
| 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等导致数据未能推送成功,则该条数据五分钟后重试,若重试五次后依旧未成功则不再重试,需要人工干预方能重新推送。
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)); //不转码会乱码
}
}
<?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;
#! /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))