轻流 x 旺店通(旗舰版)
应用场景
产品介绍:
旺店通是一款专门为电商量身定做的电商ERP系统软件,致力于提供专业的电子商务ERP解决方案,能全面覆盖线上电商、网店以及线下实体店进销存管理及wms仓储管理系统等
API文档:https://open.wangdian.cn/qjb/open/apidoc
实现目标:
通过轻流应用表单实现与旺店通(旗舰版)进行对接
Ps:
1、目前批量获取数据暂时只能存放在表格中并且只能获取部分数据,体验上会有点不好。
不过后续产品QS主动支持代码块处理即可实现定时轮训获取。如果不想要放在表格中,可以采用定制开发来实现
落地方案

图文/视频演示
帮助文档
一、旺店通(旗舰版)接入

- 获取必要参数,这里是测试环境测试用

- 正式环境必要参数获取位置

注意点:
接口规范:https://open.wangdian.cn/qjb/open/guide?path=qjbguide_jkgf
- REST风格的API,使用HTTPS协议发送POST请求调用接口。
- HTTP请求头类型为 Content-Type: application/json 或者 Content-Type: application/xml

二、签名算法
1、签名sign介绍

- 这里算法说明示例是以重量回传为例的
- 接口文档地址:https://open.wangdian.cn/qjb/open/apidoc/doc?path=wms.stockout.Sales.weighingExt

2、代码块实现
以文档所举为示例,其中timestamp计算规则:秒级时间戳, 当前时间戳减去 2012-01-01 00:00:00(1325347200), 时间与服务器时间差120s内即合法。
const crypto = require('crypto');
const { differenceInSeconds } = require('date-fns');
// 当前时间
const currentDate = new Date();
// 2012-01-01 00:00:00 的时间
const startDate = new Date(2012, 0, 1, 0, 0, 0);
// 时间戳差值
const timestampDiff = differenceInSeconds(currentDate, startDate);
// 第一步:将 appsecret 分割为 secret 和 salt
const appsecret = '693f9fed686bfe13441385da98b436b6:dd488d0802b1e431e4c72cd355847853';
const [secret, salt] = appsecret.split(':');
// 第二步:定义请求参数
const params = {
key: 'czhc',
salt: salt,
method: 'wms.stockout.Sales.weighingExt',
timestamp: timestampDiff,
v: '1.0',
sid: 'wdterp30',
body: '["xc109393939393939","",1.2,0,false]'
};
// 第三步:按键名对参数进行排序
const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
acc[key] = params[key];
return acc;
}, {});
// 第四步:拼接排序后的键值对
const concatenatedString = `${secret}${Object.entries(sortedParams).map(([key, value]) => `${key}${value}`).join('')}${secret}`;
// 第五步:计算 MD5 哈希值
const sign = crypto.createHash('md5').update(concatenatedString).digest('hex');
// 第六步:将 sign 参数添加到 params 对象中
params.sign = sign;
qf_output={params}
3、代码测试

三、订单查询为例
对应的接口文档:https://open.wangdian.cn/qjb/open/apidoc/doc?path=sales.TradeQuery.queryWithDetail

1、代码块实现
- 具体查询参数内容根据接口文档去填写,填写至params中

- appkey、appsecret以及sid需替换为咱们旺店通所提供的具体参数内容
- 这里end_time、start_time为插入表单中的字段

这里最后代码中的处理是对于接口返回的数据进行处理,由于order是数组,每个元素中又有数组detail_list,在轻流中相当于表格行中嵌入表格,轻流并不支持,所以将数组处理进行整合。
处理逻辑是:将order每个元素的数据除了detail_list数组以外的数据放进detail_list的每个元素中,然后最后整合输出新的list数组
const { differenceInSeconds } = require('date-fns');
const crypto = require('crypto');
const axios = require('axios');
// 当前时间
const currentDate = new Date();
// 2012-01-01 00:00:00 的时间
const startDate = new Date(2012, 0, 1, 0, 0, 0);
// 时间戳差值
const timestampDiff = differenceInSeconds(currentDate, startDate);
// 第一步:将 appsecret 分割为 secret 和 salt
const appsecret = '693f9fed686bfe13441385da98b436b6:dd488d0802b1e431e4c72cd355847853';
const [secret, salt] = appsecret.split(':');
let data = JSON.stringify([
{
"start_time": qf_field.{开始日期$$18639AE02$$},
"end_time": qf_field.{结束日期$$18639AE18$$}
}
]);
let parsedData = JSON.parse(data);
// 第二步:定义请求参数
const params = {
key: 'czhc',
salt: salt,
method: 'sales.TradeQuery.queryWithDetail',
timestamp: timestampDiff,
v: '1.0',
sid: 'wdterp30',
page_size:50,
page_no:0,
body: data
};
// 第三步:按键名对参数进行排序
const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
acc[key] = params[key];
return acc;
}, {});
// 第四步:拼接排序后的键值对
const concatenatedString = `${secret}${Object.entries(sortedParams).map(([key, value]) => `${key}${value}`).join('')}${secret}`;
// 第五步:计算 MD5 哈希值
const sign = crypto.createHash('md5').update(concatenatedString).digest('hex');
// 第六步:将 sign 参数添加到 params 对象中
params.sign = sign;
let config = {
method: 'post',
maxBodyLength: Infinity,
url: `http://wdt.wangdian.cn/openapi?sid=${params.sid}&key=${params.key}&salt=${params.salt}&method=${params.method}&v=${params.v}×tamp=${params.timestamp}&sign=${params.sign}`,
headers: {
'Content-Type': 'application/json'
},
data : data
};
if(parsedData[0].start_time==null || parsedData[0].end_time==null){
qf_output={'data':null}
}else{
axios(config)
.then(function (res) {
const resdata=res.data;
const orders = resdata.data.order;
const newList = [];
//由于order中有数组detail_list,在轻流中相当于表格行中嵌入表格,轻流并不支持,所以将数组处理进行整合
orders.forEach(order => {
const detailList = order.detail_list;
detailList.forEach(detail => {
const newDetail = { ...detail, ...order };
delete newDetail.detail_list;
newList.push(newDetail);
});
});
qf_output={'list':newList}
});
}
2、字段接收配置
- 这里只配置了部分字段,可以根据自己的需求去具体配置


四、供应商信息推送为例
对应的接口文档:https://open.wangdian.cn/qjb/open/apidoc/doc?path=setting.PurchaseProvider.push

1、代码块实现
- 表单配置参考

- 这里的请求参数根据具体接口文档去修改
- qf_field.{供应商编号$$18644BC92$$} :表单中供应商编号字段,其余插入字段参考该字段

- appkey、appsecret以及sid需替换为咱们旺店通所提供的具体参数内容
const { differenceInSeconds } = require('date-fns');
const crypto = require('crypto');
const axios = require('axios');
// 当前时间
const currentDate = new Date();
// 2012-01-01 00:00:00 的时间
const startDate = new Date(2012, 0, 1, 0, 0, 0);
// 时间戳差值
const timestampDiff = differenceInSeconds(currentDate, startDate);
// 第一步:将 appsecret 分割为 secret 和 salt
const appsecret = '693f9fed686bfe13441385da98b436b6:dd488d0802b1e431e4c72cd355847853';
const [secret, salt] = appsecret.split(':');
let data = JSON.stringify([
{
"provider_no":qf_field.{供应商编号$$18644BC92$$} ,
"provider_name": qf_field.{供应商名称$$18644BCA8$$},
"contact": qf_field.{联系人$$18644BCA9$$}
}
]);
// 第二步:定义请求参数
const params = {
key: 'czhc',
salt: salt,
method: 'setting.PurchaseProvider.push',
timestamp: timestampDiff,
v: '1.0',
sid: 'wdterp30',
body: data
};
// 第三步:按键名对参数进行排序
const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
acc[key] = params[key];
return acc;
}, {});
// 第四步:拼接排序后的键值对
const concatenatedString = `${secret}${Object.entries(sortedParams).map(([key, value]) => `${key}${value}`).join('')}${secret}`;
// 第五步:计算 MD5 哈希值
const sign = crypto.createHash('md5').update(concatenatedString).digest('hex');
// 第六步:将 sign 参数添加到 params 对象中
params.sign = sign;
let config = {
method: 'post',
maxBodyLength: Infinity,
url: `http://wdt.wangdian.cn/openapi?sid=${params.sid}&key=${params.key}&salt=${params.salt}&method=${params.method}&v=${params.v}×tamp=${params.timestamp}&sign=${params.sign}`,
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (res) {
const resdata=res.data;
qf_output={resdata}
});
字段接收配置
- 上述配置完成后,点击代码块,推送的结果信息会推到对应字段中


2、webhook实现
-
通过代码块计算签名之后,提交数据进入流程触发webhook去完成调用接口
-
注意点:生成签名后,需要在2min之内提交数据触发webhook,因为旺店通旗舰版时间与服务器时间差120s内即合法。

实现路径
- 代码块签名
const { differenceInSeconds } = require('date-fns');
const crypto = require('crypto');
const axios = require('axios');
// 当前时间
const currentDate = new Date();
// 2012-01-01 00:00:00 的时间
const startDate = new Date(2012, 0, 1, 0, 0, 0);
// 时间戳差值
const timestampDiff = differenceInSeconds(currentDate, startDate);
// 第一步:将 appsecret 分割为 secret 和 salt
const appsecret = '693f9fed686bfe13441385da98b436b6:dd488d0802b1e431e4c72cd355847853';
const [secret, salt] = appsecret.split(':');
let data = JSON.stringify([
{
"provider_no":qf_field.{供应商编号$$18644DBE9$$},
"provider_name":qf_field.{供应商名称$$18644DBEA$$},
"contact": qf_field.{联系人$$18644DBEB$$}
}
]);
// 第二步:定义请求参数
const params = {
key: 'czhc',
salt: salt,
method: 'setting.PurchaseProvider.push',
timestamp: timestampDiff,
v: '1.0',
sid: 'wdterp30',
body: data
};
// 第三步:按键名对参数进行排序
const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
acc[key] = params[key];
return acc;
}, {});
// 第四步:拼接排序后的键值对
const concatenatedString = `${secret}${Object.entries(sortedParams).map(([key, value]) => `${key}${value}`).join('')}${secret}`;
// 第五步:计算 MD5 哈希值
const sign = crypto.createHash('md5').update(concatenatedString).digest('hex');
// 第六步:将 sign 参数添加到 params 对象中
params.sign = sign;
qf_output={params}
字段接收配置



- webhook配置
- 只需要配置param以及body中内容即可,Method和URL是固定的

