@waffo/pancake-ts 0.1.7 → 0.2.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/dist/index.cjs CHANGED
@@ -41,9 +41,6 @@ __export(index_exports, {
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
43
 
44
- // src/http-client.ts
45
- var import_node_crypto2 = require("crypto");
46
-
47
44
  // src/errors.ts
48
45
  var WaffoPancakeError = class extends Error {
49
46
  status;
@@ -57,6 +54,45 @@ var WaffoPancakeError = class extends Error {
57
54
  }
58
55
  };
59
56
 
57
+ // src/buyer-http-client.ts
58
+ var DEFAULT_BASE_URL = "https://api.waffo.ai";
59
+ var BuyerHttpClient = class {
60
+ token;
61
+ baseUrl;
62
+ _fetch;
63
+ constructor(token, config) {
64
+ this.token = token;
65
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
66
+ this._fetch = config.fetch ?? fetch;
67
+ }
68
+ /**
69
+ * Send a Bearer-authenticated POST request and return the parsed `data` field.
70
+ *
71
+ * @param path - API path
72
+ * @param body - Request body object
73
+ * @returns Parsed `data` field from the response
74
+ * @throws {WaffoPancakeError} When the API returns errors
75
+ */
76
+ async post(path, body) {
77
+ const response = await this._fetch(`${this.baseUrl}${path}`, {
78
+ method: "POST",
79
+ headers: {
80
+ "Content-Type": "application/json",
81
+ "Authorization": `Bearer ${this.token}`
82
+ },
83
+ body: JSON.stringify(body)
84
+ });
85
+ const result = await response.json();
86
+ if ("errors" in result && result.errors) {
87
+ throw new WaffoPancakeError(response.status, result.errors);
88
+ }
89
+ return result.data;
90
+ }
91
+ };
92
+
93
+ // src/http-client.ts
94
+ var import_node_crypto2 = require("crypto");
95
+
60
96
  // src/signing.ts
61
97
  var import_node_crypto = require("crypto");
62
98
  var PKCS8_HEADER = "-----BEGIN PRIVATE KEY-----";
@@ -169,7 +205,7 @@ ${bodyHash}`;
169
205
  }
170
206
 
171
207
  // src/http-client.ts
172
- var DEFAULT_BASE_URL = "https://waffo-pancake-auth-service.vercel.app";
208
+ var DEFAULT_BASE_URL2 = "https://api.waffo.ai";
173
209
  var HttpClient = class {
174
210
  merchantId;
175
211
  privateKey;
@@ -178,7 +214,7 @@ var HttpClient = class {
178
214
  constructor(config) {
179
215
  this.merchantId = config.merchantId;
180
216
  this.privateKey = normalizePrivateKey(config.privateKey);
181
- this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
217
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
182
218
  this._fetch = config.fetch ?? fetch;
183
219
  }
184
220
  /**
@@ -230,7 +266,7 @@ var AuthResource = class {
230
266
  *
231
267
  * @example
232
268
  * const { token, expiresAt } = await client.auth.issueSessionToken({
233
- * storeId: "store_xxx",
269
+ * storeId: "STO_xxx",
234
270
  * buyerIdentity: "customer@example.com",
235
271
  * });
236
272
  */
@@ -239,21 +275,222 @@ var AuthResource = class {
239
275
  }
240
276
  };
241
277
 
278
+ // src/resources/buyer.ts
279
+ var BuyerSession = class {
280
+ constructor(http) {
281
+ this.http = http;
282
+ this.graphql = new BuyerGraphQL(http);
283
+ }
284
+ /** GraphQL query access scoped to the buyer's data. */
285
+ graphql;
286
+ /**
287
+ * Cancel a subscription order.
288
+ *
289
+ * @param params - Order to cancel
290
+ * @returns Order ID and resulting status
291
+ *
292
+ * @example
293
+ * const { orderId, status } = await buyer.cancelSubscription({ orderId: "ORD_xxx" });
294
+ * // status: "canceled" (was pending) or "canceling" (was active)
295
+ */
296
+ async cancelSubscription(params) {
297
+ return this.http.post(
298
+ "/v1/actions/subscription-order/cancel-order",
299
+ params
300
+ );
301
+ }
302
+ /**
303
+ * Cancel a one-time order (only while payment is still pending).
304
+ *
305
+ * @param params - Order to cancel
306
+ * @returns Order ID and resulting status
307
+ *
308
+ * @example
309
+ * const { orderId, status } = await buyer.cancelOnetimeOrder({ orderId: "ORD_xxx" });
310
+ */
311
+ async cancelOnetimeOrder(params) {
312
+ return this.http.post(
313
+ "/v1/actions/onetime-order/cancel-order",
314
+ params
315
+ );
316
+ }
317
+ /**
318
+ * Reactivate a subscription that is in `canceling` status.
319
+ *
320
+ * @param params - Order to reactivate
321
+ * @returns Order ID and resulting status
322
+ *
323
+ * @example
324
+ * const { orderId, status } = await buyer.reactivateSubscription({ orderId: "ORD_xxx" });
325
+ * // status: "active"
326
+ */
327
+ async reactivateSubscription(params) {
328
+ return this.http.post(
329
+ "/v1/actions/subscription-order/reactivate-order",
330
+ params
331
+ );
332
+ }
333
+ /**
334
+ * Submit a refund request for a payment.
335
+ *
336
+ * @param params - Refund ticket details
337
+ * @returns Created refund ticket
338
+ *
339
+ * @example
340
+ * const { ticket } = await buyer.createRefundTicket({
341
+ * paymentId: "PAY_xxx",
342
+ * reason: "Product not as described",
343
+ * requestedAmount: { amount: "29.00", currency: "USD" },
344
+ * });
345
+ */
346
+ async createRefundTicket(params) {
347
+ return this.http.post(
348
+ "/v1/actions/refund-ticket/create-ticket",
349
+ params
350
+ );
351
+ }
352
+ /**
353
+ * Resubmit a previously rejected refund ticket with updated details.
354
+ *
355
+ * @param params - Updated ticket details
356
+ * @returns Updated refund ticket
357
+ *
358
+ * @example
359
+ * const { ticket } = await buyer.resubmitRefundTicket({
360
+ * ticketId: "TKT_xxx",
361
+ * paymentId: "PAY_xxx",
362
+ * reason: "Updated reason with more detail",
363
+ * requestedAmount: { amount: "29.00", currency: "USD" },
364
+ * });
365
+ */
366
+ async resubmitRefundTicket(params) {
367
+ return this.http.post(
368
+ "/v1/actions/refund-ticket/resubmit-ticket",
369
+ params
370
+ );
371
+ }
372
+ };
373
+ var BuyerGraphQL = class {
374
+ constructor(http) {
375
+ this.http = http;
376
+ }
377
+ /**
378
+ * Execute a GraphQL query scoped to the buyer's data.
379
+ *
380
+ * @param params - GraphQL query and variables
381
+ * @returns GraphQL response
382
+ *
383
+ * @example
384
+ * const result = await buyer.graphql.query({
385
+ * query: `query { orders { id status } }`,
386
+ * });
387
+ */
388
+ async query(params) {
389
+ return this.http.post("/v1/graphql", params);
390
+ }
391
+ };
392
+
393
+ // src/resources/checkout-anonymous.ts
394
+ var CheckoutAnonymousResource = class {
395
+ constructor(http) {
396
+ this.http = http;
397
+ }
398
+ /**
399
+ * Create an anonymous checkout session.
400
+ *
401
+ * @param params - Checkout parameters (no buyer identity required)
402
+ * @returns Session ID, checkout URL, and expiration
403
+ *
404
+ * @example
405
+ * const result = await client.checkout.anonymous.create({
406
+ * storeId: "STO_xxx",
407
+ * productId: "PROD_xxx",
408
+ * productType: "onetime",
409
+ * currency: "USD",
410
+ * });
411
+ * // Redirect to result.checkoutUrl
412
+ */
413
+ async create(params) {
414
+ return this.http.post(
415
+ "/v1/actions/checkout/create-session",
416
+ params
417
+ );
418
+ }
419
+ };
420
+
421
+ // src/resources/checkout-authenticated.ts
422
+ var CheckoutAuthenticatedResource = class {
423
+ constructor(http) {
424
+ this.http = http;
425
+ }
426
+ /**
427
+ * Create an authenticated checkout session.
428
+ *
429
+ * Behavior:
430
+ * - Issues a session token via `issue-session-token`
431
+ * - Creates a checkout session via `create-session`
432
+ * - Appends the token to the checkout URL as a URL fragment
433
+ * - Defaults `buyerEmail` to `buyerIdentity` when omitted
434
+ *
435
+ * @param params - Checkout parameters including buyer identity
436
+ * @returns Session details with token-appended checkout URL
437
+ *
438
+ * @example
439
+ * const result = await client.checkout.authenticated.create({
440
+ * storeId: "STO_xxx",
441
+ * productId: "PROD_xxx",
442
+ * productType: "onetime",
443
+ * currency: "USD",
444
+ * buyerIdentity: "customer@example.com",
445
+ * });
446
+ * // Redirect to result.checkoutUrl (includes #token=...)
447
+ */
448
+ async create(params) {
449
+ const { buyerIdentity, buyerEmail, ...sessionFields } = params;
450
+ const [tokenResult, sessionResult] = await Promise.all([
451
+ this.http.post("/v1/actions/auth/issue-session-token", {
452
+ storeId: params.storeId,
453
+ buyerIdentity
454
+ }),
455
+ this.http.post("/v1/actions/checkout/create-session", {
456
+ ...sessionFields,
457
+ buyerEmail: buyerEmail ?? buyerIdentity
458
+ })
459
+ ]);
460
+ return {
461
+ sessionId: sessionResult.sessionId,
462
+ checkoutUrl: `${sessionResult.checkoutUrl}#token=${tokenResult.token}`,
463
+ expiresAt: sessionResult.expiresAt,
464
+ token: tokenResult.token,
465
+ tokenExpiresAt: tokenResult.expiresAt
466
+ };
467
+ }
468
+ };
469
+
242
470
  // src/resources/checkout.ts
243
471
  var CheckoutResource = class {
244
472
  constructor(http) {
245
473
  this.http = http;
474
+ this.anonymous = new CheckoutAnonymousResource(http);
475
+ this.authenticated = new CheckoutAuthenticatedResource(http);
246
476
  }
477
+ /** Anonymous checkout — no buyer identity, empty form. */
478
+ anonymous;
479
+ /** Authenticated checkout — merchant provides buyer identity. */
480
+ authenticated;
247
481
  /**
248
- * Create a checkout session. Returns a URL to redirect the customer to.
482
+ * Create a checkout session (low-level). Returns a URL to redirect the customer to.
483
+ *
484
+ * For most use cases, prefer `checkout.anonymous.create()` or
485
+ * `checkout.authenticated.create()` which handle the full flow automatically.
249
486
  *
250
487
  * @param params - Checkout session parameters
251
488
  * @returns Session ID, checkout URL, and expiration
252
489
  *
253
490
  * @example
254
491
  * const session = await client.checkout.createSession({
255
- * storeId: "store_xxx",
256
- * productId: "prod_xxx",
492
+ * storeId: "STO_xxx",
493
+ * productId: "PROD_xxx",
257
494
  * productType: "onetime",
258
495
  * currency: "USD",
259
496
  * buyerEmail: "customer@example.com",
@@ -285,7 +522,7 @@ var GraphQLResource = class {
285
522
  * @example
286
523
  * const result = await client.graphql.query({
287
524
  * query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
288
- * variables: { id: "prod_xxx" },
525
+ * variables: { id: "PROD_xxx" },
289
526
  * });
290
527
  */
291
528
  async query(params) {
@@ -306,9 +543,9 @@ var OnetimeProductsResource = class {
306
543
  *
307
544
  * @example
308
545
  * const { product } = await client.onetimeProducts.create({
309
- * storeId: "store_xxx",
546
+ * storeId: "STO_xxx",
310
547
  * name: "E-Book",
311
- * prices: { USD: { amount: 2900, taxCategory: "digital_goods" } },
548
+ * prices: { USD: { amount: "29.00", taxCategory: "digital_goods" } },
312
549
  * });
313
550
  */
314
551
  async create(params) {
@@ -322,9 +559,9 @@ var OnetimeProductsResource = class {
322
559
  *
323
560
  * @example
324
561
  * const { product } = await client.onetimeProducts.update({
325
- * id: "prod_xxx",
562
+ * id: "PROD_xxx",
326
563
  * name: "E-Book v2",
327
- * prices: { USD: { amount: 3900, taxCategory: "digital_goods" } },
564
+ * prices: { USD: { amount: "39.00", taxCategory: "digital_goods" } },
328
565
  * });
329
566
  */
330
567
  async update(params) {
@@ -337,7 +574,7 @@ var OnetimeProductsResource = class {
337
574
  * @returns Published product detail
338
575
  *
339
576
  * @example
340
- * const { product } = await client.onetimeProducts.publish({ id: "prod_xxx" });
577
+ * const { product } = await client.onetimeProducts.publish({ id: "PROD_xxx" });
341
578
  */
342
579
  async publish(params) {
343
580
  return this.http.post("/v1/actions/onetime-product/publish-product", params);
@@ -350,7 +587,7 @@ var OnetimeProductsResource = class {
350
587
  *
351
588
  * @example
352
589
  * const { product } = await client.onetimeProducts.updateStatus({
353
- * id: "prod_xxx",
590
+ * id: "PROD_xxx",
354
591
  * status: ProductVersionStatus.Inactive,
355
592
  * });
356
593
  */
@@ -375,7 +612,7 @@ var OrdersResource = class {
375
612
  *
376
613
  * @example
377
614
  * const { orderId, status } = await client.orders.cancelSubscription({
378
- * orderId: "order_xxx",
615
+ * orderId: "ORD_xxx",
379
616
  * });
380
617
  * // status: "canceled" or "canceling"
381
618
  */
@@ -397,7 +634,7 @@ var StoreMerchantsResource = class {
397
634
  *
398
635
  * @example
399
636
  * const result = await client.storeMerchants.add({
400
- * storeId: "store_xxx",
637
+ * storeId: "STO_xxx",
401
638
  * email: "member@example.com",
402
639
  * role: "admin",
403
640
  * });
@@ -413,8 +650,8 @@ var StoreMerchantsResource = class {
413
650
  *
414
651
  * @example
415
652
  * const result = await client.storeMerchants.remove({
416
- * storeId: "store_xxx",
417
- * merchantId: "merchant_xxx",
653
+ * storeId: "STO_xxx",
654
+ * merchantId: "MER_xxx",
418
655
  * });
419
656
  */
420
657
  async remove(params) {
@@ -428,8 +665,8 @@ var StoreMerchantsResource = class {
428
665
  *
429
666
  * @example
430
667
  * const result = await client.storeMerchants.updateRole({
431
- * storeId: "store_xxx",
432
- * merchantId: "merchant_xxx",
668
+ * storeId: "STO_xxx",
669
+ * merchantId: "MER_xxx",
433
670
  * role: "member",
434
671
  * });
435
672
  */
@@ -463,7 +700,7 @@ var StoresResource = class {
463
700
  *
464
701
  * @example
465
702
  * const { store } = await client.stores.update({
466
- * id: "store_xxx",
703
+ * id: "STO_xxx",
467
704
  * name: "Updated Name",
468
705
  * });
469
706
  */
@@ -477,7 +714,7 @@ var StoresResource = class {
477
714
  * @returns Deleted store entity (with `deletedAt` set)
478
715
  *
479
716
  * @example
480
- * const { store } = await client.stores.delete({ id: "store_xxx" });
717
+ * const { store } = await client.stores.delete({ id: "STO_xxx" });
481
718
  */
482
719
  async delete(params) {
483
720
  return this.http.post("/v1/actions/store/delete-store", params);
@@ -497,10 +734,10 @@ var SubscriptionProductGroupsResource = class {
497
734
  *
498
735
  * @example
499
736
  * const { group } = await client.subscriptionProductGroups.create({
500
- * storeId: "store_xxx",
737
+ * storeId: "STO_xxx",
501
738
  * name: "Pro Plans",
502
739
  * rules: { sharedTrial: true },
503
- * productIds: ["prod_aaa", "prod_bbb"],
740
+ * productIds: ["PROD_aaa", "PROD_bbb"],
504
741
  * });
505
742
  */
506
743
  async create(params) {
@@ -514,8 +751,8 @@ var SubscriptionProductGroupsResource = class {
514
751
  *
515
752
  * @example
516
753
  * const { group } = await client.subscriptionProductGroups.update({
517
- * id: "group_xxx",
518
- * productIds: ["prod_aaa", "prod_bbb", "prod_ccc"],
754
+ * id: "GRP_xxx",
755
+ * productIds: ["PROD_aaa", "PROD_bbb", "PROD_ccc"],
519
756
  * });
520
757
  */
521
758
  async update(params) {
@@ -528,7 +765,7 @@ var SubscriptionProductGroupsResource = class {
528
765
  * @returns Deleted group entity
529
766
  *
530
767
  * @example
531
- * const { group } = await client.subscriptionProductGroups.delete({ id: "group_xxx" });
768
+ * const { group } = await client.subscriptionProductGroups.delete({ id: "GRP_xxx" });
532
769
  */
533
770
  async delete(params) {
534
771
  return this.http.post("/v1/actions/subscription-product-group/delete-group", params);
@@ -540,7 +777,7 @@ var SubscriptionProductGroupsResource = class {
540
777
  * @returns Published group entity
541
778
  *
542
779
  * @example
543
- * const { group } = await client.subscriptionProductGroups.publish({ id: "group_xxx" });
780
+ * const { group } = await client.subscriptionProductGroups.publish({ id: "GRP_xxx" });
544
781
  */
545
782
  async publish(params) {
546
783
  return this.http.post("/v1/actions/subscription-product-group/publish-group", params);
@@ -560,10 +797,10 @@ var SubscriptionProductsResource = class {
560
797
  *
561
798
  * @example
562
799
  * const { product } = await client.subscriptionProducts.create({
563
- * storeId: "store_xxx",
800
+ * storeId: "STO_xxx",
564
801
  * name: "Pro Plan",
565
802
  * billingPeriod: "monthly",
566
- * prices: { USD: { amount: 999, taxCategory: "saas" } },
803
+ * prices: { USD: { amount: "9.99", taxCategory: "saas" } },
567
804
  * });
568
805
  */
569
806
  async create(params) {
@@ -577,10 +814,10 @@ var SubscriptionProductsResource = class {
577
814
  *
578
815
  * @example
579
816
  * const { product } = await client.subscriptionProducts.update({
580
- * id: "prod_xxx",
817
+ * id: "PROD_xxx",
581
818
  * name: "Pro Plan v2",
582
819
  * billingPeriod: "monthly",
583
- * prices: { USD: { amount: 1499, taxCategory: "saas" } },
820
+ * prices: { USD: { amount: "14.99", taxCategory: "saas" } },
584
821
  * });
585
822
  */
586
823
  async update(params) {
@@ -593,7 +830,7 @@ var SubscriptionProductsResource = class {
593
830
  * @returns Published product detail
594
831
  *
595
832
  * @example
596
- * const { product } = await client.subscriptionProducts.publish({ id: "prod_xxx" });
833
+ * const { product } = await client.subscriptionProducts.publish({ id: "PROD_xxx" });
597
834
  */
598
835
  async publish(params) {
599
836
  return this.http.post("/v1/actions/subscription-product/publish-product", params);
@@ -606,7 +843,7 @@ var SubscriptionProductsResource = class {
606
843
  *
607
844
  * @example
608
845
  * const { product } = await client.subscriptionProducts.updateStatus({
609
- * id: "prod_xxx",
846
+ * id: "PROD_xxx",
610
847
  * status: ProductVersionStatus.Active,
611
848
  * });
612
849
  */
@@ -762,6 +999,7 @@ var WebhooksResource = class {
762
999
  // src/client.ts
763
1000
  var WaffoPancake = class {
764
1001
  http;
1002
+ config;
765
1003
  auth;
766
1004
  stores;
767
1005
  storeMerchants;
@@ -773,6 +1011,7 @@ var WaffoPancake = class {
773
1011
  graphql;
774
1012
  webhooks;
775
1013
  constructor(config) {
1014
+ this.config = config;
776
1015
  this.http = new HttpClient(config);
777
1016
  this.auth = new AuthResource(this.http);
778
1017
  this.stores = new StoresResource(this.http);
@@ -785,6 +1024,31 @@ var WaffoPancake = class {
785
1024
  this.graphql = new GraphQLResource(this.http);
786
1025
  this.webhooks = new WebhooksResource(config.webhookPublicKey);
787
1026
  }
1027
+ /**
1028
+ * Create a buyer session for self-service operations.
1029
+ *
1030
+ * The returned session uses Bearer token authentication and provides
1031
+ * methods for order cancellation, subscription management, refund tickets,
1032
+ * and scoped GraphQL queries.
1033
+ *
1034
+ * @param token - Session token from `client.auth.issueSessionToken()`
1035
+ * @returns A buyer session with self-service methods
1036
+ *
1037
+ * @example
1038
+ * const { token } = await client.auth.issueSessionToken({
1039
+ * storeId: "STO_xxx",
1040
+ * buyerIdentity: "customer@example.com",
1041
+ * });
1042
+ * const buyer = client.buyer(token);
1043
+ * await buyer.cancelSubscription({ orderId: "ORD_xxx" });
1044
+ */
1045
+ buyer(token) {
1046
+ const buyerHttp = new BuyerHttpClient(token, {
1047
+ baseUrl: this.config.baseUrl,
1048
+ fetch: this.config.fetch
1049
+ });
1050
+ return new BuyerSession(buyerHttp);
1051
+ }
788
1052
  };
789
1053
 
790
1054
  // src/types.ts
@@ -837,8 +1101,9 @@ var SubscriptionOrderStatus = /* @__PURE__ */ ((SubscriptionOrderStatus2) => {
837
1101
  SubscriptionOrderStatus2["Pending"] = "pending";
838
1102
  SubscriptionOrderStatus2["Active"] = "active";
839
1103
  SubscriptionOrderStatus2["Canceling"] = "canceling";
840
- SubscriptionOrderStatus2["Canceled"] = "canceled";
841
1104
  SubscriptionOrderStatus2["PastDue"] = "past_due";
1105
+ SubscriptionOrderStatus2["Closed"] = "closed";
1106
+ SubscriptionOrderStatus2["Canceled"] = "canceled";
842
1107
  SubscriptionOrderStatus2["Expired"] = "expired";
843
1108
  return SubscriptionOrderStatus2;
844
1109
  })(SubscriptionOrderStatus || {});
@@ -879,6 +1144,7 @@ var ErrorLayer = /* @__PURE__ */ ((ErrorLayer2) => {
879
1144
  ErrorLayer2["Store"] = "store";
880
1145
  ErrorLayer2["Product"] = "product";
881
1146
  ErrorLayer2["Order"] = "order";
1147
+ ErrorLayer2["Ticket"] = "ticket";
882
1148
  ErrorLayer2["GraphQL"] = "graphql";
883
1149
  ErrorLayer2["Resource"] = "resource";
884
1150
  ErrorLayer2["Email"] = "email";