@waffo/pancake-ts 0.8.0 → 0.10.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 CHANGED
@@ -4,6 +4,56 @@ All notable changes to `@waffo/pancake-ts` will be documented in this file.
4
4
 
5
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
6
 
7
+ ## [0.10.0] - 2026-06-01
8
+
9
+ Expands `NotificationSettings` to the full 19-field schema (8 consumer-email + 11 merchant-notify toggles) and narrows `UpdateStoreParams.notificationSettings` to the merchant-writable subset.
10
+
11
+ ### Added
12
+
13
+ - **`NotificationSettings`** gains 11 fields aligning with the platform schema:
14
+ - Consumer email (platform-managed): `emailTrialStarted`, `emailTrialEnding`
15
+ - Merchant notify (merchant-writable): `notifySubscriptionCanceled`, `notifySubscriptionEnded`, `notifySubscriptionPastDue`, `notifySubscriptionRenewed`, `notifySubscriptionUncanceled`, `notifySubscriptionUpdated`, `notifyChargeback`, `notifyPayoutCompleted`, `notifyPayoutFailed`
16
+ - **`MerchantWritableNotificationSettings`** — new type exposing only the 11 `notify*` toggles. Use this for any merchant-side `update-store` call. `email*` toggles are managed by the PANCAKE platform (admin via DB) and silently dropped if passed to the merchant API.
17
+
18
+ ### Changed
19
+
20
+ - **`UpdateStoreParams.notificationSettings` narrowed** from `Partial<NotificationSettings>` to `Partial<MerchantWritableNotificationSettings>`. Passing `email*` keys now fails TypeScript compilation rather than being silently dropped at the server.
21
+ - README "Update store" example pruned to only show writable `notify*` fields with an explicit comment on the platform-managed `email*` subset.
22
+
23
+ ### Migration
24
+
25
+ If your `client.stores.update` calls passed any `emailOrderConfirmation` / `emailSubscription*` / `emailTrial*` keys in `notificationSettings`, remove them — these were already being dropped server-side as of v2026.5 (returned as a `warnings[].aiHint`). Only `notify*` keys are merchant-writable.
26
+
27
+ ```diff
28
+ await client.stores.update({
29
+ id: storeId,
30
+ notificationSettings: {
31
+ - emailOrderConfirmation: true,
32
+ - emailSubscriptionCycled: true,
33
+ notifyNewOrders: true,
34
+ notifyNewSubscriptions: false,
35
+ + notifyChargeback: true,
36
+ + notifyPayoutFailed: true,
37
+ },
38
+ });
39
+ ```
40
+
41
+ ## [0.9.0] - 2026-05-21
42
+
43
+ Adds flat dual-key external-id fields across write inputs, response entities, and webhook payload. The same field name now appears at every layer (REST request body / REST response / webhook payload / GraphQL).
44
+
45
+ ### Added
46
+
47
+ - `CreateCheckoutSessionParams.orderMerchantExternalId` — order business identifier (optional, max 128 chars). Inherited by orders, payments, and refunds; surfaces in webhook payload (`data.orderMerchantExternalId`) and GraphQL (`Order.orderMerchantExternalId` / `Payment.orderMerchantExternalId` / `Refund.orderMerchantExternalId`).
48
+ - `CreateRefundTicketParams.refundTicketMerchantExternalId` — refund-ticket business identifier (optional, max 128 chars). Inherited by the executed refund record on PSP success; surfaces in webhook payload (`data.refundTicketMerchantExternalId`) and GraphQL (`RefundTicket.refundTicketMerchantExternalId` / `Refund.refundTicketMerchantExternalId`).
49
+ - `RefundTicket.refundTicketMerchantExternalId` — response field (immutable across resubmits, max 128 chars).
50
+ - `WebhookEventData.orderMerchantExternalId` — present on order/payment events and on refund events (inherited from the related order).
51
+ - `WebhookEventData.refundTicketMerchantExternalId` — only present on `refund.*` events; coexists with `orderMerchantExternalId` on the same refund payload.
52
+
53
+ ### Notes
54
+
55
+ Non-breaking for SDK users — all five new fields are additive. The dual flat key naming is the canonical wire surface from this version onward.
56
+
7
57
  ## [0.8.0] - 2026-05-17
8
58
 
9
59
  ### Fixed
package/README.md CHANGED
@@ -107,6 +107,7 @@ const result = await client.checkout.authenticated.create({
107
107
  buyerEmail: "customer@example.com",
108
108
  withTrial: true, // force enable trial (false = skip, omit = default rules)
109
109
  billingDetail: { country: "US", isBusiness: false },
110
+ orderMerchantExternalId: "ORDER-2026-00891", // optional, see Business-Side Identifiers below
110
111
  });
111
112
 
112
113
  // result.checkoutUrl = "https://pancake.waffo.ai/store/{slug}/checkout/{sessionId}#token={JWT}"
@@ -125,12 +126,13 @@ const result = await client.checkout.anonymous.create({
125
126
  currency: "USD",
126
127
  });
127
128
 
128
- // Also supports priceSnapshot and withTrial
129
+ // Also supports priceSnapshot, withTrial, and orderMerchantExternalId
129
130
  const result = await client.checkout.anonymous.create({
130
131
  productId: "PROD_xxx",
131
132
  currency: "USD",
132
133
  priceSnapshot: { amount: "4.99", taxCategory: "saas" },
133
134
  withTrial: false, // skip trial for this session
135
+ orderMerchantExternalId: "ORDER-2026-00891", // optional, API Key auth only
134
136
  });
135
137
 
136
138
  window.open(result.checkoutUrl, "_blank", "noopener,noreferrer");
@@ -186,6 +188,8 @@ app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
186
188
  break;
187
189
  case WebhookEventType.RefundSucceeded:
188
190
  console.log(`Refund succeeded: ${event.data.refundReason}`);
191
+ // refund.* events carry both business identifiers (see Business-Side Identifiers section)
192
+ await ledger.closeRefundTicket(event.data.refundTicketMerchantExternalId, event.data.orderMerchantExternalId);
189
193
  break;
190
194
  }
191
195
  } catch {
@@ -253,6 +257,7 @@ const { ticket } = await buyer.createRefundTicket({
253
257
  paymentId: "PAY_xxx",
254
258
  reason: "Product not as described",
255
259
  requestedAmount: { amount: "29.00", currency: "USD" },
260
+ refundTicketMerchantExternalId: "REF-2026-00012", // optional, see Business-Side Identifiers below
256
261
  });
257
262
 
258
263
  // Resubmit a rejected refund ticket
@@ -273,6 +278,17 @@ The token is scoped to the specified store and buyer identity — buyers can onl
273
278
 
274
279
  > **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.
275
280
 
281
+ ## Business-Side Identifiers
282
+
283
+ Attach your own internal references to a checkout or a refund ticket so cross-system reconciliation does not require Waffo IDs. Two flat keys, both optional (max 128 chars):
284
+
285
+ | Field | Attach at | Inherited by |
286
+ | -------------------------------- | ------------------------------------------- | ---------------------------------------------------------- |
287
+ | `orderMerchantExternalId` | `checkout.{authenticated,anonymous}.create` | `Order`, `Payment` (incl. subscription renewals), `Refund` |
288
+ | `refundTicketMerchantExternalId` | `buyer.createRefundTicket` | `RefundTicket`, `Refund` |
289
+
290
+ The same field name appears at every layer it surfaces: request body, response entity, webhook payload (`data.orderMerchantExternalId` / `data.refundTicketMerchantExternalId`), and every GraphQL type that carries the value. A `refund.*` webhook event carries **both** keys (order key inherited from the originating order). Query by either key via GraphQL filters — see [GraphQL Guide](docs/graphql-guide.md).
291
+
276
292
  ## GraphQL — Typed Queries
277
293
 
278
294
  ```typescript
@@ -301,6 +317,16 @@ const detail = await client.graphql.query({
301
317
  }`,
302
318
  variables: { id: "STO_xxx" },
303
319
  });
320
+
321
+ // Look up by your business-side identifier (see Business-Side Identifiers above)
322
+ const byRef = await client.graphql.query({
323
+ query: `query ($ref: String!) {
324
+ payments(filter: { orderMerchantExternalId: { eq: $ref } }) {
325
+ id orderId status orderMerchantExternalId
326
+ }
327
+ }`,
328
+ variables: { ref: "ORDER-2026-00891" },
329
+ });
304
330
  ```
305
331
 
306
332
  See [GraphQL Guide](docs/graphql-guide.md) for filters, analytics queries, delivery logs, and more.
@@ -343,18 +369,16 @@ const { store } = await client.stores.create({ name: "My Store" });
343
369
 
344
370
  // Update settings (notification, checkout theme).
345
371
  // NOTE: webhook configuration moved to client.webhooks (see Webhooks section below).
372
+ // NOTE: only merchant-facing `notify*` toggles (notifyNewOrders / notifyNewSubscriptions /
373
+ // notifySubscription* / notifyChargeback / notifyPayout*) are writable here;
374
+ // consumer email toggles (emailOrderConfirmation, emailSubscription*, emailTrial*) are
375
+ // managed by the PANCAKE platform and silently dropped if passed.
346
376
  const { store: updated } = await client.stores.update({
347
377
  id: store.id,
348
378
  supportEmail: "help@example.com",
349
379
  notificationSettings: {
350
- emailOrderConfirmation: true,
351
- emailSubscriptionConfirmation: true,
352
- emailSubscriptionCycled: true,
353
- emailSubscriptionCanceled: true,
354
- emailSubscriptionRevoked: true,
355
- emailSubscriptionPastDue: true,
356
380
  notifyNewOrders: true,
357
- notifyNewSubscriptions: true,
381
+ notifyNewSubscriptions: false,
358
382
  },
359
383
  });
360
384
 
package/dist/index.cjs CHANGED
@@ -313,6 +313,11 @@ function validateEnum(field, value, allowed) {
313
313
  fail(`Invalid ${field}: expected one of [${allowed.join(", ")}], got "${value}"`);
314
314
  }
315
315
  }
316
+ function validateMaxLength(field, value, max) {
317
+ if (value !== void 0 && value.length > max) {
318
+ fail(`${field} must be at most ${max} characters, got ${value.length}`);
319
+ }
320
+ }
316
321
  function validatePositiveInteger(field, value) {
317
322
  if (!Number.isInteger(value) || value <= 0) {
318
323
  fail(`Invalid ${field}: expected positive integer, got ${value}`);
@@ -355,6 +360,7 @@ function validateCheckoutCommon(params) {
355
360
  if (params.expiresInSeconds !== void 0) {
356
361
  validatePositiveInteger("expiresInSeconds", params.expiresInSeconds);
357
362
  }
363
+ validateMaxLength("orderMerchantExternalId", params.orderMerchantExternalId, 128);
358
364
  }
359
365
 
360
366
  // src/resources/auth.ts
@@ -457,6 +463,7 @@ var BuyerSession = class {
457
463
  * paymentId: "PAY_xxx",
458
464
  * reason: "Product not as described",
459
465
  * requestedAmount: { amount: "29.00", currency: "USD" },
466
+ * refundTicketMerchantExternalId: "REF-2026-00891",
460
467
  * });
461
468
  */
462
469
  async createRefundTicket(params) {
@@ -464,6 +471,7 @@ var BuyerSession = class {
464
471
  validateRequired("reason", params.reason);
465
472
  validateAmountString("requestedAmount.amount", params.requestedAmount.amount);
466
473
  validateCurrencyCode("requestedAmount.currency", params.requestedAmount.currency);
474
+ validateMaxLength("refundTicketMerchantExternalId", params.refundTicketMerchantExternalId, 128);
467
475
  return unwrapAction(await this.http.post("/v1/actions/refund-ticket/create-ticket", params));
468
476
  }
469
477
  /**
@@ -530,12 +538,13 @@ var CheckoutAnonymousResource = class {
530
538
  * });
531
539
  *
532
540
  * @example
533
- * // Pre-fill email and billing without issuing a session token
541
+ * // Pre-fill email + billing + attach business-side order reference
534
542
  * const result = await client.checkout.anonymous.create({
535
543
  * productId: "PROD_xxx",
536
544
  * currency: "USD",
537
545
  * buyerEmail: "customer@example.com",
538
546
  * billingDetail: { country: "US", isBusiness: false, postcode: "10001" },
547
+ * orderMerchantExternalId: "ORDER-2026-00891",
539
548
  * });
540
549
  */
541
550
  async create(params) {
@@ -571,6 +580,7 @@ var CheckoutAuthenticatedResource = class {
571
580
  * currency: "USD",
572
581
  * buyerIdentity: "user-123",
573
582
  * buyerEmail: "customer@example.com",
583
+ * orderMerchantExternalId: "ORDER-2026-00891",
574
584
  * });
575
585
  * // Redirect to result.checkoutUrl (includes #token=...)
576
586
  */