@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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Waffo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # waffo-integrate
2
+
3
+ A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill that guides developers through integrating the [Waffo Payment SDK](https://github.com/waffo-com/waffo-sdk) into their projects via interactive Q&A.
4
+
5
+ ## Install
6
+
7
+ ### One command (npm)
8
+
9
+ ```bash
10
+ # Auto-detect: installs to Claude Code and/or Cursor if detected
11
+ npx @waffo/waffo-integrate
12
+
13
+ # Or specify target explicitly
14
+ npx @waffo/waffo-integrate --claude
15
+ npx @waffo/waffo-integrate --cursor
16
+ ```
17
+
18
+ ### Claude Code only
19
+
20
+ ```bash
21
+ claude /install-skill waffo-com/waffo-integrate
22
+ ```
23
+
24
+ ### Cursor post-install
25
+
26
+ After running `npx @waffo/waffo-integrate --cursor`, add to your `.cursorrules` (or `.cursor/rules/waffo-integrate.mdc`):
27
+
28
+ ```
29
+ When the user asks to integrate Waffo SDK, read and follow the instructions in .cursor/skills/waffo-integrate/SKILL.md.
30
+ Load reference files from .cursor/skills/waffo-integrate/references/ as directed by SKILL.md.
31
+ ```
32
+
33
+ ### Manual (any AI coding assistant)
34
+
35
+ Copy the `SKILL.md` and `references/` directory into your project. Point your AI assistant to read `SKILL.md` when integrating Waffo SDK.
36
+
37
+ ## What it does
38
+
39
+ An 8-step interactive wizard that:
40
+
41
+ 1. **Detects language** — Node.js / Java / Go (auto-detect or ask)
42
+ 2. **Checks project status** — existing project or new scaffold
43
+ 3. **Selects features** — payments, refunds, subscriptions, webhooks, config queries
44
+ 4. **Picks framework** — Express, Spring Boot, Gin, etc.
45
+ 5. **Previews code** — shows complete integration code for review
46
+ 6. **Writes to project** — installs SDK dependency + generates files
47
+ 7. **Generates tests** — Sandbox integration tests (or stubs if no credentials)
48
+ 8. **Verifies locally** — tunnel setup + end-to-end payment + webhook verification
49
+
50
+ ### Built-in safeguards
51
+
52
+ 13 API contract rules prevent common integration mistakes:
53
+
54
+ - UUID request IDs exceeding 32-character limit
55
+ - Subscription field name confusion (`currency` vs `orderCurrency`)
56
+ - Missing required fields (`payMethodType`, `userTerminal`, `goodsInfo`)
57
+ - Invalid enum values (`YEARLY` is not a valid `periodType`)
58
+ - Incorrect SDK initialization patterns (e.g. non-existent `fromProperties()` in Java)
59
+
60
+ ### Progressive disclosure
61
+
62
+ Only the main SKILL.md is loaded initially (~450 lines). Language-specific templates and the API contract reference are loaded on demand, saving tokens.
63
+
64
+ ```
65
+ waffo-integrate/
66
+ ├── SKILL.md # Integration flow + 13 rules
67
+ ├── references/
68
+ │ ├── api-contract.md # Field definitions + status handling guide
69
+ │ ├── node.md # Node.js/TypeScript templates
70
+ │ ├── java.md # Java/Spring Boot templates
71
+ │ └── go.md # Go templates
72
+ └── evals/
73
+ └── evals.json # Evaluation test cases
74
+ ```
75
+
76
+ ## Evaluation results
77
+
78
+ Built and tested with Anthropic's official [skill-creator](https://github.com/anthropics/claude-code/tree/main/plugins/skill-creator) plugin. 3 iterations of A/B testing (with skill vs without skill), 3 scenarios, 16 assertions:
79
+
80
+ | Metric | With Skill | Without Skill |
81
+ |--------|-----------|---------------|
82
+ | Pass rate | **100%** (16/16) | 75% (12/16) |
83
+ | Avg time | 128s | 192s (**-33%**) |
84
+ | Avg tokens | 58.8k | 66.3k (**-11%**) |
85
+
86
+ Consistent failure patterns without the skill across all 3 iterations:
87
+ - Subscription missing `goodsInfo` (3/3 rounds)
88
+ - Subscription missing `payMethodType` (3/3 rounds)
89
+ - Java using non-existent `WaffoConfig.fromProperties()` (3/3 rounds)
90
+
91
+ ## Requirements
92
+
93
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor](https://cursor.sh), or any AI coding assistant that can read markdown instructions
94
+ - A Waffo merchant account (for Sandbox testing)
95
+
96
+ ## License
97
+
98
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,462 @@
1
+ ---
2
+ name: waffo-integrate
3
+ description: Interactive guide for integrating Waffo Payment SDK into projects. Walks developers through selecting features (payments, refunds, subscriptions, webhooks), choosing language (Node.js/Java/Go), and generates production-ready integration code with Sandbox tests. Use this skill whenever the user mentions integrating Waffo SDK, adding payment functionality with Waffo, setting up Waffo webhooks, or asks about Waffo SDK usage. Trigger phrases include "integrate waffo", "waffo sdk", "waffo payment", "接入waffo", "集成waffo", "接入支付", "waffo webhook setup".
4
+ ---
5
+
6
+ # Waffo SDK Integration Guide
7
+
8
+ Help developers integrate Waffo Payment SDK into their projects through interactive feature selection. The SDK supports order payments, refunds, subscriptions, webhooks, and configuration queries across Node.js, Java, and Go.
9
+
10
+ ## Integration Flow
11
+
12
+ ```
13
+ Step 1: Detect or ask language → Node.js / Java / Go
14
+ Step 2: Detect or ask project status → existing project / new project
15
+ Step 3: Feature selection (interactive Q&A, one at a time)
16
+ Step 4: Framework selection (if Webhook chosen)
17
+ Step 5: Present code for review
18
+ Step 6: Write to project on approval
19
+ Step 7: Generate Sandbox tests
20
+ Step 8: Local end-to-end verification (if Webhook selected)
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Step 1: Language Detection
26
+
27
+ Check the project directory for language signals before asking:
28
+
29
+ | Signal | Language |
30
+ |--------|----------|
31
+ | `package.json` with TypeScript/Node deps | Node.js |
32
+ | `pom.xml` or `build.gradle` | Java |
33
+ | `go.mod` | Go |
34
+
35
+ If ambiguous or no project files, ask: "What language are you using? (Node.js / Java / Go)"
36
+
37
+ ## Step 2: Project Status
38
+
39
+ Check if the project already has a build file (package.json, pom.xml, go.mod):
40
+ - **Existing project**: Skip scaffolding, just add SDK dependency and integration files
41
+ - **New project**: Guide through project initialization first, then add SDK
42
+
43
+ ## Step 3: Feature Selection (Interactive Q&A)
44
+
45
+ Ask about each feature module one at a time. For each, briefly explain what it does so the developer can decide.
46
+
47
+ ### Module Menu
48
+
49
+ Ask in this order (most common first):
50
+
51
+ **1. Order Payment** (most developers need this)
52
+ > "Do you need to accept payments? This includes creating payment orders, querying order status, canceling unpaid orders, and capturing pre-authorized payments."
53
+
54
+ Operations: `order().create()`, `order().inquiry()`, `order().cancel()`, `order().capture()`
55
+
56
+ **2. Refund**
57
+ > "Do you need refund capability? This lets you refund paid orders (full or partial) and query refund status."
58
+
59
+ Operations: `order().refund()`, `refund().inquiry()`
60
+
61
+ **3. Subscription Management**
62
+ > "Do you need recurring billing / subscriptions? This includes creating subscriptions, querying status, canceling, pausing/resuming, and upgrading/downgrading plans."
63
+
64
+ Operations: `subscription().create()`, `subscription().inquiry()`, `subscription().cancel()`, `subscription().manage()`, `subscription().change()`, `subscription().changeInquiry()`
65
+
66
+ **4. Webhook Notifications**
67
+ > "Do you need to receive real-time notifications from Waffo? (payment results, refund results, subscription status changes). This requires a publicly accessible endpoint on your server."
68
+
69
+ Events: `PAYMENT_NOTIFICATION`, `REFUND_NOTIFICATION`, `SUBSCRIPTION_STATUS_NOTIFICATION`, `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION`, `SUBSCRIPTION_CHANGE_NOTIFICATION`
70
+
71
+ Only register handlers for events relevant to selected features. For example, if the developer only chose Order Payment + Refund, only register `onPayment` and `onRefund`.
72
+
73
+ **5. Merchant Config Query** (optional, less common)
74
+ > "Do you need to query your merchant configuration (supported currencies, merchant status)?"
75
+
76
+ Operations: `merchantConfig().inquiry()`
77
+
78
+ **6. Payment Method Query** (optional, less common)
79
+ > "Do you need to query available payment methods for your merchant?"
80
+
81
+ Operations: `payMethodConfig().inquiry()`
82
+
83
+ ### Smart Defaults
84
+
85
+ - If developer selects "Order Payment", suggest Refund as well (most payment integrations need it)
86
+ - If developer selects "Subscription", suggest Webhook (subscription lifecycle depends on it)
87
+ - Always include SDK initialization (WaffoConfig + Waffo instance) regardless of selection
88
+
89
+ ## Step 4: Framework Selection (Webhook only)
90
+
91
+ If the developer selected Webhook, ask about their web framework.
92
+
93
+ **Recommend mainstream frameworks by language:**
94
+
95
+ | Language | Recommended | Also Supported |
96
+ |----------|------------|----------------|
97
+ | Node.js | Express | NestJS, Fastify |
98
+ | Java | Spring Boot | - |
99
+ | Go | Gin | Echo, Fiber, Chi |
100
+
101
+ Ask: "What web framework are you using? If you're not sure, I recommend [Express/Spring Boot/Gin] — it's the most widely used for [language]."
102
+
103
+ ## Step 5: Present Code
104
+
105
+ Before writing any files, present the complete integration code for the developer to review. Show:
106
+
107
+ 1. **SDK initialization** (always)
108
+ 2. **Service layer** for each selected feature
109
+ 3. **Webhook route** (if selected)
110
+ 4. **Test files**
111
+
112
+ **IMPORTANT**: Before generating any code, read `references/api-contract.md` to verify required fields, enum values, and field types. This is the source of truth extracted from openapi.json — do not guess field names or assume which fields are optional.
113
+
114
+ Then use the language-specific reference files for code templates:
115
+ - Node.js: Read `references/node.md`
116
+ - Java: Read `references/java.md`
117
+ - Go: Read `references/go.md`
118
+
119
+ ## Step 6: Write to Project
120
+
121
+ After the developer approves, write files to their project:
122
+
123
+ 1. **Install SDK dependency** (run the appropriate package manager command)
124
+ 2. **Create integration files** in a sensible project structure
125
+ 3. **Show next steps** (configure credentials, set up webhook endpoint, etc.)
126
+
127
+ ### SDK Installation
128
+
129
+ Dynamically fetch the latest version before installing:
130
+
131
+ **Node.js:**
132
+ ```bash
133
+ npm view @anthropic-ai/waffo-node version # get latest
134
+ npm install @waffo/waffo-node
135
+ ```
136
+
137
+ **Java (Maven):**
138
+ Check Maven Central for latest version of `com.waffo:waffo-java`, then add to pom.xml.
139
+
140
+ **Go:**
141
+ ```bash
142
+ go get github.com/waffo-com/waffo-go@latest
143
+ ```
144
+
145
+ ### File Structure
146
+
147
+ Adapt to the project's existing structure. If none exists, use these defaults:
148
+
149
+ **Node.js:**
150
+ ```
151
+ src/
152
+ ├── config/waffo.ts # SDK initialization
153
+ ├── services/
154
+ │ ├── payment-service.ts # Order operations
155
+ │ ├── refund-service.ts # Refund operations
156
+ │ └── subscription-service.ts # Subscription operations
157
+ ├── webhooks/waffo-webhook.ts # Webhook handler + route
158
+ tests/
159
+ ├── payment.test.ts
160
+ ├── refund.test.ts
161
+ └── webhook.test.ts
162
+ ```
163
+
164
+ **Java:**
165
+ ```
166
+ src/main/java/com/example/
167
+ ├── config/WaffoConfiguration.java
168
+ ├── service/
169
+ │ ├── PaymentService.java
170
+ │ ├── RefundService.java
171
+ │ └── SubscriptionService.java
172
+ ├── controller/WaffoWebhookController.java
173
+ src/test/java/com/example/
174
+ ├── PaymentServiceTest.java
175
+ └── RefundServiceTest.java
176
+ ```
177
+
178
+ **Go:**
179
+ ```
180
+ internal/
181
+ ├── waffo/
182
+ │ ├── client.go # SDK initialization
183
+ │ ├── payment.go # Order operations
184
+ │ ├── refund.go
185
+ │ ├── subscription.go
186
+ │ └── webhook.go # Webhook handler
187
+ internal/waffo/waffo_test.go # Tests
188
+ ```
189
+
190
+ ## Step 7: Generate Tests
191
+
192
+ ### Credential Detection
193
+
194
+ Check for Waffo credentials in this order:
195
+ 1. Environment variables: `WAFFO_API_KEY`, `WAFFO_PRIVATE_KEY`, `WAFFO_PUBLIC_KEY`, `WAFFO_MERCHANT_ID`
196
+ 2. Project config files (`.env`, `application.yml`, etc.)
197
+
198
+ ### Test Strategy
199
+
200
+ **If credentials found:**
201
+ Generate integration tests that call Waffo Sandbox directly. Use `Environment.SANDBOX`. Each test should:
202
+ - Call the real API
203
+ - Assert `response.isSuccess()` with full diagnostic output on failure
204
+ - Print request IDs and response data for debugging
205
+
206
+ **If no credentials:**
207
+ Generate test stubs with environment variable guards:
208
+ ```typescript
209
+ const HAS_CREDENTIALS = !!process.env.WAFFO_API_KEY;
210
+
211
+ describe('Waffo Payment', () => {
212
+ (HAS_CREDENTIALS ? it : it.skip)('creates an order via Sandbox', async () => {
213
+ // Real API call test — runs when credentials are configured
214
+ });
215
+ });
216
+ ```
217
+
218
+ Include a comment explaining how to configure credentials to enable the tests.
219
+
220
+ ### Test Error Logging
221
+
222
+ Every API response assertion must include full diagnostic output before the assertion:
223
+ - Request identifiers (paymentRequestId, subscriptionRequest, etc.)
224
+ - Full response (code, message, data)
225
+
226
+ This is critical because Sandbox tests depend on external services — when they fail, you need the request ID to investigate.
227
+
228
+ ---
229
+
230
+ ## Step 8: Local End-to-End Verification
231
+
232
+ After code is written and tests pass, offer to run a full local verification if the developer selected **Webhook**. This proves the entire flow works: create order → pay → receive webhook callback.
233
+
234
+ Ask: "Want to verify the full payment + webhook flow locally? I'll set up a tunnel so Waffo can reach your local server."
235
+
236
+ ### Prerequisites Check
237
+
238
+ 1. **Local server running**: Check if the webhook port is listening (`lsof -i :PORT` or `curl http://localhost:PORT/health`). If not, guide the developer to start it.
239
+ 2. **Credentials configured**: Verify Waffo credentials are available (env vars or config files).
240
+
241
+ ### Tunnel Setup
242
+
243
+ Detect which tunnel tool is installed, in order of preference:
244
+
245
+ | Tool | Detection | Start Command | Get Public URL |
246
+ |------|-----------|---------------|----------------|
247
+ | cloudflared | `which cloudflared` | `cloudflared tunnel --url http://localhost:PORT` | Parse stdout for `https://*.trycloudflare.com` |
248
+ | ngrok | `which ngrok` | `ngrok http PORT` | `curl http://localhost:4040/api/tunnels` → `.tunnels[0].public_url` |
249
+
250
+ If neither is installed, show installation instructions for both and let the developer choose.
251
+
252
+ Start the tunnel in the background and capture the public URL.
253
+
254
+ ### Verification Flow
255
+
256
+ ```
257
+ 1. Start tunnel → obtain public URL
258
+ 2. Query payment methods: payMethodConfig().inquiry()
259
+ → Display supported methods so the developer knows what's available
260
+ 3. Create order:
261
+ - notifyUrl = {tunnelUrl}/{webhookPath}
262
+ - successRedirectUrl = {tunnelUrl}/verification-success
263
+ - No payMethodType specified (let checkout page show all options)
264
+ 4. Output checkoutUrl → developer opens it and completes payment manually
265
+ 5. Poll order status: order().inquiry() every 3s, timeout 120s
266
+ → Wait for orderStatus to change from PENDING
267
+ 6. Verify webhook received:
268
+ - ngrok: query inspection API at http://localhost:4040/api/requests/http
269
+ → look for POST to webhookPath with 200 response
270
+ - cloudflared: query the /waffo/last-webhook debug endpoint (see below)
271
+ 7. Output verification report
272
+ 8. Clean up: stop tunnel process
273
+ ```
274
+
275
+ ### Webhook Debug Endpoint (cloudflared only)
276
+
277
+ When cloudflared is the tunnel tool, generate a temporary debug endpoint in the developer's project that stores the last webhook received. This enables webhook verification without ngrok's inspection API.
278
+
279
+ **Node.js (Express):**
280
+ ```typescript
281
+ // Temporary — remove after verification
282
+ let lastWebhook: any = null;
283
+ app.get('/waffo/last-webhook', (req, res) => res.json(lastWebhook));
284
+ // In webhook handler, add: lastWebhook = { receivedAt: new Date(), body, signature };
285
+ ```
286
+
287
+ **Java (Spring Boot):**
288
+ ```java
289
+ // Temporary — remove after verification
290
+ private static volatile String lastWebhook = null;
291
+ @GetMapping("/waffo/last-webhook")
292
+ public String lastWebhook() { return lastWebhook; }
293
+ // In webhook handler, add: lastWebhook = body;
294
+ ```
295
+
296
+ **Go (Gin):**
297
+ ```go
298
+ // Temporary — remove after verification
299
+ var lastWebhook atomic.Value
300
+ r.GET("/waffo/last-webhook", func(c *gin.Context) { c.String(200, lastWebhook.Load().(string)) })
301
+ // In webhook handler, add: lastWebhook.Store(string(body))
302
+ ```
303
+
304
+ After verification completes, remind the developer to remove this debug endpoint.
305
+
306
+ ### Subscription Verification (if Subscription selected)
307
+
308
+ After payment verification succeeds, continue with subscription-specific verification. This requires Playwright MCP for automating the management page.
309
+
310
+ **Prerequisites**: Playwright MCP must be available. If not, output the management URL and instruct the developer to click buttons manually.
311
+
312
+ #### Subscription Verification Flow
313
+
314
+ ```
315
+ 1. Create subscription:
316
+ - notifyUrl = {tunnelUrl}/{webhookPath}
317
+ - paymentInfo.productName = 'SUBSCRIPTION'
318
+ - Use a short period (e.g., periodType='DAILY', periodInterval='1') for testing
319
+ 2. Output checkoutUrl → developer completes initial payment manually
320
+ 3. Poll subscription status: subscription().inquiry() every 3s, timeout 120s
321
+ → Wait for subscriptionStatus = ACTIVE
322
+ 4. Verify SUBSCRIPTION_STATUS_NOTIFICATION webhook received (status=ACTIVE)
323
+ 5. Get management page: subscription().manage({ subscriptionRequest })
324
+ → Obtain managementUrl from response
325
+ → Sandbox environment returns URL with mock=true already included
326
+ 6. Use Playwright to automate next period billing:
327
+ a. Navigate to managementUrl
328
+ b. Wait for page load
329
+ c. Click "Next period payment success" button
330
+ d. Wait for SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION webhook
331
+ e. Query subscription to confirm still ACTIVE
332
+ 7. Output subscription verification report
333
+ ```
334
+
335
+ #### Management Page Automation (Playwright)
336
+
337
+ The Sandbox management page (with `mock=true`) shows two test buttons at the bottom:
338
+
339
+ | Button Text | Effect | Expected Webhook |
340
+ |------------|--------|-----------------|
341
+ | "Next period payment success" | Simulates successful period billing | `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION` |
342
+ | "Next period payment failed," | Simulates failed period billing | `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION` (with failure status) |
343
+
344
+ **Playwright automation steps:**
345
+
346
+ ```
347
+ 1. browser_navigate → managementUrl (Sandbox URL already includes mock=true)
348
+ 2. browser_snapshot → verify page loaded with "Subscription Details" heading
349
+ 3. browser_click → button "Next period payment success"
350
+ 4. Wait 5-10s for webhook delivery
351
+ 5. Verify webhook received (ngrok inspection API or /waffo/last-webhook)
352
+ 6. subscription().inquiry() → confirm subscription still ACTIVE
353
+ ```
354
+
355
+ #### Page Structure Reference
356
+
357
+ The management page contains:
358
+ - **Subscription Details**: product name, status (Active), price, next billing date
359
+ - **Cancel Subscription** button
360
+ - **Payment Method** section: list of saved payment methods
361
+ - **Billing History** section: table of past billing records
362
+ - **Mock buttons** (only with `mock=true`): "Next period payment success" and "Next period payment failed,"
363
+
364
+ ### Verification Report
365
+
366
+ Output a clear summary. For payment-only verification:
367
+
368
+ ```
369
+ ┌─────────────────────────────────────────────┐
370
+ │ Waffo Payment Verification │
371
+ ├──────────────────┬──────────────────────────┤
372
+ │ Tunnel │ ✅ cloudflared / ngrok │
373
+ │ Create Order │ ✅ acquiringOrderId=xxx │
374
+ │ Checkout URL │ https://... │
375
+ │ Payment Status │ ✅ PAY_SUCCESS │
376
+ │ Webhook Received │ ✅ PAYMENT_NOTIFICATION │
377
+ │ Signature Valid │ ✅ 200 response returned │
378
+ └──────────────────┴──────────────────────────┘
379
+ ```
380
+
381
+ For subscription verification (appended to above):
382
+
383
+ ```
384
+ ┌─────────────────────────────────────────────┐
385
+ │ Waffo Subscription Verification │
386
+ ├──────────────────┬──────────────────────────┤
387
+ │ Create Sub │ ✅ subscriptionId=xxx │
388
+ │ Initial Payment │ ✅ Completed via checkout │
389
+ │ Sub Status │ ✅ ACTIVE │
390
+ │ Sub Webhook │ ✅ SUBSCRIPTION_STATUS_NOTIFICATION │
391
+ │ Manage URL │ ✅ Retrieved │
392
+ │ Next Period │ ✅ Triggered via Playwright│
393
+ │ Period Webhook │ ✅ SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION │
394
+ └──────────────────┴──────────────────────────┘
395
+ ```
396
+
397
+ If any step fails, show the failure reason and suggest fixes.
398
+
399
+ ---
400
+
401
+ ## Configuration Template
402
+
403
+ Always generate an SDK initialization module. The developer needs these credentials from the Waffo Dashboard:
404
+
405
+ | Config | Source | Required |
406
+ |--------|--------|----------|
407
+ | `apiKey` | Waffo Dashboard | Yes |
408
+ | `privateKey` | Merchant's RSA private key (PKCS8, Base64) | Yes |
409
+ | `waffoPublicKey` | Waffo's RSA public key (X509, Base64) | Yes |
410
+ | `environment` | `SANDBOX` for testing, `PRODUCTION` for live | Yes |
411
+ | `merchantId` | Waffo Dashboard | Yes |
412
+ | `connectTimeout` | Default 10000ms | No |
413
+ | `readTimeout` | Default 30000ms | No |
414
+
415
+ Recommend using environment variables for all secrets. Never hardcode credentials.
416
+
417
+ ---
418
+
419
+ ## Extended References
420
+
421
+ When the developer has questions beyond basic integration (troubleshooting, advanced patterns, status handling), point them to the relevant bundled reference files:
422
+
423
+ | Topic | Reference |
424
+ |-------|-----------|
425
+ | Field names, types, required/optional | Read `references/api-contract.md` |
426
+ | Status handling & webhook-driven state | Read `references/api-contract.md` § 7 "Status Handling Guide" |
427
+ | Node.js code patterns | Read `references/node.md` |
428
+ | Java code patterns | Read `references/java.md` |
429
+ | Go code patterns | Read `references/go.md` |
430
+ | FAQ, troubleshooting, best practices | Read `docs/INDEX.md` — knowledge base index with links to scenario-specific articles |
431
+
432
+ ---
433
+
434
+ ## Important Notes for Generated Code
435
+
436
+ 1. **Response handling**: All SDK methods return `ApiResponse<T>`. Always check `isSuccess()` before accessing `getData()`.
437
+
438
+ 2. **Error types**:
439
+ - `WaffoError` — client-side errors (validation, config)
440
+ - `WaffoUnknownStatusError` — network errors on write operations (create, refund, cancel). The operation may or may not have succeeded. The developer must query status to confirm.
441
+
442
+ 3. **Timestamp auto-injection**: The SDK automatically injects `requestedAt` / `orderRequestedAt` if not provided. No need to set these manually.
443
+
444
+ 4. **merchantId auto-injection**: The SDK automatically adds `merchantId` to all requests from config.
445
+
446
+ 5. **Webhook response**: The webhook handler returns a signed response. The developer must set `X-SIGNATURE` header and return `responseBody` as-is. Do not modify the response body.
447
+
448
+ 6. **Thread safety**: Recommend creating a single SDK instance and reusing it (singleton pattern).
449
+
450
+ 7. **Request ID length**: `paymentRequestId`, `refundRequestId`, `subscriptionRequest` all have a **max length of 32 characters**. Do NOT use raw UUIDs (36 chars). Use UUID without dashes: `crypto.randomUUID().replace(/-/g, '')` (Node.js), `UUID.randomUUID().toString().replace("-", "")` (Java), `strings.ReplaceAll(uuid.New().String(), "-", "")` (Go).
451
+
452
+ 8. **Required fields by merchant**: `userInfo.userTerminal` is required — values: `WEB` (PC/desktop browser), `APP` (mobile app, tablet), `WAP` (mobile browser), `SYSTEM` (server-to-server). Ask the developer what terminal type their users will use, and set the default accordingly. Also include `successRedirectUrl` for payment orders — most merchants require a redirect URL after payment.
453
+
454
+ 9. **paymentInfo.productName**: Use `'ONE_TIME_PAYMENT'` for one-time orders and `'SUBSCRIPTION'` for subscriptions — these are the standard product name values recognized by Waffo.
455
+
456
+ 10. **Subscription-specific field names**: Subscription create uses `currency` and `amount` (NOT `orderCurrency`/`orderAmount` used by order create). Required fields for subscription create: `subscriptionRequest`, `merchantSubscriptionId`, `currency`, `amount`, `notifyUrl`, `successRedirectUrl`, `productInfo` (with `description`, `periodType`, `periodInterval`), `userInfo` (with `userTerminal`), `goodsInfo` (with `goodsId`, `goodsName`, `goodsUrl`), `paymentInfo` (with `productName` and `payMethodType`).
457
+
458
+ 11. **PeriodType values**: Valid values are `'DAILY'`, `'WEEKLY'`, `'MONTHLY'`. There is no `YEARLY`. Period interval is a string (e.g., `'1'`), not a number.
459
+
460
+ 12. **manage() API**: `subscription().manage()` returns a `managementUrl` for the subscription management page. It only works when the subscription is `ACTIVE` (payment completed). Request requires `subscriptionRequest` or `subscriptionId`. The Sandbox management URL includes `mock=true` automatically.
461
+
462
+ 13. **payMethodType is REQUIRED for subscriptions**: `paymentInfo.payMethodType` must be set for subscription create — without it the server returns A0003. Default to `'CREDITCARD,DEBITCARD,APPLEPAY,GOOGLEPAY'` (comma-separated string supporting multiple payment methods). This is different from `payMethodName` which is optional. Do NOT omit `payMethodType` or replace it with `payMethodName`.
package/bin/install.js ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const SKILL_FILES = [
8
+ 'SKILL.md',
9
+ 'references/api-contract.md',
10
+ 'references/node.md',
11
+ 'references/java.md',
12
+ 'references/go.md',
13
+ 'docs/INDEX.md',
14
+ ];
15
+
16
+ const TARGETS = {
17
+ claude: path.join(os.homedir(), '.claude', 'skills', 'waffo-integrate'),
18
+ cursor: path.join(process.cwd(), '.cursor', 'skills', 'waffo-integrate'),
19
+ };
20
+
21
+ function copySkillFiles(targetDir, label) {
22
+ const srcDir = path.resolve(__dirname, '..');
23
+ let copied = 0;
24
+
25
+ for (const file of SKILL_FILES) {
26
+ const src = path.join(srcDir, file);
27
+ const dest = path.join(targetDir, file);
28
+
29
+ if (!fs.existsSync(src)) {
30
+ console.warn(` skip: ${file} (not found)`);
31
+ continue;
32
+ }
33
+
34
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
35
+ fs.copyFileSync(src, dest);
36
+ copied++;
37
+ }
38
+
39
+ console.log(` ${label}: installed ${copied} files -> ${targetDir}`);
40
+ }
41
+
42
+ function detect() {
43
+ const targets = [];
44
+ const args = process.argv.slice(2);
45
+
46
+ if (args.includes('--claude')) targets.push('claude');
47
+ if (args.includes('--cursor')) targets.push('cursor');
48
+
49
+ if (targets.length > 0) return targets;
50
+
51
+ // Auto-detect
52
+ const claudeDir = path.join(os.homedir(), '.claude');
53
+ const cursorDir = path.join(process.cwd(), '.cursor');
54
+
55
+ if (fs.existsSync(claudeDir)) targets.push('claude');
56
+ if (fs.existsSync(cursorDir)) targets.push('cursor');
57
+
58
+ if (targets.length === 0) {
59
+ // Default: install for claude
60
+ targets.push('claude');
61
+ }
62
+
63
+ return targets;
64
+ }
65
+
66
+ function main() {
67
+ console.log('waffo-integrate: installing skill...\n');
68
+
69
+ const targets = detect();
70
+
71
+ for (const target of targets) {
72
+ copySkillFiles(TARGETS[target], target === 'claude' ? 'Claude Code' : 'Cursor');
73
+ }
74
+
75
+ console.log('\nDone! Usage:');
76
+ if (targets.includes('claude')) {
77
+ console.log(' Claude Code: say "integrate waffo" or "接入waffo" to trigger the skill');
78
+ }
79
+ if (targets.includes('cursor')) {
80
+ console.log(' Cursor: add to .cursorrules:');
81
+ console.log(' When integrating Waffo SDK, read .cursor/skills/waffo-integrate/SKILL.md');
82
+ }
83
+ }
84
+
85
+ main();