@waffo/waffo-node 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1066 -385
- package/dist/index.d.mts +1567 -4258
- package/dist/index.d.ts +1567 -4258
- package/dist/index.js +1128 -1597
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1105 -1560
- package/dist/index.mjs.map +1 -1
- package/package.json +28 -43
- package/README.ja.md +0 -580
- package/README.zh-CN.md +0 -580
package/README.zh-CN.md
DELETED
|
@@ -1,580 +0,0 @@
|
|
|
1
|
-
# Waffo PSP Node.js SDK
|
|
2
|
-
|
|
3
|
-
Waffo PSP(支付服务提供商)收单服务的官方 Node.js SDK。本 SDK 提供基于 RSA 签名的安全 API 通信,以及完整的收单操作类型定义支持。
|
|
4
|
-
|
|
5
|
-
**语言**: [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md)
|
|
6
|
-
|
|
7
|
-
## 快速开始
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import {
|
|
11
|
-
Waffo,
|
|
12
|
-
Environment,
|
|
13
|
-
CurrencyCode,
|
|
14
|
-
ProductName,
|
|
15
|
-
} from '@waffo/waffo-node';
|
|
16
|
-
|
|
17
|
-
// 1. 初始化 SDK
|
|
18
|
-
const waffo = new Waffo({
|
|
19
|
-
apiKey: 'your-api-key',
|
|
20
|
-
privateKey: 'your-base64-encoded-private-key',
|
|
21
|
-
environment: Environment.SANDBOX,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// 2. 创建订单
|
|
25
|
-
const result = await waffo.order.create({
|
|
26
|
-
paymentRequestId: 'REQ_001',
|
|
27
|
-
merchantOrderId: 'ORDER_001',
|
|
28
|
-
orderCurrency: CurrencyCode.IDR,
|
|
29
|
-
orderAmount: '100000',
|
|
30
|
-
orderDescription: '商品购买',
|
|
31
|
-
notifyUrl: 'https://merchant.com/notify',
|
|
32
|
-
merchantInfo: { merchantId: 'your-merchant-id' },
|
|
33
|
-
userInfo: {
|
|
34
|
-
userId: 'user_001',
|
|
35
|
-
userEmail: 'user@example.com',
|
|
36
|
-
},
|
|
37
|
-
paymentInfo: {
|
|
38
|
-
productName: ProductName.ONE_TIME_PAYMENT,
|
|
39
|
-
payMethodType: 'EWALLET',
|
|
40
|
-
payMethodName: 'DANA',
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 3. 处理响应
|
|
45
|
-
if (result.success) {
|
|
46
|
-
console.log('订单创建成功:', result.data);
|
|
47
|
-
// 引导用户跳转到支付页面
|
|
48
|
-
if (result.data?.orderAction) {
|
|
49
|
-
const action = JSON.parse(result.data.orderAction);
|
|
50
|
-
window.location.href = action.webUrl;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
> **提示**:需要生成新的 RSA 密钥对?使用 `Waffo.generateKeyPair()` 来创建:
|
|
56
|
-
> ```typescript
|
|
57
|
-
> const keyPair = Waffo.generateKeyPair();
|
|
58
|
-
> console.log(keyPair.privateKey); // 妥善保管,用于 SDK 初始化
|
|
59
|
-
> console.log(keyPair.publicKey); // 提供给 Waffo
|
|
60
|
-
> ```
|
|
61
|
-
|
|
62
|
-
## 特性
|
|
63
|
-
|
|
64
|
-
- RSA-2048 请求签名与响应验证
|
|
65
|
-
- 完整的 TypeScript 类型定义支持
|
|
66
|
-
- 零生产依赖(仅使用 Node.js 内置 `crypto` 模块)
|
|
67
|
-
- 支持沙箱和生产环境
|
|
68
|
-
- 双模块格式支持(ESM/CommonJS)
|
|
69
|
-
- 订单管理(创建、查询、取消、退款、捕获)
|
|
70
|
-
- 订阅管理(创建、查询、取消、获取管理链接)
|
|
71
|
-
- 退款状态查询
|
|
72
|
-
- 商户配置查询(交易限额、每日限额)
|
|
73
|
-
- 支付方式配置查询(可用性、维护时段)
|
|
74
|
-
- Webhook 处理器,支持自动签名验证和事件路由
|
|
75
|
-
- Webhook 签名验证工具
|
|
76
|
-
- 直接 HTTP 客户端访问,支持自定义 API 请求
|
|
77
|
-
- 请求时间戳参数自动默认值
|
|
78
|
-
|
|
79
|
-
## 时间戳参数自动默认值
|
|
80
|
-
|
|
81
|
-
所有时间戳参数(`orderRequestedAt`、`requestedAt`、`captureRequestedAt`)均为**可选参数**,如不提供将自动使用当前时间(`new Date().toISOString()`):
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// 时间戳自动设置为当前时间
|
|
85
|
-
await waffo.order.create({
|
|
86
|
-
paymentRequestId: 'REQ_001',
|
|
87
|
-
merchantOrderId: 'ORDER_001',
|
|
88
|
-
// ... 其他必填字段
|
|
89
|
-
// orderRequestedAt 自动设置
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// 或显式提供自定义时间戳
|
|
93
|
-
await waffo.order.create({
|
|
94
|
-
paymentRequestId: 'REQ_001',
|
|
95
|
-
merchantOrderId: 'ORDER_001',
|
|
96
|
-
orderRequestedAt: '2025-01-01T00:00:00.000Z', // 自定义时间戳
|
|
97
|
-
// ... 其他必填字段
|
|
98
|
-
});
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
此特性适用于:
|
|
102
|
-
- `CreateOrderParams.orderRequestedAt`
|
|
103
|
-
- `CancelOrderParams.orderRequestedAt`
|
|
104
|
-
- `RefundOrderParams.requestedAt`
|
|
105
|
-
- `CaptureOrderParams.captureRequestedAt`
|
|
106
|
-
- `CreateSubscriptionParams.requestedAt`
|
|
107
|
-
- `CancelSubscriptionParams.requestedAt`
|
|
108
|
-
|
|
109
|
-
## 安装
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npm install @waffo/waffo-node
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## 使用指南
|
|
116
|
-
|
|
117
|
-
### 初始化 SDK
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { Waffo, Environment } from '@waffo/waffo-node';
|
|
121
|
-
|
|
122
|
-
const waffo = new Waffo({
|
|
123
|
-
apiKey: 'your-api-key',
|
|
124
|
-
privateKey: 'your-base64-encoded-private-key',
|
|
125
|
-
environment: Environment.SANDBOX, // 或 Environment.PRODUCTION
|
|
126
|
-
});
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### 生成 RSA 密钥对
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { Waffo } from '@waffo/waffo-node';
|
|
133
|
-
|
|
134
|
-
const keyPair = Waffo.generateKeyPair();
|
|
135
|
-
console.log(keyPair.privateKey); // Base64 编码的 PKCS8 私钥
|
|
136
|
-
console.log(keyPair.publicKey); // Base64 编码的 X509 公钥
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 创建订单
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
const result = await waffo.order.create({
|
|
143
|
-
paymentRequestId: 'REQ_001',
|
|
144
|
-
merchantOrderId: 'ORDER_001',
|
|
145
|
-
orderCurrency: CurrencyCode.IDR,
|
|
146
|
-
orderAmount: '100000',
|
|
147
|
-
orderDescription: '商品购买',
|
|
148
|
-
notifyUrl: 'https://merchant.com/notify',
|
|
149
|
-
merchantInfo: {
|
|
150
|
-
merchantId: 'your-merchant-id',
|
|
151
|
-
},
|
|
152
|
-
userInfo: {
|
|
153
|
-
userId: 'user_001',
|
|
154
|
-
userEmail: 'user@example.com',
|
|
155
|
-
userPhone: '+62-81234567890',
|
|
156
|
-
userTerminal: UserTerminalType.WEB,
|
|
157
|
-
},
|
|
158
|
-
paymentInfo: {
|
|
159
|
-
productName: ProductName.ONE_TIME_PAYMENT,
|
|
160
|
-
payMethodType: 'EWALLET',
|
|
161
|
-
payMethodName: 'DANA',
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (result.success) {
|
|
166
|
-
console.log('订单创建成功:', result.data);
|
|
167
|
-
} else {
|
|
168
|
-
console.error('错误:', result.error);
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### 查询订单状态
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
const result = await waffo.order.inquiry({
|
|
176
|
-
acquiringOrderId: 'A202512230000001',
|
|
177
|
-
// 或使用 paymentRequestId: 'REQ_001'
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
if (result.success) {
|
|
181
|
-
console.log('订单状态:', result.data?.orderStatus);
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### 取消订单
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
const result = await waffo.order.cancel({
|
|
189
|
-
acquiringOrderId: 'A202512230000001',
|
|
190
|
-
merchantId: 'your-merchant-id',
|
|
191
|
-
// orderRequestedAt 为可选参数,默认为当前时间
|
|
192
|
-
});
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### 订单退款
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
const result = await waffo.order.refund({
|
|
199
|
-
refundRequestId: 'REFUND_001',
|
|
200
|
-
acquiringOrderId: 'A202512230000001',
|
|
201
|
-
merchantId: 'your-merchant-id',
|
|
202
|
-
refundAmount: '50000',
|
|
203
|
-
refundReason: '用户申请退款',
|
|
204
|
-
refundNotifyUrl: 'https://merchant.com/refund-notify',
|
|
205
|
-
// requestedAt 为可选参数,默认为当前时间
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### 查询退款状态
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const result = await waffo.refund.inquiry({
|
|
213
|
-
refundRequestId: 'REFUND_001',
|
|
214
|
-
// 或使用 acquiringRefundOrderId: 'R202512230000001'
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
if (result.success) {
|
|
218
|
-
console.log('退款状态:', result.data?.refundStatus);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### 捕获预授权支付
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
const result = await waffo.order.capture({
|
|
226
|
-
acquiringOrderId: 'A202512230000001',
|
|
227
|
-
merchantId: 'your-merchant-id',
|
|
228
|
-
captureAmount: '100000',
|
|
229
|
-
// captureRequestedAt 为可选参数,默认为当前时间
|
|
230
|
-
});
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### 创建订阅
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
const result = await waffo.subscription.create({
|
|
237
|
-
subscriptionRequest: 'SUB_REQ_001',
|
|
238
|
-
merchantSubscriptionId: 'MERCHANT_SUB_001',
|
|
239
|
-
currency: CurrencyCode.PHP,
|
|
240
|
-
amount: '100',
|
|
241
|
-
productInfo: {
|
|
242
|
-
periodType: PeriodType.MONTHLY,
|
|
243
|
-
periodInterval: '1',
|
|
244
|
-
numberOfPeriod: '12',
|
|
245
|
-
description: '月度订阅',
|
|
246
|
-
},
|
|
247
|
-
paymentInfo: {
|
|
248
|
-
productName: ProductName.SUBSCRIPTION,
|
|
249
|
-
payMethodType: 'EWALLET',
|
|
250
|
-
payMethodName: 'GCASH',
|
|
251
|
-
},
|
|
252
|
-
merchantInfo: { merchantId: 'your-merchant-id' },
|
|
253
|
-
userInfo: {
|
|
254
|
-
userId: 'user_001',
|
|
255
|
-
userEmail: 'user@example.com',
|
|
256
|
-
},
|
|
257
|
-
goodsInfo: {
|
|
258
|
-
goodsId: 'GOODS_001',
|
|
259
|
-
goodsName: '高级会员',
|
|
260
|
-
},
|
|
261
|
-
notifyUrl: 'https://merchant.com/subscription/notify',
|
|
262
|
-
// requestedAt 为可选参数,默认为当前时间
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
if (result.success) {
|
|
266
|
-
console.log('订阅创建成功:', result.data);
|
|
267
|
-
// 引导用户完成订阅签约
|
|
268
|
-
if (result.data?.subscriptionAction?.webUrl) {
|
|
269
|
-
window.location.href = result.data.subscriptionAction.webUrl;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### 查询订阅状态
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
const result = await waffo.subscription.inquiry({
|
|
278
|
-
merchantId: 'your-merchant-id',
|
|
279
|
-
subscriptionId: 'SUB_202512230000001',
|
|
280
|
-
paymentDetails: '1', // 包含支付历史
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (result.success) {
|
|
284
|
-
console.log('订阅状态:', result.data?.subscriptionStatus);
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### 取消订阅
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
const result = await waffo.subscription.cancel({
|
|
292
|
-
merchantId: 'your-merchant-id',
|
|
293
|
-
subscriptionId: 'SUB_202512230000001',
|
|
294
|
-
// requestedAt 为可选参数,默认为当前时间
|
|
295
|
-
});
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### 获取订阅管理链接
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
const result = await waffo.subscription.manage({
|
|
302
|
-
subscriptionId: 'SUB_202512230000001',
|
|
303
|
-
// 或使用 subscriptionRequest: 'SUB_REQ_001'
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
if (result.success) {
|
|
307
|
-
console.log('管理链接:', result.data?.managementUrl);
|
|
308
|
-
console.log('过期时间:', result.data?.expiresAt);
|
|
309
|
-
// 引导用户管理其订阅
|
|
310
|
-
window.location.href = result.data?.managementUrl;
|
|
311
|
-
}
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### 查询商户配置
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
const result = await waffo.merchantConfig.inquiry({
|
|
318
|
-
merchantId: 'your-merchant-id',
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
if (result.success) {
|
|
322
|
-
console.log('每日限额:', result.data?.totalDailyLimit);
|
|
323
|
-
console.log('剩余每日限额:', result.data?.remainingDailyLimit);
|
|
324
|
-
console.log('单笔交易限额:', result.data?.transactionLimit);
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
### 查询支付方式配置
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
const result = await waffo.payMethodConfig.inquiry({
|
|
332
|
-
merchantId: 'your-merchant-id',
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
if (result.success) {
|
|
336
|
-
result.data?.payMethodDetails.forEach(method => {
|
|
337
|
-
console.log(`${method.payMethodName}: ${method.currentStatus === '1' ? '可用' : '不可用'}`);
|
|
338
|
-
if (method.fixedMaintenanceRules) {
|
|
339
|
-
console.log('维护时段:', method.fixedMaintenanceRules);
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Webhook 处理器
|
|
346
|
-
|
|
347
|
-
SDK 提供了内置的 Webhook 处理器,可自动处理签名验证、事件路由和响应签名:
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
// 在 Webhook 处理器中
|
|
351
|
-
app.post('/webhook', async (req, res) => {
|
|
352
|
-
const signature = req.headers['x-signature'] as string;
|
|
353
|
-
const body = JSON.stringify(req.body);
|
|
354
|
-
|
|
355
|
-
const result = await waffo.webhook.handle(body, signature, {
|
|
356
|
-
onPayment: async ({ notification }) => {
|
|
357
|
-
console.log('支付状态:', notification.result.orderStatus);
|
|
358
|
-
// 处理支付通知
|
|
359
|
-
},
|
|
360
|
-
onRefund: async ({ notification }) => {
|
|
361
|
-
console.log('退款状态:', notification.result.refundStatus);
|
|
362
|
-
// 处理退款通知
|
|
363
|
-
},
|
|
364
|
-
onSubscriptionStatus: async ({ notification }) => {
|
|
365
|
-
console.log('订阅状态:', notification.result.subscriptionStatus);
|
|
366
|
-
// 处理订阅状态变更
|
|
367
|
-
},
|
|
368
|
-
onSubscriptionPayment: async ({ notification }) => {
|
|
369
|
-
console.log('订阅支付:', notification.result.orderStatus);
|
|
370
|
-
// 处理订阅周期性支付
|
|
371
|
-
},
|
|
372
|
-
onError: async (error) => {
|
|
373
|
-
console.error('Webhook 错误:', error.message);
|
|
374
|
-
},
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
return res.json(result.response);
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### 手动 Webhook 签名验证
|
|
382
|
-
|
|
383
|
-
如需更细粒度的控制,可以使用底层 Webhook 工具函数:
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
import {
|
|
387
|
-
verifyWebhookSignature,
|
|
388
|
-
buildSuccessResponse,
|
|
389
|
-
buildFailedResponse,
|
|
390
|
-
isPaymentNotification,
|
|
391
|
-
isRefundNotification,
|
|
392
|
-
} from '@waffo/waffo-node';
|
|
393
|
-
|
|
394
|
-
// 在 Webhook 处理器中
|
|
395
|
-
app.post('/webhook', (req, res) => {
|
|
396
|
-
const signature = req.headers['x-signature'];
|
|
397
|
-
const body = JSON.stringify(req.body);
|
|
398
|
-
|
|
399
|
-
// 验证签名
|
|
400
|
-
const verifyResult = verifyWebhookSignature(body, signature, waffoPublicKey);
|
|
401
|
-
|
|
402
|
-
if (!verifyResult.valid) {
|
|
403
|
-
return res.json(buildFailedResponse('签名验证失败', merchantPrivateKey));
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// 根据类型处理通知
|
|
407
|
-
const notification = verifyResult.notification;
|
|
408
|
-
|
|
409
|
-
if (isPaymentNotification(notification)) {
|
|
410
|
-
// 处理支付通知
|
|
411
|
-
console.log('支付状态:', notification.result.orderStatus);
|
|
412
|
-
} else if (isRefundNotification(notification)) {
|
|
413
|
-
// 处理退款通知
|
|
414
|
-
console.log('退款状态:', notification.result.refundStatus);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// 返回成功响应
|
|
418
|
-
return res.json(buildSuccessResponse(merchantPrivateKey));
|
|
419
|
-
});
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### 直接 HTTP 客户端访问
|
|
423
|
-
|
|
424
|
-
用于 SDK 方法未覆盖的自定义 API 请求:
|
|
425
|
-
|
|
426
|
-
```typescript
|
|
427
|
-
const response = await waffo.httpClient.post<CustomResponseType>('/custom/endpoint', {
|
|
428
|
-
body: { key: 'value' }
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
if (response.success) {
|
|
432
|
-
console.log(response.data);
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
## 配置选项
|
|
437
|
-
|
|
438
|
-
| 选项 | 类型 | 必填 | 默认值 | 描述 |
|
|
439
|
-
|------|------|------|--------|------|
|
|
440
|
-
| `apiKey` | string | 是 | - | Waffo 提供的 API 密钥 |
|
|
441
|
-
| `privateKey` | string | 是 | - | Base64 编码的 PKCS8 私钥 |
|
|
442
|
-
| `waffoPublicKey` | string | 否 | 内置公钥 | 用于响应验证的自定义 Waffo 公钥 |
|
|
443
|
-
| `environment` | Environment | 否 | PRODUCTION | API 环境(SANDBOX 或 PRODUCTION) |
|
|
444
|
-
| `timeout` | number | 否 | 30000 | 请求超时时间(毫秒) |
|
|
445
|
-
| `logger` | Logger | 否 | - | 调试用的日志实例(可使用 `console`) |
|
|
446
|
-
|
|
447
|
-
## API 响应格式
|
|
448
|
-
|
|
449
|
-
所有 API 方法返回 `ApiResponse<T>` 对象:
|
|
450
|
-
|
|
451
|
-
```typescript
|
|
452
|
-
interface ApiResponse<T> {
|
|
453
|
-
success: boolean; // 请求是否成功
|
|
454
|
-
statusCode: number; // HTTP 状态码
|
|
455
|
-
data?: T; // 响应数据(成功时)
|
|
456
|
-
error?: string; // 错误信息(失败时)
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
## 类型定义
|
|
461
|
-
|
|
462
|
-
SDK 导出完整的 TypeScript 类型定义,包括:
|
|
463
|
-
|
|
464
|
-
- `Environment` - SDK 环境枚举
|
|
465
|
-
- `CountryCode` - ISO 3166-1 alpha-3 国家代码
|
|
466
|
-
- `CurrencyCode` - ISO 4217 货币代码
|
|
467
|
-
- `ProductName` - 支付产品类型枚举(ONE_TIME_PAYMENT、SUBSCRIPTION)
|
|
468
|
-
- `payMethodType` - 支付方式类别(字符串: "EWALLET"、"CREDITCARD"、"BANKTRANSFER"、"ONLINE_BANKING"、"DIGITAL_BANKING"、"OTC"、"DEBITCARD")
|
|
469
|
-
- `payMethodName` - 具体支付方式(字符串: "OVO"、"DANA"、"GOPAY"、"GCASH"、"CC_VISA"、"CC_MASTERCARD"、"VA_BCA"、"VA_BNI" 等)
|
|
470
|
-
- `OrderStatus` - 订单状态枚举(PAY_IN_PROGRESS、AUTHORIZATION_REQUIRED、PAY_SUCCESS、ORDER_CLOSE 等)
|
|
471
|
-
- `RefundStatus` - 退款状态枚举(REFUND_IN_PROGRESS、ORDER_PARTIALLY_REFUNDED、ORDER_FULLY_REFUNDED、ORDER_REFUND_FAILED)
|
|
472
|
-
- `SubscriptionStatus` - 订阅状态枚举(AUTHORIZATION_REQUIRED、ACTIVE、PAUSED、MERCHANT_CANCELLED 等)
|
|
473
|
-
- `PeriodType` - 订阅周期类型枚举(DAILY、WEEKLY、MONTHLY、YEARLY)
|
|
474
|
-
- `UserTerminalType` - 用户终端类型枚举(WEB、APP、IN_WALLET_APP、IN_MINI_PROGRAM)
|
|
475
|
-
- 所有 API 操作的请求/响应接口
|
|
476
|
-
|
|
477
|
-
## 开发
|
|
478
|
-
|
|
479
|
-
```bash
|
|
480
|
-
# 安装依赖
|
|
481
|
-
npm install
|
|
482
|
-
|
|
483
|
-
# 构建(同时生成 ESM 和 CJS)
|
|
484
|
-
npm run build
|
|
485
|
-
|
|
486
|
-
# 运行测试
|
|
487
|
-
npm test
|
|
488
|
-
|
|
489
|
-
# 运行测试并生成覆盖率报告
|
|
490
|
-
npm run test:coverage
|
|
491
|
-
|
|
492
|
-
# 监听模式运行测试
|
|
493
|
-
npm run test:watch
|
|
494
|
-
|
|
495
|
-
# 代码检查
|
|
496
|
-
npm run lint
|
|
497
|
-
|
|
498
|
-
# 代码检查并自动修复
|
|
499
|
-
npm run lint:fix
|
|
500
|
-
|
|
501
|
-
# 格式化代码
|
|
502
|
-
npm run format
|
|
503
|
-
|
|
504
|
-
# 检查代码格式
|
|
505
|
-
npm run format:check
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### 代码质量
|
|
509
|
-
|
|
510
|
-
本项目使用以下工具:
|
|
511
|
-
- **ESLint** - 代码检查,支持 TypeScript
|
|
512
|
-
- **Prettier** - 代码格式化
|
|
513
|
-
- **Husky** - Git 钩子
|
|
514
|
-
- **lint-staged** - 对暂存文件运行检查
|
|
515
|
-
|
|
516
|
-
提交代码时,pre-commit 钩子会自动对暂存的 `.ts` 文件运行 ESLint 和 Prettier。
|
|
517
|
-
|
|
518
|
-
### 运行 E2E 测试
|
|
519
|
-
|
|
520
|
-
E2E 测试需要 Waffo 沙箱环境凭证。SDK 支持多个商户配置,用于不同的测试场景,并基于官方 Waffo 测试用例文档提供全面的测试覆盖。
|
|
521
|
-
|
|
522
|
-
```bash
|
|
523
|
-
# 复制模板文件并填写凭证
|
|
524
|
-
cp .env.template .env
|
|
525
|
-
# 编辑 .env 文件,填入你的凭证
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
环境变量说明:
|
|
529
|
-
|
|
530
|
-
| 变量名 | 必填 | 描述 |
|
|
531
|
-
|--------|------|------|
|
|
532
|
-
| `WAFFO_PUBLIC_KEY` | 否 | 用于签名验证的 Waffo 公钥(通用) |
|
|
533
|
-
| `ACQUIRING_MERCHANT_ID` | 是* | 支付/订单测试用的商户 ID |
|
|
534
|
-
| `ACQUIRING_API_KEY` | 是* | 支付/订单测试用的 API 密钥 |
|
|
535
|
-
| `ACQUIRING_MERCHANT_PRIVATE_KEY` | 是* | 支付/订单测试用的私钥 |
|
|
536
|
-
| `SUBSCRIPTION_MERCHANT_ID` | 是** | 订阅测试用的商户 ID |
|
|
537
|
-
| `SUBSCRIPTION_API_KEY` | 是** | 订阅测试用的 API 密钥 |
|
|
538
|
-
| `SUBSCRIPTION_MERCHANT_PRIVATE_KEY` | 是** | 订阅测试用的私钥 |
|
|
539
|
-
|
|
540
|
-
\* 运行支付/订单 E2E 测试时必填
|
|
541
|
-
\** 运行订阅 E2E 测试时必填
|
|
542
|
-
|
|
543
|
-
**E2E 测试覆盖范围:**
|
|
544
|
-
|
|
545
|
-
| 模块 | 测试用例 |
|
|
546
|
-
|------|----------|
|
|
547
|
-
| 创建订单 | 支付成功/失败、渠道拒绝 (C0005)、幂等错误 (A0011)、系统错误 (C0001)、未知状态 (E0001) |
|
|
548
|
-
| 查询订单 | 支付前/支付后查询 |
|
|
549
|
-
| 取消订单 | 支付前取消、渠道不支持 (A0015)、已支付 (A0013) |
|
|
550
|
-
| 退款订单 | 全额/部分退款、参数校验 (A0003)、退款规则 (A0014) |
|
|
551
|
-
| 创建订阅 | 订阅成功/失败、下期支付模拟 |
|
|
552
|
-
| 取消订阅 | 商户发起取消 |
|
|
553
|
-
| Webhook 通知 | 支付、退款、订阅通知签名验证 |
|
|
554
|
-
|
|
555
|
-
**沙箱环境金额触发规则:**
|
|
556
|
-
|
|
557
|
-
| 金额模式 | 错误码 | 描述 |
|
|
558
|
-
|----------|--------|------|
|
|
559
|
-
| 9, 90, 990, 1990, 19990 | C0005 | 渠道拒绝 |
|
|
560
|
-
| 9.1, 91, 991, 1991, 19991 | C0001 | 系统错误 |
|
|
561
|
-
| 9.2, 92, 992, 1992, 19992 | E0001 | 未知状态 |
|
|
562
|
-
| 9.3, 93, 993, 1993, 19993 | C0001 | 取消系统错误 |
|
|
563
|
-
| 9.4, 94, 994, 1994, 19994 | E0001 | 取消未知状态 |
|
|
564
|
-
| 9.5, 95, 995, 1995, 19995 | C0001 | 退款系统错误 |
|
|
565
|
-
| 9.6, 96, 996, 1996, 199996 | E0001 | 退款未知状态 |
|
|
566
|
-
|
|
567
|
-
```bash
|
|
568
|
-
# 运行所有测试
|
|
569
|
-
npm test
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
## 构建产物
|
|
573
|
-
|
|
574
|
-
SDK 使用 [tsup](https://tsup.egoist.dev/) 构建,输出以下文件:
|
|
575
|
-
|
|
576
|
-
| 文件 | 格式 | 描述 |
|
|
577
|
-
|------|------|------|
|
|
578
|
-
| `dist/index.js` | CommonJS | 用于 `require()` 导入 |
|
|
579
|
-
| `dist/index.mjs` | ESM | 用于 `import` 语句 |
|
|
580
|
-
| `dist/index.d.ts` | TypeScript | 类型声明文件 |
|