@waffo/pancake-ts 0.1.7 → 0.2.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/CHANGELOG.md +32 -2
- package/README.md +282 -364
- package/dist/index.cjs +303 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +502 -53
- package/dist/index.d.ts +502 -53
- package/dist/index.js +303 -37
- package/dist/index.js.map +1 -1
- package/docs/api-reference.md +877 -0
- package/docs/graphql-guide.md +664 -0
- package/docs/webhook-guide.md +456 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for the Waffo Pancake Merchant of Record (MoR) payment platform.
|
|
4
4
|
|
|
5
|
-
- Zero runtime dependencies, ESM
|
|
5
|
+
- Zero runtime dependencies, ESM + CJS, Node >= 20
|
|
6
6
|
- Automatic RSA-SHA256 request signing with deterministic idempotency keys
|
|
7
7
|
- Full TypeScript type definitions (15 enums, 40+ interfaces)
|
|
8
8
|
- Webhook verification with embedded public keys (test/prod)
|
|
@@ -15,258 +15,305 @@ npm install @waffo/pancake-ts
|
|
|
15
15
|
|
|
16
16
|
## Quick Start
|
|
17
17
|
|
|
18
|
+
> Most merchants create stores and products in the [Dashboard](https://pancake.waffo.ai/dashboard). The SDK is primarily used for **checkout integration** — redirecting buyers from your site to the Waffo checkout page.
|
|
19
|
+
|
|
18
20
|
```typescript
|
|
19
21
|
import { WaffoPancake } from "@waffo/pancake-ts";
|
|
20
22
|
|
|
23
|
+
// Merchant ID and API Key are available in Dashboard > Settings > Developers
|
|
21
24
|
const client = new WaffoPancake({
|
|
22
|
-
merchantId: process.env.WAFFO_MERCHANT_ID!,
|
|
25
|
+
merchantId: process.env.WAFFO_MERCHANT_ID!, // MER_{base62} format
|
|
23
26
|
privateKey: process.env.WAFFO_PRIVATE_KEY!,
|
|
24
27
|
});
|
|
25
28
|
|
|
26
|
-
// Create a
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
const { product } = await client.onetimeProducts.create({
|
|
31
|
-
storeId: store.id,
|
|
32
|
-
name: "E-Book: TypeScript Handbook",
|
|
33
|
-
prices: {
|
|
34
|
-
USD: { amount: 2900, taxCategory: "digital_goods" },
|
|
35
|
-
EUR: { amount: 2700, taxCategory: "digital_goods" },
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Create a checkout session and redirect the buyer
|
|
40
|
-
const session = await client.checkout.createSession({
|
|
41
|
-
storeId: store.id,
|
|
42
|
-
productId: product.id,
|
|
29
|
+
// Create a checkout session — one call handles token + session + URL
|
|
30
|
+
const result = await client.checkout.authenticated.create({
|
|
31
|
+
storeId: "STO_xxx", // from Dashboard > Stores
|
|
32
|
+
productId: "PROD_xxx", // from Dashboard > Products
|
|
43
33
|
productType: "onetime",
|
|
44
34
|
currency: "USD",
|
|
35
|
+
buyerIdentity: req.user.email, // your user's identity
|
|
45
36
|
});
|
|
46
|
-
// => redirect buyer to session.checkoutUrl
|
|
47
37
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
38
|
+
// Redirect buyer to the checkout page (opens in new tab)
|
|
39
|
+
res.json({ checkoutUrl: result.checkoutUrl });
|
|
40
|
+
// => checkoutUrl includes #token=... (form pre-filled)
|
|
52
41
|
```
|
|
53
42
|
|
|
54
43
|
## Configuration
|
|
55
44
|
|
|
56
45
|
| Parameter | Type | Required | Description |
|
|
57
46
|
|-----------|------|----------|-------------|
|
|
58
|
-
| `merchantId` | `string` | Yes | Merchant ID
|
|
59
|
-
| `privateKey` | `string` | Yes | RSA private key (see [
|
|
60
|
-
| `baseUrl` | `string` | No | API base URL
|
|
47
|
+
| `merchantId` | `string` | Yes | Merchant ID in `MER_{base62}` format |
|
|
48
|
+
| `privateKey` | `string` | Yes | RSA private key in PEM format (auto-normalized, see [docs](docs/api-reference.md)) |
|
|
49
|
+
| `baseUrl` | `string` | No | API base URL override |
|
|
61
50
|
| `fetch` | `typeof fetch` | No | Custom fetch implementation |
|
|
62
|
-
| `webhookPublicKey` | `string \| { test?, prod? }` | No | Custom webhook public key(s)
|
|
51
|
+
| `webhookPublicKey` | `string \| { test?, prod? }` | No | Custom webhook public key(s) |
|
|
52
|
+
|
|
53
|
+
The SDK auto-normalizes key formats: standard PEM, PKCS#1, literal `\n` from env vars, raw base64, and Windows line endings are all accepted.
|
|
54
|
+
|
|
55
|
+
## Checkout Integration
|
|
56
|
+
|
|
57
|
+
Waffo supports two checkout modes based on whether the merchant knows the buyer's identity:
|
|
58
|
+
|
|
59
|
+
- **Merchants with their own sites** know who the buyer is — they have user accounts, login systems, or collect buyer info before checkout. The merchant provides the buyer's identity upfront, and the checkout form arrives pre-filled.
|
|
60
|
+
- **Template stores and shared links** have no prior buyer context — the buyer arrives directly at the checkout page and fills in their own details.
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
| Mode | Method | Buyer Identity | Form State | Use Case |
|
|
63
|
+
|------|--------|---------------|------------|----------|
|
|
64
|
+
| **Authenticated** | `checkout.authenticated.create()` | Merchant provides | Pre-filled | Merchant sites with user accounts |
|
|
65
|
+
| **Anonymous** | `checkout.anonymous.create()` | Not provided | Empty | Template stores, one-time purchase links |
|
|
65
66
|
|
|
66
|
-
The
|
|
67
|
+
> **We recommend authenticated checkout whenever possible.** The most important reason: authenticated checkout binds the order to the `buyerIdentity` you provide, which is a **merchant-controlled stable identifier**. Even if the buyer changes the email on the checkout form, the order is still tied to the identity you specified. In anonymous mode, the buyer self-reports their email on the form — if they enter a different address, the system treats them as a new user, which means **previous orders become unlinked** and **subscription trial periods can be exploited** (a new email = a new user = a fresh trial).
|
|
68
|
+
>
|
|
69
|
+
> | | Authenticated | Anonymous |
|
|
70
|
+
> |---|---|---|
|
|
71
|
+
> | **Identity** | Merchant-provided, stable across orders | Self-reported email, may vary |
|
|
72
|
+
> | **Form** | Pre-filled from merchant-provided identity | Empty, buyer fills manually |
|
|
73
|
+
> | **Post-purchase** | Full self-service (see [Buyer Self-Service](#buyer-self-service)) | Create orders only — no post-purchase self-service |
|
|
74
|
+
> | **Session** | 5-minute TTL, auto-refreshes | 1-minute, single-use |
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|--------|---------|-------|
|
|
70
|
-
| Standard PKCS#8 PEM | `-----BEGIN PRIVATE KEY-----\n...` | Recommended |
|
|
71
|
-
| PKCS#1 PEM | `-----BEGIN RSA PRIVATE KEY-----\n...` | Also accepted |
|
|
72
|
-
| Literal `\n` (env vars) | `"-----BEGIN PRIVATE KEY-----\\nMIIE..."` | Common when stored in `.env` or CI secrets |
|
|
73
|
-
| Windows line endings | `\r\n` | Converted to `\n` |
|
|
74
|
-
| Raw base64 (no headers) | `MIIEvQIBADANBgkqhki...` | Wrapped with PKCS#8 headers automatically |
|
|
75
|
-
| Single-line base64 with headers | Header + all base64 on one line + footer | Re-wrapped to 64-char lines |
|
|
76
|
+
Both modes support **dynamic pricing** and **trial control** at checkout time:
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
- `priceSnapshot` — override the product's stored price with a custom amount (e.g., coupon, volume discount)
|
|
79
|
+
- `withTrial` — explicitly enable or disable the trial period for subscriptions (`true` = force trial, `false` = skip trial, omit = use default rules)
|
|
80
|
+
|
|
81
|
+
### Authenticated Checkout (Recommended)
|
|
82
|
+
|
|
83
|
+
The merchant provides buyer identity — the SDK issues a session token, creates a checkout session, and returns a checkout URL with the token appended as a URL fragment. One call does everything.
|
|
78
84
|
|
|
79
85
|
```typescript
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
// Basic — buyer identity only
|
|
87
|
+
const result = await client.checkout.authenticated.create({
|
|
88
|
+
storeId: "STO_xxx",
|
|
89
|
+
productId: "PROD_xxx",
|
|
90
|
+
productType: "onetime",
|
|
91
|
+
currency: "USD",
|
|
92
|
+
buyerIdentity: "customer@example.com",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// With dynamic pricing — override stored price (e.g., coupon, volume discount)
|
|
96
|
+
const result = await client.checkout.authenticated.create({
|
|
97
|
+
storeId: "STO_xxx",
|
|
98
|
+
productId: "PROD_xxx",
|
|
99
|
+
productType: "onetime",
|
|
100
|
+
currency: "USD",
|
|
101
|
+
buyerIdentity: "customer@example.com",
|
|
102
|
+
priceSnapshot: { amount: "19.99", taxCategory: "digital_goods" },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Subscription with trial control + billing detail pre-fill
|
|
106
|
+
const result = await client.checkout.authenticated.create({
|
|
107
|
+
storeId: "STO_xxx",
|
|
108
|
+
productId: "PROD_xxx",
|
|
109
|
+
productType: "subscription",
|
|
110
|
+
currency: "USD",
|
|
111
|
+
buyerIdentity: "customer@example.com",
|
|
112
|
+
withTrial: true, // force enable trial (false = skip, omit = default rules)
|
|
113
|
+
billingDetail: { country: "US", isBusiness: false },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// result.checkoutUrl = "https://pancake.waffo.ai/store/{slug}/checkout/{sessionId}#token={JWT}"
|
|
117
|
+
window.open(result.checkoutUrl, "_blank", "noopener,noreferrer");
|
|
84
118
|
```
|
|
85
119
|
|
|
86
|
-
|
|
120
|
+
The token is passed via the URL fragment (after `#`), which is never sent to the server and never appears in the `Referer` header.
|
|
87
121
|
|
|
88
|
-
|
|
122
|
+
### Anonymous Checkout
|
|
89
123
|
|
|
90
|
-
|
|
91
|
-
|----------|--------|-------------|
|
|
92
|
-
| 1 | `options.publicKey` | Per-call override (highest priority, skips all resolution) |
|
|
93
|
-
| 2 | `config.webhookPublicKey[env]` | Config object per-environment key |
|
|
94
|
-
| 3 | `config.webhookPublicKey` (string) | Config shared key (both environments) |
|
|
95
|
-
| 4 | `WAFFO_WEBHOOK_TEST_PUBLIC_KEY` / `WAFFO_WEBHOOK_PROD_PUBLIC_KEY` | Environment variable per-environment |
|
|
96
|
-
| 5 | `WAFFO_WEBHOOK_PUBLIC_KEY` | Environment variable shared |
|
|
97
|
-
| 6 | Built-in hardcoded key | SDK-embedded Waffo public key (default) |
|
|
124
|
+
No buyer identity required — the buyer fills in billing details manually on the checkout page.
|
|
98
125
|
|
|
99
126
|
```typescript
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
const result = await client.checkout.anonymous.create({
|
|
128
|
+
storeId: "STO_xxx",
|
|
129
|
+
productId: "PROD_xxx",
|
|
130
|
+
productType: "onetime",
|
|
131
|
+
currency: "USD",
|
|
132
|
+
});
|
|
102
133
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
// Also supports priceSnapshot and withTrial
|
|
135
|
+
const result = await client.checkout.anonymous.create({
|
|
136
|
+
storeId: "STO_xxx",
|
|
137
|
+
productId: "PROD_xxx",
|
|
138
|
+
productType: "subscription",
|
|
139
|
+
currency: "USD",
|
|
140
|
+
priceSnapshot: { amount: "4.99", taxCategory: "saas" },
|
|
141
|
+
withTrial: false, // skip trial for this session
|
|
111
142
|
});
|
|
112
143
|
|
|
113
|
-
|
|
114
|
-
// export WAFFO_WEBHOOK_TEST_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
|
|
115
|
-
// export WAFFO_WEBHOOK_PROD_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
|
|
116
|
-
new WaffoPancake({ merchantId: "m_1", privateKey: "..." });
|
|
117
|
-
// => SDK auto-reads from env vars, falls back to built-in keys
|
|
144
|
+
window.open(result.checkoutUrl, "_blank", "noopener,noreferrer");
|
|
118
145
|
```
|
|
119
146
|
|
|
120
|
-
###
|
|
147
|
+
### Opening the Checkout Page
|
|
121
148
|
|
|
122
|
-
|
|
149
|
+
**We recommend opening the checkout page in a new tab** rather than navigating in the current page:
|
|
123
150
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
| PKCS#1 PEM | `-----BEGIN RSA PUBLIC KEY-----\n...` | Also accepted |
|
|
128
|
-
| Literal `\n` (env vars) | `"-----BEGIN PUBLIC KEY-----\\nMIIB..."` | Common when stored in `.env` or CI secrets |
|
|
129
|
-
| Windows line endings | `\r\n` | Converted to `\n` |
|
|
130
|
-
| Raw base64 (no headers) | `MIIBIjANBgkqhki...` | Wrapped with SPKI headers automatically |
|
|
131
|
-
| Single-line base64 with headers | Header + all base64 on one line + footer | Re-wrapped to 64-char lines |
|
|
151
|
+
- Buyers can return to your site immediately after payment or if they close the checkout tab
|
|
152
|
+
- Merchant page state (cart, forms, scroll position) is preserved
|
|
153
|
+
- Payment flow is decoupled from the browsing experience, reducing checkout abandonment
|
|
132
154
|
|
|
133
|
-
|
|
155
|
+
```typescript
|
|
156
|
+
// Recommended: open in a new tab
|
|
157
|
+
window.open(result.checkoutUrl, "_blank", "noopener,noreferrer");
|
|
134
158
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
| `client.stores` | `create()` `update()` `delete()` | Store management (webhook, notification, checkout settings) |
|
|
139
|
-
| `client.storeMerchants` | `add()` `remove()` `updateRole()` | Store member management (coming soon, returns 501) |
|
|
140
|
-
| `client.onetimeProducts` | `create()` `update()` `publish()` `updateStatus()` | One-time product CRUD with multi-currency pricing and version management |
|
|
141
|
-
| `client.subscriptionProducts` | `create()` `update()` `publish()` `updateStatus()` | Subscription product CRUD with billing period and version management |
|
|
142
|
-
| `client.subscriptionProductGroups` | `create()` `update()` `delete()` `publish()` | Product groups for shared trial and plan switching |
|
|
143
|
-
| `client.orders` | `cancelSubscription()` | Order management (pending→canceled, active→canceling) |
|
|
144
|
-
| `client.checkout` | `createSession()` | Create a checkout session with trial toggle, billing detail, and price snapshot |
|
|
145
|
-
| `client.graphql` | `query<T>()` | Typed GraphQL queries (Query only, no Mutations) |
|
|
146
|
-
| `client.webhooks` | `verify<T>()` | Webhook signature verification (uses configured `webhookPublicKey` or built-in keys) |
|
|
147
|
-
|
|
148
|
-
See [API Reference](docs/api-reference.md) for complete parameter tables and return types.
|
|
159
|
+
// Or via an <a> tag
|
|
160
|
+
// <a href={checkoutUrl} target="_blank" rel="noopener noreferrer">Proceed to Checkout</a>
|
|
161
|
+
```
|
|
149
162
|
|
|
150
|
-
|
|
163
|
+
> **Not recommended:** `window.location.href = result.checkoutUrl` replaces the current page, preventing buyers from returning to your site without browser back navigation.
|
|
151
164
|
|
|
152
|
-
|
|
165
|
+
See [API Reference — Checkout](docs/api-reference.md#checkout) for full parameter tables and `BillingDetail` field requirements.
|
|
153
166
|
|
|
154
|
-
|
|
155
|
-
1. Issue Session Token → Obtain a buyer identity credential (JWT)
|
|
156
|
-
2. Create Checkout Session → Create a session and get the checkout URL
|
|
157
|
-
3. Open Checkout Page → Open the checkout in a new browser tab
|
|
158
|
-
```
|
|
167
|
+
## Webhook Verification
|
|
159
168
|
|
|
160
|
-
|
|
169
|
+
After a buyer completes payment, Waffo sends webhook events to your server. The SDK provides two ways to verify signatures:
|
|
161
170
|
|
|
162
|
-
|
|
171
|
+
### Standalone Function (built-in keys)
|
|
163
172
|
|
|
164
173
|
```typescript
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
import { verifyWebhook, WebhookEventType } from "@waffo/pancake-ts";
|
|
175
|
+
|
|
176
|
+
// Express (IMPORTANT: use raw body — parsed JSON breaks signature verification)
|
|
177
|
+
app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
|
|
178
|
+
try {
|
|
179
|
+
const event = verifyWebhook(
|
|
180
|
+
req.body.toString("utf-8"),
|
|
181
|
+
req.headers["x-waffo-signature"] as string,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Respond immediately, process asynchronously
|
|
185
|
+
res.status(200).send("OK");
|
|
186
|
+
|
|
187
|
+
switch (event.eventType) {
|
|
188
|
+
case WebhookEventType.OrderCompleted:
|
|
189
|
+
console.log(`Order ${event.data.orderId} completed`);
|
|
190
|
+
break;
|
|
191
|
+
case WebhookEventType.SubscriptionActivated:
|
|
192
|
+
console.log(`Subscription activated for ${event.data.buyerEmail}`);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
res.status(401).send("Invalid signature");
|
|
197
|
+
}
|
|
168
198
|
});
|
|
169
|
-
```
|
|
170
199
|
|
|
171
|
-
|
|
200
|
+
// Next.js App Router
|
|
201
|
+
export async function POST(request: Request) {
|
|
202
|
+
const body = await request.text();
|
|
203
|
+
const sig = request.headers.get("x-waffo-signature");
|
|
204
|
+
try {
|
|
205
|
+
const event = verifyWebhook(body, sig);
|
|
206
|
+
return new Response("OK");
|
|
207
|
+
} catch {
|
|
208
|
+
return new Response("Invalid signature", { status: 401 });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
172
212
|
|
|
173
|
-
|
|
213
|
+
### Client Instance Method (multi-level key resolution)
|
|
174
214
|
|
|
175
215
|
```typescript
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
buyerEmail: "customer@example.com",
|
|
184
|
-
successUrl: "https://example.com/thank-you",
|
|
216
|
+
const client = new WaffoPancake({
|
|
217
|
+
merchantId: "MER_xxx",
|
|
218
|
+
privateKey: "...",
|
|
219
|
+
webhookPublicKey: {
|
|
220
|
+
test: process.env.WAFFO_TEST_PUB_KEY!,
|
|
221
|
+
prod: process.env.WAFFO_PROD_PUB_KEY!,
|
|
222
|
+
},
|
|
185
223
|
});
|
|
186
|
-
|
|
187
|
-
// https://waffo.ai/store/{slug}/checkout/{sessionId}#token={JWT}
|
|
224
|
+
const event = client.webhooks.verify(rawBody, sig, { environment: "prod" });
|
|
188
225
|
```
|
|
189
226
|
|
|
190
|
-
|
|
227
|
+
See [Webhook Guide](docs/webhook-guide.md) for event types, dual-environment key architecture, key resolution chain, retry mechanism, and best practices.
|
|
191
228
|
|
|
192
|
-
|
|
229
|
+
## Buyer Self-Service
|
|
193
230
|
|
|
194
|
-
|
|
231
|
+
Beyond checkout, you can let buyers manage their own orders and subscriptions — for example, embedding a "Cancel Subscription" or "Request Refund" button in your site.
|
|
195
232
|
|
|
196
|
-
|
|
197
|
-
- Merchant page state (cart, forms, scroll position) is preserved
|
|
198
|
-
- Payment flow is decoupled from the browsing experience, reducing checkout abandonment
|
|
233
|
+
Issue a session token, then use `client.buyer(token)` to get a session with self-service methods:
|
|
199
234
|
|
|
200
235
|
```typescript
|
|
201
|
-
//
|
|
202
|
-
|
|
236
|
+
// Your backend — issue a session token for the buyer
|
|
237
|
+
const { token } = await client.auth.issueSessionToken({
|
|
238
|
+
storeId: "STO_xxx",
|
|
239
|
+
buyerIdentity: req.user.email,
|
|
240
|
+
});
|
|
203
241
|
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
```
|
|
242
|
+
// Create a buyer session
|
|
243
|
+
const buyer = client.buyer(token);
|
|
207
244
|
|
|
208
|
-
|
|
245
|
+
// Cancel a subscription
|
|
246
|
+
const { orderId, status } = await buyer.cancelSubscription({ orderId: "ORD_xxx" });
|
|
247
|
+
// status: "canceling" (active) or "canceled" (pending)
|
|
209
248
|
|
|
210
|
-
|
|
249
|
+
// Reactivate a canceled subscription
|
|
250
|
+
await buyer.reactivateSubscription({ orderId: "ORD_xxx" });
|
|
211
251
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
import { WaffoPancake, CheckoutSessionProductType } from "@waffo/pancake-ts";
|
|
252
|
+
// Cancel a one-time order (while payment is pending)
|
|
253
|
+
await buyer.cancelOnetimeOrder({ orderId: "ORD_yyy" });
|
|
215
254
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
255
|
+
// Submit a refund request
|
|
256
|
+
const { ticket } = await buyer.createRefundTicket({
|
|
257
|
+
paymentId: "PAY_xxx",
|
|
258
|
+
reason: "Product not as described",
|
|
259
|
+
requestedAmount: { amount: "29.00", currency: "USD" },
|
|
219
260
|
});
|
|
220
261
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const { token } = await client.auth.issueSessionToken({
|
|
228
|
-
storeId: "store_xxx",
|
|
229
|
-
buyerIdentity: buyerEmail,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// Step 2: Create checkout session
|
|
233
|
-
const session = await client.checkout.createSession({
|
|
234
|
-
storeId: "store_xxx",
|
|
235
|
-
productId,
|
|
236
|
-
productType: CheckoutSessionProductType.Onetime,
|
|
237
|
-
currency,
|
|
238
|
-
buyerEmail,
|
|
239
|
-
successUrl: "https://example.com/thank-you",
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// Return URL to frontend (frontend opens in new tab)
|
|
243
|
-
res.json({ checkoutUrl: session.checkoutUrl });
|
|
262
|
+
// Resubmit a rejected refund ticket
|
|
263
|
+
await buyer.resubmitRefundTicket({
|
|
264
|
+
ticketId: "TKT_xxx",
|
|
265
|
+
paymentId: "PAY_xxx",
|
|
266
|
+
reason: "Updated reason with more detail",
|
|
267
|
+
requestedAmount: { amount: "29.00", currency: "USD" },
|
|
244
268
|
});
|
|
245
|
-
```
|
|
246
269
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
method: "POST",
|
|
251
|
-
headers: { "Content-Type": "application/json" },
|
|
252
|
-
body: JSON.stringify({ productId: "prod_xxx", currency: "USD", buyerEmail: "customer@example.com" }),
|
|
270
|
+
// Query the buyer's own orders via GraphQL
|
|
271
|
+
const result = await buyer.graphql.query({
|
|
272
|
+
query: `query { orders { id status createdAt } }`,
|
|
253
273
|
});
|
|
254
|
-
const { checkoutUrl } = await res.json();
|
|
255
|
-
window.open(checkoutUrl, "_blank", "noopener,noreferrer");
|
|
256
274
|
```
|
|
257
275
|
|
|
258
|
-
|
|
276
|
+
The token is scoped to the specified store and buyer identity — buyers can only access their own data. Token TTL is 5 minutes and auto-refreshes on each API call.
|
|
277
|
+
|
|
278
|
+
> **Note**: This uses the same `buyerIdentity` as `checkout.authenticated.create()`. Orders placed via authenticated checkout are automatically tied to this identity, so the buyer can manage them later with a token issued here.
|
|
259
279
|
|
|
260
|
-
|
|
280
|
+
## GraphQL — Typed Queries
|
|
261
281
|
|
|
262
282
|
```typescript
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
283
|
+
// Simple query
|
|
284
|
+
interface StoresQuery {
|
|
285
|
+
stores: Array<{ id: string; name: string; status: string }>;
|
|
286
|
+
}
|
|
287
|
+
const result = await client.graphql.query<StoresQuery>({
|
|
288
|
+
query: `query { stores { id name status } }`,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Query with variables
|
|
292
|
+
const product = await client.graphql.query({
|
|
293
|
+
query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
|
|
294
|
+
variables: { id: "PROD_xxx" },
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Nested relationships in a single request
|
|
298
|
+
const detail = await client.graphql.query({
|
|
299
|
+
query: `query ($id: ID!) {
|
|
300
|
+
store(id: $id) {
|
|
301
|
+
id name
|
|
302
|
+
onetimeProducts { id name status prices }
|
|
303
|
+
subscriptionProducts { id name billingPeriod status }
|
|
304
|
+
}
|
|
305
|
+
}`,
|
|
306
|
+
variables: { id: "STO_xxx" },
|
|
266
307
|
});
|
|
267
308
|
```
|
|
268
309
|
|
|
269
|
-
|
|
310
|
+
See [GraphQL Guide](docs/graphql-guide.md) for filters, analytics queries, delivery logs, and more.
|
|
311
|
+
|
|
312
|
+
## Programmatic Store & Product Management
|
|
313
|
+
|
|
314
|
+
> Most merchants manage stores and products in the [Dashboard](https://pancake.waffo.ai/dashboard). The following APIs are for merchants who need programmatic automation.
|
|
315
|
+
|
|
316
|
+
### Stores
|
|
270
317
|
|
|
271
318
|
```typescript
|
|
272
319
|
// Create a store
|
|
@@ -298,20 +345,20 @@ const { store: updated } = await client.stores.update({
|
|
|
298
345
|
const { store: deleted } = await client.stores.delete({ id: store.id });
|
|
299
346
|
```
|
|
300
347
|
|
|
301
|
-
###
|
|
348
|
+
### Products
|
|
302
349
|
|
|
303
350
|
```typescript
|
|
304
|
-
import { TaxCategory, ProductVersionStatus } from "@waffo/pancake-ts";
|
|
351
|
+
import { TaxCategory, BillingPeriod, ProductVersionStatus } from "@waffo/pancake-ts";
|
|
305
352
|
|
|
306
|
-
//
|
|
353
|
+
// One-time product with multi-currency pricing
|
|
307
354
|
const { product } = await client.onetimeProducts.create({
|
|
308
|
-
storeId: "
|
|
355
|
+
storeId: "STO_xxx",
|
|
309
356
|
name: "E-Book: TypeScript Handbook",
|
|
310
357
|
description: "Complete TypeScript guide for developers",
|
|
311
358
|
prices: {
|
|
312
|
-
USD: { amount:
|
|
313
|
-
EUR: { amount:
|
|
314
|
-
JPY: { amount: 4500, taxCategory: TaxCategory.DigitalGoods },
|
|
359
|
+
USD: { amount: "29.00", taxCategory: TaxCategory.DigitalGoods },
|
|
360
|
+
EUR: { amount: "27.00", taxCategory: TaxCategory.DigitalGoods },
|
|
361
|
+
JPY: { amount: "4500", taxCategory: TaxCategory.DigitalGoods },
|
|
315
362
|
},
|
|
316
363
|
media: [{ type: "image", url: "https://example.com/cover.jpg", alt: "Book cover" }],
|
|
317
364
|
metadata: { sku: "ebook-ts-001" },
|
|
@@ -321,7 +368,7 @@ const { product } = await client.onetimeProducts.create({
|
|
|
321
368
|
await client.onetimeProducts.update({
|
|
322
369
|
id: product.id,
|
|
323
370
|
name: "E-Book: TypeScript Handbook v2",
|
|
324
|
-
prices: { USD: { amount:
|
|
371
|
+
prices: { USD: { amount: "39.00", taxCategory: "digital_goods" } },
|
|
325
372
|
});
|
|
326
373
|
|
|
327
374
|
// Publish test version → production
|
|
@@ -329,40 +376,32 @@ await client.onetimeProducts.publish({ id: product.id });
|
|
|
329
376
|
|
|
330
377
|
// Deactivate
|
|
331
378
|
await client.onetimeProducts.updateStatus({ id: product.id, status: ProductVersionStatus.Inactive });
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Subscription Products — Create with Billing Period
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
import { BillingPeriod, TaxCategory } from "@waffo/pancake-ts";
|
|
338
379
|
|
|
339
|
-
|
|
340
|
-
|
|
380
|
+
// Subscription product
|
|
381
|
+
const { product: sub } = await client.subscriptionProducts.create({
|
|
382
|
+
storeId: "STO_xxx",
|
|
341
383
|
name: "Pro Plan",
|
|
342
384
|
billingPeriod: BillingPeriod.Monthly,
|
|
343
|
-
prices: { USD: { amount:
|
|
344
|
-
description: "Unlimited access to all features",
|
|
385
|
+
prices: { USD: { amount: "9.99", taxCategory: TaxCategory.SaaS } },
|
|
345
386
|
});
|
|
346
|
-
|
|
347
|
-
// Same update/publish/updateStatus pattern as onetime products
|
|
348
|
-
await client.subscriptionProducts.publish({ id: product.id });
|
|
387
|
+
await client.subscriptionProducts.publish({ id: sub.id });
|
|
349
388
|
```
|
|
350
389
|
|
|
351
|
-
### Subscription Product Groups
|
|
390
|
+
### Subscription Product Groups
|
|
352
391
|
|
|
353
392
|
```typescript
|
|
354
393
|
// Create a group linking related subscription tiers
|
|
355
394
|
const { group } = await client.subscriptionProductGroups.create({
|
|
356
|
-
storeId: "
|
|
395
|
+
storeId: "STO_xxx",
|
|
357
396
|
name: "Pro Plans",
|
|
358
397
|
rules: { sharedTrial: true },
|
|
359
|
-
productIds: ["
|
|
398
|
+
productIds: ["PROD_aaa", "PROD_bbb"],
|
|
360
399
|
});
|
|
361
400
|
|
|
362
401
|
// Update members (full replacement, not merge)
|
|
363
402
|
await client.subscriptionProductGroups.update({
|
|
364
403
|
id: group.id,
|
|
365
|
-
productIds: ["
|
|
404
|
+
productIds: ["PROD_aaa", "PROD_bbb", "PROD_ccc"],
|
|
366
405
|
});
|
|
367
406
|
|
|
368
407
|
// Publish / delete
|
|
@@ -370,161 +409,15 @@ await client.subscriptionProductGroups.publish({ id: group.id });
|
|
|
370
409
|
await client.subscriptionProductGroups.delete({ id: group.id });
|
|
371
410
|
```
|
|
372
411
|
|
|
373
|
-
### Orders
|
|
412
|
+
### Orders
|
|
374
413
|
|
|
375
414
|
```typescript
|
|
376
415
|
const { orderId, status } = await client.orders.cancelSubscription({
|
|
377
|
-
orderId: "
|
|
416
|
+
orderId: "ORD_xxx",
|
|
378
417
|
});
|
|
379
418
|
// status: "canceled" (was pending) or "canceling" (was active, PSP notified)
|
|
380
419
|
```
|
|
381
420
|
|
|
382
|
-
### Checkout — Create a Session
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
import { CheckoutSessionProductType } from "@waffo/pancake-ts";
|
|
386
|
-
|
|
387
|
-
// One-time product checkout
|
|
388
|
-
const session = await client.checkout.createSession({
|
|
389
|
-
storeId: "store_xxx",
|
|
390
|
-
productId: "prod_xxx",
|
|
391
|
-
productType: CheckoutSessionProductType.Onetime,
|
|
392
|
-
currency: "USD",
|
|
393
|
-
buyerEmail: "customer@example.com",
|
|
394
|
-
successUrl: "https://example.com/thank-you",
|
|
395
|
-
});
|
|
396
|
-
// => redirect buyer to session.checkoutUrl
|
|
397
|
-
|
|
398
|
-
// Subscription with trial and billing detail
|
|
399
|
-
const subSession = await client.checkout.createSession({
|
|
400
|
-
storeId: "store_xxx",
|
|
401
|
-
productId: "prod_yyy",
|
|
402
|
-
productType: CheckoutSessionProductType.Subscription,
|
|
403
|
-
currency: "USD",
|
|
404
|
-
withTrial: true,
|
|
405
|
-
billingDetail: { country: "US", isBusiness: false, state: "CA", postcode: "94105" },
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### GraphQL — Typed Queries
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
// Simple query
|
|
413
|
-
interface StoresQuery {
|
|
414
|
-
stores: Array<{ id: string; name: string; status: string }>;
|
|
415
|
-
}
|
|
416
|
-
const result = await client.graphql.query<StoresQuery>({
|
|
417
|
-
query: `query { stores { id name status } }`,
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
// Query with variables
|
|
421
|
-
const product = await client.graphql.query({
|
|
422
|
-
query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
|
|
423
|
-
variables: { id: "prod_xxx" },
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
// Nested relationships in a single request
|
|
427
|
-
const detail = await client.graphql.query({
|
|
428
|
-
query: `query ($id: ID!) {
|
|
429
|
-
store(id: $id) {
|
|
430
|
-
id name
|
|
431
|
-
onetimeProducts { id name status prices }
|
|
432
|
-
subscriptionProducts { id name billingPeriod status }
|
|
433
|
-
}
|
|
434
|
-
}`,
|
|
435
|
-
variables: { id: "store_xxx" },
|
|
436
|
-
});
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
See [GraphQL Guide](docs/graphql-guide.md) for introspection, filters, pagination, and more examples.
|
|
440
|
-
|
|
441
|
-
## Webhook Verification
|
|
442
|
-
|
|
443
|
-
Two ways to verify webhooks: the **standalone function** `verifyWebhook()` with built-in public keys, or the **client instance method** `client.webhooks.verify()` which uses the configured `webhookPublicKey`.
|
|
444
|
-
|
|
445
|
-
### Option A — Standalone Function (built-in keys)
|
|
446
|
-
|
|
447
|
-
```typescript
|
|
448
|
-
import { verifyWebhook, WebhookEventType } from "@waffo/pancake-ts";
|
|
449
|
-
|
|
450
|
-
// Express (IMPORTANT: use raw body — parsed JSON breaks signature verification)
|
|
451
|
-
app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
|
|
452
|
-
try {
|
|
453
|
-
const event = verifyWebhook(
|
|
454
|
-
req.body.toString("utf-8"),
|
|
455
|
-
req.headers["x-waffo-signature"] as string,
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
// Respond immediately, process asynchronously
|
|
459
|
-
res.status(200).send("OK");
|
|
460
|
-
|
|
461
|
-
// Use event.id for idempotent deduplication
|
|
462
|
-
switch (event.eventType) {
|
|
463
|
-
case WebhookEventType.OrderCompleted:
|
|
464
|
-
console.log(`Order ${event.data.orderId} completed`);
|
|
465
|
-
break;
|
|
466
|
-
case WebhookEventType.SubscriptionActivated:
|
|
467
|
-
console.log(`Subscription activated for ${event.data.buyerEmail}`);
|
|
468
|
-
break;
|
|
469
|
-
case WebhookEventType.SubscriptionCanceled:
|
|
470
|
-
console.log(`Subscription canceled: ${event.data.orderId}`);
|
|
471
|
-
break;
|
|
472
|
-
case WebhookEventType.RefundSucceeded:
|
|
473
|
-
console.log(`Refund ${event.data.amount} ${event.data.currency}`);
|
|
474
|
-
break;
|
|
475
|
-
}
|
|
476
|
-
} catch {
|
|
477
|
-
res.status(401).send("Invalid signature");
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
// Next.js App Router
|
|
482
|
-
export async function POST(request: Request) {
|
|
483
|
-
const body = await request.text();
|
|
484
|
-
const sig = request.headers.get("x-waffo-signature");
|
|
485
|
-
try {
|
|
486
|
-
const event = verifyWebhook(body, sig);
|
|
487
|
-
// handle event ...
|
|
488
|
-
return new Response("OK");
|
|
489
|
-
} catch {
|
|
490
|
-
return new Response("Invalid signature", { status: 401 });
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Options: specify environment, disable/customize replay protection
|
|
495
|
-
const event = verifyWebhook(body, sig, { environment: "prod" });
|
|
496
|
-
const event = verifyWebhook(body, sig, { toleranceMs: 0 }); // disable replay check
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Option B — Client Instance Method (multi-level key resolution)
|
|
500
|
-
|
|
501
|
-
`client.webhooks.verify()` uses the [multi-level fallback chain](#webhook-public-key-resolution) automatically: config keys → env vars → built-in keys.
|
|
502
|
-
|
|
503
|
-
```typescript
|
|
504
|
-
// Per-environment keys via config
|
|
505
|
-
const client = new WaffoPancake({
|
|
506
|
-
merchantId: process.env.WAFFO_MERCHANT_ID!,
|
|
507
|
-
privateKey: process.env.WAFFO_PRIVATE_KEY!,
|
|
508
|
-
webhookPublicKey: {
|
|
509
|
-
test: process.env.WAFFO_TEST_PUB_KEY!,
|
|
510
|
-
prod: process.env.WAFFO_PROD_PUB_KEY!,
|
|
511
|
-
},
|
|
512
|
-
});
|
|
513
|
-
const event = client.webhooks.verify(rawBody, sig, { environment: "prod" });
|
|
514
|
-
|
|
515
|
-
// Or rely on env vars (WAFFO_WEBHOOK_TEST_PUBLIC_KEY / WAFFO_WEBHOOK_PROD_PUBLIC_KEY)
|
|
516
|
-
const client2 = new WaffoPancake({
|
|
517
|
-
merchantId: process.env.WAFFO_MERCHANT_ID!,
|
|
518
|
-
privateKey: process.env.WAFFO_PRIVATE_KEY!,
|
|
519
|
-
});
|
|
520
|
-
const event2 = client2.webhooks.verify(rawBody, sig); // auto-detect environment
|
|
521
|
-
|
|
522
|
-
// Per-call override (highest priority, skips all resolution)
|
|
523
|
-
const event3 = client.webhooks.verify(rawBody, sig, { publicKey: oneOffKey });
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
See [Webhook Guide](docs/webhook-guide.md) for event types, signature algorithm, public key resolution, and best practices.
|
|
527
|
-
|
|
528
421
|
## Error Handling
|
|
529
422
|
|
|
530
423
|
API errors throw `WaffoPancakeError` with the HTTP status code and a call-stack-ordered errors array.
|
|
@@ -543,25 +436,46 @@ try {
|
|
|
543
436
|
}
|
|
544
437
|
```
|
|
545
438
|
|
|
439
|
+
## Resources
|
|
440
|
+
|
|
441
|
+
| Namespace | Methods | Description |
|
|
442
|
+
|-----------|---------|-------------|
|
|
443
|
+
| `client.checkout.authenticated` | `create()` | Authenticated checkout (recommended) |
|
|
444
|
+
| `client.checkout.anonymous` | `create()` | Anonymous checkout |
|
|
445
|
+
| `client.checkout` | `createSession()` | Low-level checkout session |
|
|
446
|
+
| `client.buyer(token)` | `cancelSubscription()` `cancelOnetimeOrder()` `reactivateSubscription()` `createRefundTicket()` `resubmitRefundTicket()` | Buyer self-service |
|
|
447
|
+
| `client.buyer(token).graphql` | `query<T>()` | Buyer-scoped GraphQL queries |
|
|
448
|
+
| `client.webhooks` | `verify<T>()` | Webhook signature verification |
|
|
449
|
+
| `client.graphql` | `query<T>()` | Merchant GraphQL queries |
|
|
450
|
+
| `client.auth` | `issueSessionToken()` | Issue a buyer session token (JWT) |
|
|
451
|
+
| `client.stores` | `create()` `update()` `delete()` | Store management |
|
|
452
|
+
| `client.storeMerchants` | `add()` `remove()` `updateRole()` | Store members (coming soon) |
|
|
453
|
+
| `client.onetimeProducts` | `create()` `update()` `publish()` `updateStatus()` | One-time products |
|
|
454
|
+
| `client.subscriptionProducts` | `create()` `update()` `publish()` `updateStatus()` | Subscription products |
|
|
455
|
+
| `client.subscriptionProductGroups` | `create()` `update()` `delete()` `publish()` | Product groups |
|
|
456
|
+
| `client.orders` | `cancelSubscription()` | Order management |
|
|
457
|
+
|
|
458
|
+
## Documentation
|
|
459
|
+
|
|
460
|
+
| Document | Content |
|
|
461
|
+
|----------|---------|
|
|
462
|
+
| [API Reference](docs/api-reference.md) | Complete method reference — parameters, return types, `BillingDetail` fields |
|
|
463
|
+
| [GraphQL Guide](docs/graphql-guide.md) | Queries, filters, analytics, introspection, delivery logs |
|
|
464
|
+
| [Webhook Guide](docs/webhook-guide.md) | Signature verification, event types, key resolution, retry mechanism |
|
|
465
|
+
| [Changelog](CHANGELOG.md) | Version history and migration guides |
|
|
466
|
+
|
|
546
467
|
## Exports
|
|
547
468
|
|
|
548
|
-
### Classes
|
|
469
|
+
### Classes & Functions
|
|
549
470
|
|
|
550
471
|
| Export | Description |
|
|
551
472
|
|--------|-------------|
|
|
552
473
|
| `WaffoPancake` | SDK client with auto-signed requests |
|
|
553
474
|
| `WaffoPancakeError` | API error with status and call-stack errors |
|
|
554
|
-
|
|
555
|
-
### Functions
|
|
556
|
-
|
|
557
|
-
| Export | Description |
|
|
558
|
-
|--------|-------------|
|
|
559
|
-
| `verifyWebhook` | RSA-SHA256 webhook signature verification |
|
|
475
|
+
| `verifyWebhook` | Standalone webhook signature verification |
|
|
560
476
|
|
|
561
477
|
### Enums
|
|
562
478
|
|
|
563
|
-
Runtime-accessible values. Both `Enum.Value` and string literal syntax are supported.
|
|
564
|
-
|
|
565
479
|
| Export | Values |
|
|
566
480
|
|--------|--------|
|
|
567
481
|
| `Environment` | `Test`, `Prod` |
|
|
@@ -571,18 +485,18 @@ Runtime-accessible values. Both `Enum.Value` and string literal syntax are suppo
|
|
|
571
485
|
| `EntityStatus` | `Active`, `Inactive`, `Suspended` |
|
|
572
486
|
| `StoreRole` | `Owner`, `Admin`, `Member` |
|
|
573
487
|
| `OnetimeOrderStatus` | `Pending`, `Completed`, `Canceled` |
|
|
574
|
-
| `SubscriptionOrderStatus` | `Pending`, `Active`, `Canceling`, `
|
|
488
|
+
| `SubscriptionOrderStatus` | `Pending`, `Active`, `Canceling`, `PastDue`, `Closed`, `Canceled`, `Expired` |
|
|
575
489
|
| `PaymentStatus` | `Pending`, `Succeeded`, `Failed`, `Canceled` |
|
|
576
490
|
| `RefundTicketStatus` | `Pending`, `Approved`, `Rejected`, `Processing`, `Succeeded`, `Failed` |
|
|
577
491
|
| `RefundStatus` | `Succeeded`, `Failed` |
|
|
578
492
|
| `MediaType` | `Image`, `Video` |
|
|
579
493
|
| `CheckoutSessionProductType` | `Onetime`, `Subscription` |
|
|
580
|
-
| `ErrorLayer` | `Gateway`, `User`, `Store`, `Product`, `Order`, `GraphQL`, `Resource`, `Email` |
|
|
494
|
+
| `ErrorLayer` | `Gateway`, `User`, `Store`, `Product`, `Order`, `Ticket`, `GraphQL`, `Resource`, `Email` |
|
|
581
495
|
| `WebhookEventType` | `OrderCompleted`, `SubscriptionActivated`, `SubscriptionPaymentSucceeded`, `SubscriptionCanceling`, `SubscriptionUncanceled`, `SubscriptionUpdated`, `SubscriptionCanceled`, `SubscriptionPastDue`, `RefundSucceeded`, `RefundFailed` |
|
|
582
496
|
|
|
583
497
|
### Types
|
|
584
498
|
|
|
585
|
-
Key types: `WaffoPancakeConfig`, `
|
|
499
|
+
Key types: `WaffoPancakeConfig`, `AuthenticatedCheckoutParams`, `AuthenticatedCheckoutResult`, `AnonymousCheckoutParams`, `CheckoutSessionResult`, `Store`, `OnetimeProductDetail`, `SubscriptionProductDetail`, `WebhookEvent<T>`, `GraphQLResponse<T>`, and 30+ more. See [API Reference](docs/api-reference.md#types) for the full list.
|
|
586
500
|
|
|
587
501
|
## Development
|
|
588
502
|
|
|
@@ -591,7 +505,7 @@ npm run lint # ESLint 9 (TypeScript ESLint + import order + JSDoc)
|
|
|
591
505
|
npm run test # Vitest
|
|
592
506
|
npm run test:watch # Vitest in watch mode
|
|
593
507
|
npm run test:coverage # Vitest with v8 coverage
|
|
594
|
-
npm run build #
|
|
508
|
+
npm run build # tsup → ESM + CJS + DTS
|
|
595
509
|
```
|
|
596
510
|
|
|
597
511
|
## Project Structure
|
|
@@ -600,7 +514,8 @@ npm run build # TypeScript compilation to dist/
|
|
|
600
514
|
src/
|
|
601
515
|
├── index.ts # Unified export entry
|
|
602
516
|
├── client.ts # WaffoPancake main class
|
|
603
|
-
├── http-client.ts # HTTP client (auto-signing + idempotency)
|
|
517
|
+
├── http-client.ts # HTTP client (API Key, auto-signing + idempotency)
|
|
518
|
+
├── buyer-http-client.ts # HTTP client (Bearer token, buyer self-service)
|
|
604
519
|
├── signing.ts # RSA-SHA256 request signing
|
|
605
520
|
├── errors.ts # WaffoPancakeError
|
|
606
521
|
├── webhooks.ts # Webhook verification (embedded keys)
|
|
@@ -613,13 +528,16 @@ src/
|
|
|
613
528
|
├── onetime-products.ts
|
|
614
529
|
├── subscription-products.ts
|
|
615
530
|
├── subscription-product-groups.ts
|
|
531
|
+
├── buyer.ts
|
|
616
532
|
├── orders.ts
|
|
617
533
|
├── checkout.ts
|
|
534
|
+
├── checkout-anonymous.ts
|
|
535
|
+
├── checkout-authenticated.ts
|
|
618
536
|
├── graphql.ts
|
|
619
537
|
└── webhooks.ts
|
|
620
538
|
docs/
|
|
621
539
|
├── api-reference.md # Complete API reference
|
|
622
|
-
├── graphql-guide.md # GraphQL
|
|
540
|
+
├── graphql-guide.md # GraphQL queries & analytics
|
|
623
541
|
└── webhook-guide.md # Webhook verification guide
|
|
624
542
|
```
|
|
625
543
|
|