在向云端推送这个 wechatpay-axios-plugin
业务实现时,发现0.1系列还不够好用,还需要进行更多层级的包裹包装,遂再次做了重大更新,让SDK使用起来更简单、飘逸。
先看官方文档,每一个接口,文档都至少标示了请求URL
请求方式
请求参数
返回参数
这几个要素,URL
可以拆分成 Base
及 URI
,按照这种思路,封装SDK其实完全就可以不用动脑,即,对URI
资源的 POST
或 GET
请求(条件带上参数
),取得返回参数
。
更近一步,我们设想一下,如果把众多接口的URI
按照斜线(/
slash
)分割,然后组织在一起,是不是就可以构建出一颗树,这颗树的每个节点(实体Entity
)都存在有若干个方法(HTTP METHODs
),这是不是就能把接口SDK实现
更简单化了?!
例如:
- /v3/certificates
- /v3/bill/tradebill
- /v3/ecommerce/fund/withdraw
- /v3/ecommerce/profitsharing/orders
- /v3/marketing/busifavor/users/{openid}/coupons/{coupon_code}/appids/{appid}
树形化即:
v3
├── certificates
├── bill
│ └── tradebill
├── ecommerce
│ ├── fund
│ │ └── withdraw
│ └── profitsharing
│ └── orders
└── marketing
└── busifavor
└── users
└── {openid}
└── coupons
└── {coupon_code}
└── appids
└── {appid}
按照这种树形构想,我们来看下需要做的封装实现
工作:
- 把实体对象,按照实体的排列顺序,映射出请求的URI;
- 每个对象实体,包含有若干操作方法,其中可选带参数发起RPC请求;
- 随官方放出更多的接口,SDK需要能够弹性扩容;
wechatpay-axios-plugin~0.2.0 版本实现了上述这3个目标,代码包如下截屏:
我们用伪代码来校验看一下这个封装实现
:
require('util').inspect.defaultOptions.depth = 10;
const { Wechatpay } = require('wechatpay-axios-plugin');
const wxpay = new Wechatpay({mchid: '1', serial: '2', privateKey: '3', certs: {'4': '5'}});
wxpay.v3.certificates;
wxpay.v3.bill.tradebill;
wxpay.v3.ecommerce.fund.withdraw;
wxpay.v3.marketing.busifavor.users['{openid}'].coupons.$coupon_code$.appids['wx233544546545989'];
console.info(wxpay);
//以下是输出内容
{
entities: [],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
v3: {
entities: [ 'v3' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
certificates: {
entities: [ 'v3', 'certificates' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload]
},
bill: {
entities: [ 'v3', 'bill' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
tradebill: {
entities: [ 'v3', 'bill', 'tradebill' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload]
}
},
ecommerce: {
entities: [ 'v3', 'ecommerce' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
fund: {
entities: [ 'v3', 'ecommerce', 'fund' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
withdraw: {
entities: [ 'v3', 'ecommerce', 'fund', 'withdraw' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload]
}
}
},
marketing: {
entities: [ 'v3', 'marketing' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
busifavor: {
entities: [ 'v3', 'marketing', 'busifavor' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
users: {
entities: [ 'v3', 'marketing', 'busifavor', 'users' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
'{openid}': {
entities: [ 'v3', 'marketing', 'busifavor', 'users', '{openid}' ],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
coupons: {
entities: [
'v3',
'marketing',
'busifavor',
'users',
'{openid}',
'coupons'
],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
'$coupon_code$': {
entities: [
'v3',
'marketing',
'busifavor',
'users',
'{openid}',
'coupons',
'{coupon_code}'
],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
appids: {
entities: [
'v3',
'marketing',
'busifavor',
'users',
'{openid}',
'coupons',
'{coupon_code}',
'appids'
],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload],
wx233544546545989: {
entities: [
'v3',
'marketing',
'busifavor',
'users',
'{openid}',
'coupons',
'{coupon_code}',
'appids',
'wx233544546545989'
],
withEntities: [Function: withEntities],
get: [AsyncFunction: get],
post: [AsyncFunction: post],
upload: [AsyncFunction: upload]
}
}
}
}
}
}
}
}
}
}
注: API树实体节点,存储在每个 entities
属性上,方便后续的get
, post
抑或 upload
方法调用调用前,反构成最终请求的URI
;特别地,对于动态树实体节点来说,每个实体节点均提供了 withEntities
方法,用来在最终请求前,把动态实体节点替换成实际的值。
正常用法示例如下:
const {Wechatpay} = require('wechatpay-axios-plugin');
const wxpay = new Wechatpay({/*初始化参数,README有*/}, {/*可选调整axios的参数*/});
//拿证书
wxpay.v3.certificates.get();
//带参申请交易账单
wxpay.v3.bill.tradebill.get({params: {bill_date}});
//带参发起账户余额提现
wxpay.v3.ecommerce.fund.withdraw.post({sub_mchid, out_request_no, amount, remark, bank_memo});
//查询用户单张券详情
wxpay.v3.marketing.busifavor.users['{openid}'].coupons.$coupon_code$.appids['wx233544546545989'].withEntities({openid, coupon_code}).get();
请求APIv3是不是就“丧心病狂”般的简单了?!
详细功能说明及用法示例,npmjs及github的README均有。
如果喜欢,就给来个 赞 及 Star 吧。
回复楼上同学,云函数发小程序红包参考代码
// file: cloudfunctions/thanksgivenday/index.js const cloud = require('wx-server-sdk') const {Formatter, Wechatpay, Hash} = require('wechatpay-axios-plugin') cloud.init({env: cloud.DYNAMIC_CURRENT_ENV}) const v2SecretKey = '' const mch_id = '' const SUCCESS = 'SUCCESS' const wxpay = Wechatpay.xmlBased({ mchid: mch_id, secret: v2SecretKey, merchant: { cert: '-----BEGIN CERTIFICATE-----' + '...' + '-----END CERTIFICATE-----', key: '-----BEGIN PRIVATE KEY-----' + '...' + '-----END PRIVATE KEY-----', }, }) exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const {data: {return_code, result_code, package: pkgSource}} = await wxpay.post('/mmpaymkttransfers/sendminiprogramhb', { mch_id, wxappid: wxContext.APPID, re_openid: wxContext.OPENID, nonce_str: Formatter.nonce(), mch_billno: `HB${+new Date}`, send_name: '感恩节真情回馈', total_amount: 1, total_num: 1, wishing: '感恩有你', act_name: '云开发红包活动', remark: '云开发红包活动20201126', notify_way: 'MINI_PROGRAM_JSAPI', scene_id: 'PRODUCT_2', }) let toMiniPage = {package: void 0, paySign: void 0, message: '出错了'} if (return_code == SUCCESS && result_code == SUCCESS) { // 二次签名数据结构 toMiniPage = { timeStamp: `${Formatter.timestamp()}`, nonceStr: Formatter.nonce(), package: encodeURIComponent(pkgSource), signType: 'MD5', } const paySign = Hash.md5(Formatter.queryStringLike(Formatter.ksort(toMiniPage)), v2SecretKey) toMiniPage = Object.assign(toMiniPage, {paySign, openid: wxContext.OPENID}) } return toMiniPage } //file: miniprogram/pages/index/index.js const app = getApp() Page({ data: {}, async onLoad(options) { const opts = wx.getLaunchOptionsSync() if ([1047, 1011, 1025, 1124].includes(opts.sence)) { wx.cloud.callFunction({ name: 'thanksgivenday', data: {}, success(res) { let fromCloud = {...res.result} console.log('[cloud] [thanksgivenday] success', fromCloud) const {openid, paySign, message} = fromCloud if (openid && paySign && message == void 0) { delete fromCloud.openid fromCloud = Object.assign(fromCloud, { success(hb) { console.info(hb) }, fail(hb) { console.error(hb) }, complete(hb) { console.log(hb) } }) wx.sendBizRedPacket(fromCloud) } }, fail(err) { console.error('[cloud] [thanksgivenday] failed', err) } }) } } })
请教一下,使用您这个模块,在‘云函数’里是否可以实现‘现金红包’功能?