@waffo/pancake-ts 0.1.5 → 0.1.9

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/dist/index.cjs CHANGED
@@ -169,7 +169,7 @@ ${bodyHash}`;
169
169
  }
170
170
 
171
171
  // src/http-client.ts
172
- var DEFAULT_BASE_URL = "https://waffo-pancake-auth-service.vercel.app";
172
+ var DEFAULT_BASE_URL = "https://api.waffo.ai";
173
173
  var HttpClient = class {
174
174
  merchantId;
175
175
  privateKey;
@@ -230,7 +230,7 @@ var AuthResource = class {
230
230
  *
231
231
  * @example
232
232
  * const { token, expiresAt } = await client.auth.issueSessionToken({
233
- * storeId: "store_xxx",
233
+ * storeId: "STO_xxx",
234
234
  * buyerIdentity: "customer@example.com",
235
235
  * });
236
236
  */
@@ -239,21 +239,107 @@ var AuthResource = class {
239
239
  }
240
240
  };
241
241
 
242
+ // src/resources/checkout-anonymous.ts
243
+ var CheckoutAnonymousResource = class {
244
+ constructor(http) {
245
+ this.http = http;
246
+ }
247
+ /**
248
+ * Create an anonymous checkout session.
249
+ *
250
+ * @param params - Checkout parameters (no buyer identity required)
251
+ * @returns Session ID, checkout URL, and expiration
252
+ *
253
+ * @example
254
+ * const result = await client.checkout.anonymous.create({
255
+ * storeId: "STO_xxx",
256
+ * productId: "PROD_xxx",
257
+ * productType: "onetime",
258
+ * currency: "USD",
259
+ * });
260
+ * // Redirect to result.checkoutUrl
261
+ */
262
+ async create(params) {
263
+ return this.http.post(
264
+ "/v1/actions/checkout/create-session",
265
+ params
266
+ );
267
+ }
268
+ };
269
+
270
+ // src/resources/checkout-authenticated.ts
271
+ var CheckoutAuthenticatedResource = class {
272
+ constructor(http) {
273
+ this.http = http;
274
+ }
275
+ /**
276
+ * Create an authenticated checkout session.
277
+ *
278
+ * Behavior:
279
+ * - Issues a session token via `issue-session-token`
280
+ * - Creates a checkout session via `create-session`
281
+ * - Appends the token to the checkout URL as a URL fragment
282
+ * - Defaults `buyerEmail` to `buyerIdentity` when omitted
283
+ *
284
+ * @param params - Checkout parameters including buyer identity
285
+ * @returns Session details with token-appended checkout URL
286
+ *
287
+ * @example
288
+ * const result = await client.checkout.authenticated.create({
289
+ * storeId: "STO_xxx",
290
+ * productId: "PROD_xxx",
291
+ * productType: "onetime",
292
+ * currency: "USD",
293
+ * buyerIdentity: "customer@example.com",
294
+ * });
295
+ * // Redirect to result.checkoutUrl (includes #token=...)
296
+ */
297
+ async create(params) {
298
+ const { buyerIdentity, buyerEmail, ...sessionFields } = params;
299
+ const [tokenResult, sessionResult] = await Promise.all([
300
+ this.http.post("/v1/actions/auth/issue-session-token", {
301
+ storeId: params.storeId,
302
+ buyerIdentity
303
+ }),
304
+ this.http.post("/v1/actions/checkout/create-session", {
305
+ ...sessionFields,
306
+ buyerEmail: buyerEmail ?? buyerIdentity
307
+ })
308
+ ]);
309
+ return {
310
+ sessionId: sessionResult.sessionId,
311
+ checkoutUrl: `${sessionResult.checkoutUrl}#token=${tokenResult.token}`,
312
+ expiresAt: sessionResult.expiresAt,
313
+ token: tokenResult.token,
314
+ tokenExpiresAt: tokenResult.expiresAt
315
+ };
316
+ }
317
+ };
318
+
242
319
  // src/resources/checkout.ts
243
320
  var CheckoutResource = class {
244
321
  constructor(http) {
245
322
  this.http = http;
323
+ this.anonymous = new CheckoutAnonymousResource(http);
324
+ this.authenticated = new CheckoutAuthenticatedResource(http);
246
325
  }
326
+ /** Anonymous checkout — visitor enters without a session token. */
327
+ anonymous;
328
+ /** Authenticated checkout — merchant provides buyer identity. */
329
+ authenticated;
247
330
  /**
248
- * Create a checkout session. Returns a URL to redirect the customer to.
331
+ * Create a checkout session (low-level). Returns a URL to redirect the customer to.
332
+ *
333
+ * For most use cases, prefer `checkout.anonymous.create()` or
334
+ * `checkout.authenticated.create()` which handle the full flow automatically.
249
335
  *
250
336
  * @param params - Checkout session parameters
251
337
  * @returns Session ID, checkout URL, and expiration
252
338
  *
253
339
  * @example
254
340
  * const session = await client.checkout.createSession({
255
- * storeId: "store_xxx",
256
- * productId: "prod_xxx",
341
+ * storeId: "STO_xxx",
342
+ * productId: "PROD_xxx",
257
343
  * productType: "onetime",
258
344
  * currency: "USD",
259
345
  * buyerEmail: "customer@example.com",
@@ -285,7 +371,7 @@ var GraphQLResource = class {
285
371
  * @example
286
372
  * const result = await client.graphql.query({
287
373
  * query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
288
- * variables: { id: "prod_xxx" },
374
+ * variables: { id: "PROD_xxx" },
289
375
  * });
290
376
  */
291
377
  async query(params) {
@@ -306,9 +392,9 @@ var OnetimeProductsResource = class {
306
392
  *
307
393
  * @example
308
394
  * const { product } = await client.onetimeProducts.create({
309
- * storeId: "store_xxx",
395
+ * storeId: "STO_xxx",
310
396
  * name: "E-Book",
311
- * prices: { USD: { amount: 2900, taxCategory: "digital_goods" } },
397
+ * prices: { USD: { amount: "29.00", taxCategory: "digital_goods" } },
312
398
  * });
313
399
  */
314
400
  async create(params) {
@@ -322,9 +408,9 @@ var OnetimeProductsResource = class {
322
408
  *
323
409
  * @example
324
410
  * const { product } = await client.onetimeProducts.update({
325
- * id: "prod_xxx",
411
+ * id: "PROD_xxx",
326
412
  * name: "E-Book v2",
327
- * prices: { USD: { amount: 3900, taxCategory: "digital_goods" } },
413
+ * prices: { USD: { amount: "39.00", taxCategory: "digital_goods" } },
328
414
  * });
329
415
  */
330
416
  async update(params) {
@@ -337,7 +423,7 @@ var OnetimeProductsResource = class {
337
423
  * @returns Published product detail
338
424
  *
339
425
  * @example
340
- * const { product } = await client.onetimeProducts.publish({ id: "prod_xxx" });
426
+ * const { product } = await client.onetimeProducts.publish({ id: "PROD_xxx" });
341
427
  */
342
428
  async publish(params) {
343
429
  return this.http.post("/v1/actions/onetime-product/publish-product", params);
@@ -350,7 +436,7 @@ var OnetimeProductsResource = class {
350
436
  *
351
437
  * @example
352
438
  * const { product } = await client.onetimeProducts.updateStatus({
353
- * id: "prod_xxx",
439
+ * id: "PROD_xxx",
354
440
  * status: ProductVersionStatus.Inactive,
355
441
  * });
356
442
  */
@@ -375,7 +461,7 @@ var OrdersResource = class {
375
461
  *
376
462
  * @example
377
463
  * const { orderId, status } = await client.orders.cancelSubscription({
378
- * orderId: "order_xxx",
464
+ * orderId: "ORD_xxx",
379
465
  * });
380
466
  * // status: "canceled" or "canceling"
381
467
  */
@@ -397,7 +483,7 @@ var StoreMerchantsResource = class {
397
483
  *
398
484
  * @example
399
485
  * const result = await client.storeMerchants.add({
400
- * storeId: "store_xxx",
486
+ * storeId: "STO_xxx",
401
487
  * email: "member@example.com",
402
488
  * role: "admin",
403
489
  * });
@@ -413,8 +499,8 @@ var StoreMerchantsResource = class {
413
499
  *
414
500
  * @example
415
501
  * const result = await client.storeMerchants.remove({
416
- * storeId: "store_xxx",
417
- * merchantId: "merchant_xxx",
502
+ * storeId: "STO_xxx",
503
+ * merchantId: "MER_xxx",
418
504
  * });
419
505
  */
420
506
  async remove(params) {
@@ -428,8 +514,8 @@ var StoreMerchantsResource = class {
428
514
  *
429
515
  * @example
430
516
  * const result = await client.storeMerchants.updateRole({
431
- * storeId: "store_xxx",
432
- * merchantId: "merchant_xxx",
517
+ * storeId: "STO_xxx",
518
+ * merchantId: "MER_xxx",
433
519
  * role: "member",
434
520
  * });
435
521
  */
@@ -463,7 +549,7 @@ var StoresResource = class {
463
549
  *
464
550
  * @example
465
551
  * const { store } = await client.stores.update({
466
- * id: "store_xxx",
552
+ * id: "STO_xxx",
467
553
  * name: "Updated Name",
468
554
  * });
469
555
  */
@@ -477,7 +563,7 @@ var StoresResource = class {
477
563
  * @returns Deleted store entity (with `deletedAt` set)
478
564
  *
479
565
  * @example
480
- * const { store } = await client.stores.delete({ id: "store_xxx" });
566
+ * const { store } = await client.stores.delete({ id: "STO_xxx" });
481
567
  */
482
568
  async delete(params) {
483
569
  return this.http.post("/v1/actions/store/delete-store", params);
@@ -497,10 +583,10 @@ var SubscriptionProductGroupsResource = class {
497
583
  *
498
584
  * @example
499
585
  * const { group } = await client.subscriptionProductGroups.create({
500
- * storeId: "store_xxx",
586
+ * storeId: "STO_xxx",
501
587
  * name: "Pro Plans",
502
588
  * rules: { sharedTrial: true },
503
- * productIds: ["prod_aaa", "prod_bbb"],
589
+ * productIds: ["PROD_aaa", "PROD_bbb"],
504
590
  * });
505
591
  */
506
592
  async create(params) {
@@ -514,8 +600,8 @@ var SubscriptionProductGroupsResource = class {
514
600
  *
515
601
  * @example
516
602
  * const { group } = await client.subscriptionProductGroups.update({
517
- * id: "group_xxx",
518
- * productIds: ["prod_aaa", "prod_bbb", "prod_ccc"],
603
+ * id: "GRP_xxx",
604
+ * productIds: ["PROD_aaa", "PROD_bbb", "PROD_ccc"],
519
605
  * });
520
606
  */
521
607
  async update(params) {
@@ -528,7 +614,7 @@ var SubscriptionProductGroupsResource = class {
528
614
  * @returns Deleted group entity
529
615
  *
530
616
  * @example
531
- * const { group } = await client.subscriptionProductGroups.delete({ id: "group_xxx" });
617
+ * const { group } = await client.subscriptionProductGroups.delete({ id: "GRP_xxx" });
532
618
  */
533
619
  async delete(params) {
534
620
  return this.http.post("/v1/actions/subscription-product-group/delete-group", params);
@@ -540,7 +626,7 @@ var SubscriptionProductGroupsResource = class {
540
626
  * @returns Published group entity
541
627
  *
542
628
  * @example
543
- * const { group } = await client.subscriptionProductGroups.publish({ id: "group_xxx" });
629
+ * const { group } = await client.subscriptionProductGroups.publish({ id: "GRP_xxx" });
544
630
  */
545
631
  async publish(params) {
546
632
  return this.http.post("/v1/actions/subscription-product-group/publish-group", params);
@@ -560,10 +646,10 @@ var SubscriptionProductsResource = class {
560
646
  *
561
647
  * @example
562
648
  * const { product } = await client.subscriptionProducts.create({
563
- * storeId: "store_xxx",
649
+ * storeId: "STO_xxx",
564
650
  * name: "Pro Plan",
565
651
  * billingPeriod: "monthly",
566
- * prices: { USD: { amount: 999, taxCategory: "saas" } },
652
+ * prices: { USD: { amount: "9.99", taxCategory: "saas" } },
567
653
  * });
568
654
  */
569
655
  async create(params) {
@@ -577,10 +663,10 @@ var SubscriptionProductsResource = class {
577
663
  *
578
664
  * @example
579
665
  * const { product } = await client.subscriptionProducts.update({
580
- * id: "prod_xxx",
666
+ * id: "PROD_xxx",
581
667
  * name: "Pro Plan v2",
582
668
  * billingPeriod: "monthly",
583
- * prices: { USD: { amount: 1499, taxCategory: "saas" } },
669
+ * prices: { USD: { amount: "14.99", taxCategory: "saas" } },
584
670
  * });
585
671
  */
586
672
  async update(params) {
@@ -593,7 +679,7 @@ var SubscriptionProductsResource = class {
593
679
  * @returns Published product detail
594
680
  *
595
681
  * @example
596
- * const { product } = await client.subscriptionProducts.publish({ id: "prod_xxx" });
682
+ * const { product } = await client.subscriptionProducts.publish({ id: "PROD_xxx" });
597
683
  */
598
684
  async publish(params) {
599
685
  return this.http.post("/v1/actions/subscription-product/publish-product", params);
@@ -606,7 +692,7 @@ var SubscriptionProductsResource = class {
606
692
  *
607
693
  * @example
608
694
  * const { product } = await client.subscriptionProducts.updateStatus({
609
- * id: "prod_xxx",
695
+ * id: "PROD_xxx",
610
696
  * status: ProductVersionStatus.Active,
611
697
  * });
612
698
  */
@@ -654,6 +740,23 @@ function rsaVerify(signatureInput, v1, publicKey) {
654
740
  verifier.update(signatureInput);
655
741
  return verifier.verify(publicKey, v1, "base64");
656
742
  }
743
+ function resolveKeyForEnv(env, configKeys) {
744
+ if (typeof configKeys === "string") {
745
+ return normalizePublicKey(configKeys);
746
+ }
747
+ if (configKeys?.[env]) {
748
+ return normalizePublicKey(configKeys[env]);
749
+ }
750
+ const envSpecific = env === "test" ? process.env.WAFFO_WEBHOOK_TEST_PUBLIC_KEY : process.env.WAFFO_WEBHOOK_PROD_PUBLIC_KEY;
751
+ if (envSpecific) {
752
+ return normalizePublicKey(envSpecific);
753
+ }
754
+ const generic = process.env.WAFFO_WEBHOOK_PUBLIC_KEY;
755
+ if (generic) {
756
+ return normalizePublicKey(generic);
757
+ }
758
+ return env === "test" ? TEST_PUBLIC_KEY : PROD_PUBLIC_KEY;
759
+ }
657
760
  function verifyWebhook(payload, signatureHeader, options) {
658
761
  if (!signatureHeader) {
659
762
  throw new Error("Missing X-Waffo-Signature header");
@@ -673,27 +776,25 @@ function verifyWebhook(payload, signatureHeader, options) {
673
776
  }
674
777
  }
675
778
  const signatureInput = `${t}.${payload}`;
676
- const customKey = options?.publicKey;
677
- if (customKey) {
678
- const normalizedKey = normalizePublicKey(customKey);
779
+ const directKey = options?.publicKey;
780
+ if (directKey) {
781
+ const normalizedKey = normalizePublicKey(directKey);
679
782
  if (!rsaVerify(signatureInput, v1, normalizedKey)) {
680
783
  throw new Error("Invalid webhook signature (custom key)");
681
784
  }
682
785
  } else {
786
+ const configKeys = options?.publicKeys;
683
787
  const env = options?.environment;
684
- if (env === "test") {
685
- if (!rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY)) {
686
- throw new Error("Invalid webhook signature (test key)");
687
- }
688
- } else if (env === "prod") {
689
- if (!rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY)) {
690
- throw new Error("Invalid webhook signature (prod key)");
788
+ if (env === "test" || env === "prod") {
789
+ const key = resolveKeyForEnv(env, configKeys);
790
+ if (!rsaVerify(signatureInput, v1, key)) {
791
+ throw new Error(`Invalid webhook signature (${env} key)`);
691
792
  }
692
793
  } else {
693
- const prodValid = rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY);
694
- if (!prodValid) {
695
- const testValid = rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY);
696
- if (!testValid) {
794
+ const prodKey = resolveKeyForEnv("prod", configKeys);
795
+ if (!rsaVerify(signatureInput, v1, prodKey)) {
796
+ const testKey = resolveKeyForEnv("test", configKeys);
797
+ if (!rsaVerify(signatureInput, v1, testKey)) {
697
798
  throw new Error("Invalid webhook signature (tried both prod and test keys)");
698
799
  }
699
800
  }
@@ -704,15 +805,19 @@ function verifyWebhook(payload, signatureHeader, options) {
704
805
 
705
806
  // src/resources/webhooks.ts
706
807
  var WebhooksResource = class {
707
- /** @param publicKey - Optional custom RSA public key (PEM or raw base64) */
708
- constructor(publicKey) {
709
- this.publicKey = publicKey;
808
+ /** @param publicKeys - Optional config-level public key(s) from WaffoPancakeConfig */
809
+ constructor(publicKeys) {
810
+ this.publicKeys = publicKeys;
710
811
  }
711
812
  /**
712
813
  * Verify and parse an incoming webhook event.
713
814
  *
714
- * When the client was created with a `webhookPublicKey`, that key is used
715
- * automatically. You can still override per-call via `options.publicKey`.
815
+ * Key resolution order:
816
+ * 1. `options.publicKey` per-call override (highest priority)
817
+ * 2. `config.webhookPublicKey[env]` or `config.webhookPublicKey` (string)
818
+ * 3. `WAFFO_WEBHOOK_{TEST|PROD}_PUBLIC_KEY` environment variable
819
+ * 4. `WAFFO_WEBHOOK_PUBLIC_KEY` environment variable
820
+ * 5. Built-in hardcoded key
716
821
  *
717
822
  * @param payload - Raw request body string (must be unparsed)
718
823
  * @param signatureHeader - Value of the `X-Waffo-Signature` header
@@ -724,13 +829,17 @@ var WebhooksResource = class {
724
829
  * const event = client.webhooks.verify(rawBody, signatureHeader);
725
830
  *
726
831
  * @example
727
- * // Override tolerance per call
728
- * const event = client.webhooks.verify(rawBody, sig, { toleranceMs: 0 });
832
+ * // Specify environment
833
+ * const event = client.webhooks.verify(rawBody, sig, { environment: "test" });
834
+ *
835
+ * @example
836
+ * // Per-call key override
837
+ * const event = client.webhooks.verify(rawBody, sig, { publicKey: oneOffKey });
729
838
  */
730
839
  verify(payload, signatureHeader, options) {
731
840
  const mergedOptions = {
732
841
  ...options,
733
- publicKey: options?.publicKey ?? this.publicKey
842
+ publicKeys: options?.publicKeys ?? this.publicKeys
734
843
  };
735
844
  return verifyWebhook(payload, signatureHeader, mergedOptions);
736
845
  }
@@ -814,8 +923,9 @@ var SubscriptionOrderStatus = /* @__PURE__ */ ((SubscriptionOrderStatus2) => {
814
923
  SubscriptionOrderStatus2["Pending"] = "pending";
815
924
  SubscriptionOrderStatus2["Active"] = "active";
816
925
  SubscriptionOrderStatus2["Canceling"] = "canceling";
817
- SubscriptionOrderStatus2["Canceled"] = "canceled";
818
926
  SubscriptionOrderStatus2["PastDue"] = "past_due";
927
+ SubscriptionOrderStatus2["Closed"] = "closed";
928
+ SubscriptionOrderStatus2["Canceled"] = "canceled";
819
929
  SubscriptionOrderStatus2["Expired"] = "expired";
820
930
  return SubscriptionOrderStatus2;
821
931
  })(SubscriptionOrderStatus || {});
@@ -856,6 +966,7 @@ var ErrorLayer = /* @__PURE__ */ ((ErrorLayer2) => {
856
966
  ErrorLayer2["Store"] = "store";
857
967
  ErrorLayer2["Product"] = "product";
858
968
  ErrorLayer2["Order"] = "order";
969
+ ErrorLayer2["Ticket"] = "ticket";
859
970
  ErrorLayer2["GraphQL"] = "graphql";
860
971
  ErrorLayer2["Resource"] = "resource";
861
972
  ErrorLayer2["Email"] = "email";