仓储系统接口文档

业务流程

第三方电商平台与中外运订单仓储系统的数据交换按照业务的需要主要有以下几个:推送采购单、取消采购单、采购单货物清点回调、线下发送提货单(保税模式)、提货单出库回调、推送订单、取消订单、缺货通知、打单回调、分拣回调、打包回调、打包回调、出库回调、海关扣留通知、库存查询、盘库结果回调、轨迹查询。如下图所示:

详细介绍

采购单

1.推送采购单推送采购单是整个业务开始的第一步,采购单由第三方电商平台推送给中外运订单仓储系统,告知中外运该电商平台于何时有何商品及件数到达某个仓库,以便中外运仓库展开后续收货等操作。

2.取消采购单取消采购单是指第三方电商平台对之前已经推送过的采购单(中外运仓库尚未收货)进行取消的操作。

3.采购单货物清点回调采购单货物清点回调是指中外运仓库在收到第三方电商平台指定采购单的货物时,将货物的数量及货物的损坏情况推送/反馈给第三方电商平台,若仓库方在入库理货时发现商品数量与采购单上标识的不符,会与第三方电商平台先行线下沟通达成一致后,再调用采购单入库接口推送入库数据。

提货出库(保税模式)

该模式中外运方只负责将第三方电商平台在中外运国外仓库的商品按照提货单发送到电商平台国内指定保税仓,不涉订单及其他后续流程。

1.线下发送提货单第三方电商平台可通过事先约定好的模式告知中外运方需要提货的商品及明细,以便中外运仓库进行相应操作。

2.提货单出库回调提货单出库回调是指中外运仓库在将指定提货单的商品进行出库操作之后,将操作明细反馈给第三方电商平台。

订单

订单由第三方电商平台按照中外运方指定的格式及内容发送给中外运方,中外运方按照订单中的商品信息及收货信息进行后续的发货和申报操作。订单的状态按照操作的顺序(已取消状态不参与排序)有以下状态:待处理、已打单、已分拣、已打包、已出库、航空配载、已申报、海关扣留、已清关、转交国内快递、正在派送、已签收、已取消
这些状态分别对应操作流程中不同的环节。

1.推送订单推送订单是指第三方电商平台将客户的订单中的部分信息推送给中外运订单仓储系统,以便中外运仓库进行订单商品的打包出库等后续操作。

2.取消订单取消订单是指第三方电商平台将已发送的订单取消的操作,当订单的状态在已打包及后续的某个状态时,该订单将不可取消。

3.缺货通知缺货通知是指中外运方在某个订单对应的商品缺货时发送给第三方电商平台的通知,以便电商平台进行后续的采购或者其他操作。

4.打单回调打单回调是指中外运方推送给第三方电商平台,告知某个订单已经开始在仓库中进行处理了。

5.分拣回调分拣回调是指中外运方推送给第三方电商平台,告知某个订单已经在仓库中进行了分拣的操作以及分拣的商品的明细。

6.打包回调打包回调是指中外运方推送给第三方电商平台,告知某个订单已经在仓库中进行了打包的操作以及打包的商品的明细。

7.出库回调出库回调是指中外运方推送给第三方电商平台,告知某个订单已经在仓库中进行了出库的操作、订单的包裹信息(国内快递单号)以及出库的商品的明细,至此订单在中外运国外转运仓的操作结束。

8.海关扣留通知海关扣留通知是指中外运方在订单对应的包裹到运抵国内指定海关关口,进行申报之后海关反馈的状态,扣留原因会在反馈的信息中展示。

库存信息

1.库存查询库存查询是指第三方电商平台查询指定商品在中外运订单仓储系统中当前的库存信息。

2.盘库结果回调盘库结果回调是指中外运仓库方根据第三方电商平台的要求进行指定商品的盘库,并将盘库的结果反馈给电商平台。

轨迹信息

1.轨迹查询轨迹查询是指第三方电商平台根据订单号从中外运订单仓储系统中获取该订单当前的轨迹信息。

接口文档

第三方发送数据流程

身份验证,系统会给每个接入用户分配一个唯一的身份ID(AppID)及与之对应的访问秘钥(AuthCode),API用户在向接口正式发送或接收数据前,必须先把AppID和AuthCode发送到指定的验证地址,验证通过后,API接口会返回一个有时效的Token(两小时内有效):

验证地址:https://efreight.cn/api/auth

Request数据:{‘appid’:AppID, ‘auth_token’:AuthCode}
测试系统:    AppID:*****      AuthCode: *****
Response数据:Auth_token

2.发送接收数据,将请求报文和身份验证通过后获取的Token同时发送给数据处理接口,我们的平台处理完成后会返回成功或失败报文:

数据接口地址:https://efreight.cn/api/post
Request数据:
{
    ‘auth_token’: Auth_token,   //上一步生成的token
    ‘platform’: ‘xxxx’,//xxxx:与订单系统约定好的平台名称
    ‘function’: ‘xxxxOrder’ ,
    ‘type’: ‘1’          
    ‘data’: XML报文  
}

参数说明:

auth_token    上一步生成的token
platform:    生产环境:xxxx,测试环境:xxxx(需提前沟通约定)
function    海淘订单接口: HTOrder
            直邮订单接口: DMOrder
            轨迹接口:LogisticTrace
            身份证接口:IdentityUpload
            商品备案信息接口:MerchantRegisterInfo
            采购订单接口: InboundPlan 
            取消采购单: CancelPurchase 
            取消用户订单: CancelOrder
            库存查询: Inventory
            (以上接口在实际过程中可能根据接入用户的需求不同而不同,需双方提前约定)
type    0: JSON,1: XML
data    报文格式见下文说明

Response数据:

对于不同的请求报文有不同的返回数据,下述为默认情况下的标准报文。

<ServiceResult>
<ResultCode>1</ResultCode>                  <!—ResultCode为1或2时成功,其他为失败-->
<ResultContent>服务调用成功</ResultContent>   <!—如果失败,则此节点为失败原因-->
<ResultData></ResultData>                     <!—成功后,返回的数据-->
</ServiceResult>

接口明细

1.推送采购单

{
    "purchase_id": "purchase_id", // 采购单号;String类型,建议预留40位
    "transport_service_code": "ems", // 物流供应商代号,非必填;String类型
    "transport_order_id": "qaws12345", // 物流单号,非必填;String类型
    "remark": "集装箱号xxx,司机电话xxx", // String类型
    "expect_arrival_time": "2014-11-06 16:16:20", // 预期抵达时间,非必填;String类型
    "order_items": [// 物品列表
        {
            "sku_id": "sku_id", // sku_id;String类型,建议预留40位
            "product_no": "12345", // 自定义商品序列号 与sku一一对应 可打印成条形码;String类型,建议预留40位
            "goods_name": "飞利浦电饭煲", // 商品名;String类型
            "sku_name": "飞利浦电饭煲(白金内胆)", // 规格名称;String类型
            "qty": 1000 // 数量;int类型
        }, 
        {
            "sku_id": "sku_id", 
            "product_no": "12345", 
            "goods_name": "飞利浦电饭煲", 
            "sku_name": "飞利浦电饭煲(铝合金内胆)", 
            "qty": 1000
        }
    ]
}

2.取消采购单报文

{
    "purchase_id":"test_purchase_id"    // 采购单号
}

3.海淘订单请求报文

<Order>
    <!--商品供应商:ABC,SINOTRANS-->
    <SupplyVendor>SINOTRANS</SupplyVendor>
    <!--转运渠道:跨境个人物品,跨境直邮-->
    <SupplyChannel/>
    <!--清关口岸:广州,成都,杭州-->
    <ClearPort/>
    <!--填电商企业名称,需提前约定好-->
    <eStoreNo>电商企业编码</eStoreNo>
    <!--第三方商城的订单号-->
    <OrderNo>订单编号</OrderNo>
    <!--可不填-->
    <PaymentNo>支付单号</PaymentNo>
    <!--提前约定好的快递单号-->
    <ParcelCode>包裹单号</ParcelCode>
    <OrderTotalAmount>订单总金额</OrderTotalAmount>
    <OrderTotalCount>总件数</OrderTotalCount>
    <!--可不填-->
    <FreightCharge>运费</FreightCharge>
    <!--可不填-->
    <TarrifCharge>关税</TarrifCharge>
    <OrderTime>成交时间</OrderTime>
    <Note>订单备注</Note>
    <Buyer>
        <BuyerID>购买人电商账号</BuyerID>
        <BuyerName>购买人姓名</BuyerName>
        <BuyerEmail>购买人邮箱</BuyerEmail>
        <BuyerPhone>联系电话</BuyerPhone>
        <BuyerAddress>地址</BuyerAddress>
        <BuyerIdentityCardType>证件类型</BuyerIdentityCardType>
        <BuyerIdentityCardNo>证件号码</BuyerIdentityCardNo>
    </Buyer>
    <Consignee>
        <!--必填-->
        <ConsigneeName>收件人姓名</ConsigneeName>
        <ConsigneeEmail>收件人邮箱</ConsigneeEmail>
        <!--必填-->
        <ConsigneePhone>收件人电话</ConsigneePhone>
        <ConsigneePost>收件人邮编</ConsigneePost>
        <!--必填-->
        <ConsigneeAddress>收件人地址</ConsigneeAddress>
        <!--必填,报关时使用-->
        <ConsigneeIdentityCardType>证件类型</ConsigneeIdentityCardType>
        <!--必填,报关时使用-->
        <ConsigneeIdentityCardNo>证件号码</ConsigneeIdentityCardNo>
    </Consignee>
    <OrderDetailList>
        <OrderDetailItem>
            <!--此处请填写约定好的商品ID-->
            <SKUCode>商品编号</SKUCode>
            <SKUName>商品名称</SKUName>
            <SKUModel>规格型号</SKUModel>
            <SKUPrice>单价</SKUPrice>
            <SKUCount>数量</SKUCount>
            <NETWeight>净重</NETWeight>
        </OrderDetailItem>
        <OrderDetailItem>
            <SKUCode>商品编号</SKUCode>
            <SKUName>商品名称</SKUName>
            <SKUModel>规格型号</SKUModel>
            <SKUPrice>单价</SKUPrice>
            <SKUCount>数量</SKUCount>
            <NETWeight>净重</NETWeight>
        </OrderDetailItem>
    </OrderDetailList>
</Order>

4.直邮订单报文

<Order>
    <!--商品供应商:ABC,SINOTRANS-->
    <SupplyVendor>SINOTRANS</SupplyVendor>
    <!--转运渠道:跨境直邮,保税-->
    <SupplyChannel/>
    <!--清关口岸:广州,成都,杭州-->
    <ClearPort/>
    <!--填电商企业名称,需提前约定好-->
    <eStoreNo>电商企业编码</eStoreNo>
    <!--第三方商城的订单号-->
    <OrderNo>订单编号</OrderNo>
    <!--可不填-->
    <PaymentNo>支付单号</PaymentNo>
    <!--提前约定好的快递单号-->
    <ParcelCode>包裹单号</ParcelCode>
    <OrderTotalAmount>订单总金额</OrderTotalAmount>
    <OrderTotalCount>总件数</OrderTotalCount>
    <!--可不填-->
    <FreightCharge>运费</FreightCharge>
    <!--可不填-->
    <TarrifCharge>关税</TarrifCharge>
    <OrderTime>成交时间</OrderTime>
    <Note>订单备注</Note>
    <Buyer>
        <BuyerID>购买人电商账号</BuyerID>
        <BuyerName>购买人姓名</BuyerName>
        <BuyerEmail>购买人邮箱</BuyerEmail>
        <BuyerPhone>联系电话</BuyerPhone>
        <BuyerAddress>地址</BuyerAddress>
        <BuyerIdentityCardType>证件类型</BuyerIdentityCardType>
        <BuyerIdentityCardNo>证件号码</BuyerIdentityCardNo>
    </Buyer>
    <Consignee>
        <!--必填-->
        <ConsigneeName>收件人姓名</ConsigneeName>
        <ConsigneeEmail>收件人邮箱</ConsigneeEmail>
        <!--必填-->
        <ConsigneePhone>收件人电话</ConsigneePhone>
        <ConsigneePost>收件人邮编</ConsigneePost>
        <!--必填-->
        <ConsigneeAddress>收件人地址</ConsigneeAddress>
        <ConsigneeIdentityCardType>证件类型</ConsigneeIdentityCardType>
        <ConsigneeIdentityCardNo>证件号码</ConsigneeIdentityCardNo>
    </Consignee>
    <OrderDetailList>
        <OrderDetailItem>
            <!--此处请填写约定好的商品ID-->
            <SKUCode>商品编号</SKUCode>
            <SKUName>商品名称</SKUName>
            <!--如Size:S、M、L或颜色等信息-->
            <SKUModel>规格型号</SKUModel>
            <!--申报需要用到的单价,不是你们商城的价格-->
            <SKUPrice>单价</SKUPrice>
            <SKUCount>数量</SKUCount>
            <NETWeight>净重</NETWeight>
            <mailTaxNo>行邮税号</mailTaxNo>
            <!--海关备案产生 -->
            <goodsItemNo>商品料号</goodsItemNo>
            <!--142:中国-->
            <productionMarketingCountry>产销国编码</productionMarketingCountry>
            <!--142:人民币-->
            <bargainCurrency>币种编码</bargainCurrency>
            <!--申报数量-->
            <declareCount>1.00</declareCount>
            <!--011: 件-->
            <declareMeasureUnit>申报计量单位</declareMeasureUnit>
            <goodsRoughWeight>商品毛重</goodsRoughWeight>
            <firstUnit>第一单位</firstUnit>
            <firstCount>第一数量</firstCount>
            <secondUnit/>
            <secondCount/>
        </OrderDetailItem>
        <OrderDetailItem>
            <!--此处请填写约定好的商品ID-->
            <SKUCode>商品编号</SKUCode>
            <SKUName>商品名称</SKUName>
            <!--如Size:S、M、L或颜色等信息-->
            <SKUModel>规格型号</SKUModel>
            <!--申报需要用到的单价,不是你们商城的价格-->
            <SKUPrice>单价</SKUPrice>
            <SKUCount>数量</SKUCount>
            <NETWeight>净重</NETWeight>
            <mailTaxNo>行邮税号</mailTaxNo>
            <!--海关备案产生 -->
            <goodsItemNo>商品料号</goodsItemNo>
            <!--142:中国-->
            <productionMarketingCountry>产销国编码</productionMarketingCountry>
            <!--142:人民币-->
            <bargainCurrency>币种编码</bargainCurrency>
            <!--申报数量-->
            <declareCount>1.00</declareCount>
            <!--011: 件-->
            <declareMeasureUnit>申报计量单位</declareMeasureUnit>
            <goodsRoughWeight>商品毛重</goodsRoughWeight>
            <firstUnit>第一单位</firstUnit>
            <firstCount>第一数量</firstCount>
            <secondUnit/>
            <secondCount/>
        </OrderDetailItem>
    </OrderDetailList>
</Order>

5.取消订单

{
    "order_id":"test_order_id"   //订单号
}

6.库存查询

{
    "sku_ids": [// 查询的sku id列表
        "sku_id_1", 
        "sku_id_2", 
        "sku_id_3"
    ]
}

7.轨迹请求报文

<OrderNo>订单编号</OrderNo><!--第三方商城的订单号-->

8.身份证上传报文
对应有商品备案的模式,即直邮订单,不需要传身份证

<IdentityInfo>
    <!-- 证件人名称 -->
    <IDName>张三</IDName>
    <!-- 证件号码 -->
    <IDCard>身份证号</IDCard>
    <!-- 证件类型 -->
    <MsgType>身份证</MsgType>
    <Forwarder/>
    <InputHandler>上传人</InputHandler>
    <Filepaths>
        <Filepath>
            <!-- 0:正面,1:反面 -->
            <frontandback>0</frontandback>
            <ImgData><![CDATA[ 图片base64编码 ]]></ImgData>
        </Filepath>
        <Filepath>
            <!-- 0:正面,1:反面 -->
            <frontandback>1</frontandback>
            <ImgData><![CDATA[ 图片base64编码 ]]></ImgData>
        </Filepath>
    </Filepaths>
</IdentityInfo>

第三方接收数据流程

由第三方提供用于接收中外运推送数据的地址, 中外运方负责推送仓库作业中关键节点的数据。

1.凡涉及时间类型,统一使用如下格式『2014-12-12 23:23:23』,如果部分数值取不到,显示00,例如:『2014-12-12 00:00:00』

2.凡涉及重量的数值,单位均取『克』

3.接口都采用HTTP POST请求,用于认证和容错的相关字段直接放POST请求的body parameter,具体报文以JSON字符串放在data这个parameter里面,data的格式详见下文

4.第三方返回数据格式:

{
    "success”:true/false,
    "error_msg":"失败原因"
}

5.如果一方访问对方接口的请求失败,则其必须保证用之前请求的流水号(notify_id)发起重试请求,直至返回成功结果

    sign                 abc1234
    notify_type          31
    notify_id            opopz
    notify_time          2014-12-12 23:23:23
    wms_id               zwy
    stock_id             123
    owner_id             456
    data                 {"weight":1,"transport_order_id": "123"}

参数解释

sign:加密签名,算法见附录
notify_type:10 - 导入采购单、11 - 取消采购单、20 - 用户下单、21 - 取消用户下单、30 - 采购单入库回调、31 - 用户订单出库回调、40 - 指令仓库发货、50 - 盘点、51 – 提货出库、60 - 库存查询
notify_id:网络请求流水号,notify_id相同的请求被认为是同一个请求的多次重试
notify_time:请求时间戳
wms_id:第三方WMS系统在网易系统中的代号,双方提前约定;String类型
stock_id:具体仓库在网易系统中的ID,会提前告知第三方WMS;int类型
owner_id:网易在第三方WMS系统中的ID;int类型
data:具体数据,标准JSON格式字符串,详见下文

接口明细

1.采购单入库回调
当采购货物到达仓库,仓库完成货品清点,将货品入库情况通知第三方。

{
    "purchase_id": "test_purchase_id",// 入库操作对应的采购单号
    "transport_service_code": "EMS",// 物流商,非必填 
    "transport_order_id": "qaws12345",// 物流号,非必填 
    "arrival_begin_time": "2014-11-06 16:16:20",// 入库开始时间
    "arrival_end_time": "2014-11-06 16:16:20",// 入库结束时间
    "order_items": [// 物品列表
        {
            "sku_id": "sku id", 
            "weight": 110, // 入库时给物品进行称重,单位『克』;int类型
            "qty_good": 1000, // 良品数
            "qty_not_good": 1000,// 不良品数 
            "qty_bad": 1000// 次品数
        }, 
        {
            "sku_id": "sku id", 
            "weight": 110, 
            "qty_good": 1000, 
            "qty_not_good": 1000, 
            "qty_bad": 1000
        }
    ]
}

2.用户订单出库(出库细分为打单、分拣、打包、发货等子状态)回调
中外运仓库处理第三方电商平台推送过来的订单,遇到打单,分拣,打包,发货,分拣缺货,海关扣留等节点时,通过该接口通知第三方电商平台。

{
    "order_id": "test_order_id",// 订单号 
    "order_status": 100,// 细分为50 - 打单(打印分拣单)、100 - 分拣(分拣员拣货)、200 - 打包(按照用户订单打包)、300 - 发货(包裹交付物流商揽收)、400 - 分拣缺货(拣货时发现库存不足)、500 - 海关扣留等子状态;int类型 
    "weight": 110,//发货时须要返回包裹实际重量,用于结算运费,单位『克』;int类型 
    "transport_service_code": "EMS",//物流商,发货(300)时才须要这个字段 
    "transport_order_id": "qaws12345",//物流号,发货(300)时才须要这个字段 
    "order_items": [//物品清单,发货(300)时须要填写物品列表,其他子状态不需要
        {
            "sku_id": "sku id",//商品sku
            "qty": 1000//商品件数
        }, 
        {
            "sku_id": "sku id", 
            "qty": 1000
        }
    ]
}

3.盘点情况回调
仓库进行例行盘点或者网易提出盘点要求时,仓库方进行盘点并将结果回传网易。

{
    "check_id": "test_check_id",// 盘点单号,具体格式见下文;String类型 
    "type": 0, // 0 – 盘盈;1 – 盘亏;int类型
    "remark": "关于此次盘点情况的说明", 
    "inventory": [
        {
            "sku_id": "test_sku_id_1", // sku_id
            "good_profit_qty": 1,// 良品盘盈数 
            "good_loss_qty": 0, // 良品盘亏数
            "bad_profit_qty": 1, // 次品盘盈数
            "bad_loss_qty": 0// 次品盘亏数
        }, 
        {
            "sku_id": "test_sku_id_2", 
            "good_profit_qty": 0, 
            "good_loss_qty": 1, 
            "bad_profit_qty": 0, 
            "bad_loss_qty": 1
        }
    ]
}

4.提货出库回调
第三方电商或者海关等有关部门直接从中外运仓库提货,仓库方与第三方电商平台沟通确认后调用此接口通知电商系统此次提货的出库情况。

{
    "lading_id": "test_lading_id", // 提货单号,具体格式见下文;String类型
    "type": 2, // 2 – 提货出库;int类型
    "remark": "关于此次提货情况的说明", // 提货情况备注
    "inventory": [
        {
            "sku_id": "test_sku_id_1", // sku_id
            "good_qty": 1, // 良品提货数
            "bad_qty": 1// 次品提货数
        }, 
        {
            "sku_id": "test_sku_id_2", 
            "good_qty": 1, 
            "bad_qty": 1
        }
    ]
}

为了提高加密安全性,签名算法为RSA加密
RSA代码见下面RSA.java文件内容,主要包括:
genRSAKeyPair – 生成公私钥对
sign(PrivateKey privateKey, String src, String encode) – 使用私钥从src(即:接口定义中的『data』字段)生成sign;encode取值『UTF-8』
verify(PublicKey publicKey, String sign, String src, String encode) – 使用公钥验证sign合法性;encode取值『UTF-8』

RSA.java
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * RSA加密,公私钥、加密串等都是16进制编码
 * 
 * @author mamingli
 */
public class RSA {

    private static final Logger logger = Logger.getLogger(RSA.class);

    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    /**
     * 得到私钥对象
     * 
     * @param key
     *            密钥字符串(经过16进制编码)
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws PayException {
        try {
            byte[] keyBytes = StringUtil.hexStrToBytes(key.trim());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            String info = "getPrivateKey failed: " + key + " | " + e.getMessage();
            logger.error(info, e);
            throw new PayException(PayException.NORMAL_ERROR, info, e);
        }
    }

    /**
     * 得到公钥对象
     * 
     * @param key
     *            密钥字符串(经过16进制编码)
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws PayException {
        try {
            byte[] keyBytes = StringUtil.hexStrToBytes(key.trim());
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            String info = "getPublicKey failed: " + key + " | " + e.getMessage();
            logger.error(info, e);
            throw new PayException(PayException.NORMAL_ERROR, info, e);
        }
    }

    /**
     * 本方法使用SHA1withRSA签名算法产生签名
     * 
     * @param privateKey
     *            privateKey 签名时使用的私钥(16进制编码)
     * @param src
     *            src 签名的原字符串
     * @return String 签名的返回结果(16进制编码)。当产生签名出错的时候,返回null。
     * @throws PayException
     */
    public static String sign(PrivateKey privateKey, String src, String encode) throws PayException {
        try {
            Signature sigEng = Signature.getInstance(SIGN_ALGORITHMS);
            sigEng.initSign(privateKey);
            sigEng.update(src.getBytes(encode));
            byte[] signature = sigEng.sign();
            return StringUtil.bytesToHexStr(signature);
        } catch (Exception e) {
            String info = "sign failed: " + src + " | " + e.getMessage();
            logger.error(info, e);
            throw new PayException(PayException.GENERATE_SIGNATURE_FAIL, info, e);
        }
    }

    /**
     * 本方法使用SHA1withRSA签名算法验证签名
     * 
     * @param publicKey
     *            pubKey 验证签名时使用的公钥(16进制编码)
     * @param sign
     *            sign 签名结果(16进制编码)
     * @param src
     *            src 签名的原字符串
     * @throws PayException
     *             验证失败时抛出异常
     */
    public static void verify(PublicKey publicKey, String sign, String src, String encode) throws PayException {
        try {
            if (StringUtils.isBlank(sign) || StringUtils.isBlank(src)) {
                throw new PayException(PayException.VERIFY_SIGNATURE_FAIL, "sign or src isBlank");
            }
            Signature sigEng = Signature.getInstance("SHA1withRSA");
            sigEng.initVerify(publicKey);
            sigEng.update(src.getBytes(encode));
            byte[] sign1 = StringUtil.hexStrToBytes(sign);
            if (!sigEng.verify(sign1)) {
                throw new PayException(PayException.VERIFY_SIGNATURE_FAIL);
            }
        } catch (Exception e) {
            String info = "verify failed: " + sign + " | " + src + " | " + e.getMessage();
            logger.error(info, e);
            throw new PayException(PayException.VERIFY_SIGNATURE_FAIL, info, e);
        }
    }

    /**
     * 本方法用于产生1024位RSA公私钥对。
     * 
     * @return 私钥、公钥
     */
    private static String[] genRSAKeyPair() {
        KeyPairGenerator rsaKeyGen = null;
        KeyPair rsaKeyPair = null;
        try {
            logger.error("Generating a pair of RSA key ... ");
            rsaKeyGen = KeyPairGenerator.getInstance("RSA");
            SecureRandom random = new SecureRandom();
            random.setSeed(("" + System.currentTimeMillis() * Math.random() * Math.random()).getBytes(Charset
                    .forName("UTF-8")));
            rsaKeyGen.initialize(1024, random);
            rsaKeyPair = rsaKeyGen.genKeyPair();
            PublicKey rsaPublic = rsaKeyPair.getPublic();
            PrivateKey rsaPrivate = rsaKeyPair.getPrivate();

            String privateAndPublic[] = new String[2];
            privateAndPublic[0] = StringUtil.bytesToHexStr(rsaPrivate.getEncoded());
            privateAndPublic[1] = StringUtil.bytesToHexStr(rsaPublic.getEncoded());
            logger.error("私钥:" + privateAndPublic[0]);
            logger.error("公钥:" + privateAndPublic[1]);
            logger.error("1024-bit RSA key GENERATED.");

            return privateAndPublic;
        } catch (Exception e) {
            logger.error("genRSAKeyPair error:" + e.getMessage(), e);
            return null;
        }
    }

}

关于product_no的详细说明

商品出厂时打印在商品上面的条形码其实存在可以对应多个sku的情况,建议电商平台为每个sku生成一个product_no,这个product_no有双重作用:

保税模式下,在海关备案,即:作为料号
中外运仓库方打印成条形码,贴在每一个单个货品上,用于取代商品出厂时原生的条形码,以便在仓库作业时扫描

文/Mars白云(简书作者)
原文链接
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容