@waffo/waffo-node 2.0.2
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 +21 -0
- package/README.ja.md +580 -0
- package/README.md +580 -0
- package/README.zh-CN.md +580 -0
- package/dist/index.d.mts +4639 -0
- package/dist/index.d.ts +4639 -0
- package/dist/index.js +1795 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1750 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
# Waffo PSP SDK for Node.js
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for Waffo PSP (Payment Service Provider) acquiring services. This SDK provides secure API communication with RSA signing and comprehensive type definitions for payment acquiring operations.
|
|
4
|
+
|
|
5
|
+
**Language**: [English](./README.md) | [中文](./README.zh-CN.md) | [日本語](./README.ja.md)
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import {
|
|
11
|
+
Waffo,
|
|
12
|
+
Environment,
|
|
13
|
+
CurrencyCode,
|
|
14
|
+
ProductName,
|
|
15
|
+
} from '@waffo/waffo-node';
|
|
16
|
+
|
|
17
|
+
// 1. Initialize 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. Create an order
|
|
25
|
+
const result = await waffo.order.create({
|
|
26
|
+
paymentRequestId: 'REQ_001',
|
|
27
|
+
merchantOrderId: 'ORDER_001',
|
|
28
|
+
orderCurrency: CurrencyCode.IDR,
|
|
29
|
+
orderAmount: '100000',
|
|
30
|
+
orderDescription: 'Product purchase',
|
|
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. Handle response
|
|
45
|
+
if (result.success) {
|
|
46
|
+
console.log('Order created:', result.data);
|
|
47
|
+
// Redirect user to payment page
|
|
48
|
+
if (result.data?.orderAction) {
|
|
49
|
+
const action = JSON.parse(result.data.orderAction);
|
|
50
|
+
window.location.href = action.webUrl;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> **Tip**: Need to generate a new RSA key pair? Use `Waffo.generateKeyPair()` to create one:
|
|
56
|
+
> ```typescript
|
|
57
|
+
> const keyPair = Waffo.generateKeyPair();
|
|
58
|
+
> console.log(keyPair.privateKey); // Keep this secure, use for SDK initialization
|
|
59
|
+
> console.log(keyPair.publicKey); // Share this with Waffo
|
|
60
|
+
> ```
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- RSA-2048 request signing and response verification
|
|
65
|
+
- Full TypeScript support with comprehensive type definitions
|
|
66
|
+
- Zero production dependencies (uses only Node.js built-in `crypto` module)
|
|
67
|
+
- Support for Sandbox and Production environments
|
|
68
|
+
- Dual ESM/CommonJS module support
|
|
69
|
+
- Order management (create, query, cancel, refund, capture)
|
|
70
|
+
- Subscription management (create, query, cancel, manage)
|
|
71
|
+
- Refund status inquiry
|
|
72
|
+
- Merchant configuration inquiry (transaction limits, daily limits)
|
|
73
|
+
- Payment method configuration inquiry (availability, maintenance schedules)
|
|
74
|
+
- Webhook handler with automatic signature verification and event routing
|
|
75
|
+
- Webhook signature verification utilities
|
|
76
|
+
- Direct HTTP client access for custom API requests
|
|
77
|
+
- Automatic timestamp defaults for request parameters
|
|
78
|
+
|
|
79
|
+
## Automatic Timestamp Defaults
|
|
80
|
+
|
|
81
|
+
All timestamp parameters (`orderRequestedAt`, `requestedAt`, `captureRequestedAt`) are **optional** and will automatically default to the current time (`new Date().toISOString()`) if not provided:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Timestamp is automatically set to current time
|
|
85
|
+
await waffo.order.create({
|
|
86
|
+
paymentRequestId: 'REQ_001',
|
|
87
|
+
merchantOrderId: 'ORDER_001',
|
|
88
|
+
// ... other required fields
|
|
89
|
+
// orderRequestedAt is automatically set
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Or explicitly provide a custom timestamp
|
|
93
|
+
await waffo.order.create({
|
|
94
|
+
paymentRequestId: 'REQ_001',
|
|
95
|
+
merchantOrderId: 'ORDER_001',
|
|
96
|
+
orderRequestedAt: '2025-01-01T00:00:00.000Z', // Custom timestamp
|
|
97
|
+
// ... other required fields
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This applies to:
|
|
102
|
+
- `CreateOrderParams.orderRequestedAt`
|
|
103
|
+
- `CancelOrderParams.orderRequestedAt`
|
|
104
|
+
- `RefundOrderParams.requestedAt`
|
|
105
|
+
- `CaptureOrderParams.captureRequestedAt`
|
|
106
|
+
- `CreateSubscriptionParams.requestedAt`
|
|
107
|
+
- `CancelSubscriptionParams.requestedAt`
|
|
108
|
+
|
|
109
|
+
## Installation
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install @waffo/waffo-node
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Usage
|
|
116
|
+
|
|
117
|
+
### Initialize the 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, // or Environment.PRODUCTION
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Generate RSA Key Pair
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { Waffo } from '@waffo/waffo-node';
|
|
133
|
+
|
|
134
|
+
const keyPair = Waffo.generateKeyPair();
|
|
135
|
+
console.log(keyPair.privateKey); // Base64 encoded PKCS8 private key
|
|
136
|
+
console.log(keyPair.publicKey); // Base64 encoded X509 public key
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Create an Order
|
|
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: 'Product purchase',
|
|
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('Order created:', result.data);
|
|
167
|
+
} else {
|
|
168
|
+
console.error('Error:', result.error);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Query Order Status
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const result = await waffo.order.inquiry({
|
|
176
|
+
acquiringOrderId: 'A202512230000001',
|
|
177
|
+
// or use paymentRequestId: 'REQ_001'
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (result.success) {
|
|
181
|
+
console.log('Order status:', result.data?.orderStatus);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Cancel an Order
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const result = await waffo.order.cancel({
|
|
189
|
+
acquiringOrderId: 'A202512230000001',
|
|
190
|
+
merchantId: 'your-merchant-id',
|
|
191
|
+
// orderRequestedAt is optional, defaults to current time
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Refund an Order
|
|
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: 'Customer requested refund',
|
|
204
|
+
refundNotifyUrl: 'https://merchant.com/refund-notify',
|
|
205
|
+
// requestedAt is optional, defaults to current time
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Query Refund Status
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const result = await waffo.refund.inquiry({
|
|
213
|
+
refundRequestId: 'REFUND_001',
|
|
214
|
+
// or use acquiringRefundOrderId: 'R202512230000001'
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (result.success) {
|
|
218
|
+
console.log('Refund status:', result.data?.refundStatus);
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Capture a Pre-authorized Payment
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const result = await waffo.order.capture({
|
|
226
|
+
acquiringOrderId: 'A202512230000001',
|
|
227
|
+
merchantId: 'your-merchant-id',
|
|
228
|
+
captureAmount: '100000',
|
|
229
|
+
// captureRequestedAt is optional, defaults to current time
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Create a Subscription
|
|
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: 'Monthly subscription',
|
|
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: 'Premium Plan',
|
|
260
|
+
},
|
|
261
|
+
notifyUrl: 'https://merchant.com/subscription/notify',
|
|
262
|
+
// requestedAt is optional, defaults to current time
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (result.success) {
|
|
266
|
+
console.log('Subscription created:', result.data);
|
|
267
|
+
// Redirect user to complete subscription signing
|
|
268
|
+
if (result.data?.subscriptionAction?.webUrl) {
|
|
269
|
+
window.location.href = result.data.subscriptionAction.webUrl;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Query Subscription Status
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const result = await waffo.subscription.inquiry({
|
|
278
|
+
merchantId: 'your-merchant-id',
|
|
279
|
+
subscriptionId: 'SUB_202512230000001',
|
|
280
|
+
paymentDetails: '1', // Include payment history
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (result.success) {
|
|
284
|
+
console.log('Subscription status:', result.data?.subscriptionStatus);
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Cancel a Subscription
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
const result = await waffo.subscription.cancel({
|
|
292
|
+
merchantId: 'your-merchant-id',
|
|
293
|
+
subscriptionId: 'SUB_202512230000001',
|
|
294
|
+
// requestedAt is optional, defaults to current time
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Get Subscription Management URL
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const result = await waffo.subscription.manage({
|
|
302
|
+
subscriptionId: 'SUB_202512230000001',
|
|
303
|
+
// or use subscriptionRequest: 'SUB_REQ_001'
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (result.success) {
|
|
307
|
+
console.log('Management URL:', result.data?.managementUrl);
|
|
308
|
+
console.log('Expires at:', result.data?.expiresAt);
|
|
309
|
+
// Redirect user to manage their subscription
|
|
310
|
+
window.location.href = result.data?.managementUrl;
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Query Merchant Configuration
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const result = await waffo.merchantConfig.inquiry({
|
|
318
|
+
merchantId: 'your-merchant-id',
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (result.success) {
|
|
322
|
+
console.log('Daily Limit:', result.data?.totalDailyLimit);
|
|
323
|
+
console.log('Remaining Daily Limit:', result.data?.remainingDailyLimit);
|
|
324
|
+
console.log('Transaction Limit:', result.data?.transactionLimit);
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Query Payment Method Configuration
|
|
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' ? 'Available' : 'Unavailable'}`);
|
|
338
|
+
if (method.fixedMaintenanceRules) {
|
|
339
|
+
console.log('Maintenance periods:', method.fixedMaintenanceRules);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Webhook Handler
|
|
346
|
+
|
|
347
|
+
The SDK provides a built-in webhook handler that automatically handles signature verification, event routing, and response signing:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// In your webhook handler
|
|
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('Payment status:', notification.result.orderStatus);
|
|
358
|
+
// Process payment notification
|
|
359
|
+
},
|
|
360
|
+
onRefund: async ({ notification }) => {
|
|
361
|
+
console.log('Refund status:', notification.result.refundStatus);
|
|
362
|
+
// Process refund notification
|
|
363
|
+
},
|
|
364
|
+
onSubscriptionStatus: async ({ notification }) => {
|
|
365
|
+
console.log('Subscription status:', notification.result.subscriptionStatus);
|
|
366
|
+
// Process subscription status change
|
|
367
|
+
},
|
|
368
|
+
onSubscriptionPayment: async ({ notification }) => {
|
|
369
|
+
console.log('Subscription payment:', notification.result.orderStatus);
|
|
370
|
+
// Process subscription recurring payment
|
|
371
|
+
},
|
|
372
|
+
onError: async (error) => {
|
|
373
|
+
console.error('Webhook error:', error.message);
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
return res.json(result.response);
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Manual Webhook Signature Verification
|
|
382
|
+
|
|
383
|
+
For more control, you can use the low-level webhook utilities:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
import {
|
|
387
|
+
verifyWebhookSignature,
|
|
388
|
+
buildSuccessResponse,
|
|
389
|
+
buildFailedResponse,
|
|
390
|
+
isPaymentNotification,
|
|
391
|
+
isRefundNotification,
|
|
392
|
+
} from '@waffo/waffo-node';
|
|
393
|
+
|
|
394
|
+
// In your webhook handler
|
|
395
|
+
app.post('/webhook', (req, res) => {
|
|
396
|
+
const signature = req.headers['x-signature'];
|
|
397
|
+
const body = JSON.stringify(req.body);
|
|
398
|
+
|
|
399
|
+
// Verify signature
|
|
400
|
+
const verifyResult = verifyWebhookSignature(body, signature, waffoPublicKey);
|
|
401
|
+
|
|
402
|
+
if (!verifyResult.valid) {
|
|
403
|
+
return res.json(buildFailedResponse('Signature verification failed', merchantPrivateKey));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Handle notification based on type
|
|
407
|
+
const notification = verifyResult.notification;
|
|
408
|
+
|
|
409
|
+
if (isPaymentNotification(notification)) {
|
|
410
|
+
// Handle payment notification
|
|
411
|
+
console.log('Payment status:', notification.result.orderStatus);
|
|
412
|
+
} else if (isRefundNotification(notification)) {
|
|
413
|
+
// Handle refund notification
|
|
414
|
+
console.log('Refund status:', notification.result.refundStatus);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Return success response
|
|
418
|
+
return res.json(buildSuccessResponse(merchantPrivateKey));
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Direct HTTP Client Access
|
|
423
|
+
|
|
424
|
+
For custom API requests not covered by the SDK methods:
|
|
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
|
+
## Configuration Options
|
|
437
|
+
|
|
438
|
+
| Option | Type | Required | Default | Description |
|
|
439
|
+
|--------|------|----------|---------|-------------|
|
|
440
|
+
| `apiKey` | string | Yes | - | API key provided by Waffo |
|
|
441
|
+
| `privateKey` | string | Yes | - | Base64 encoded PKCS8 private key |
|
|
442
|
+
| `waffoPublicKey` | string | No | Built-in | Custom Waffo public key for response verification |
|
|
443
|
+
| `environment` | Environment | No | PRODUCTION | API environment (SANDBOX or PRODUCTION) |
|
|
444
|
+
| `timeout` | number | No | 30000 | Request timeout in milliseconds |
|
|
445
|
+
| `logger` | Logger | No | - | Logger instance for debugging (can use `console`) |
|
|
446
|
+
|
|
447
|
+
## API Response Format
|
|
448
|
+
|
|
449
|
+
All API methods return an `ApiResponse<T>` object:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
interface ApiResponse<T> {
|
|
453
|
+
success: boolean; // Whether the request was successful
|
|
454
|
+
statusCode: number; // HTTP status code
|
|
455
|
+
data?: T; // Response data (on success)
|
|
456
|
+
error?: string; // Error message (on failure)
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Type Definitions
|
|
461
|
+
|
|
462
|
+
The SDK exports comprehensive TypeScript types including:
|
|
463
|
+
|
|
464
|
+
- `Environment` - SDK environment enum
|
|
465
|
+
- `CountryCode` - ISO 3166-1 alpha-3 country codes
|
|
466
|
+
- `CurrencyCode` - ISO 4217 currency codes
|
|
467
|
+
- `ProductName` - Payment product type enum (ONE_TIME_PAYMENT, SUBSCRIPTION)
|
|
468
|
+
- `payMethodType` - Payment method category (string: "EWALLET", "CREDITCARD", "BANKTRANSFER", "ONLINE_BANKING", "DIGITAL_BANKING", "OTC", "DEBITCARD")
|
|
469
|
+
- `payMethodName` - Specific payment method (string: "OVO", "DANA", "GOPAY", "GCASH", "CC_VISA", "CC_MASTERCARD", "VA_BCA", "VA_BNI", etc.)
|
|
470
|
+
- `OrderStatus` - Order status enum (PAY_IN_PROGRESS, AUTHORIZATION_REQUIRED, PAY_SUCCESS, ORDER_CLOSE, etc.)
|
|
471
|
+
- `RefundStatus` - Refund status enum (REFUND_IN_PROGRESS, ORDER_PARTIALLY_REFUNDED, ORDER_FULLY_REFUNDED, ORDER_REFUND_FAILED)
|
|
472
|
+
- `SubscriptionStatus` - Subscription status enum (AUTHORIZATION_REQUIRED, ACTIVE, PAUSED, MERCHANT_CANCELLED, etc.)
|
|
473
|
+
- `PeriodType` - Subscription period type enum (DAILY, WEEKLY, MONTHLY, YEARLY)
|
|
474
|
+
- `UserTerminalType` - User terminal type enum (WEB, APP, IN_WALLET_APP, IN_MINI_PROGRAM)
|
|
475
|
+
- Request/Response interfaces for all API operations
|
|
476
|
+
|
|
477
|
+
## Development
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
# Install dependencies
|
|
481
|
+
npm install
|
|
482
|
+
|
|
483
|
+
# Build (generates both ESM and CJS)
|
|
484
|
+
npm run build
|
|
485
|
+
|
|
486
|
+
# Run tests
|
|
487
|
+
npm test
|
|
488
|
+
|
|
489
|
+
# Run tests with coverage
|
|
490
|
+
npm run test:coverage
|
|
491
|
+
|
|
492
|
+
# Run tests in watch mode
|
|
493
|
+
npm run test:watch
|
|
494
|
+
|
|
495
|
+
# Lint code
|
|
496
|
+
npm run lint
|
|
497
|
+
|
|
498
|
+
# Lint and auto-fix
|
|
499
|
+
npm run lint:fix
|
|
500
|
+
|
|
501
|
+
# Format code
|
|
502
|
+
npm run format
|
|
503
|
+
|
|
504
|
+
# Check formatting
|
|
505
|
+
npm run format:check
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Code Quality
|
|
509
|
+
|
|
510
|
+
This project uses:
|
|
511
|
+
- **ESLint** - Code linting with TypeScript support
|
|
512
|
+
- **Prettier** - Code formatting
|
|
513
|
+
- **Husky** - Git hooks
|
|
514
|
+
- **lint-staged** - Run linters on staged files
|
|
515
|
+
|
|
516
|
+
Pre-commit hooks automatically run ESLint and Prettier on staged `.ts` files.
|
|
517
|
+
|
|
518
|
+
### Running E2E Tests
|
|
519
|
+
|
|
520
|
+
E2E tests require Waffo sandbox credentials. The SDK supports multiple merchant configurations for different test scenarios and provides comprehensive test coverage based on the official Waffo test cases document.
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
# Copy the template and fill in your credentials
|
|
524
|
+
cp .env.template .env
|
|
525
|
+
# Edit .env with your credentials
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Environment variables:
|
|
529
|
+
|
|
530
|
+
| Variable | Required | Description |
|
|
531
|
+
|----------|----------|-------------|
|
|
532
|
+
| `WAFFO_PUBLIC_KEY` | No | Waffo public key for signature verification (shared) |
|
|
533
|
+
| `ACQUIRING_MERCHANT_ID` | Yes* | Merchant ID for payment/order tests |
|
|
534
|
+
| `ACQUIRING_API_KEY` | Yes* | API key for payment/order tests |
|
|
535
|
+
| `ACQUIRING_MERCHANT_PRIVATE_KEY` | Yes* | Private key for payment/order tests |
|
|
536
|
+
| `SUBSCRIPTION_MERCHANT_ID` | Yes** | Merchant ID for subscription tests |
|
|
537
|
+
| `SUBSCRIPTION_API_KEY` | Yes** | API key for subscription tests |
|
|
538
|
+
| `SUBSCRIPTION_MERCHANT_PRIVATE_KEY` | Yes** | Private key for subscription tests |
|
|
539
|
+
|
|
540
|
+
\* Required for running acquiring/payment E2E tests
|
|
541
|
+
\** Required for running subscription E2E tests
|
|
542
|
+
|
|
543
|
+
**E2E Test Coverage:**
|
|
544
|
+
|
|
545
|
+
| Module | Test Cases |
|
|
546
|
+
|--------|------------|
|
|
547
|
+
| Create Order | Payment success/failure, channel rejection (C0005), idempotency error (A0011), system error (C0001), unknown status (E0001) |
|
|
548
|
+
| Inquiry Order | Query before/after payment |
|
|
549
|
+
| Cancel Order | Cancel before payment, channel not supported (A0015), already paid (A0013) |
|
|
550
|
+
| Refund Order | Full/partial refund, parameter validation (A0003), refund rules (A0014) |
|
|
551
|
+
| Create Subscription | Subscription success/failure, next period payment simulation |
|
|
552
|
+
| Cancel Subscription | Merchant-initiated cancellation |
|
|
553
|
+
| Webhook Notifications | Signature verification for payment, refund, and subscription notifications |
|
|
554
|
+
|
|
555
|
+
**Sandbox Amount Triggers:**
|
|
556
|
+
|
|
557
|
+
| Amount Pattern | Error Code | Description |
|
|
558
|
+
|----------------|------------|-------------|
|
|
559
|
+
| 9, 90, 990, 1990, 19990 | C0005 | Channel rejection |
|
|
560
|
+
| 9.1, 91, 991, 1991, 19991 | C0001 | System error |
|
|
561
|
+
| 9.2, 92, 992, 1992, 19992 | E0001 | Unknown status |
|
|
562
|
+
| 9.3, 93, 993, 1993, 19993 | C0001 | Cancel system error |
|
|
563
|
+
| 9.4, 94, 994, 1994, 19994 | E0001 | Cancel unknown status |
|
|
564
|
+
| 9.5, 95, 995, 1995, 19995 | C0001 | Refund system error |
|
|
565
|
+
| 9.6, 96, 996, 1996, 199996 | E0001 | Refund unknown status |
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# Run all tests
|
|
569
|
+
npm test
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
## Build Output
|
|
573
|
+
|
|
574
|
+
The SDK is built using [tsup](https://tsup.egoist.dev/) and outputs:
|
|
575
|
+
|
|
576
|
+
| File | Format | Description |
|
|
577
|
+
|------|--------|-------------|
|
|
578
|
+
| `dist/index.js` | CommonJS | For `require()` imports |
|
|
579
|
+
| `dist/index.mjs` | ESM | For `import` statements |
|
|
580
|
+
| `dist/index.d.ts` | TypeScript | Type declarations |
|