❓每次打款前,你都要核对三四遍卡号金额,生怕输错账户,打错款目
❓繁杂的付款工作占用大部分的工作时间,财务产出的整体价值不高
❓依靠人工手动从银端获取回款,不仅耗时长,而且效率低
用轻流,这些烦恼都可以统统甩开!
通过丰富的 open api 接口和强大的 q-linker、webhook数据传输能力,直接在轻流系统内就可以实现支付宝收款/退款、银行卡转账并查询流水和电子回单啦!
(⚠️银企直联目前仅支持中国银行)
口说无凭,一起来看看实现效果吧👇
🌟实现步骤
教程目录:
因步骤十分详尽所以较长,耐心些,按步骤操作就可以实现视频中的同款效果
(如果你还不是轻流用户,快点击👉👈,体验试用吧~)
支付宝扫码支付 教程
1、准备工作
- 点击登录👉👈
- 进入后台,创建网页应用
- 绑定产品——【当面付】,并进行开通
- 接口加签方式设置
2、获取调用接口所需具体公共参数
以扫码付接口为例:
- appid:应用id
3、表单设计
1. 支付宝订单预创建并支付
1.1. 生成支付链接
1.1.1. 代码注释
具体调用接口文档:
⚠️注意:所生成的二维码有效时间为2小时
app_id:需要替换成具体调用的应用id
qf_field.{商品列表信息$$17b54dd0c$$},为表单中商品列表信息表格字段
其中以 field_169208787 为例,表示商品列表信息表格中商品数量子字段 id
field_id 获取方式
qf_field.{商户订单号$$17b54dd0a$$},为表单中商户订单号字段
qf_field.{订单总金额(单位为元)$$17b54dd0b$$},为表单中订单总金额(单位为元)字段
qf_field.{订单标题$$17b44c092$$},为表单中订单标题字段
应用私钥:只需要替换成具体调用的应用私钥
生成支付链接:
const axios = require(‘axios’);
const crypto = require(‘crypto’);
const { format } = require(‘date-fns’);const currentdate = new date();
const formatteddate = format(currentdate, ‘yyyy-mm-dd hh:mm:ss’);
// 原始数据
const data = {
“timestamp”:formatteddate,
“method”: “alipay.trade.precreate”,
“app_id”: “2021003155650***”,
“sign_type”: “rsa2”,
“version”: “1.0”,
“charset”: “utf-8”
};
const table = qf_field.{商品列表信息$$17b54dd0c$$};const goodsdetail = table.map(item => {
const newitem = {
“quantity”: item.field_169208787,
“goods_name”: item.field_169208786,
“goods_id”: item.field_169208785,
“price”: item.field_169208788
};
return newitem;
});//将新的 goods_detail 赋值给 data 中的 biz_content
data.biz_content =`{“out_trade_no”: qf_field.{商户订单号$$17b54dd0a$$},”total_amount”:qf_field.{订单总金额(单位为元)$$17b54dd0b$$},”subject”:qf_field.{订单标题$$17b44c092$$},”goods_detail”:${json.stringify(goodsdetail)}}`;// 第一步:剔除sign字段
const { sign, …datawithoutsign } = data;// 第二步:剔除值为空的参数
const filtereddata = object.fromentries(object.entries(datawithoutsign).filter(([key, value]) => value !== ”));// 第三步:按键的ascii码值递增排序
const sortedparams = object.keys(filtereddata).sort();// 第四步:将排序后的参数与其对应的值按照”参数=参数值”的格式组合起来
const concatenatedparams = sortedparams.map(key => `${key}=${filtereddata[key]}`).join(‘&’);// 第五步:生成待签名字符串
const signstring = concatenatedparams;// 使用私钥对待签名字符串进行加密
const privatekey = `—–begin private key—–
mi****danbgkqhkig9w0baqefaascbkcwggsjageaa……………
—–end private key—–`;const signer = crypto.createsign(‘rsa-sha256’);
signer.update(signstring, ‘utf8’);
const signature = signer.sign(privatekey);
const sig=signature.tostring(‘base64’);
const encodedsig = encodeuricomponent(sig);let config = {
method: ‘get’,
maxbodylength: infinity,
url: `https://openapi.alipay.com/gateway.do?timestamp=${formatteddate}&method=${data.method}&app_id=${data.app_id}&sign_type=${data.sign_type}&sign=${encodedsig}&version=${data.version}&charset=${data.charset}&biz_content=${data.biz_content}`
};axios(config).then(function (res) {
qf_output={‘data’:res.data}
});
1.1.2. 代码测试
1.1.3. 解析配置
1.1.4. 字段接收配置
1.2. 生成支付二维码
该字段为q_linker,调用接口将支付链接转为支付二维码(具体接口文档:)
- 需要点击申请appid,去获取对应的app_id和app_srcret
1.2.1. q-linker 配置
- url:https://www.mxnzp.com/api/qrcode/create/single
- queryparam:
- method:get
- json path:
- 字段接收配置
1.3. 确认支付状态
1.3.1. 代码注释具体调用接口文档:
app_id:需要替换成具体调用的应用id
qf_field.{商户订单号$$17b54dd0a$$},为表单中商户订单号字段
应用私钥:只需要替换成具体调用的应用私钥
确认支付状态:
const crypto = require(‘crypto’);
const { format } = require(‘date-fns’);const currentdate = new date();
const formatteddate = format(currentdate, ‘yyyy-mm-dd hh:mm:ss’);
// 原始数据
const data = {
“timestamp”:formatteddate,
“method”: “alipay.trade.query”,
“app_id”: “2021003155650***”,
“sign_type”: “rsa2”,
“version”: “1.0”,
“charset”: “utf-8”
};
data.biz_content =`{“out_trade_no”: qf_field.{商户订单号$$17b54dd0a$$}}`;
// 第一步:剔除sign字段
const { sign, …datawithoutsign } = data;
// 第二步:剔除值为空的参数
const filtereddata = object.fromentries(object.entries(datawithoutsign).filter(([key, value]) => value !== ”));// 第三步:按键的ascii码值递增排序
const sortedparams = object.keys(filtereddata).sort();// 第四步:将排序后的参数与其对应的值按照”参数=参数值”的格式组合起来
const concatenatedparams = sortedparams.map(key => `${key}=${filtereddata[key]}`).join(‘&’);// 第五步:生成待签名字符串
const signstring = concatenatedparams;// 使用私钥对待签名字符串进行加密
const privatekey = `—–begin private key—–
mii****bgkqhkig9w0baqefaascbkcwggsjageaa………………….
—–end private key—–`;const signer = crypto.createsign(‘rsa-sha256’);
signer.update(signstring, ‘utf8’);
const signature = signer.sign(privatekey);
const sig=signature.tostring(‘base64’);
const encodedsig = encodeuricomponent(sig);
let config = {
method: ‘get’,
maxbodylength: infinity,
url: `https://openapi.alipay.com/gateway.do?timestamp=${formatteddate}&method=${data.method}&app_id=${data.app_id}&sign_type=${data.sign_type}&sign=${encodedsig}&version=${data.version}&charset=${data.charset}&biz_content=${data.biz_content}`
};
axios(config).then(function (res) {
qf_output={‘data’:res.data}
});
1.3.2. 代码测试
1.3.3. 解析配置
1.3.4. 字段接收配置
1.4. 流程配置
1.4.1. 申请人节点配置
1.4.2. 确认支付信息并扫码支付
1.4.3. 告知用户
1.4.4. webhook节点
- accesstoken:轻商城——拓展插件——openapi
- 节点id获取方式
2. 支付宝申请订单退款
2.1. 商户订单号
2.2. 订单总金额
2.3. 退款
2.3.1. 代码注释具体调用接口文档:
⚠️注意:交易超过约定时间(签约时设置的可退款时间)的订单无法进行退款。
app_id:需要替换成具体调用的应用id
qf_field.{商户订单号$$17b598970$$}:表单中商户订单号字段
qf_field.{退款金额$$17b598089$$}:表单中退款金额字段
qf_field.{退款原因说明$$17b59808a$$}:表单中退款原因说明字段
应用私钥:只需要替换成具体调用的应用私钥
退款:
const crypto = require(‘crypto’);
const { format } = require(‘date-fns’);const currentdate = new date();
const formatteddate = format(currentdate, ‘yyyy-mm-dd hh:mm:ss’);
// 原始数据
const data = {
“timestamp”:formatteddate,
“method”: “alipay.trade.refund”,
“app_id”: “2021003155650***”,
“sign_type”: “rsa2”,
“version”: “1.0”,
“charset”: “utf-8”
};
data.biz_content =`{“out_trade_no”:qf_field.{商户订单号$$17b598970$$},”refund_amount”:qf_field.{退款金额$$17b598089$$},”refund_reason”:qf_field.{退款原因说明$$17b59808a$$}}`;
// 第一步:剔除sign字段
const { sign, …datawithoutsign } = data;
// 第二步:剔除值为空的参数
const filtereddata = object.fromentries(object.entries(datawithoutsign).filter(([key, value]) => value !== ”));// 第三步:按键的ascii码值递增排序
const sortedparams = object.keys(filtereddata).sort();// 第四步:将排序后的参数与其对应的值按照”参数=参数值”的格式组合起来
const concatenatedparams = sortedparams.map(key => `${key}=${filtereddata[key]}`).join(‘&’);// 第五步:生成待签名字符串
const signstring = concatenatedparams;// 使用私钥对待签名字符串进行加密
const privatekey = `—–begin private key—–
mii*****nbgkqhkig9w0baqefaascbkcwggsjageaa……………………
—–end private key—–`;const signer = crypto.createsign(‘rsa-sha256’);
signer.update(signstring, ‘utf8’);
const signature = signer.sign(privatekey);
const sig=signature.tostring(‘base64’);
const encodedsig = encodeuricomponent(sig);
let config = {
method: ‘get’,
maxbodylength: infinity,
url: `https://openapi.alipay.com/gateway.do?timestamp=${formatteddate}&method=${data.method}&app_id=${data.app_id}&sign_type=${data.sign_type}&sign=${encodedsig}&version=${data.version}&charset=${data.charset}&biz_content=${data.biz_content}`
};axios(config).then(function (res) {
qf_output={‘data’:res.data}});
2.3.2. 解析配置
2.3.3. 字段接收配置
银企直联(中国银行)教程
在实现中国银行的公对公转账,公对私转账,查询流水,获取电子回执单功能
1、落地方案
需要二次开发,由中间件根据中国银行银企对接接口规范要求,封装以下四个接口:
-
- 公对公转账
- 公对私转账
- 查询流水
- 电子回单查询
1.1 转账
转账效果有两种:直接转出不提示,需要在中国银行app上确认,两种方式需要在银行柜台做变更
📚方案简述:
-
- 在轻流发起转账,通过webhook携带三个参数访问中间件;
- 中间件根据参数访问对应的前置机接口,前置机执行对应操作将转账消息加入银行队列
- 根据转账的表现,银行执行转账操作时,会给中国银行app发送确认消息(或者直接转账成功,具体表现需要银行柜台进行修改)
1.2 查询流水
📚方案简述:
-
- 查询银行流水为定时任务,根据业务需要可设置多久拉取一次
- 拉取昨天和今天的流水数据,并在中间件解析成符合轻流要求的格式
- 中间件通过qs被动将数据推送至轻流应用中,并比对更新
1.3 电子回单查询
📚方案简述:
- 中间件访问回单下载接口后,前置机返回回单文件的名称
- 银行生成请求时间段内的回单,回单为pdf文件,统一放在压缩文件内。
- 中间件轮询是否已经生成回单文件,生成后将文件上传至对象存储服务,并返回文件的路径;
- 中间件将返回的文件路径推送至轻流,并匹配至轻流上传附件字段。
🎉最终效果
- 在轻流实现转账服务
- 可在轻流应用中查询到流水和电子回单
⚠️备注
- 银企直联属于银行系统,每年需要付款使用
- 银行会提供前置机架构,所有对银行的请求都会先到前置机处理
- 只有windows的系统,不能云部署
- 访问中国银行银企对接接口需要先签到,签到有效期为15分钟,可缓存登录状态