@waffo/pancake-nextjs 0.1.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 ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@waffo/pancake-nextjs` will be documented in this file.
4
+
5
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2026-04-10
8
+
9
+ ### Added
10
+
11
+ - **`<WaffoPancakeProvider>`** — Context provider that manages the Waffo Pancake client and buyer token lifecycle. Auto-issues session tokens, caches in memory, refreshes 30s before expiry. All buyer hooks read from context — no manual token management.
12
+ - **`<CheckoutButton>`** — React component for one-click checkout. Three modes: `type="link"` (instant redirect, no API call), anonymous (API session), `type="authenticated"` (API session + token). All props flattened directly on the component. Popup blocker avoidance via synchronous `window.open`.
13
+ - **`useCheckout()`** — React hook for programmatic checkout flow. Same three modes as `CheckoutButton`. Returns `{ checkout, isLoading, error }`.
14
+ - **`Webhook()`** — Next.js route handler factory for webhook signature verification. Auto-dispatches to 10 event-specific handlers (`onOrderCompleted`, `onSubscriptionActivated`, `onRefundSucceeded`, etc.). Supports catch-all `onPayload` handler.
15
+ - **`useBuyer()`** — React hook for buyer self-service actions. Works with Provider (no args) or standalone (client + token). Actions: `cancelSubscription`, `cancelOnetimeOrder`, `reactivateSubscription`, `createRefundTicket`, `resubmitRefundTicket`, `query`.
16
+ - **`useBuyerOrders()`** — Fetch buyer's order history (one-time + subscription) with product info, payments, and billing cycle.
17
+ - **`useBuyerPayments()`** — Fetch buyer's payment records with amounts, status, and failure reasons.
18
+ - **`useBuyerRefundTickets()`** — Fetch buyer's refund tickets with status and requested amounts.
19
+ - **`useMerchantOrders(client, { storeId })`** — Fetch recent orders for a store.
20
+ - **`useMerchantSales(client, storeId)`** — Fetch sales overview: revenue, order count, customer count, status breakdown, revenue trend.
21
+ - **`useMerchantSubscriptions(client, storeId)`** — Fetch subscription overview: active/canceling/pastDue counts + full list.
22
+ - **Re-exports from `@waffo/pancake-ts`** — `WaffoPancake`, `WaffoPancakeError`, `TaxCategory`, `WebhookEventType`, `PriceInfo`, `BillingDetail`, `WebhookEvent`, `WaffoPancakeConfig`. One package, all imports.
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # @waffo/pancake-nextjs
2
+
3
+ Next.js SDK for the [Waffo Pancake](https://waffo.ai) Merchant of Record (MoR) payment platform.
4
+
5
+ - Three checkout modes: link (instant redirect), anonymous (API), authenticated (API + token)
6
+ - Popup blocker avoidance — synchronous `window.open` in click handler
7
+ - Webhook route handler with automatic signature verification and event dispatch
8
+ - Buyer self-service hooks with automatic token lifecycle management
9
+ - Server action architecture — private keys never leave the server
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @waffo/pancake-nextjs
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ > Most merchants create stores and products in the [Dashboard](https://pancake.waffo.ai/dashboard). This SDK is primarily used for **embedding checkout, webhooks, and buyer self-service** into your Next.js app.
20
+
21
+ The fastest integration — a link checkout button, no API keys needed:
22
+
23
+ ```tsx
24
+ import { CheckoutButton } from "@waffo/pancake-nextjs";
25
+
26
+ // Product ID and store slug are available in Dashboard > Products
27
+ <CheckoutButton type="link" storeSlug="my-store" productId="PROD_xxx" currency="USD">
28
+ Buy Now
29
+ </CheckoutButton>
30
+ ```
31
+
32
+ For API-level control (dynamic pricing, buyer identity, trial overrides), set up server actions first:
33
+
34
+ ```ts
35
+ // app/lib/waffo.ts — define once, import everywhere
36
+ "use server";
37
+ import {
38
+ createCheckoutAction,
39
+ createBuyerTokenAction,
40
+ createBuyerSessionAction,
41
+ createMerchantQueryAction,
42
+ } from "@waffo/pancake-nextjs/server";
43
+
44
+ // Merchant ID and API Key are available in Dashboard > Settings > Developers
45
+ const config = {
46
+ merchantId: process.env.WAFFO_MERCHANT_ID!,
47
+ privateKey: process.env.WAFFO_PRIVATE_KEY!,
48
+ };
49
+
50
+ export const checkout = createCheckoutAction(config);
51
+ export const issueBuyerToken = createBuyerTokenAction(config);
52
+ export const buyerAction = createBuyerSessionAction(config);
53
+ export const merchantQuery = createMerchantQueryAction(config);
54
+ ```
55
+
56
+ Private keys are captured in server action closures — they never reach the browser.
57
+
58
+ ## Checkout Integration
59
+
60
+ Waffo supports three checkout modes based on how much control the merchant needs:
61
+
62
+ | Mode | `type` | Needs Server Action? | Use Case |
63
+ |------|--------|:--------------------:|----------|
64
+ | **Link** | `"link"` | No | Landing pages, email campaigns. Redirects to product page which auto-creates a session. |
65
+ | **Anonymous** | omit | Yes | API-level control without buyer identity. Buyer fills in details on checkout page. |
66
+ | **Authenticated** | `"authenticated"` | Yes | Merchant provides buyer identity. Form pre-filled. Enables buyer self-service. |
67
+
68
+ > **We recommend authenticated checkout whenever possible.** It binds orders to a stable merchant-controlled identifier. In anonymous mode, the buyer self-reports their email — if they enter a different address, previous orders become unlinked and subscription trial periods can be exploited.
69
+
70
+ ### Link Checkout
71
+
72
+ No server action needed. Builds a product page URL and redirects directly:
73
+
74
+ ```tsx
75
+ import { CheckoutButton } from "@waffo/pancake-nextjs";
76
+
77
+ // Basic
78
+ <CheckoutButton type="link" storeSlug="my-store" productId="PROD_xxx" currency="USD">
79
+ Buy Now — $29
80
+ </CheckoutButton>
81
+
82
+ // With all options
83
+ <CheckoutButton
84
+ type="link"
85
+ storeSlug="my-store"
86
+ productId="PROD_xxx"
87
+ currency="USD"
88
+ email="buyer@example.com"
89
+ successUrl="https://example.com/thank-you"
90
+ country="US"
91
+ test={false}
92
+ >
93
+ Buy Now
94
+ </CheckoutButton>
95
+ ```
96
+
97
+ ### Anonymous Checkout
98
+
99
+ Creates a checkout session via server action, then redirects:
100
+
101
+ ```tsx
102
+ import { CheckoutButton } from "@waffo/pancake-nextjs";
103
+ import { checkout } from "./lib/waffo";
104
+
105
+ // Basic — use product's stored price
106
+ <CheckoutButton action={checkout} productId="PROD_xxx" currency="USD">
107
+ Buy Now
108
+ </CheckoutButton>
109
+
110
+ // Dynamic pricing — override with a coupon or volume discount
111
+ <CheckoutButton action={checkout} productId="PROD_xxx" currency="USD" priceSnapshot={{ amount: "19.99", taxCategory: "digital_goods" }}>
112
+ Buy Now — $19.99 (20% off)
113
+ </CheckoutButton>
114
+
115
+ // Subscription with trial control + billing pre-fill
116
+ <CheckoutButton action={checkout} productId="PROD_xxx" currency="USD" withTrial={true} billingDetail={{ country: "JP", isBusiness: false }}>
117
+ Start Free Trial
118
+ </CheckoutButton>
119
+ ```
120
+
121
+ ### Authenticated Checkout (Recommended)
122
+
123
+ Creates a session **and** a token. The checkout form arrives pre-filled with the buyer's identity:
124
+
125
+ ```tsx
126
+ import { CheckoutButton } from "@waffo/pancake-nextjs";
127
+ import { checkout } from "./lib/waffo";
128
+
129
+ // Basic — form pre-filled with email
130
+ <CheckoutButton type="authenticated" action={checkout} productId="PROD_xxx" currency="USD" buyerIdentity={user.email}>
131
+ Upgrade to Pro
132
+ </CheckoutButton>
133
+
134
+ // Dynamic pricing + popup mode (keeps your page open during checkout)
135
+ <CheckoutButton
136
+ type="authenticated"
137
+ action={checkout}
138
+ productId="PROD_xxx"
139
+ currency="USD"
140
+ buyerIdentity={user.email}
141
+ priceSnapshot={{ amount: "7.99", taxCategory: "saas" }}
142
+ mode="popup"
143
+ loadingChildren="Opening checkout..."
144
+ >
145
+ Upgrade — $7.99/mo
146
+ </CheckoutButton>
147
+
148
+ // Full pre-fill — identity + billing + skip trial
149
+ <CheckoutButton
150
+ type="authenticated"
151
+ action={checkout}
152
+ productId="PROD_xxx"
153
+ currency="USD"
154
+ buyerIdentity={user.email}
155
+ billingDetail={{ country: "US", isBusiness: true, state: "CA" }}
156
+ withTrial={false}
157
+ successUrl="https://example.com/dashboard?upgraded=true"
158
+ >
159
+ Skip Trial, Start Now
160
+ </CheckoutButton>
161
+ ```
162
+
163
+ ### useCheckout Hook
164
+
165
+ For programmatic control — same props as `CheckoutButton`, returns `{ checkout, isLoading, error }`:
166
+
167
+ ```tsx
168
+ import { useCheckout } from "@waffo/pancake-nextjs";
169
+ import { checkout as checkoutAction } from "./lib/waffo";
170
+
171
+ const { checkout, isLoading, error } = useCheckout({
172
+ type: "authenticated",
173
+ action: checkoutAction,
174
+ productId: "PROD_xxx",
175
+ currency: "USD",
176
+ buyerIdentity: user.email,
177
+ });
178
+
179
+ <button onClick={checkout} disabled={isLoading}>
180
+ {isLoading ? "Creating session..." : "Buy Now"}
181
+ </button>
182
+ ```
183
+
184
+ ### Navigation Modes
185
+
186
+ Both `CheckoutButton` and `useCheckout` support two navigation modes via the `mode` prop:
187
+
188
+ - `"redirect"` (default) — navigates the current page. Buyer returns via `successUrl`.
189
+ - `"popup"` — opens a new tab. Link mode opens the URL directly; API modes show a loading page first, then redirect once the session is ready.
190
+
191
+ ## Webhook Verification
192
+
193
+ After a buyer completes payment, Waffo sends webhook events to your server. The `Webhook` factory creates a Next.js route handler that verifies signatures and dispatches events:
194
+
195
+ ```ts
196
+ // app/api/webhooks/waffo/route.ts
197
+ import { Webhook } from "@waffo/pancake-nextjs";
198
+
199
+ export const POST = Webhook({
200
+ verifyOptions: { environment: "prod" },
201
+
202
+ // Event-specific handlers
203
+ onOrderCompleted: async (event) => {
204
+ console.log(`Order ${event.data.orderId} completed`);
205
+ await grantAccess(event.data.orderId, event.data.buyerEmail);
206
+ },
207
+ onSubscriptionActivated: async (event) => {
208
+ await enableSubscription(event.data.orderId);
209
+ },
210
+ onSubscriptionPaymentSucceeded: async (event) => {
211
+ await extendAccess(event.data.orderId);
212
+ },
213
+ onSubscriptionCanceled: async (event) => {
214
+ await revokeAccess(event.data.orderId);
215
+ },
216
+ onRefundSucceeded: async (event) => {
217
+ await revokeAccess(event.data.orderId);
218
+ },
219
+
220
+ // Catch-all — fires for every event (optional)
221
+ onPayload: async (event) => {
222
+ console.log(`Received ${event.eventType}`, event.data);
223
+ },
224
+ });
225
+ ```
226
+
227
+ Returns `200` on success, `401` on invalid signature, `500` if a handler throws. Full event list: `onOrderCompleted`, `onSubscriptionActivated`, `onSubscriptionPaymentSucceeded`, `onSubscriptionCanceling`, `onSubscriptionUncanceled`, `onSubscriptionUpdated`, `onSubscriptionCanceled`, `onSubscriptionPastDue`, `onRefundSucceeded`, `onRefundFailed`.
228
+
229
+ ## Buyer Self-Service
230
+
231
+ Beyond checkout, you can let buyers manage their own orders and subscriptions. Wrap with `WaffoPancakeProvider` — it auto-issues tokens and refreshes them before expiry:
232
+
233
+ ```tsx
234
+ import { WaffoPancakeProvider, useBuyer, useBuyerOrders, useBuyerPayments, useBuyerRefundTickets } from "@waffo/pancake-nextjs";
235
+ import { issueBuyerToken, buyerAction } from "./lib/waffo";
236
+
237
+ // Wrap once — provider manages token lifecycle
238
+ export default function AccountLayout({ user }: { user: { email: string } }) {
239
+ return (
240
+ <WaffoPancakeProvider buyer={{ identity: user.email, storeId: "STO_xxx", issueToken: issueBuyerToken, sessionAction: buyerAction }}>
241
+ <AccountPage />
242
+ </WaffoPancakeProvider>
243
+ );
244
+ }
245
+
246
+ // All hooks work without passing token or client
247
+ function AccountPage() {
248
+ const { data: orders, isLoading, refetch } = useBuyerOrders();
249
+ const { data: payments } = useBuyerPayments();
250
+ const { data: tickets } = useBuyerRefundTickets();
251
+ const buyer = useBuyer();
252
+
253
+ if (isLoading) return <p>Loading...</p>;
254
+
255
+ return (
256
+ <div>
257
+ {/* Subscription management */}
258
+ {orders?.subscriptionOrders.map((sub) => (
259
+ <div key={sub.id}>
260
+ <p>{sub.product?.name} — {sub.status}</p>
261
+ {sub.status === "active" && (
262
+ <button onClick={() => buyer.cancelSubscription.execute({ orderId: sub.id })}>
263
+ Cancel
264
+ </button>
265
+ )}
266
+ {sub.status === "canceling" && (
267
+ <button onClick={() => buyer.reactivateSubscription.execute({ orderId: sub.id })}>
268
+ Undo Cancellation
269
+ </button>
270
+ )}
271
+ </div>
272
+ ))}
273
+
274
+ {/* Order history */}
275
+ {orders?.onetimeOrders.map((order) => (
276
+ <p key={order.id}>{order.product?.name} — {order.status}</p>
277
+ ))}
278
+ </div>
279
+ );
280
+ }
281
+ ```
282
+
283
+ ### Buyer Hooks
284
+
285
+ | Hook | Returns | Auto-fetches? |
286
+ |------|---------|:-------------:|
287
+ | `useBuyer()` | `cancelSubscription`, `cancelOnetimeOrder`, `reactivateSubscription`, `createRefundTicket`, `resubmitRefundTicket`, `query` | No — call `.execute()` |
288
+ | `useBuyerOrders()` | `{ onetimeOrders, subscriptionOrders }` with product, payments, billing cycle | Yes |
289
+ | `useBuyerPayments()` | Payment records — amount, status, failure reason | Yes |
290
+ | `useBuyerRefundTickets()` | Refund tickets — status, reason, amount | Yes |
291
+
292
+ Action hooks return `{ execute, isLoading, error, data }`. Data hooks return `{ data, isLoading, error, refetch }`.
293
+
294
+ ## Merchant Data
295
+
296
+ Pre-built hooks for merchant dashboards. Pass a server action and store ID:
297
+
298
+ ```tsx
299
+ import { useMerchantSales, useMerchantOrders, useMerchantSubscriptions } from "@waffo/pancake-nextjs";
300
+ import { merchantQuery } from "./lib/waffo";
301
+
302
+ function Dashboard() {
303
+ const storeId = "STO_xxx";
304
+ const { data: sales } = useMerchantSales(merchantQuery, storeId);
305
+ const { data: orders, refetch } = useMerchantOrders(merchantQuery, { storeId, limit: 10 });
306
+ const { data: subs } = useMerchantSubscriptions(merchantQuery, storeId);
307
+
308
+ // sales: { totalRevenue, totalOrders, totalCustomers, currency, ordersByStatus, revenueByPeriod }
309
+ // orders: { onetimeOrders, subscriptionOrders } — each with product, payments, testMode
310
+ // subs: { activeCount, cancelingCount, pastDueCount, totalCount, subscriptions }
311
+ }
312
+ ```
313
+
314
+ All merchant hooks return `{ data, isLoading, error, refetch }`.
315
+
316
+ ## Server Actions
317
+
318
+ | Factory | Returns | Description |
319
+ |---------|---------|-------------|
320
+ | `createCheckoutAction(config)` | `CheckoutAction` | Checkout session creation (anonymous + authenticated) |
321
+ | `createBuyerTokenAction(config)` | `BuyerTokenAction` | Buyer session token issuance |
322
+ | `createBuyerSessionAction(config)` | `BuyerSessionAction` | Buyer self-service operations |
323
+ | `createMerchantQueryAction(config)` | `MerchantQueryAction` | Merchant GraphQL queries |
324
+
325
+ Import from `@waffo/pancake-nextjs/server`. Config requires `merchantId` and `privateKey`.
326
+
327
+ ## Exports
328
+
329
+ ### Classes & Enums
330
+
331
+ | Export | Description |
332
+ |--------|-------------|
333
+ | `WaffoPancakeError` | API error with HTTP status and call-stack errors |
334
+ | `TaxCategory` | `DigitalGoods`, `SaaS`, `Software`, `Ebook`, `OnlineCourse`, `Consulting`, `ProfessionalService` |
335
+ | `WebhookEventType` | `OrderCompleted`, `SubscriptionActivated`, `SubscriptionCanceled`, etc. |
336
+
337
+ ### Types
338
+
339
+ Key types: `PriceInfo`, `BillingDetail`, `WebhookEvent`, `CheckoutAction`, `BuyerTokenAction`, `BuyerSessionAction`, `MerchantQueryAction`, `BuyerConfig`, `CheckoutButtonProps`, `CheckoutMode`, `UseCheckoutReturn`, `UseBuyerReturn`, `BuyerActionState<T>`, `QueryState<T>`, `SalesOverview`, `SubscriptionOverview`, `WebhookConfig`.
340
+
341
+ ## Development
342
+
343
+ ```bash
344
+ npm run lint # ESLint 9 (TypeScript ESLint + import order + JSDoc + react-hooks)
345
+ npm run test # Vitest (jsdom)
346
+ npm run test:watch # Vitest in watch mode
347
+ npm run test:coverage # Vitest with v8 coverage
348
+ npm run build # tsup → client (ESM+CJS "use client") + server (ESM+CJS)
349
+ ```
350
+
351
+ ## Project Structure
352
+
353
+ ```
354
+ src/
355
+ ├── index.ts # Client barrel export ("use client")
356
+ ├── server.ts # Server barrel export (action factories)
357
+ ├── types.ts # Shared type definitions
358
+ ├── provider.tsx # <WaffoPancakeProvider> — token lifecycle
359
+ ├── checkout-button.tsx # <CheckoutButton> component
360
+ ├── use-checkout.ts # useCheckout() — link + anonymous + authenticated
361
+ ├── use-buyer.ts # useBuyer() — buyer actions
362
+ ├── use-buyer-data.ts # useBuyerOrders / useBuyerPayments / useBuyerRefundTickets
363
+ ├── use-merchant-data.ts # useMerchantOrders / useMerchantSales / useMerchantSubscriptions
364
+ ├── use-query.ts # Shared useQuery helper
365
+ ├── webhook.ts # Webhook() route handler factory
366
+ └── __tests__/
367
+ ```
368
+
369
+ ## License
370
+
371
+ MIT