Copy Skill
Open the raw Skill file and copy the full content to your clipboard.
Download Skill (.md)
Download the Skill file to add to your AI coding agent or IDE.
The
@waffo/pancake-ts SDK is a server-side TypeScript client for the Waffo Pancake payment API. It handles request signing, checkout session creation, webhook verification, and GraphQL queries.
Gotchas — Read This First
These are the mistakes that break integrations. Read before writing any code.| Gotcha | Why It Breaks | Fix |
|---|---|---|
| Reading webhook body as JSON | request.json() re-serializes the body, changing whitespace. Signature verification compares against the original raw bytes. | Always use request.text() (App Router / Hono) or express.raw() (Express). |
| Using localtunnel for webhooks | localtunnel strips custom HTTP headers. X-Waffo-Signature never reaches your handler. | Use cloudflared tunnel --url http://localhost:3000 instead. |
Forgetting .publish() | Products are created in test environment by default. Production checkout sessions for unpublished products will fail silently. | Call client.onetimeProducts.publish({ id }) or client.subscriptionProducts.publish({ id }) before going live. |
Accessing result instead of result.data in GraphQL | The GraphQL client returns { data: T | null, errors?: [...] }. Fields are nested under .data. | Always destructure: const stores = result.data?.stores ?? []. |
| Passing dollar amounts instead of cents | All amounts use smallest currency unit. $29.00 = 2900, ¥1000 = 1000 (JPY has no decimal). | Double-check: if creating a $9.99 product, pass 999 not 9.99. |
Using $id: ID! in GraphQL variables | Backend uses $id: String!, not $id: ID!. Using the wrong type silently returns null. | Always declare ID variables as String!. |
productIds in group update is full replacement | Calling subscriptionProductGroups.update({ productIds: [...] }) replaces the entire list — it does not append. | Always pass the complete desired list, not just new additions. |
Use Cases
Waffo Pancake is a merchant-of-record payment platform. The SDK fits projects that need:- SaaS subscription billing — monthly/yearly plans with upgrade/downgrade (e.g., Free/Pro/Team tiers)
- Digital product sales — one-time purchases for e-books, templates, courses, licenses
- Per-usage payments — charge per download, API call, or generated report
- Hybrid models — subscriptions + one-time purchases combined
- Multi-currency pricing — per-currency prices with automatic tax handling
| Project Type | Payment Model | Products to Create |
|---|---|---|
| AI Skills marketplace | Per-download + Pro subscription | 1 one-time ($0.99/download) + 2 subscriptions (monthly/yearly) |
| Online course platform | One-time per course | 1 one-time per course (199) |
| SaaS (Starter/Pro/Enterprise) | Subscription tiers | 3 subscriptions + 1 product group for plan switching |
| Template shop | One-time per template | 1 one-time per template, or 1 shared product with priceSnapshot override |
| API credits | Credit packs + subscription | 1 one-time per pack + subscription for monthly quota |
Installation & Setup
PEM Key Handling
Escaped newlines (simplest):Quick Start: Create from Scratch
Quick Start: Use Existing Products
If products already exist in the Dashboard, copy their IDs and go straight to checkout:API Reference
Stores
One-Time Products
digital_goods | saas | software | ebook | online_course | consulting | professional_service
Subscription Products
Subscription Product Groups
Groups enable shared trials and plan switching between subscription products.Checkout Sessions
Cancel Subscription
GraphQL Queries
Read-only. UseString! for ID variables (not ID!).
Webhook Verification
The SDK embeds public keys for both environments. Verification is one function call.Next.js App Router
Express
Hono
Verification Options
Event Types
| Event | Trigger |
|---|---|
order.completed | One-time payment succeeded |
subscription.activated | First subscription payment succeeded |
subscription.payment_succeeded | Renewal payment succeeded |
subscription.canceling | Cancel initiated (active until period end) |
subscription.uncanceled | Cancellation withdrawn |
subscription.updated | Plan changed (upgrade/downgrade) |
subscription.canceled | Subscription fully terminated |
subscription.past_due | Renewal payment failed |
refund.succeeded | Refund completed |
refund.failed | Refund failed |
Event Shape
Configuring Webhook URLs
Error Handling
errors[0] is the root cause (deepest layer), errors[n] is the outermost caller.
Development Tips
- Webhook tunneling — use
cloudflared, not localtunnel (see Gotchas).
-
Idempotency is automatic — the SDK generates deterministic keys from
merchantId + path + body. Retries are safe. -
Test → Prod workflow — products default to test. Use
.publish()to promote. Webhook events includemode: "test" | "prod"so your handler can distinguish.
Quick Start Checklist
npm install @waffo/pancake-ts- Set
WAFFO_MERCHANT_IDandWAFFO_PRIVATE_KEYenv vars - Initialize
new WaffoPancake({ merchantId, privateKey }) - Create or reference a store
- Create or reference product(s)
- Create checkout:
client.checkout.createSession(...)→ redirect tocheckoutUrl - Handle webhooks:
verifyWebhook(rawBody, sig)— must userequest.text() - Configure webhook URL:
client.stores.update({ webhookSettings: ... })