@fantastic.dev/better-stripe 0.9.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/MIGRATION.md +12 -0
  3. package/README.md +29 -1
  4. package/dist/client/billing/checkout.d.ts +1 -0
  5. package/dist/client/billing/checkout.d.ts.map +1 -1
  6. package/dist/client/billing/checkout.js +5 -1
  7. package/dist/client/billing/checkout.js.map +1 -1
  8. package/dist/client/billing/providerReads.d.ts +18 -0
  9. package/dist/client/billing/providerReads.d.ts.map +1 -0
  10. package/dist/client/billing/providerReads.js +15 -0
  11. package/dist/client/billing/providerReads.js.map +1 -0
  12. package/dist/client/billing/subscriptions.d.ts +1 -1
  13. package/dist/client/billing/subscriptions.d.ts.map +1 -1
  14. package/dist/client/billing/subscriptions.js.map +1 -1
  15. package/dist/client/connect/payoutSnapshot.d.ts +28 -0
  16. package/dist/client/connect/payoutSnapshot.d.ts.map +1 -0
  17. package/dist/client/connect/payoutSnapshot.js +81 -0
  18. package/dist/client/connect/payoutSnapshot.js.map +1 -0
  19. package/dist/client/connect/transfers.d.ts.map +1 -1
  20. package/dist/client/connect/transfers.js +13 -32
  21. package/dist/client/connect/transfers.js.map +1 -1
  22. package/dist/client/core/accounts.d.ts +12 -23
  23. package/dist/client/core/accounts.d.ts.map +1 -1
  24. package/dist/client/core/accounts.js +5 -1
  25. package/dist/client/core/accounts.js.map +1 -1
  26. package/dist/client/core/idempotency.d.ts +5 -0
  27. package/dist/client/core/idempotency.d.ts.map +1 -0
  28. package/dist/client/core/idempotency.js +10 -0
  29. package/dist/client/core/idempotency.js.map +1 -0
  30. package/dist/client/index.d.ts +31 -25
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/client/index.js +29 -52
  33. package/dist/client/index.js.map +1 -1
  34. package/dist/client/settlement/allocation.d.ts +30 -0
  35. package/dist/client/settlement/allocation.d.ts.map +1 -0
  36. package/dist/client/settlement/allocation.js +71 -0
  37. package/dist/client/settlement/allocation.js.map +1 -0
  38. package/dist/client/settlement/commissions.d.ts +27 -0
  39. package/dist/client/settlement/commissions.d.ts.map +1 -0
  40. package/dist/client/settlement/commissions.js +69 -0
  41. package/dist/client/settlement/commissions.js.map +1 -0
  42. package/dist/client/settlement/execution.d.ts +27 -0
  43. package/dist/client/settlement/execution.d.ts.map +1 -0
  44. package/dist/client/settlement/execution.js +70 -0
  45. package/dist/client/settlement/execution.js.map +1 -0
  46. package/dist/client/settlement/reconciliation.d.ts +10 -0
  47. package/dist/client/settlement/reconciliation.d.ts.map +1 -0
  48. package/dist/client/settlement/reconciliation.js +73 -0
  49. package/dist/client/settlement/reconciliation.js.map +1 -0
  50. package/dist/client/settlement/settleSale.d.ts +21 -0
  51. package/dist/client/settlement/settleSale.d.ts.map +1 -0
  52. package/dist/client/settlement/settleSale.js +27 -0
  53. package/dist/client/settlement/settleSale.js.map +1 -0
  54. package/dist/client/settlement/verifiedSale.d.ts +25 -0
  55. package/dist/client/settlement/verifiedSale.d.ts.map +1 -0
  56. package/dist/client/settlement/verifiedSale.js +100 -0
  57. package/dist/client/settlement/verifiedSale.js.map +1 -0
  58. package/dist/client/webhooks/asyncHooks.d.ts +5 -0
  59. package/dist/client/webhooks/asyncHooks.d.ts.map +1 -0
  60. package/dist/client/webhooks/asyncHooks.js +20 -0
  61. package/dist/client/webhooks/asyncHooks.js.map +1 -0
  62. package/dist/component/_generated/component.d.ts +80 -0
  63. package/dist/component/_generated/component.d.ts.map +1 -1
  64. package/dist/component/schema.d.ts +67 -0
  65. package/dist/component/schema.js +4 -0
  66. package/dist/component/schema.js.map +1 -1
  67. package/dist/component/settlement/mutations.d.ts +47 -0
  68. package/dist/component/settlement/mutations.d.ts.map +1 -0
  69. package/dist/component/settlement/mutations.js +104 -0
  70. package/dist/component/settlement/mutations.js.map +1 -0
  71. package/dist/component/settlement/queries.d.ts +29 -0
  72. package/dist/component/settlement/queries.d.ts.map +1 -0
  73. package/dist/component/settlement/queries.js +35 -0
  74. package/dist/component/settlement/queries.js.map +1 -0
  75. package/dist/component/settlement/schema.d.ts +99 -0
  76. package/dist/component/settlement/schema.d.ts.map +1 -0
  77. package/dist/component/settlement/schema.js +35 -0
  78. package/dist/component/settlement/schema.js.map +1 -0
  79. package/dist/react/workflows/Checkout.d.ts +3 -1
  80. package/dist/react/workflows/Checkout.d.ts.map +1 -1
  81. package/dist/react/workflows/Checkout.js +3 -2
  82. package/dist/react/workflows/Checkout.js.map +1 -1
  83. package/dist/react/workflows/Payouts.d.ts +3 -18
  84. package/dist/react/workflows/Payouts.d.ts.map +1 -1
  85. package/dist/react/workflows/Payouts.js +2 -2
  86. package/dist/react/workflows/Payouts.js.map +1 -1
  87. package/docs/react-workflows.md +5 -2
  88. package/docs/settlement.md +158 -0
  89. package/package.json +2 -1
  90. package/src/client/billing/checkout-idempotency.test.ts +82 -0
  91. package/src/client/billing/checkout.ts +6 -1
  92. package/src/client/billing/providerReads.test.ts +60 -0
  93. package/src/client/billing/providerReads.ts +30 -0
  94. package/src/client/billing/subscriptions.ts +1 -8
  95. package/src/client/connect/payoutSnapshot.test.ts +91 -0
  96. package/src/client/connect/payoutSnapshot.ts +130 -0
  97. package/src/client/connect/transfers.test.ts +15 -4
  98. package/src/client/connect/transfers.ts +13 -42
  99. package/src/client/core/account-idempotency.test.ts +78 -0
  100. package/src/client/core/accounts.ts +18 -25
  101. package/src/client/core/idempotency.test.ts +10 -0
  102. package/src/client/core/idempotency.ts +11 -0
  103. package/src/client/index.ts +84 -103
  104. package/src/client/settlement/allocation.test.ts +98 -0
  105. package/src/client/settlement/allocation.ts +110 -0
  106. package/src/client/settlement/commissions.test.ts +103 -0
  107. package/src/client/settlement/commissions.ts +125 -0
  108. package/src/client/settlement/execution.test.ts +186 -0
  109. package/src/client/settlement/execution.ts +134 -0
  110. package/src/client/settlement/reconciliation.ts +119 -0
  111. package/src/client/settlement/settleSale.ts +59 -0
  112. package/src/client/settlement/verifiedSale.test.ts +139 -0
  113. package/src/client/settlement/verifiedSale.ts +159 -0
  114. package/src/client/webhooks/asyncHooks.ts +38 -0
  115. package/src/client/webhooks/processors.test.ts +9 -9
  116. package/src/component/_generated/api.ts +4 -0
  117. package/src/component/_generated/component.ts +100 -0
  118. package/src/component/schema.ts +8 -0
  119. package/src/component/settlement/mutations.ts +119 -0
  120. package/src/component/settlement/queries.ts +38 -0
  121. package/src/component/settlement/schema.ts +45 -0
  122. package/src/component/settlement-claims.test.ts +77 -0
  123. package/src/react/components/EmbeddedDisputes.render.test.tsx +2 -2
  124. package/src/react/index.payment-element.test.ts +22 -37
  125. package/src/react/workflows/Checkout.test.tsx +30 -0
  126. package/src/react/workflows/Checkout.tsx +10 -3
  127. package/src/react/workflows/Payouts.test.tsx +33 -0
  128. package/src/react/workflows/Payouts.tsx +21 -15
@@ -1,3 +1,5 @@
1
+ import { recoverSettlementTransfers } from "../settlement/reconciliation.js";
2
+ import { executeResolvedTransfers } from "../settlement/execution.js";
1
3
  import type Stripe from "stripe";
2
4
 
3
5
  import type { SplitRecipient } from "../../component/lib/fees.js";
@@ -303,6 +305,7 @@ export async function reverseTransfers(
303
305
  throw new Error("reverseTransfers: amount must be a non-negative integer");
304
306
  }
305
307
 
308
+ await recoverSettlementTransfers(stripe, component, ctx, opts.sourceChargeId);
306
309
  if (opts.operationId) {
307
310
  const claim = (await runMutationOrThrow(
308
311
  ctx,
@@ -421,6 +424,7 @@ export async function reverseTransfersForRefund(
421
424
  return { reversals: [] };
422
425
  }
423
426
 
427
+ await recoverSettlementTransfers(stripe, component, ctx, opts.sourceChargeId);
424
428
  const claim = (await runMutationOrThrow(
425
429
  ctx,
426
430
  componentRef(component, "connect/mutations/claimReversalSlices"),
@@ -460,48 +464,15 @@ export async function createSplitTransfers(
460
464
  ): Promise<SplitResult> {
461
465
  const result = computeSplit(opts.amount, opts.feeConfig, opts.split);
462
466
 
463
- // Ledger-level idempotency (BTS-24): skip any (charge, destination, role) leg
464
- // already recorded, so a webhook retry — even after a partial run — never
465
- // double-transfers. Complements the deterministic Stripe idempotency key.
466
- const existing = ((await ctx.runQuery(
467
- componentRef(component, "connect/queries/listTransfersByCharge"),
468
- { sourceChargeId: opts.sourceChargeId },
469
- )) ?? []) as LedgerTransfer[];
470
- const done = new Set(
471
- existing.map((t) => `${t.destinationAccountId}:${t.role ?? ""}`),
472
- );
473
-
474
- for (const t of result.transfers) {
475
- if (t.amount <= 0) continue;
476
- if (done.has(`${t.destinationAccountId}:${t.role}`)) continue;
477
- const transfer = await stripe.transfers.create(
478
- {
479
- amount: t.amount,
480
- currency: opts.currency,
481
- destination: t.destinationAccountId,
482
- source_transaction: opts.sourceChargeId,
483
- metadata: { bsRole: t.role, bsSourceCharge: opts.sourceChargeId },
484
- },
485
- // Deterministic key so a webhook retry can't double-transfer (BTS-24).
486
- {
487
- idempotencyKey: `bs_split_${opts.sourceChargeId}_${t.destinationAccountId}_${t.role}`,
488
- },
489
- );
490
- await runMutationOrThrow(
491
- ctx,
492
- componentRef(component, "connect/mutations/upsertTransfer"),
493
- {
494
- stripeTransferId: transfer.id,
495
- sourceChargeId: opts.sourceChargeId,
496
- destinationAccountId: t.destinationAccountId,
497
- amount: t.amount,
498
- currency: opts.currency,
499
- role: t.role,
500
- status: "paid" as const,
501
- paymentId: opts.paymentId,
502
- },
503
- );
504
- }
467
+ await executeResolvedTransfers(stripe, component, ctx, {
468
+ sourceChargeId: opts.sourceChargeId,
469
+ gross: opts.amount,
470
+ currency: opts.currency,
471
+ processingFee: 0,
472
+ platformNet: result.platformRetained,
473
+ transfers: result.transfers,
474
+ paymentId: opts.paymentId,
475
+ });
505
476
 
506
477
  return result;
507
478
  }
@@ -0,0 +1,78 @@
1
+ // @vitest-environment edge-runtime
2
+ import type Stripe from "stripe";
3
+ import { convexTest } from "convex-test";
4
+ import { componentsGeneric, defineSchema } from "convex/server";
5
+ import { expect, it, vi } from "vitest";
6
+ import componentSchema from "../../component/schema.js";
7
+ import type { Component, RunCtx } from "../helpers.js";
8
+ import { createAccount, getOrCreateAccount } from "./accounts.js";
9
+ const modules = import.meta.glob("../../component/**/*.*s");
10
+ it("reuses one provider account for concurrent calls and recovers a persistence failure", async () => {
11
+ const t = convexTest(defineSchema({}), modules);
12
+ t.registerComponent("betterStripe", componentSchema, modules);
13
+ const component = componentsGeneric().betterStripe as unknown as Component;
14
+ const provider = new Map<string, { params: string; id: string }>();
15
+ let created = 0;
16
+ const sdk = {
17
+ v2: {
18
+ core: {
19
+ accounts: {
20
+ create: vi.fn(
21
+ async (params: unknown, options?: { idempotencyKey?: string }) => {
22
+ const key = options?.idempotencyKey ?? `unkeyed-${++created}`,
23
+ existing = provider.get(key),
24
+ fingerprint = JSON.stringify(params);
25
+ if (existing && existing.params !== fingerprint)
26
+ throw new Error("idempotency_error");
27
+ if (existing) return { id: existing.id };
28
+ const value = { params: fingerprint, id: `acct_${++created}` };
29
+ provider.set(key, value);
30
+ return { id: value.id };
31
+ },
32
+ ),
33
+ },
34
+ },
35
+ },
36
+ };
37
+ let fail = true;
38
+ const ctx: RunCtx = {
39
+ runQuery: t.query as RunCtx["runQuery"],
40
+ runMutation: (async (
41
+ ref: Parameters<typeof t.mutation>[0],
42
+ args: Record<string, unknown>,
43
+ ) => {
44
+ if (fail) {
45
+ fail = false;
46
+ throw new Error("persistence unavailable");
47
+ }
48
+ return t.mutation(ref, args);
49
+ }) as RunCtx["runMutation"],
50
+ };
51
+ const opts = {
52
+ userId: "buyer",
53
+ email: "buyer@example.com",
54
+ idempotencyKey: "account:buyer:v1",
55
+ };
56
+ await expect(
57
+ createAccount(sdk as unknown as Stripe, component, ctx, opts),
58
+ ).rejects.toThrow("persistence unavailable");
59
+ const [a, b] = await Promise.all([
60
+ getOrCreateAccount(sdk as unknown as Stripe, component, ctx, opts),
61
+ getOrCreateAccount(sdk as unknown as Stripe, component, ctx, opts),
62
+ ]);
63
+ expect(a.stripeAccountId).toBe(b.stripeAccountId);
64
+ expect(provider.size).toBe(1);
65
+ expect(sdk.v2.core.accounts.create).toHaveBeenCalledWith(expect.anything(), {
66
+ idempotencyKey: opts.idempotencyKey,
67
+ });
68
+ await expect(
69
+ createAccount(sdk as unknown as Stripe, component, ctx, {
70
+ ...opts,
71
+ email: "changed@example.com",
72
+ }),
73
+ ).rejects.toThrow("idempotency_error");
74
+ const row = await t.query(component.core.queries.getAccountByUserId, {
75
+ userId: "buyer",
76
+ });
77
+ expect(row?.stripeAccountId).toBe(a.stripeAccountId);
78
+ });
@@ -1,3 +1,14 @@
1
+ import { idempotencyOptions } from "./idempotency.js";
2
+ export type CreateAccountOptions = {
3
+ userId: string;
4
+ email?: string;
5
+ name?: string;
6
+ country?: string;
7
+ orgId?: string;
8
+ metadata?: Record<string, string>;
9
+ /** Stable account-provisioning key; use a different key for a different account. */
10
+ idempotencyKey?: string;
11
+ };
1
12
  import type Stripe from "stripe";
2
13
 
3
14
  import type { Component, RunCtx } from "../helpers.js";
@@ -31,14 +42,7 @@ export async function createAccount(
31
42
  stripe: Stripe,
32
43
  component: Component,
33
44
  ctx: RunCtx,
34
- opts: {
35
- userId: string;
36
- email?: string;
37
- name?: string;
38
- country?: string;
39
- orgId?: string;
40
- metadata?: Record<string, string>;
41
- },
45
+ opts: CreateAccountOptions,
42
46
  ) {
43
47
  const metadata = {
44
48
  ...(opts.metadata ?? {}),
@@ -56,7 +60,10 @@ export async function createAccount(
56
60
  };
57
61
  }
58
62
 
59
- const account = await stripe.v2.core.accounts.create(createParams);
63
+ const requestOptions = idempotencyOptions(opts.idempotencyKey);
64
+ const account = requestOptions
65
+ ? await stripe.v2.core.accounts.create(createParams, requestOptions)
66
+ : await stripe.v2.core.accounts.create(createParams);
60
67
 
61
68
  await runMutationOrThrow(
62
69
  ctx,
@@ -191,14 +198,7 @@ export async function getOrCreateAccount(
191
198
  stripe: Stripe,
192
199
  component: Component,
193
200
  ctx: RunCtx,
194
- opts: {
195
- userId: string;
196
- email?: string;
197
- name?: string;
198
- country?: string;
199
- orgId?: string;
200
- metadata?: Record<string, string>;
201
- },
201
+ opts: CreateAccountOptions,
202
202
  ) {
203
203
  const existing = opts.orgId
204
204
  ? await ctx.runQuery(
@@ -238,14 +238,7 @@ export async function ensureCustomerAccount(
238
238
  stripe: Stripe,
239
239
  component: Component,
240
240
  ctx: RunCtx,
241
- opts: {
242
- userId: string;
243
- email?: string;
244
- name?: string;
245
- country?: string;
246
- orgId?: string;
247
- metadata?: Record<string, string>;
248
- },
241
+ opts: CreateAccountOptions,
249
242
  ): Promise<{
250
243
  stripeAccountId: string;
251
244
  accountId: string | null;
@@ -0,0 +1,10 @@
1
+ import { expect, it } from "vitest";
2
+ import { idempotencyOptions } from "./idempotency.js";
3
+ it("preserves omitted options and validates explicit request identities", () => {
4
+ expect(idempotencyOptions(undefined)).toBeUndefined();
5
+ expect(idempotencyOptions("logical-order:123")).toEqual({
6
+ idempotencyKey: "logical-order:123",
7
+ });
8
+ for (const key of ["", " ", "bad\nkey", "x".repeat(256)])
9
+ expect(() => idempotencyOptions(key)).toThrow();
10
+ });
@@ -0,0 +1,11 @@
1
+ import { throwStripeError } from "../errors.js";
2
+ /** Explicit request identity. Omission preserves the SDK's existing behavior. */
3
+ export function idempotencyOptions(key: string | undefined) {
4
+ if (key === undefined) return undefined;
5
+ if (!key.trim() || key.length > 255 || /[\r\n]/.test(key))
6
+ throwStripeError(
7
+ "INVALID_CONFIGURATION",
8
+ "Idempotency keys must be nonempty, at most 255 characters, and contain no line breaks.",
9
+ );
10
+ return { idempotencyKey: key };
11
+ }
@@ -1,3 +1,37 @@
1
+ export type {
2
+ CheckoutSessionReadOptions,
3
+ SubscriptionReadOptions,
4
+ CouponReadOptions,
5
+ } from "./billing/providerReads.js";
6
+ import * as providerReads from "./billing/providerReads.js";
7
+ export type { CreateAccountOptions } from "./core/accounts.js";
8
+ import { buildAsyncHookRoutes } from "./webhooks/asyncHooks.js";
9
+ import * as saleContextImpl from "./settlement/verifiedSale.js";
10
+ import * as settlementImpl from "./settlement/settleSale.js";
11
+ import * as commissionImpl from "./settlement/commissions.js";
12
+ import * as payoutSnapshotImpl from "./connect/payoutSnapshot.js";
13
+ export { allocateSale } from "./settlement/allocation.js";
14
+ export type {
15
+ SaleAllocation,
16
+ SaleAllocationOptions,
17
+ } from "./settlement/allocation.js";
18
+ export type {
19
+ VerifiedSale,
20
+ VerifiedSaleOptions,
21
+ } from "./settlement/verifiedSale.js";
22
+ export type { SettleSaleOptions } from "./settlement/settleSale.js";
23
+ export type { SettlementResult } from "./settlement/execution.js";
24
+ export type {
25
+ CommissionTerm,
26
+ CommissionSelectionOptions,
27
+ CommissionSelection,
28
+ CommissionDecision,
29
+ } from "./settlement/commissions.js";
30
+ export type {
31
+ RecipientPayoutSnapshot,
32
+ RecipientPayoutRow,
33
+ PayoutSnapshotOptions,
34
+ } from "./connect/payoutSnapshot.js";
1
35
  import { internalActionGeneric, internalMutationGeneric } from "convex/server";
2
36
  import { v } from "convex/values";
3
37
  import Stripe from "stripe";
@@ -146,9 +180,7 @@ function clonePlatformFee(config: PlatformFeeConfig): PlatformFeeConfig {
146
180
  };
147
181
  }
148
182
 
149
- // =============================================================================
150
183
  // BETTER STRIPE CLIENT
151
- // =============================================================================
152
184
 
153
185
  export class BetterStripe {
154
186
  public component: Component;
@@ -212,9 +244,7 @@ export class BetterStripe {
212
244
  return getStripeClient(this.apiKey, apiVersion);
213
245
  }
214
246
 
215
- // ============================================================================
216
247
  // Static defaults (preserved for backwards compatibility)
217
- // ============================================================================
218
248
 
219
249
  static DEFAULT_ACCOUNT_CONFIGURATION =
220
250
  accountsImpl.DEFAULT_ACCOUNT_CONFIGURATION;
@@ -222,21 +252,9 @@ export class BetterStripe {
222
252
  static DEFAULT_CUSTOMER_CONFIGURATION =
223
253
  accountsImpl.DEFAULT_CUSTOMER_CONFIGURATION;
224
254
 
225
- // ============================================================================
226
255
  // ACCOUNT METHODS
227
- // ============================================================================
228
256
 
229
- async createAccount(
230
- ctx: RunCtx,
231
- opts: {
232
- userId: string;
233
- email?: string;
234
- name?: string;
235
- country?: string;
236
- orgId?: string;
237
- metadata?: Record<string, string>;
238
- },
239
- ) {
257
+ async createAccount(ctx: RunCtx, opts: accountsImpl.CreateAccountOptions) {
240
258
  return accountsImpl.createAccount(this.stripe(), this.component, ctx, opts);
241
259
  }
242
260
 
@@ -276,14 +294,7 @@ export class BetterStripe {
276
294
 
277
295
  async getOrCreateAccount(
278
296
  ctx: RunCtx,
279
- opts: {
280
- userId: string;
281
- email?: string;
282
- name?: string;
283
- country?: string;
284
- orgId?: string;
285
- metadata?: Record<string, string>;
286
- },
297
+ opts: accountsImpl.CreateAccountOptions,
287
298
  ) {
288
299
  return accountsImpl.getOrCreateAccount(
289
300
  this.stripe(),
@@ -453,14 +464,7 @@ export class BetterStripe {
453
464
  */
454
465
  async ensureCustomerAccount(
455
466
  ctx: RunCtx,
456
- opts: {
457
- userId: string;
458
- email?: string;
459
- name?: string;
460
- country?: string;
461
- orgId?: string;
462
- metadata?: Record<string, string>;
463
- },
467
+ opts: accountsImpl.CreateAccountOptions,
464
468
  ) {
465
469
  return accountsImpl.ensureCustomerAccount(
466
470
  this.stripe(),
@@ -470,9 +474,7 @@ export class BetterStripe {
470
474
  );
471
475
  }
472
476
 
473
- // ============================================================================
474
477
  // PRODUCT METHODS
475
- // ============================================================================
476
478
 
477
479
  async createProduct(
478
480
  ctx: RunCtx,
@@ -559,9 +561,7 @@ export class BetterStripe {
559
561
  return productsImpl.deleteProduct(this.stripe(), ctx, opts);
560
562
  }
561
563
 
562
- // ============================================================================
563
564
  // PRICE METHODS
564
- // ============================================================================
565
565
 
566
566
  async createPrice(
567
567
  ctx: RunCtx,
@@ -649,13 +649,12 @@ export class BetterStripe {
649
649
  return pricesImpl.listStripePrices(this.stripe(), ctx, opts);
650
650
  }
651
651
 
652
- // ============================================================================
653
652
  // CHECKOUT METHODS
654
- // ============================================================================
655
653
 
656
654
  async createCheckoutSession(
657
655
  ctx: RunCtx,
658
656
  opts: {
657
+ idempotencyKey?: string;
659
658
  userId: string;
660
659
  orgId?: string;
661
660
  stripePriceId: string;
@@ -744,9 +743,7 @@ export class BetterStripe {
744
743
  return checkoutImpl.updateCheckoutSession(this.stripe(), ctx, opts);
745
744
  }
746
745
 
747
- // ============================================================================
748
746
  // SUBSCRIPTION METHODS
749
- // ============================================================================
750
747
 
751
748
  /**
752
749
  * Create a subscription directly for a V2 buyer (`customer_account`), off the
@@ -943,14 +940,7 @@ export class BetterStripe {
943
940
  accountId?: string;
944
941
  userId: string;
945
942
  orgId?: string;
946
- status:
947
- | "active"
948
- | "trialing"
949
- | "past_due"
950
- | "canceled"
951
- | "incomplete"
952
- | "unpaid"
953
- | "paused";
943
+ status: SubscriptionStatus;
954
944
  priceId?: string;
955
945
  quantity?: number;
956
946
  currentPeriodStart?: string;
@@ -970,9 +960,7 @@ export class BetterStripe {
970
960
  return subscriptionsImpl.getTrialStatus(this.component, ctx, opts);
971
961
  }
972
962
 
973
- // ============================================================================
974
963
  // INVOICE METHODS
975
- // ============================================================================
976
964
 
977
965
  async listInvoices(
978
966
  ctx: RunCtx,
@@ -1039,9 +1027,7 @@ export class BetterStripe {
1039
1027
  return invoicesImpl.getInvoiceFromStripe(this.stripe(), ctx, opts);
1040
1028
  }
1041
1029
 
1042
- // ============================================================================
1043
1030
  // PAYMENT METHOD METHODS
1044
- // ============================================================================
1045
1031
 
1046
1032
  async listPaymentMethods(
1047
1033
  ctx: RunCtx,
@@ -1072,9 +1058,7 @@ export class BetterStripe {
1072
1058
  return paymentMethodsImpl.setDefaultPaymentMethod(this.stripe(), ctx, opts);
1073
1059
  }
1074
1060
 
1075
- // ============================================================================
1076
1061
  // ACCOUNT LINK METHODS
1077
- // ============================================================================
1078
1062
 
1079
1063
  async createAccountLink(
1080
1064
  ctx: RunCtx,
@@ -1150,9 +1134,7 @@ export class BetterStripe {
1150
1134
  return accountLinksImpl.getCountrySpecs(this.stripe(), ctx, opts);
1151
1135
  }
1152
1136
 
1153
- // ============================================================================
1154
1137
  // PAYOUT METHODS
1155
- // ============================================================================
1156
1138
 
1157
1139
  async createPayout(
1158
1140
  ctx: RunCtx,
@@ -1253,6 +1235,50 @@ export class BetterStripe {
1253
1235
  * (`{ available, pending, currency }`, minor units) per currency — each entry
1254
1236
  * feeds the headless `PayoutSchedule` component's `balance` prop directly.
1255
1237
  */
1238
+ async getCheckoutSessionFromStripe(
1239
+ _ctx: RunCtx,
1240
+ opts: providerReads.CheckoutSessionReadOptions,
1241
+ ) {
1242
+ return providerReads.getCheckoutSessionFromStripe(this.stripe(), opts);
1243
+ }
1244
+ async getSubscriptionFromStripe(
1245
+ _ctx: RunCtx,
1246
+ opts: providerReads.SubscriptionReadOptions,
1247
+ ) {
1248
+ return providerReads.getSubscriptionFromStripe(this.stripe(), opts);
1249
+ }
1250
+ async getCouponFromStripe(
1251
+ _ctx: RunCtx,
1252
+ opts: providerReads.CouponReadOptions,
1253
+ ) {
1254
+ return providerReads.getCouponFromStripe(this.stripe(), opts);
1255
+ }
1256
+ async getVerifiedSale(
1257
+ _ctx: RunCtx,
1258
+ opts: saleContextImpl.VerifiedSaleOptions,
1259
+ ) {
1260
+ return saleContextImpl.getVerifiedSale(this.stripe(), opts);
1261
+ }
1262
+ async settleSale(ctx: RunCtx, opts: settlementImpl.SettleSaleOptions) {
1263
+ return settlementImpl.settleSale(this.stripe(), this.component, ctx, opts);
1264
+ }
1265
+ async selectCommissionInvoice(
1266
+ ctx: RunCtx,
1267
+ opts: commissionImpl.CommissionSelectionOptions,
1268
+ ) {
1269
+ return commissionImpl.selectCommissionInvoice(
1270
+ this.stripe(),
1271
+ this.component,
1272
+ ctx,
1273
+ opts,
1274
+ );
1275
+ }
1276
+ async getPayoutSnapshot(
1277
+ _ctx: RunCtx,
1278
+ opts: payoutSnapshotImpl.PayoutSnapshotOptions,
1279
+ ) {
1280
+ return payoutSnapshotImpl.getPayoutSnapshot(this.stripe(), opts);
1281
+ }
1256
1282
  async getAccountBalance(_ctx: RunCtx, opts: { stripeAccountId: string }) {
1257
1283
  return payoutsImpl.getAccountBalance(this.stripe(), opts);
1258
1284
  }
@@ -1268,9 +1294,7 @@ export class BetterStripe {
1268
1294
  return payoutsImpl.listPayouts(this.component, ctx, opts);
1269
1295
  }
1270
1296
 
1271
- // ============================================================================
1272
1297
  // REFUND METHODS
1273
- // ============================================================================
1274
1298
 
1275
1299
  /**
1276
1300
  * Issue a refund (BTS-34). `refundApplicationFee` and `reverseTransfer`
@@ -1325,9 +1349,7 @@ export class BetterStripe {
1325
1349
  return refundsImpl.listRefunds(this.component, ctx, opts);
1326
1350
  }
1327
1351
 
1328
- // ============================================================================
1329
1352
  // DISPUTE METHODS
1330
- // ============================================================================
1331
1353
 
1332
1354
  async getDisputeByStripeId(
1333
1355
  ctx: RunCtx,
@@ -1397,9 +1419,7 @@ export class BetterStripe {
1397
1419
  return disputesImpl.closeDispute(this.stripe(), this.component, ctx, opts);
1398
1420
  }
1399
1421
 
1400
- // ============================================================================
1401
1422
  // WEBHOOK ENDPOINT MANAGEMENT
1402
- // ============================================================================
1403
1423
 
1404
1424
  async listWebhookEndpoints(ctx: RunCtx, opts?: { limit?: number }) {
1405
1425
  return webhookEndpointsImpl.listWebhookEndpoints(this.stripe(), ctx, opts);
@@ -1439,9 +1459,7 @@ export class BetterStripe {
1439
1459
  return webhookEndpointsImpl.setupEventDestination(this.stripe(), ctx, opts);
1440
1460
  }
1441
1461
 
1442
- // ============================================================================
1443
1462
  // WEBHOOK EVENT QUERIES
1444
- // ============================================================================
1445
1463
 
1446
1464
  async listWebhookEvents(
1447
1465
  ctx: RunCtx,
@@ -1462,17 +1480,13 @@ export class BetterStripe {
1462
1480
  }>;
1463
1481
  }
1464
1482
 
1465
- // ============================================================================
1466
1483
  // DATA MANAGEMENT
1467
- // ============================================================================
1468
1484
 
1469
1485
  async clearAll(ctx: RunCtx): Promise<{ cleared: number; tables: string[] }> {
1470
1486
  return dataImpl.clearAll(this.component, ctx);
1471
1487
  }
1472
1488
 
1473
- // ============================================================================
1474
1489
  // SYNC METHODS
1475
- // ============================================================================
1476
1490
 
1477
1491
  async syncAllAccounts(ctx: RunCtx) {
1478
1492
  return accountsImpl.syncAllAccounts(this.stripe(), this.component, ctx);
@@ -1490,9 +1504,7 @@ export class BetterStripe {
1490
1504
  );
1491
1505
  }
1492
1506
 
1493
- // ============================================================================
1494
1507
  // WEBHOOK HANDLER EXPORT (BetterAuth-style)
1495
- // ============================================================================
1496
1508
 
1497
1509
  /**
1498
1510
  * Build the two webhook handler function definitions for the app to export
@@ -1811,38 +1823,7 @@ export class BetterStripe {
1811
1823
  ),
1812
1824
  };
1813
1825
 
1814
- /** One routing entry per async hook — identical mapping to the old API. */
1815
- const ASYNC: Record<
1816
- AsyncHookName,
1817
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined
1818
- > = {
1819
- afterAccountUpdated: hooks?.onAccountUpdated as
1820
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1821
- afterCheckoutCompleted: hooks?.onCheckoutCompleted as
1822
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1823
- afterSubscriptionUpdated: hooks?.onSubscriptionUpdated as
1824
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1825
- afterSubscriptionCanceled: hooks?.onSubscriptionCanceled as
1826
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1827
- afterTrialEnding: hooks?.onTrialEnding as
1828
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1829
- afterInvoicePaid: hooks?.onInvoicePaid as
1830
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1831
- afterInvoicePaymentFailed: hooks?.onInvoicePaymentFailed as
1832
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1833
- afterPaymentSucceeded: hooks?.onPaymentSucceeded as
1834
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1835
- afterPaymentFailed: hooks?.onPaymentFailed as
1836
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1837
- afterPayoutCompleted: hooks?.onPayoutCompleted as
1838
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1839
- afterRefundCreated: hooks?.onRefundCreated as
1840
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1841
- afterDisputeCreated: hooks?.onDisputeCreated as
1842
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1843
- afterDisputeClosed: hooks?.onDisputeClosed as
1844
- ((ctx: AsyncHookCtx, doc: unknown) => Promise<void>) | undefined,
1845
- };
1826
+ const ASYNC = buildAsyncHookRoutes(hooks);
1846
1827
 
1847
1828
  const syncWebhook = internalMutationGeneric({
1848
1829
  args: { dispatcher: v.string(), data: v.any() },