@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.js CHANGED
@@ -126,7 +126,7 @@ ${bodyHash}`;
126
126
  }
127
127
 
128
128
  // src/http-client.ts
129
- var DEFAULT_BASE_URL = "https://waffo-pancake-auth-service.vercel.app";
129
+ var DEFAULT_BASE_URL = "https://api.waffo.ai";
130
130
  var HttpClient = class {
131
131
  merchantId;
132
132
  privateKey;
@@ -187,7 +187,7 @@ var AuthResource = class {
187
187
  *
188
188
  * @example
189
189
  * const { token, expiresAt } = await client.auth.issueSessionToken({
190
- * storeId: "store_xxx",
190
+ * storeId: "STO_xxx",
191
191
  * buyerIdentity: "customer@example.com",
192
192
  * });
193
193
  */
@@ -196,21 +196,107 @@ var AuthResource = class {
196
196
  }
197
197
  };
198
198
 
199
+ // src/resources/checkout-anonymous.ts
200
+ var CheckoutAnonymousResource = class {
201
+ constructor(http) {
202
+ this.http = http;
203
+ }
204
+ /**
205
+ * Create an anonymous checkout session.
206
+ *
207
+ * @param params - Checkout parameters (no buyer identity required)
208
+ * @returns Session ID, checkout URL, and expiration
209
+ *
210
+ * @example
211
+ * const result = await client.checkout.anonymous.create({
212
+ * storeId: "STO_xxx",
213
+ * productId: "PROD_xxx",
214
+ * productType: "onetime",
215
+ * currency: "USD",
216
+ * });
217
+ * // Redirect to result.checkoutUrl
218
+ */
219
+ async create(params) {
220
+ return this.http.post(
221
+ "/v1/actions/checkout/create-session",
222
+ params
223
+ );
224
+ }
225
+ };
226
+
227
+ // src/resources/checkout-authenticated.ts
228
+ var CheckoutAuthenticatedResource = class {
229
+ constructor(http) {
230
+ this.http = http;
231
+ }
232
+ /**
233
+ * Create an authenticated checkout session.
234
+ *
235
+ * Behavior:
236
+ * - Issues a session token via `issue-session-token`
237
+ * - Creates a checkout session via `create-session`
238
+ * - Appends the token to the checkout URL as a URL fragment
239
+ * - Defaults `buyerEmail` to `buyerIdentity` when omitted
240
+ *
241
+ * @param params - Checkout parameters including buyer identity
242
+ * @returns Session details with token-appended checkout URL
243
+ *
244
+ * @example
245
+ * const result = await client.checkout.authenticated.create({
246
+ * storeId: "STO_xxx",
247
+ * productId: "PROD_xxx",
248
+ * productType: "onetime",
249
+ * currency: "USD",
250
+ * buyerIdentity: "customer@example.com",
251
+ * });
252
+ * // Redirect to result.checkoutUrl (includes #token=...)
253
+ */
254
+ async create(params) {
255
+ const { buyerIdentity, buyerEmail, ...sessionFields } = params;
256
+ const [tokenResult, sessionResult] = await Promise.all([
257
+ this.http.post("/v1/actions/auth/issue-session-token", {
258
+ storeId: params.storeId,
259
+ buyerIdentity
260
+ }),
261
+ this.http.post("/v1/actions/checkout/create-session", {
262
+ ...sessionFields,
263
+ buyerEmail: buyerEmail ?? buyerIdentity
264
+ })
265
+ ]);
266
+ return {
267
+ sessionId: sessionResult.sessionId,
268
+ checkoutUrl: `${sessionResult.checkoutUrl}#token=${tokenResult.token}`,
269
+ expiresAt: sessionResult.expiresAt,
270
+ token: tokenResult.token,
271
+ tokenExpiresAt: tokenResult.expiresAt
272
+ };
273
+ }
274
+ };
275
+
199
276
  // src/resources/checkout.ts
200
277
  var CheckoutResource = class {
201
278
  constructor(http) {
202
279
  this.http = http;
280
+ this.anonymous = new CheckoutAnonymousResource(http);
281
+ this.authenticated = new CheckoutAuthenticatedResource(http);
203
282
  }
283
+ /** Anonymous checkout — visitor enters without a session token. */
284
+ anonymous;
285
+ /** Authenticated checkout — merchant provides buyer identity. */
286
+ authenticated;
204
287
  /**
205
- * Create a checkout session. Returns a URL to redirect the customer to.
288
+ * Create a checkout session (low-level). Returns a URL to redirect the customer to.
289
+ *
290
+ * For most use cases, prefer `checkout.anonymous.create()` or
291
+ * `checkout.authenticated.create()` which handle the full flow automatically.
206
292
  *
207
293
  * @param params - Checkout session parameters
208
294
  * @returns Session ID, checkout URL, and expiration
209
295
  *
210
296
  * @example
211
297
  * const session = await client.checkout.createSession({
212
- * storeId: "store_xxx",
213
- * productId: "prod_xxx",
298
+ * storeId: "STO_xxx",
299
+ * productId: "PROD_xxx",
214
300
  * productType: "onetime",
215
301
  * currency: "USD",
216
302
  * buyerEmail: "customer@example.com",
@@ -242,7 +328,7 @@ var GraphQLResource = class {
242
328
  * @example
243
329
  * const result = await client.graphql.query({
244
330
  * query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
245
- * variables: { id: "prod_xxx" },
331
+ * variables: { id: "PROD_xxx" },
246
332
  * });
247
333
  */
248
334
  async query(params) {
@@ -263,9 +349,9 @@ var OnetimeProductsResource = class {
263
349
  *
264
350
  * @example
265
351
  * const { product } = await client.onetimeProducts.create({
266
- * storeId: "store_xxx",
352
+ * storeId: "STO_xxx",
267
353
  * name: "E-Book",
268
- * prices: { USD: { amount: 2900, taxCategory: "digital_goods" } },
354
+ * prices: { USD: { amount: "29.00", taxCategory: "digital_goods" } },
269
355
  * });
270
356
  */
271
357
  async create(params) {
@@ -279,9 +365,9 @@ var OnetimeProductsResource = class {
279
365
  *
280
366
  * @example
281
367
  * const { product } = await client.onetimeProducts.update({
282
- * id: "prod_xxx",
368
+ * id: "PROD_xxx",
283
369
  * name: "E-Book v2",
284
- * prices: { USD: { amount: 3900, taxCategory: "digital_goods" } },
370
+ * prices: { USD: { amount: "39.00", taxCategory: "digital_goods" } },
285
371
  * });
286
372
  */
287
373
  async update(params) {
@@ -294,7 +380,7 @@ var OnetimeProductsResource = class {
294
380
  * @returns Published product detail
295
381
  *
296
382
  * @example
297
- * const { product } = await client.onetimeProducts.publish({ id: "prod_xxx" });
383
+ * const { product } = await client.onetimeProducts.publish({ id: "PROD_xxx" });
298
384
  */
299
385
  async publish(params) {
300
386
  return this.http.post("/v1/actions/onetime-product/publish-product", params);
@@ -307,7 +393,7 @@ var OnetimeProductsResource = class {
307
393
  *
308
394
  * @example
309
395
  * const { product } = await client.onetimeProducts.updateStatus({
310
- * id: "prod_xxx",
396
+ * id: "PROD_xxx",
311
397
  * status: ProductVersionStatus.Inactive,
312
398
  * });
313
399
  */
@@ -332,7 +418,7 @@ var OrdersResource = class {
332
418
  *
333
419
  * @example
334
420
  * const { orderId, status } = await client.orders.cancelSubscription({
335
- * orderId: "order_xxx",
421
+ * orderId: "ORD_xxx",
336
422
  * });
337
423
  * // status: "canceled" or "canceling"
338
424
  */
@@ -354,7 +440,7 @@ var StoreMerchantsResource = class {
354
440
  *
355
441
  * @example
356
442
  * const result = await client.storeMerchants.add({
357
- * storeId: "store_xxx",
443
+ * storeId: "STO_xxx",
358
444
  * email: "member@example.com",
359
445
  * role: "admin",
360
446
  * });
@@ -370,8 +456,8 @@ var StoreMerchantsResource = class {
370
456
  *
371
457
  * @example
372
458
  * const result = await client.storeMerchants.remove({
373
- * storeId: "store_xxx",
374
- * merchantId: "merchant_xxx",
459
+ * storeId: "STO_xxx",
460
+ * merchantId: "MER_xxx",
375
461
  * });
376
462
  */
377
463
  async remove(params) {
@@ -385,8 +471,8 @@ var StoreMerchantsResource = class {
385
471
  *
386
472
  * @example
387
473
  * const result = await client.storeMerchants.updateRole({
388
- * storeId: "store_xxx",
389
- * merchantId: "merchant_xxx",
474
+ * storeId: "STO_xxx",
475
+ * merchantId: "MER_xxx",
390
476
  * role: "member",
391
477
  * });
392
478
  */
@@ -420,7 +506,7 @@ var StoresResource = class {
420
506
  *
421
507
  * @example
422
508
  * const { store } = await client.stores.update({
423
- * id: "store_xxx",
509
+ * id: "STO_xxx",
424
510
  * name: "Updated Name",
425
511
  * });
426
512
  */
@@ -434,7 +520,7 @@ var StoresResource = class {
434
520
  * @returns Deleted store entity (with `deletedAt` set)
435
521
  *
436
522
  * @example
437
- * const { store } = await client.stores.delete({ id: "store_xxx" });
523
+ * const { store } = await client.stores.delete({ id: "STO_xxx" });
438
524
  */
439
525
  async delete(params) {
440
526
  return this.http.post("/v1/actions/store/delete-store", params);
@@ -454,10 +540,10 @@ var SubscriptionProductGroupsResource = class {
454
540
  *
455
541
  * @example
456
542
  * const { group } = await client.subscriptionProductGroups.create({
457
- * storeId: "store_xxx",
543
+ * storeId: "STO_xxx",
458
544
  * name: "Pro Plans",
459
545
  * rules: { sharedTrial: true },
460
- * productIds: ["prod_aaa", "prod_bbb"],
546
+ * productIds: ["PROD_aaa", "PROD_bbb"],
461
547
  * });
462
548
  */
463
549
  async create(params) {
@@ -471,8 +557,8 @@ var SubscriptionProductGroupsResource = class {
471
557
  *
472
558
  * @example
473
559
  * const { group } = await client.subscriptionProductGroups.update({
474
- * id: "group_xxx",
475
- * productIds: ["prod_aaa", "prod_bbb", "prod_ccc"],
560
+ * id: "GRP_xxx",
561
+ * productIds: ["PROD_aaa", "PROD_bbb", "PROD_ccc"],
476
562
  * });
477
563
  */
478
564
  async update(params) {
@@ -485,7 +571,7 @@ var SubscriptionProductGroupsResource = class {
485
571
  * @returns Deleted group entity
486
572
  *
487
573
  * @example
488
- * const { group } = await client.subscriptionProductGroups.delete({ id: "group_xxx" });
574
+ * const { group } = await client.subscriptionProductGroups.delete({ id: "GRP_xxx" });
489
575
  */
490
576
  async delete(params) {
491
577
  return this.http.post("/v1/actions/subscription-product-group/delete-group", params);
@@ -497,7 +583,7 @@ var SubscriptionProductGroupsResource = class {
497
583
  * @returns Published group entity
498
584
  *
499
585
  * @example
500
- * const { group } = await client.subscriptionProductGroups.publish({ id: "group_xxx" });
586
+ * const { group } = await client.subscriptionProductGroups.publish({ id: "GRP_xxx" });
501
587
  */
502
588
  async publish(params) {
503
589
  return this.http.post("/v1/actions/subscription-product-group/publish-group", params);
@@ -517,10 +603,10 @@ var SubscriptionProductsResource = class {
517
603
  *
518
604
  * @example
519
605
  * const { product } = await client.subscriptionProducts.create({
520
- * storeId: "store_xxx",
606
+ * storeId: "STO_xxx",
521
607
  * name: "Pro Plan",
522
608
  * billingPeriod: "monthly",
523
- * prices: { USD: { amount: 999, taxCategory: "saas" } },
609
+ * prices: { USD: { amount: "9.99", taxCategory: "saas" } },
524
610
  * });
525
611
  */
526
612
  async create(params) {
@@ -534,10 +620,10 @@ var SubscriptionProductsResource = class {
534
620
  *
535
621
  * @example
536
622
  * const { product } = await client.subscriptionProducts.update({
537
- * id: "prod_xxx",
623
+ * id: "PROD_xxx",
538
624
  * name: "Pro Plan v2",
539
625
  * billingPeriod: "monthly",
540
- * prices: { USD: { amount: 1499, taxCategory: "saas" } },
626
+ * prices: { USD: { amount: "14.99", taxCategory: "saas" } },
541
627
  * });
542
628
  */
543
629
  async update(params) {
@@ -550,7 +636,7 @@ var SubscriptionProductsResource = class {
550
636
  * @returns Published product detail
551
637
  *
552
638
  * @example
553
- * const { product } = await client.subscriptionProducts.publish({ id: "prod_xxx" });
639
+ * const { product } = await client.subscriptionProducts.publish({ id: "PROD_xxx" });
554
640
  */
555
641
  async publish(params) {
556
642
  return this.http.post("/v1/actions/subscription-product/publish-product", params);
@@ -563,7 +649,7 @@ var SubscriptionProductsResource = class {
563
649
  *
564
650
  * @example
565
651
  * const { product } = await client.subscriptionProducts.updateStatus({
566
- * id: "prod_xxx",
652
+ * id: "PROD_xxx",
567
653
  * status: ProductVersionStatus.Active,
568
654
  * });
569
655
  */
@@ -611,6 +697,23 @@ function rsaVerify(signatureInput, v1, publicKey) {
611
697
  verifier.update(signatureInput);
612
698
  return verifier.verify(publicKey, v1, "base64");
613
699
  }
700
+ function resolveKeyForEnv(env, configKeys) {
701
+ if (typeof configKeys === "string") {
702
+ return normalizePublicKey(configKeys);
703
+ }
704
+ if (configKeys?.[env]) {
705
+ return normalizePublicKey(configKeys[env]);
706
+ }
707
+ const envSpecific = env === "test" ? process.env.WAFFO_WEBHOOK_TEST_PUBLIC_KEY : process.env.WAFFO_WEBHOOK_PROD_PUBLIC_KEY;
708
+ if (envSpecific) {
709
+ return normalizePublicKey(envSpecific);
710
+ }
711
+ const generic = process.env.WAFFO_WEBHOOK_PUBLIC_KEY;
712
+ if (generic) {
713
+ return normalizePublicKey(generic);
714
+ }
715
+ return env === "test" ? TEST_PUBLIC_KEY : PROD_PUBLIC_KEY;
716
+ }
614
717
  function verifyWebhook(payload, signatureHeader, options) {
615
718
  if (!signatureHeader) {
616
719
  throw new Error("Missing X-Waffo-Signature header");
@@ -630,27 +733,25 @@ function verifyWebhook(payload, signatureHeader, options) {
630
733
  }
631
734
  }
632
735
  const signatureInput = `${t}.${payload}`;
633
- const customKey = options?.publicKey;
634
- if (customKey) {
635
- const normalizedKey = normalizePublicKey(customKey);
736
+ const directKey = options?.publicKey;
737
+ if (directKey) {
738
+ const normalizedKey = normalizePublicKey(directKey);
636
739
  if (!rsaVerify(signatureInput, v1, normalizedKey)) {
637
740
  throw new Error("Invalid webhook signature (custom key)");
638
741
  }
639
742
  } else {
743
+ const configKeys = options?.publicKeys;
640
744
  const env = options?.environment;
641
- if (env === "test") {
642
- if (!rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY)) {
643
- throw new Error("Invalid webhook signature (test key)");
644
- }
645
- } else if (env === "prod") {
646
- if (!rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY)) {
647
- throw new Error("Invalid webhook signature (prod key)");
745
+ if (env === "test" || env === "prod") {
746
+ const key = resolveKeyForEnv(env, configKeys);
747
+ if (!rsaVerify(signatureInput, v1, key)) {
748
+ throw new Error(`Invalid webhook signature (${env} key)`);
648
749
  }
649
750
  } else {
650
- const prodValid = rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY);
651
- if (!prodValid) {
652
- const testValid = rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY);
653
- if (!testValid) {
751
+ const prodKey = resolveKeyForEnv("prod", configKeys);
752
+ if (!rsaVerify(signatureInput, v1, prodKey)) {
753
+ const testKey = resolveKeyForEnv("test", configKeys);
754
+ if (!rsaVerify(signatureInput, v1, testKey)) {
654
755
  throw new Error("Invalid webhook signature (tried both prod and test keys)");
655
756
  }
656
757
  }
@@ -661,15 +762,19 @@ function verifyWebhook(payload, signatureHeader, options) {
661
762
 
662
763
  // src/resources/webhooks.ts
663
764
  var WebhooksResource = class {
664
- /** @param publicKey - Optional custom RSA public key (PEM or raw base64) */
665
- constructor(publicKey) {
666
- this.publicKey = publicKey;
765
+ /** @param publicKeys - Optional config-level public key(s) from WaffoPancakeConfig */
766
+ constructor(publicKeys) {
767
+ this.publicKeys = publicKeys;
667
768
  }
668
769
  /**
669
770
  * Verify and parse an incoming webhook event.
670
771
  *
671
- * When the client was created with a `webhookPublicKey`, that key is used
672
- * automatically. You can still override per-call via `options.publicKey`.
772
+ * Key resolution order:
773
+ * 1. `options.publicKey` per-call override (highest priority)
774
+ * 2. `config.webhookPublicKey[env]` or `config.webhookPublicKey` (string)
775
+ * 3. `WAFFO_WEBHOOK_{TEST|PROD}_PUBLIC_KEY` environment variable
776
+ * 4. `WAFFO_WEBHOOK_PUBLIC_KEY` environment variable
777
+ * 5. Built-in hardcoded key
673
778
  *
674
779
  * @param payload - Raw request body string (must be unparsed)
675
780
  * @param signatureHeader - Value of the `X-Waffo-Signature` header
@@ -681,13 +786,17 @@ var WebhooksResource = class {
681
786
  * const event = client.webhooks.verify(rawBody, signatureHeader);
682
787
  *
683
788
  * @example
684
- * // Override tolerance per call
685
- * const event = client.webhooks.verify(rawBody, sig, { toleranceMs: 0 });
789
+ * // Specify environment
790
+ * const event = client.webhooks.verify(rawBody, sig, { environment: "test" });
791
+ *
792
+ * @example
793
+ * // Per-call key override
794
+ * const event = client.webhooks.verify(rawBody, sig, { publicKey: oneOffKey });
686
795
  */
687
796
  verify(payload, signatureHeader, options) {
688
797
  const mergedOptions = {
689
798
  ...options,
690
- publicKey: options?.publicKey ?? this.publicKey
799
+ publicKeys: options?.publicKeys ?? this.publicKeys
691
800
  };
692
801
  return verifyWebhook(payload, signatureHeader, mergedOptions);
693
802
  }
@@ -771,8 +880,9 @@ var SubscriptionOrderStatus = /* @__PURE__ */ ((SubscriptionOrderStatus2) => {
771
880
  SubscriptionOrderStatus2["Pending"] = "pending";
772
881
  SubscriptionOrderStatus2["Active"] = "active";
773
882
  SubscriptionOrderStatus2["Canceling"] = "canceling";
774
- SubscriptionOrderStatus2["Canceled"] = "canceled";
775
883
  SubscriptionOrderStatus2["PastDue"] = "past_due";
884
+ SubscriptionOrderStatus2["Closed"] = "closed";
885
+ SubscriptionOrderStatus2["Canceled"] = "canceled";
776
886
  SubscriptionOrderStatus2["Expired"] = "expired";
777
887
  return SubscriptionOrderStatus2;
778
888
  })(SubscriptionOrderStatus || {});
@@ -813,6 +923,7 @@ var ErrorLayer = /* @__PURE__ */ ((ErrorLayer2) => {
813
923
  ErrorLayer2["Store"] = "store";
814
924
  ErrorLayer2["Product"] = "product";
815
925
  ErrorLayer2["Order"] = "order";
926
+ ErrorLayer2["Ticket"] = "ticket";
816
927
  ErrorLayer2["GraphQL"] = "graphql";
817
928
  ErrorLayer2["Resource"] = "resource";
818
929
  ErrorLayer2["Email"] = "email";