在向云端推送这个 wechatpay-axios-plugin 业务实现时,发现0.1系列还不够好用,还需要进行更多层级的包裹包装,遂再次做了重大更新,让SDK使用起来更简单、飘逸。

先看官方文档,每一个接口,文档都至少标示了请求URL 请求方式 请求参数 返回参数 这几个要素,URL 可以拆分成 BaseURI,按照这种思路,封装SDK其实完全就可以不用动脑,即,对URI资源的 POSTGET 请求(条件带上参数),取得返回参数

更近一步,我们设想一下,如果把众多接口的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}

树形化即:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
v3
├── certificates
├── bill
│   └── tradebill
├── ecommerce
│   ├── fund
│   │   └── withdraw
│   └── profitsharing
│       └── orders
└── marketing
    └── busifavor
        └── users
            └── {openid}
                └── coupons
                    └── {coupon_code}
                        └── appids
                            └── {appid}

按照这种树形构想,我们来看下需要做的封装实现工作:

  1. 把实体对象,按照实体的排列顺序,映射出请求的URI;
  2. 每个对象实体,包含有若干操作方法,其中可选带参数发起RPC请求;
  3. 随官方放出更多的接口,SDK需要能够弹性扩容;

wechatpay-axios-plugin~0.2.0 版本实现了上述这3个目标,我们用伪代码来校验看一下这个封装实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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 方法,用来在最终请求前,把动态实体节点替换成实际的值。

正常用法示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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 吧。