@waffo/pancake-ts 0.5.2 → 0.6.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,28 @@ 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.6.0] - 2026-05-07
8
+
9
+ ### BREAKING
10
+
11
+ - **`UpdateStoreParams.webhookSettings` removed** — webhook configuration is no longer managed through `client.stores.update()`. The legacy single-URL JSONB shape (`testWebhookUrl` / `prodWebhookUrl` / `testEvents` / `prodEvents`) has been replaced by a multi-row `store.store_webhooks` table supporting multiple webhooks and multiple channels per store.
12
+ - **`WebhookSettings` type removed** from public exports. Replaced by `StoreWebhook`, `WebhookChannel`, and the new `Add/Update/RemoveWebhookParams` types.
13
+ - The server's `update-store` endpoint accepts the legacy `webhookSettings` field for backward compatibility but **ignores it** and returns `200` with a top-level `warnings` array containing `WEBHOOK_SETTINGS_IGNORED` and an `aiHint` describing the migration path. Old SDK calls will silently lose webhook updates — upgrade to use the new methods below.
14
+
15
+ ### Added
16
+
17
+ - **`client.webhooks.add / update / remove`** — manage webhook endpoints (HTTP / Feishu / Discord / Telegram / Slack) via dedicated mutations.
18
+ - `channel` field selects the payload format. HTTP keeps the existing RSA-signed envelope (no breakage on the merchant consumption side); IM platforms render their native card / embed / attachment formats.
19
+ - `secret` field stores channel-specific credentials (e.g. Telegram `chat_id`).
20
+ - Any valid HTTPS URL accepted; merchant ensures the URL matches the chosen channel.
21
+ - Hard-delete; historical `webhook_deliveries` rows are retained with `storeWebhookId = null` for audit.
22
+ - **`StoreWebhook`, `WebhookChannel`, `AddWebhookParams`, `UpdateWebhookParams`, `RemoveWebhookParams`** types exported.
23
+
24
+ ### Migration
25
+
26
+ - **List webhooks**: query GraphQL `Store.storeWebhooks` (filtered automatically by environment via `test_mode`). The SDK does not expose a `list` method — `client.graphql.query` is the only read path, by design.
27
+ - **Create/Update/Delete**: replace `client.stores.update({ id, webhookSettings: {...} })` with `client.webhooks.add({...})` / `update({...})` / `remove({ id })`.
28
+
7
29
  ## [0.5.2] - 2026-04-22
8
30
 
9
31
  ### Added
package/README.md CHANGED
@@ -315,16 +315,11 @@ See [GraphQL Guide](docs/graphql-guide.md) for filters, analytics queries, deliv
315
315
  // Create a store
316
316
  const { store } = await client.stores.create({ name: "My Store" });
317
317
 
318
- // Update settings (webhook, notification, checkout theme)
318
+ // Update settings (notification, checkout theme).
319
+ // NOTE: webhook configuration moved to client.webhooks (see Webhooks section below).
319
320
  const { store: updated } = await client.stores.update({
320
321
  id: store.id,
321
322
  supportEmail: "help@example.com",
322
- webhookSettings: {
323
- testWebhookUrl: "https://example.com/webhooks",
324
- prodWebhookUrl: null,
325
- testEvents: ["order.completed", "subscription.activated"],
326
- prodEvents: [],
327
- },
328
323
  notificationSettings: {
329
324
  emailOrderConfirmation: true,
330
325
  emailSubscriptionConfirmation: true,
@@ -341,6 +336,51 @@ const { store: updated } = await client.stores.update({
341
336
  const { store: deleted } = await client.stores.delete({ id: store.id });
342
337
  ```
343
338
 
339
+ ### Webhooks
340
+
341
+ Manage webhook endpoints across HTTP, Feishu, Discord, Telegram, and Slack. Each store can have up to 20 webhooks across all channels.
342
+
343
+ ```typescript
344
+ // Add a standard HTTPS webhook (RSA-signed envelope)
345
+ const { webhook } = await client.webhooks.add({
346
+ storeId: store.id,
347
+ channel: "http",
348
+ url: "https://example.com/webhooks/pancake",
349
+ events: ["order.completed", "refund.succeeded"],
350
+ testMode: false,
351
+ });
352
+
353
+ // Add a Discord webhook (uses Discord embed format)
354
+ await client.webhooks.add({
355
+ storeId: store.id,
356
+ channel: "discord",
357
+ url: "https://discord.com/api/webhooks/123/abc",
358
+ events: ["order.completed"],
359
+ testMode: false,
360
+ });
361
+
362
+ // Add a Telegram webhook (chat_id stored in `secret`)
363
+ await client.webhooks.add({
364
+ storeId: store.id,
365
+ channel: "telegram",
366
+ url: "https://api.telegram.org/bot123:ABC/sendMessage",
367
+ events: ["order.completed"],
368
+ testMode: false,
369
+ secret: "8737101383",
370
+ });
371
+
372
+ // Update events
373
+ await client.webhooks.update({
374
+ id: webhook.id,
375
+ events: ["order.completed", "refund.succeeded", "subscription.canceled"],
376
+ });
377
+
378
+ // Hard-delete a webhook (delivery history retained for audit)
379
+ await client.webhooks.remove({ id: webhook.id });
380
+ ```
381
+
382
+ > **Listing**: query the configured webhook list via GraphQL `Store.storeWebhooks` (filtered by environment automatically). The SDK does not expose a `list` method — `client.graphql.query` is the only read path, by design.
383
+
344
384
  ### Products
345
385
 
346
386
  ```typescript
@@ -434,22 +474,22 @@ try {
434
474
 
435
475
  ## Resources
436
476
 
437
- | Namespace | Methods | Description |
438
- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ |
439
- | `client.checkout.authenticated` | `create()` | Authenticated checkout (recommended) |
440
- | `client.checkout.anonymous` | `create()` | Anonymous checkout |
441
- | `client.checkout` | `createSession()` | Low-level checkout session |
442
- | `client.buyer(token)` | `cancelSubscription()` `cancelOnetimeOrder()` `reactivateSubscription()` `createRefundTicket()` `resubmitRefundTicket()` | Buyer self-service |
443
- | `client.buyer(token).graphql` | `query<T>()` | Buyer-scoped GraphQL queries |
444
- | `client.webhooks` | `verify<T>()` | Webhook signature verification |
445
- | `client.graphql` | `query<T>()` | Merchant GraphQL queries |
446
- | `client.auth` | `issueSessionToken()` | Issue a buyer session token (JWT) |
447
- | `client.stores` | `create()` `update()` `delete()` | Store management |
448
- | `client.storeMerchants` | `add()` `remove()` `updateRole()` | Store members (coming soon) |
449
- | `client.onetimeProducts` | `create()` `update()` `publish()` `updateStatus()` | One-time products |
450
- | `client.subscriptionProducts` | `create()` `update()` `publish()` `updateStatus()` | Subscription products |
451
- | `client.subscriptionProductGroups` | `create()` `update()` `delete()` `publish()` | Product groups |
452
- | `client.orders` | `cancelSubscription()` | Order management |
477
+ | Namespace | Methods | Description |
478
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- |
479
+ | `client.checkout.authenticated` | `create()` | Authenticated checkout (recommended) |
480
+ | `client.checkout.anonymous` | `create()` | Anonymous checkout |
481
+ | `client.checkout` | `createSession()` | Low-level checkout session |
482
+ | `client.buyer(token)` | `cancelSubscription()` `cancelOnetimeOrder()` `reactivateSubscription()` `createRefundTicket()` `resubmitRefundTicket()` | Buyer self-service |
483
+ | `client.buyer(token).graphql` | `query<T>()` | Buyer-scoped GraphQL queries |
484
+ | `client.webhooks` | `verify<T>()` `add()` `update()` `remove()` | Webhook config + signature verification |
485
+ | `client.graphql` | `query<T>()` | Merchant GraphQL queries |
486
+ | `client.auth` | `issueSessionToken()` | Issue a buyer session token (JWT) |
487
+ | `client.stores` | `create()` `update()` `delete()` | Store management |
488
+ | `client.storeMerchants` | `add()` `remove()` `updateRole()` | Store members (coming soon) |
489
+ | `client.onetimeProducts` | `create()` `update()` `publish()` `updateStatus()` | One-time products |
490
+ | `client.subscriptionProducts` | `create()` `update()` `publish()` `updateStatus()` | Subscription products |
491
+ | `client.subscriptionProductGroups` | `create()` `update()` `delete()` `publish()` | Product groups |
492
+ | `client.orders` | `cancelSubscription()` | Order management |
453
493
 
454
494
  ## Documentation
455
495
 
package/dist/index.cjs CHANGED
@@ -833,9 +833,13 @@ var StoresResource = class {
833
833
  /**
834
834
  * Update an existing store's settings.
835
835
  *
836
- * Settings objects (`webhookSettings`, `notificationSettings`, `checkoutSettings`)
837
- * support partial updates: omitted sub-fields keep existing values, `null` clears
838
- * a field. Pass the entire settings object as `null` to clear all fields.
836
+ * Settings objects (`notificationSettings`, `checkoutSettings`) support
837
+ * partial updates: omitted sub-fields keep existing values, `null` clears a
838
+ * field. Pass the entire settings object as `null` to clear all fields.
839
+ *
840
+ * **BREAKING (2026-05)**: the legacy `webhookSettings` parameter is removed.
841
+ * Use `client.webhooks.add / update / remove` to manage webhook endpoints,
842
+ * and query the configured webhook list via GraphQL `Store.storeWebhooks`.
839
843
  *
840
844
  * @param params - Fields to update (only provided fields are changed)
841
845
  * @returns Updated store entity
@@ -848,10 +852,10 @@ var StoresResource = class {
848
852
  * });
849
853
  *
850
854
  * @example
851
- * // Clear test webhook URL while keeping other webhook settings
855
+ * // Toggle a notification preference
852
856
  * const { store } = await client.stores.update({
853
857
  * id: "STO_xxx",
854
- * webhookSettings: { testWebhookUrl: null },
858
+ * notificationSettings: { emailOrderConfirmation: false },
855
859
  * });
856
860
  */
857
861
  async update(params) {
@@ -1132,10 +1136,91 @@ function verifyWebhook(payload, signatureHeader, options) {
1132
1136
 
1133
1137
  // src/resources/webhooks.ts
1134
1138
  var WebhooksResource = class {
1135
- /** @param publicKeys - Optional config-level public key(s) from WaffoPancakeConfig */
1136
- constructor(publicKeys) {
1139
+ /**
1140
+ * @param http - HTTP client (used for add/update/remove)
1141
+ * @param publicKeys - Optional config-level public key(s) from WaffoPancakeConfig
1142
+ */
1143
+ constructor(http, publicKeys) {
1144
+ this.http = http;
1137
1145
  this.publicKeys = publicKeys;
1138
1146
  }
1147
+ /**
1148
+ * Add a webhook endpoint to a store.
1149
+ *
1150
+ * @param params - Webhook configuration
1151
+ * @returns Created webhook entity
1152
+ *
1153
+ * @example
1154
+ * // HTTP webhook (RSA-signed envelope, default)
1155
+ * const { webhook } = await client.webhooks.add({
1156
+ * storeId: "STO_xxx",
1157
+ * channel: "http",
1158
+ * url: "https://example.com/webhook",
1159
+ * events: ["order.completed", "refund.succeeded"],
1160
+ * testMode: false,
1161
+ * });
1162
+ *
1163
+ * @example
1164
+ * // Discord webhook (uses Discord embed format)
1165
+ * await client.webhooks.add({
1166
+ * storeId: "STO_xxx",
1167
+ * channel: "discord",
1168
+ * url: "https://discord.com/api/webhooks/...",
1169
+ * events: ["order.completed"],
1170
+ * testMode: false,
1171
+ * });
1172
+ *
1173
+ * @example
1174
+ * // Telegram bot — chat_id goes in `secret`; URL is the bot's sendMessage endpoint
1175
+ * await client.webhooks.add({
1176
+ * storeId: "STO_xxx",
1177
+ * channel: "telegram",
1178
+ * url: "https://api.telegram.org/bot123:ABC/sendMessage",
1179
+ * events: ["order.completed"],
1180
+ * testMode: false,
1181
+ * secret: "8737101383",
1182
+ * });
1183
+ */
1184
+ async add(params) {
1185
+ validateShortId("storeId", params.storeId, "STO");
1186
+ validateRequired("channel", params.channel);
1187
+ validateRequired("url", params.url);
1188
+ return this.http.post("/v1/actions/store/add-webhook", params);
1189
+ }
1190
+ /**
1191
+ * Update an existing webhook (only `url`, `events`, and `secret` are mutable).
1192
+ *
1193
+ * `channel` and `testMode` cannot be changed — remove the webhook and
1194
+ * re-add it instead. URL changes must remain on the same channel host
1195
+ * whitelist.
1196
+ *
1197
+ * @param params - Fields to update
1198
+ * @returns Updated webhook entity
1199
+ *
1200
+ * @example
1201
+ * await client.webhooks.update({
1202
+ * id: "11111111-2222-3333-4444-555555555555",
1203
+ * events: ["order.completed", "refund.succeeded", "subscription.canceled"],
1204
+ * });
1205
+ */
1206
+ async update(params) {
1207
+ validateRequired("id", params.id);
1208
+ return this.http.post("/v1/actions/store/update-webhook", params);
1209
+ }
1210
+ /**
1211
+ * Hard-delete a webhook. Historical `webhook_deliveries` rows are retained
1212
+ * (with `storeWebhookId` set to null) for audit purposes.
1213
+ *
1214
+ * @param params - Webhook to remove
1215
+ * @returns The removed webhook entity (snapshot before deletion)
1216
+ *
1217
+ * @example
1218
+ * await client.webhooks.remove({ id: "11111111-..." });
1219
+ */
1220
+ async remove(params) {
1221
+ validateRequired("id", params.id);
1222
+ return this.http.post("/v1/actions/store/remove-webhook", params);
1223
+ }
1139
1224
  /**
1140
1225
  * Verify and parse an incoming webhook event.
1141
1226
  *
@@ -1199,7 +1284,7 @@ var WaffoPancake = class {
1199
1284
  this.orders = new OrdersResource(this.http);
1200
1285
  this.checkout = new CheckoutResource(this.http);
1201
1286
  this.graphql = new GraphQLResource(this.http);
1202
- this.webhooks = new WebhooksResource(config.webhookPublicKey);
1287
+ this.webhooks = new WebhooksResource(this.http, config.webhookPublicKey);
1203
1288
  }
1204
1289
  /**
1205
1290
  * Create a buyer session for self-service operations.