@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 +21 -0
- package/README.md +98 -0
- package/SKILL.md +462 -0
- package/bin/install.js +85 -0
- package/docs/INDEX.md +50 -0
- package/package.json +29 -0
- package/references/api-contract.md +539 -0
- package/references/go.md +688 -0
- package/references/java.md +615 -0
- package/references/node.md +576 -0
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();
|