@waffo/waffo-integrate 1.0.0

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/docs/INDEX.md ADDED
@@ -0,0 +1,50 @@
1
+ # Waffo Integration Knowledge Base
2
+
3
+ Extended documentation for common scenarios, troubleshooting, and best practices when integrating Waffo Payment SDK.
4
+
5
+ > **For AI assistants**: When a developer's question is not covered by `SKILL.md` or `references/`, search this index for relevant articles and read the linked document.
6
+
7
+ ---
8
+
9
+ ## FAQ
10
+
11
+ | Topic | Document | Description |
12
+ |-------|----------|-------------|
13
+ | _Coming soon_ | | |
14
+
15
+ <!-- Example:
16
+ | Subscription billing retry | [faq/subscription-retry.md](faq/subscription-retry.md) | How Waffo handles failed recurring payments and retry logic |
17
+ | Apple Pay integration | [faq/applepay-setup.md](faq/applepay-setup.md) | Domain verification, merchant ID setup, and sandbox testing |
18
+ -->
19
+
20
+ ## Troubleshooting
21
+
22
+ | Symptom | Document | Description |
23
+ |---------|----------|-------------|
24
+ | _Coming soon_ | | |
25
+
26
+ <!-- Example:
27
+ | A0003 error on subscription create | [troubleshooting/a0003-missing-fields.md](troubleshooting/a0003-missing-fields.md) | Required fields that openapi.json doesn't mark as required |
28
+ | Webhook not received | [troubleshooting/webhook-not-received.md](troubleshooting/webhook-not-received.md) | Common causes: URL not public, signature mismatch, firewall |
29
+ -->
30
+
31
+ ## Best Practices
32
+
33
+ | Topic | Document | Description |
34
+ |-------|----------|-------------|
35
+ | _Coming soon_ | | |
36
+
37
+ <!-- Example:
38
+ | Idempotency design | [best-practices/idempotency.md](best-practices/idempotency.md) | Request ID generation, retry strategy, deduplication |
39
+ | Multi-currency checkout | [best-practices/multi-currency.md](best-practices/multi-currency.md) | orderCurrency vs userCurrency, FX handling |
40
+ -->
41
+
42
+ ---
43
+
44
+ ## Contributing
45
+
46
+ To add a new article:
47
+
48
+ 1. Create a `.md` file in the appropriate subdirectory (`faq/`, `troubleshooting/`, `best-practices/`)
49
+ 2. Add an entry to the table above with a link and one-line description
50
+ 3. Submit a PR
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@waffo/waffo-integrate",
3
+ "version": "1.0.0",
4
+ "description": "Claude Code / Cursor skill for integrating Waffo Payment SDK (Node.js / Java / Go)",
5
+ "bin": {
6
+ "waffo-integrate": "./bin/install.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "SKILL.md",
11
+ "references/",
12
+ "docs/"
13
+ ],
14
+ "keywords": [
15
+ "waffo",
16
+ "payment",
17
+ "sdk",
18
+ "claude-code",
19
+ "cursor",
20
+ "skill",
21
+ "integration"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/waffo-com/waffo-integrate.git"
26
+ },
27
+ "license": "MIT",
28
+ "author": "Waffo"
29
+ }
@@ -0,0 +1,539 @@
1
+ # Waffo API Contract Reference
2
+
3
+ Source: `openapi.json` (authoritative). All field names are camelCase JSON.
4
+
5
+ ## Response Envelope
6
+
7
+ All responses: `{ code: string, msg: string, data: <DataSchema> }`
8
+
9
+ ---
10
+
11
+ ## 1. Order Module
12
+
13
+ ### Order Create — `POST /api/v1/order/create`
14
+
15
+ **Request** (`AcqOrderCreateRequest`):
16
+ - `paymentRequestId`: string (required) — max 32, idempotent key
17
+ - `merchantOrderId`: string (required) — max 64
18
+ - `orderCurrency`: string (required) — max 12, e.g. "IDR"
19
+ - `orderAmount`: string (required) — max 24
20
+ - `userCurrency`: string — max 12, cross-currency only
21
+ - `orderDescription`: string (required) — max 128
22
+ - `orderRequestedAt`: string/date-time (required)
23
+ - `orderExpiredAt`: string/date-time
24
+ - `successRedirectUrl`: string — max 512
25
+ - `failedRedirectUrl`: string — max 512
26
+ - `cancelRedirectUrl`: string — max 512
27
+ - `notifyUrl`: string (required) — max 256
28
+ - `extendInfo`: string — max 128, JSON
29
+ - `merchantInfo`: MerchantInfo (required)
30
+ - `userInfo`: OrderUserInfo (required)
31
+ - `goodsInfo`: OrderGoodsInfo
32
+ - `paymentInfo`: OrderPaymentInfo (required)
33
+ - `cardInfo`: CardInfo
34
+ - `paymentTokenData`: string — max 8192, ApplePay/GooglePay
35
+ - `riskData`: OrderRiskData
36
+ - `addressInfo`: OrderAddressInfo
37
+
38
+ **Response** (`AcqOrderCreatedResponse`):
39
+ - `paymentRequestId`: string (required)
40
+ - `merchantOrderId`: string (required)
41
+ - `acquiringOrderId`: string (required)
42
+ - `orderStatus`: string (required) — "PAY_IN_PROGRESS" | "AUTHORIZATION_REQUIRED" | "AUTHED_WAITING_CAPTURE" | "PAY_SUCCESS" | "ORDER_CLOSE"
43
+ - `orderAction`: string — JSON with actionType, webUrl, deeplinkUrl, actionData
44
+
45
+ ### Order Inquiry — `POST /api/v1/order/inquiry`
46
+
47
+ **Request** (`AcqOrderInquiryRequest`): provide one of:
48
+ - `paymentRequestId`: string
49
+ - `acquiringOrderId`: string
50
+
51
+ **Response** (`AcqOrderInquiry`):
52
+ - `paymentRequestId`: string (required)
53
+ - `merchantOrderId`: string (required)
54
+ - `acquiringOrderId`: string (required)
55
+ - `orderStatus`: string (required) — same enum as Create
56
+ - `orderAction`: string
57
+ - `orderCurrency`: string (required)
58
+ - `orderAmount`: string (required)
59
+ - `finalDealAmount`: string (required)
60
+ - `orderDescription`: string (required)
61
+ - `merchantInfo`: MerchantInfo
62
+ - `userInfo`: OrderUserInfo
63
+ - `goodsInfo`: OrderGoodsInfo
64
+ - `paymentInfo`: OrderPaymentInfo
65
+ - `addressInfo`: OrderAddressInfo
66
+ - `orderRequestedAt`: string
67
+ - `orderExpiredAt`: string
68
+ - `orderUpdatedAt`: string
69
+ - `orderCompletedAt`: string
70
+ - `refundExpiryAt`: string
71
+ - `cancelRedirectUrl`: string
72
+ - `orderFailedReason`: string — JSON
73
+ - `extendInfo`: string
74
+ - `userCurrency`: string
75
+ - `subscriptionInfo`: SubscriptionInfo
76
+
77
+ ### Order Cancel — `POST /api/v1/order/cancel`
78
+
79
+ **Request** (`AcqOrderCancelRequest`): provide paymentRequestId or acquiringOrderId:
80
+ - `paymentRequestId`: string — max 32
81
+ - `acquiringOrderId`: string — max 32
82
+ - `merchantId`: string (required) — max 64
83
+ - `orderRequestedAt`: string/date-time (required)
84
+
85
+ **Response** (`AcqOrderCancelResponse`):
86
+ - `paymentRequestId`: string (required)
87
+ - `merchantOrderId`: string (required)
88
+ - `acquiringOrderId`: string (required)
89
+ - `orderStatus`: string (required) — "ORDER_CLOSE"
90
+
91
+ ---
92
+
93
+ ## 2. Refund Module
94
+
95
+ ### Order Refund — `POST /api/v1/order/refund`
96
+
97
+ **Request** (`AcqOrderRefundRequest`):
98
+ - `refundRequestId`: string (required) — max 32, idempotent key
99
+ - `acquiringOrderId`: string (required) — max 32
100
+ - `merchantRefundOrderId`: string — max 64
101
+ - `merchantId`: string (required) — max 64
102
+ - `requestedAt`: string/date-time (required)
103
+ - `refundAmount`: string (required) — min 1 char
104
+ - `refundReason`: string (required) — max 256
105
+ - `refundNotifyUrl`: string — max 256
106
+ - `extendInfo`: string — max 128, JSON
107
+ - `refundSource`: string
108
+ - `userInfo`: RefundOrderUserInfo
109
+
110
+ **Response** (`AcqOrderRefundResponse`):
111
+ - `refundRequestId`: string (required)
112
+ - `merchantRefundOrderId`: string
113
+ - `acquiringOrderId`: string (required)
114
+ - `acquiringRefundOrderId`: string (required)
115
+ - `refundAmount`: string (required)
116
+ - `refundStatus`: string (required) — "REFUND_IN_PROGRESS" | "ORDER_PARTIALLY_REFUNDED" | "ORDER_FULLY_REFUNDED" | "ORDER_REFUND_FAILED"
117
+ - `remainingRefundAmount`: string (required)
118
+ - `refundSource`: string (required) — "MERCHANT" | "RDR" | "ETHOCA"
119
+
120
+ ### Refund Inquiry — `POST /api/v1/refund/inquiry`
121
+
122
+ **Request** (`AcqOrderRefundInquiryRequest`): provide one of:
123
+ - `refundRequestId`: string
124
+ - `acquiringRefundOrderId`: string
125
+
126
+ **Response** (`AcqOrderRefundInquiry`):
127
+ - `refundRequestId`: string (required)
128
+ - `merchantRefundOrderId`: string
129
+ - `acquiringOrderId`: string (required)
130
+ - `acquiringRefundOrderId`: string (required)
131
+ - `origPaymentRequestId`: string (required)
132
+ - `refundAmount`: string (required)
133
+ - `refundStatus`: string (required) — same enum as Refund
134
+ - `refundReason`: string (required)
135
+ - `refundRequestedAt`: string (required)
136
+ - `refundUpdatedAt`: string (required)
137
+ - `refundFailedReason`: string — JSON
138
+ - `extendInfo`: string
139
+ - `userCurrency`: string
140
+ - `finalDealAmount`: string (required)
141
+ - `remainingRefundAmount`: string (required)
142
+ - `userInfo`: RefundOrderUserInfo
143
+ - `refundCompletedAt`: string
144
+ - `refundSource`: string — "MERCHANT" | "RDR" | "ETHOCA"
145
+
146
+ ---
147
+
148
+ ## 3. Subscription Module
149
+
150
+ > **IMPORTANT**: Subscription uses `currency`/`amount`; Order uses `orderCurrency`/`orderAmount`. Subscription uses `requestedAt`; Order uses `orderRequestedAt`.
151
+
152
+ ### Subscription Create — `POST /api/v1/subscription/create`
153
+
154
+ **Request** (`AcqSubscriptionCreateRequest`):
155
+ - `subscriptionRequest`: string (required) — max 32, idempotent key
156
+ - `merchantSubscriptionId`: string (required) — max 64
157
+ - `currency`: string (required) — max 12
158
+ - `amount`: string (required) — max 24
159
+ - `userCurrency`: string — max 12
160
+ - `productInfo`: ProductInfo (required)
161
+ - `merchantInfo`: MerchantInfo (required)
162
+ - `userInfo`: UserInfo (required)
163
+ - `goodsInfo`: GoodsInfo
164
+ - `addressInfo`: AddressInfo
165
+ - `paymentInfo`: PaymentInfo (required — productName, payMethodName required)
166
+ - `requestedAt`: string/date-time (required)
167
+ - `successRedirectUrl`: string (required) — max 512
168
+ - `failedRedirectUrl`: string (required) — max 512
169
+ - `cancelRedirectUrl`: string (required) — max 512
170
+ - `notifyUrl`: string (required) — max 256
171
+ - `subscriptionManagementUrl`: string (required) — max 256
172
+ - `extendInfo`: string — max 256, JSON
173
+ - `orderExpiredAt`: string/date-time
174
+ - `riskData`: RiskData
175
+ - `acqAgreement`: MerchantAcqProductAgreementEntity
176
+
177
+ **Response** (`AcqSubscriptionCreateResponse`):
178
+ - `subscriptionRequest`: string
179
+ - `merchantSubscriptionId`: string
180
+ - `subscriptionId`: string
181
+ - `payMethodSubscriptionId`: string
182
+ - `subscriptionStatus`: string — "AUTHORIZATION_REQUIRED" | "IN_PROGRESS" | "ACTIVE" | "CLOSE" | "MERCHANT_CANCELLED" | "USER_CANCELLED" | "CHANNEL_CANCELLED" | "EXPIRED"
183
+ - `subscriptionAction`: string — JSON with webUrl
184
+
185
+ ### Subscription Inquiry — `POST /api/v1/subscription/inquiry`
186
+
187
+ **Request** (`SubscriptionInquiryRequest`):
188
+ - `subscriptionRequest`: string — max 32
189
+ - `subscriptionId`: string — max 64
190
+ - `paymentDetails`: integer — 0 (default) or 1
191
+
192
+ **Response** (`SubscriptionInquiryResponse`):
193
+ - `subscriptionRequest`: string
194
+ - `merchantSubscriptionId`: string
195
+ - `subscriptionId`: string
196
+ - `payMethodSubscriptionId`: string
197
+ - `subscriptionStatus`: string — same enum as Create
198
+ - `subscriptionAction`: string
199
+ - `currency`: string
200
+ - `userCurrency`: string
201
+ - `amount`: string
202
+ - `productInfo`: ProductInfo
203
+ - `merchantInfo`: MerchantInfo
204
+ - `userInfo`: UserInfo
205
+ - `paymentInfo`: PaymentInfo
206
+ - `requestedAt`: string
207
+ - `updatedAt`: string
208
+ - `failedReason`: string — JSON
209
+ - `subscriptionManagementUrl`: string
210
+ - `extendInfo`: string
211
+ - `paymentDetails`: PaymentDetail[]
212
+ - `goodsInfo`: GoodsInfo
213
+ - `addressInfo`: AddressInfo
214
+
215
+ ### Subscription Cancel — `POST /api/v1/subscription/cancel`
216
+
217
+ **Request** (`SubscriptionCancelRequest`):
218
+ - `subscriptionId`: string (required) — max 64
219
+ - `merchantId`: string (required) — max 64
220
+ - `requestedAt`: string/date-time (required)
221
+
222
+ **Response** (`SubscriptionCancelResponse`):
223
+ - `merchantSubscriptionId`: string
224
+ - `subscriptionRequest`: string
225
+ - `subscriptionId`: string
226
+ - `orderStatus`: string — "CLOSE" | "MERCHANT_CANCELLED" | "CHANNEL_CANCELLED" | "EXPIRED"
227
+
228
+ ### Subscription Manage — `POST /api/v1/subscription/manage`
229
+
230
+ **Request** (`SubscriptionManageRequest`):
231
+ - `subscriptionId`: string — max 64
232
+ - `subscriptionRequest`: string — max 64
233
+
234
+ **Response** (`SubscriptionManageResponse`):
235
+ - `subscriptionRequest`: string
236
+ - `merchantSubscriptionId`: string
237
+ - `subscriptionId`: string
238
+ - `managementUrl`: string
239
+ - `expiredAt`: string — ISO 8601
240
+ - `subscriptionStatus`: string — same status enum
241
+
242
+ ### Subscription Change — `POST /api/v1/subscription/change`
243
+
244
+ **Request** (`SubscriptionChangeRequest`):
245
+ - `subscriptionRequest`: string (required) — max 32, NEW subscription request id
246
+ - `merchantSubscriptionId`: string — max 64
247
+ - `originSubscriptionRequest`: string (required) — max 32
248
+ - `remainingAmount`: string (required) — max 24
249
+ - `currency`: string (required) — max 12
250
+ - `userCurrency`: string — max 12
251
+ - `requestedAt`: string/date-time (required)
252
+ - `successRedirectUrl`: string — max 256
253
+ - `failedRedirectUrl`: string — max 256
254
+ - `notifyUrl`: string (required) — max 256
255
+ - `cancelRedirectUrl`: string — max 512
256
+ - `subscriptionManagementUrl`: string — max 256
257
+ - `extendInfo`: string — max 256, JSON
258
+ - `orderExpiredAt`: string/date-time
259
+ - `productInfoList`: ChangeProductInfo[] (required) — minItems 1
260
+ - `merchantInfo`: ChangeMerchantInfo (required)
261
+ - `userInfo`: ChangeUserInfo (required)
262
+ - `goodsInfo`: ChangeGoodsInfo (required)
263
+ - `addressInfo`: ChangeAddressInfo
264
+ - `paymentInfo`: ChangePaymentInfo (required — productName required)
265
+ - `riskData`: ChangeRiskData
266
+
267
+ **Response** (`SubscriptionChangeResponse`):
268
+ - `originSubscriptionRequest`: string
269
+ - `subscriptionRequest`: string
270
+ - `merchantSubscriptionId`: string
271
+ - `subscriptionChangeStatus`: string — "IN_PROGRESS" | "AUTHORIZATION_REQUIRED" | "SUCCESS" | "CLOSED"
272
+ - `subscriptionAction`: string
273
+ - `subscriptionId`: string (required)
274
+
275
+ ### Subscription Change Inquiry — `POST /api/v1/subscription/change/inquiry`
276
+
277
+ **Request** (`SubscriptionChangeInquiryRequest`):
278
+ - `originSubscriptionRequest`: string (required) — max 32
279
+ - `subscriptionRequest`: string (required) — max 32
280
+
281
+ **Response** (`SubscriptionChangeInquiryResponse`): mirrors change request fields plus status — see openapi.json for full field list.
282
+
283
+ ---
284
+
285
+ ## 4. Config Module
286
+
287
+ ### Merchant Config Inquiry — `POST /api/v1/merchantconfig/inquiry`
288
+
289
+ **Request**: `merchantId`: string (required) — max 64
290
+
291
+ **Response** (`AcqMerchantConfigInquiryResponse`):
292
+ - `merchantId`: string (required)
293
+ - `totalDailyLimit`: string — JSON `{"currency":"value"}`
294
+ - `remainingDailyLimit`: string — JSON
295
+ - `transactionLimit`: string — JSON
296
+
297
+ ### Pay Method Config Inquiry — `POST /api/v1/paymethodconfig/inquiry`
298
+
299
+ **Request**: `merchantId`: string (required) — max 64
300
+
301
+ **Response** (`PayMethodConfigInquiryResponse`):
302
+ - `merchantId`: string (required)
303
+ - `payMethodDetails`: PayMethodDetail[] (required)
304
+
305
+ ---
306
+
307
+ ## 5. Shared Schemas
308
+
309
+ ### MerchantInfo
310
+ - `merchantId`: string (required) — max 64
311
+ - `subMerchantId`: string — max 64
312
+
313
+ ### UserInfo (Subscription)
314
+ - `userId`: string (required) — max 64
315
+ - `userEmail`: string (required) — max 64
316
+ - `userPhone`: string — max 16
317
+ - `userFirstName`: string — max 64
318
+ - `userLastName`: string — max 64
319
+ - `userCreatedAt`: string — max 24
320
+
321
+ ### OrderUserInfo (`AcqOrderMerchantUserInfo`)
322
+ - `userId`: string (required) — max 64
323
+ - `userEmail`: string (required) — max 64
324
+ - `userTerminal`: string (required) — "WEB" | "APP" | "IN_WALLET_APP" | "IN_MINI_PROGRAM", max 32
325
+ - `userPhone`: string — max 30
326
+ - `userCountryCode`: string — max 3
327
+ - `userFirstName`: string — max 64
328
+ - `userLastName`: string — max 64
329
+ - `userBrowserIp`: string — max 128, required for direct card
330
+ - `userAgent`: string — max 256, required for direct card
331
+ - `userCreatedAt`: string
332
+ - `userReceiptUrl`: string — read-only in inquiry
333
+
334
+ ### ProductInfo (Subscription)
335
+ - `description`: string (required) — max 128
336
+ - `periodType`: string (required) — "DAILY" | "WEEKLY" | "MONTHLY", max 12
337
+ - `periodInterval`: string (required) — max 12
338
+ - `numberOfPeriod`: string — max 24
339
+ - `trialPeriodAmount`: string — max 24, must be > 0
340
+ - `numberOfTrialPeriod`: string — max 12
341
+ - `trialPeriodType`: string — "DAILY" | "WEEKLY" | "MONTHLY"
342
+ - `trialPeriodInterval`: string — max 12
343
+ - `startDateTime`: string — max 24 (inquiry response only)
344
+ - `endDateTime`: string — max 24 (inquiry response only)
345
+ - `nextPaymentDateTime`: string — max 24 (inquiry response only)
346
+ - `currentPeriod`: string — max 24 (inquiry response only)
347
+
348
+ ### ChangeProductInfo (in productInfoList[])
349
+ Same as ProductInfo but adds:
350
+ - `amount`: string (required) — max 12
351
+
352
+ ### PaymentInfo (Subscription)
353
+ - `productName`: string (required) — "SUBSCRIPTION" | "MINI_PROGRAM_SUBSCRIPTION", max 32
354
+ - `payMethodType`: string — "EWALLET" | "CREDITCARD" | "DEBITCARD", max 16
355
+ - `payMethodName`: string (required) — max 24
356
+ - `payMethodProperties`: string — max 256, JSON
357
+ - `payMethodResponse`: string — max 256, read-only
358
+ - `payMethodUserAccountType`: string — "EMAIL" | "PHONE_NO" | "ACCOUNT_ID", max 24
359
+ - `payMethodUserAccountNo`: string — max 64
360
+ - `payMethodPublicUid`: string — max 128
361
+ - `payMethodUserAccessToken`: string — max 128
362
+
363
+ ### OrderPaymentInfo (`AcqOrderPaymentInfo`)
364
+ - `productName`: string (required) — "ONE_TIME_PAYMENT" | "DIRECT_PAYMENT", max 32
365
+ - `payMethodType`: string — "EWALLET" | "ONLINE_BANKING" | "DIGITAL_BANKING" | "OTC" | "CREDITCARD" | "DEBITCARD", max 256
366
+ - `payMethodName`: string — max 256
367
+ - `payMethodProperties`: string — max 1024, JSON
368
+ - `payMethodResponse`: string — read-only
369
+ - `userPaymentAccessToken`: string — max 256, for ONE_CLICK_PAYMENT
370
+ - `payMethodUserAccountNo`: string — max 64
371
+ - `payMethodUserAccountType`: string — max 24
372
+ - `payOption`: string — read-only: "BALANCE" | "DEBIT_CARD" | "CREDIT_CARD"
373
+ - `cashierLanguage`: string — max 24, IETF BCP 47
374
+ - `cashierAppearance`: string — JSON theme config
375
+ - `payMethodCountry`: string — max 3
376
+ - `captureMode`: string — max 24
377
+ - `merchantInitiatedMode`: string — "scheduled" | "unscheduled", max 24
378
+
379
+ ### GoodsInfo (Subscription)
380
+ - `goodsId`: string
381
+ - `goodsName`: string
382
+ - `goodsCategory`: string
383
+ - `goodsUrl`: string
384
+ - `appName`: string
385
+ - `skuName`: string
386
+ - `goodsUniquePrice`: string
387
+ - `goodsQuantity`: integer
388
+
389
+ ### OrderGoodsInfo (`AcqOrderGoodsInfo`)
390
+ - `goodsId`: string — max 128
391
+ - `goodsName`: string (required) — max 64
392
+ - `skuName`: string — max 32
393
+ - `goodsUniquePrice`: string — max 16
394
+ - `goodsQuantity`: integer
395
+ - `goodsCategory`: string — max 32
396
+ - `goodsUrl`: string (required) — max 128
397
+ - `appName`: string (required) — max 32
398
+
399
+ ### AddressInfo
400
+ - `address`: string
401
+ - `city`: string
402
+ - `region`: string
403
+ - `postcode`: string
404
+ - `addressCountryCode`: string
405
+
406
+ ### CardInfo (`AcqCardInfo`)
407
+ - `cardNumber`: string (required) — 14-24 chars
408
+ - `cardExpiryYear`: integer (required) — 4 digits, min 2024
409
+ - `cardExpiryMonth`: integer (required) — 1-12
410
+ - `cardCvv`: string (required) — 3-4 chars
411
+ - `cardHolderName`: string (required) — max 128
412
+ - `threeDsDecision`: string — "3DS_FORCE" | "3DS_ATTEMPT" | "NO_3DS"
413
+
414
+ ### SubscriptionInfo (in Order Inquiry response)
415
+ - `subscriptionId`: string (required)
416
+ - `period`: string (required)
417
+ - `merchantRequest`: string (required)
418
+ - `subscriptionRequest`: string (required)
419
+
420
+ ### PaymentDetail (in Subscription Inquiry response)
421
+ - `acquiringOrderId`: string
422
+ - `orderCurrency`: string
423
+ - `orderAmount`: string
424
+ - `orderStatus`: string — "PAY_SUCCESS" | "ORDER_CLOSE"
425
+ - `orderUpdatedAt`: string
426
+ - `period`: string
427
+
428
+ ### PayMethodDetail (in PayMethod Config response)
429
+ - `productName`: string (required) — "ONE_TIME_PAYMENT" | "DIRECT_PAYMENT"
430
+ - `payMethodName`: string (required)
431
+ - `country`: string (required)
432
+ - `currentStatus`: string (required) — "1" (available) | "0" (unavailable)
433
+ - `fixedMaintenanceRules`: FixedMaintenanceRule[] (required)
434
+ - `fixedMaintenanceTimezone`: string (required)
435
+
436
+ ### RefundOrderUserInfo
437
+ - `userType`: string (required) — "INDIVIDUAL" | "BUSINESS"
438
+ - `userFirstName`: string — max 64
439
+ - `userMiddleName`: string — max 64
440
+ - `userLastName`: string — max 64
441
+ - `nationality`: string — max 3
442
+ - `userEmail`: string — max 64
443
+ - `userPhone`: string — max 16
444
+ - `userBirthDay`: string — max 12
445
+ - `userIDType`: string — max 24
446
+ - `userIDNumber`: string — max 64
447
+ - `userIDIssueDate`: string — max 12, dd/mm/yyyy
448
+ - `userIDExpiryDate`: string — max 12, dd/mm/yyyy
449
+ - `userBankInfo`: RefundOrderUserBankInfo
450
+
451
+ ### RefundOrderUserBankInfo
452
+ - `bankAccountNo`: string — max 64
453
+ - `bankCode`: string — max 64
454
+ - `bankName`: string — max 64
455
+ - `bankCity`: string — max 64
456
+ - `bankBranch`: string — max 64
457
+
458
+ ### RiskData (Subscription — all required)
459
+ - `userType`: string — "Individual" | "Agent" | "Institution" | "Internal"
460
+ - `userCategory`: string — "Member" | "Non-Member"
461
+ - `userLegalName`: string — max 128
462
+ - `userDisplayName`: string — max 128
463
+ - `userRegistrationIp`: string — max 24
464
+ - `userLastSeenIp`: string — max 24
465
+ - `userIsNew`: string — "Yes" | "No"
466
+ - `userIsFirstPurchase`: string — "Yes" | "No"
467
+
468
+ ### OrderRiskData (Order — all optional, same fields as RiskData)
469
+
470
+ ---
471
+
472
+ ## 6. Field Name Gotchas
473
+
474
+ | Concept | Order API field | Subscription API field |
475
+ |---------|----------------|----------------------|
476
+ | Currency | `orderCurrency` | `currency` |
477
+ | Amount | `orderAmount` | `amount` |
478
+ | Request time | `orderRequestedAt` | `requestedAt` |
479
+ | Request ID | `paymentRequestId` | `subscriptionRequest` |
480
+ | Waffo ID | `acquiringOrderId` | `subscriptionId` |
481
+ | Merchant ID field | nested in `merchantInfo` | nested in `merchantInfo` (or top-level `merchantId` for cancel) |
482
+ | Redirect URLs | optional | `successRedirectUrl`, `failedRedirectUrl`, `cancelRedirectUrl` all required for create |
483
+ | GoodsInfo required fields | `goodsName`, `goodsUrl`, `appName` | none (all optional) |
484
+ | UserInfo required fields | `userId`, `userEmail`, `userTerminal` | `userId`, `userEmail` |
485
+ | PaymentInfo productName | "ONE_TIME_PAYMENT" / "DIRECT_PAYMENT" | "SUBSCRIPTION" / "MINI_PROGRAM_SUBSCRIPTION" |
486
+
487
+ ---
488
+
489
+ ## 7. Status Handling Guide
490
+
491
+ ### Order Statuses
492
+
493
+ | Status | Meaning | Recommended Action |
494
+ |--------|---------|-------------------|
495
+ | `PAY_IN_PROGRESS` | Payment processing | Wait for webhook or poll via `order().inquiry()` |
496
+ | `AUTHORIZATION_REQUIRED` | User must complete 3DS or redirect | Redirect user to `orderAction.webUrl` |
497
+ | `AUTHED_WAITING_CAPTURE` | Pre-auth completed, awaiting capture | Call `order().capture()` when ready to settle |
498
+ | `PAY_SUCCESS` | Payment completed | Fulfill the order |
499
+ | `ORDER_CLOSE` | Payment failed or expired | Show failure to user, allow retry with new `paymentRequestId` |
500
+
501
+ ### Refund Statuses
502
+
503
+ | Status | Meaning | Recommended Action |
504
+ |--------|---------|-------------------|
505
+ | `REFUND_IN_PROGRESS` | Refund processing | Wait for `REFUND_NOTIFICATION` webhook |
506
+ | `ORDER_FULLY_REFUNDED` | Full refund completed | Update order state, notify user |
507
+ | `ORDER_PARTIALLY_REFUNDED` | Partial refund completed | Update refund balance, allow further refunds |
508
+ | `ORDER_REFUND_FAILED` | Refund rejected | Check `refundFailedReason`, may need manual intervention |
509
+
510
+ ### Subscription Statuses
511
+
512
+ | Status | Meaning | Recommended Action |
513
+ |--------|---------|-------------------|
514
+ | `AUTHORIZATION_REQUIRED` | User must complete initial payment | Redirect to `subscriptionAction.webUrl` |
515
+ | `IN_PROGRESS` | Subscription being set up | Wait for webhook or poll via `subscription().inquiry()` |
516
+ | `ACTIVE` | Subscription active and billing | Enable entitlements; `manage()` API available |
517
+ | `CLOSE` | Creation failed or closed | Treat as terminal failure |
518
+ | `MERCHANT_CANCELLED` | Merchant initiated cancellation | Disable renewal, keep access until period end |
519
+ | `USER_CANCELLED` | User cancelled via management page | Disable renewal, keep access until period end |
520
+ | `CHANNEL_CANCELLED` | Payment channel cancelled (e.g. card expired) | Notify user, prompt to update payment method |
521
+ | `EXPIRED` | All billing periods completed | Remove entitlements |
522
+
523
+ ### Subscription Change Statuses
524
+
525
+ | Status | Meaning | Recommended Action |
526
+ |--------|---------|-------------------|
527
+ | `IN_PROGRESS` | Change processing | Keep current plan active until confirmed |
528
+ | `AUTHORIZATION_REQUIRED` | User must authorize new payment | Redirect to `subscriptionAction.webUrl` |
529
+ | `SUCCESS` | Plan change completed | Activate new plan, deactivate old |
530
+ | `CLOSED` | Change failed | Keep original plan unchanged |
531
+
532
+ ### Unknown Status / Timeout Handling
533
+
534
+ When an API call times out or returns a network error on a **write operation** (create, refund, cancel):
535
+
536
+ 1. **Do NOT treat it as failure** — the operation may have succeeded server-side
537
+ 2. **Do NOT retry with a new request ID** — this would create a duplicate operation
538
+ 3. **Query the status** — use the corresponding inquiry API with the same request ID
539
+ 4. **Trust the webhook** — if a webhook arrives before you query, it is the source of truth