@fantastic.dev/better-stripe 0.14.0 → 0.16.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 (138) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/MIGRATION.md +6 -0
  3. package/README.md +40 -1
  4. package/dist/client/billing/checkoutOptions.d.ts +27 -0
  5. package/dist/client/billing/checkoutOptions.d.ts.map +1 -0
  6. package/dist/client/billing/checkoutOptions.js +2 -0
  7. package/dist/client/billing/checkoutOptions.js.map +1 -0
  8. package/dist/client/billing/intents.d.ts +32 -0
  9. package/dist/client/billing/intents.d.ts.map +1 -0
  10. package/dist/client/billing/intents.js +71 -0
  11. package/dist/client/billing/intents.js.map +1 -0
  12. package/dist/client/connect/payoutSnapshot.d.ts +2 -0
  13. package/dist/client/connect/payoutSnapshot.d.ts.map +1 -1
  14. package/dist/client/connect/payoutSnapshot.js +2 -0
  15. package/dist/client/connect/payoutSnapshot.js.map +1 -1
  16. package/dist/client/core/fees.d.ts +2 -0
  17. package/dist/client/core/fees.d.ts.map +1 -1
  18. package/dist/client/core/fees.js +7 -0
  19. package/dist/client/core/fees.js.map +1 -1
  20. package/dist/client/index.d.ts +18 -26
  21. package/dist/client/index.d.ts.map +1 -1
  22. package/dist/client/index.js +18 -16
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/client/readContracts.d.ts +50 -0
  25. package/dist/client/readContracts.d.ts.map +1 -0
  26. package/dist/client/readContracts.js +24 -0
  27. package/dist/client/readContracts.js.map +1 -0
  28. package/dist/client/types/documents.d.ts +2 -0
  29. package/dist/client/types/documents.d.ts.map +1 -1
  30. package/dist/client/webhooks/processors.js +2 -0
  31. package/dist/client/webhooks/processors.js.map +1 -1
  32. package/dist/component/_generated/component.d.ts +177 -0
  33. package/dist/component/_generated/component.d.ts.map +1 -1
  34. package/dist/component/connect/mutations.d.ts +2 -0
  35. package/dist/component/connect/mutations.d.ts.map +1 -1
  36. package/dist/component/connect/mutations.js +8 -8
  37. package/dist/component/connect/mutations.js.map +1 -1
  38. package/dist/component/connect/queries.d.ts +102 -2
  39. package/dist/component/connect/queries.d.ts.map +1 -1
  40. package/dist/component/connect/queries.js +88 -45
  41. package/dist/component/connect/queries.js.map +1 -1
  42. package/dist/component/connect/schema.d.ts +12 -2
  43. package/dist/component/connect/schema.d.ts.map +1 -1
  44. package/dist/component/connect/schema.js +10 -1
  45. package/dist/component/connect/schema.js.map +1 -1
  46. package/dist/component/connect/validators.d.ts +13 -2
  47. package/dist/component/connect/validators.d.ts.map +1 -1
  48. package/dist/component/connect/validators.js +4 -0
  49. package/dist/component/connect/validators.js.map +1 -1
  50. package/dist/component/earnings/legacyReads.d.ts +51 -0
  51. package/dist/component/earnings/legacyReads.d.ts.map +1 -0
  52. package/dist/component/earnings/legacyReads.js +43 -0
  53. package/dist/component/earnings/legacyReads.js.map +1 -0
  54. package/dist/component/earnings/mutations.d.ts +9 -0
  55. package/dist/component/earnings/mutations.d.ts.map +1 -0
  56. package/dist/component/earnings/mutations.js +23 -0
  57. package/dist/component/earnings/mutations.js.map +1 -0
  58. package/dist/component/earnings/reads.d.ts +14 -0
  59. package/dist/component/earnings/reads.d.ts.map +1 -0
  60. package/dist/component/earnings/reads.js +28 -0
  61. package/dist/component/earnings/reads.js.map +1 -0
  62. package/dist/component/earnings/schema.d.ts +20 -0
  63. package/dist/component/earnings/schema.d.ts.map +1 -0
  64. package/dist/component/earnings/schema.js +12 -0
  65. package/dist/component/earnings/schema.js.map +1 -0
  66. package/dist/component/earnings/triggers.d.ts +1290 -0
  67. package/dist/component/earnings/triggers.d.ts.map +1 -0
  68. package/dist/component/earnings/triggers.js +68 -0
  69. package/dist/component/earnings/triggers.js.map +1 -0
  70. package/dist/component/products/queries.d.ts +47 -0
  71. package/dist/component/products/queries.d.ts.map +1 -1
  72. package/dist/component/products/queries.js +59 -0
  73. package/dist/component/products/queries.js.map +1 -1
  74. package/dist/component/schema.d.ts +31 -2
  75. package/dist/component/schema.js +2 -0
  76. package/dist/component/schema.js.map +1 -1
  77. package/dist/react/components/EarningsSummary.d.ts +3 -1
  78. package/dist/react/components/EarningsSummary.d.ts.map +1 -1
  79. package/dist/react/components/EarningsSummary.js +3 -3
  80. package/dist/react/components/EarningsSummary.js.map +1 -1
  81. package/dist/react/hooks/useConfirmPayment.d.ts +1 -1
  82. package/dist/react/hooks/useConfirmPayment.d.ts.map +1 -1
  83. package/dist/react/hooks/useConfirmPayment.js +13 -2
  84. package/dist/react/hooks/useConfirmPayment.js.map +1 -1
  85. package/dist/react/hooks/useCurrencyEarnings.d.ts +31 -0
  86. package/dist/react/hooks/useCurrencyEarnings.d.ts.map +1 -0
  87. package/dist/react/hooks/useCurrencyEarnings.js +30 -0
  88. package/dist/react/hooks/useCurrencyEarnings.js.map +1 -0
  89. package/dist/react/index.d.ts +5 -0
  90. package/dist/react/index.d.ts.map +1 -1
  91. package/dist/react/index.js +4 -0
  92. package/dist/react/index.js.map +1 -1
  93. package/dist/react/workflows/SaveCard.d.ts +27 -0
  94. package/dist/react/workflows/SaveCard.d.ts.map +1 -0
  95. package/dist/react/workflows/SaveCard.js +56 -0
  96. package/dist/react/workflows/SaveCard.js.map +1 -0
  97. package/docs/react-workflows.md +47 -0
  98. package/docs/read-contracts.md +136 -0
  99. package/package.json +3 -2
  100. package/src/client/billing/checkoutOptions.ts +27 -0
  101. package/src/client/billing/intents.test.ts +216 -0
  102. package/src/client/billing/intents.ts +120 -0
  103. package/src/client/billing/intentsTransport.test.ts +94 -0
  104. package/src/client/connect/payoutSnapshot.test.ts +27 -0
  105. package/src/client/connect/payoutSnapshot.ts +4 -0
  106. package/src/client/core/fees.ts +8 -0
  107. package/src/client/index.ts +40 -43
  108. package/src/client/readContracts.ts +90 -0
  109. package/src/client/readPagination.test.ts +62 -0
  110. package/src/client/types/documents.ts +2 -0
  111. package/src/client/webhooks/payout-canceled.integration.test.ts +25 -0
  112. package/src/client/webhooks/processors.ts +2 -0
  113. package/src/component/_generated/api.ts +8 -0
  114. package/src/component/_generated/component.ts +201 -0
  115. package/src/component/connect/mutations.ts +8 -8
  116. package/src/component/connect/queries.ts +110 -48
  117. package/src/component/connect/schema.ts +10 -1
  118. package/src/component/connect/validators.ts +4 -0
  119. package/src/component/currency-migration.test.ts +119 -0
  120. package/src/component/currency-totals.test.ts +95 -0
  121. package/src/component/earnings/legacyReads.ts +59 -0
  122. package/src/component/earnings/mutations.ts +21 -0
  123. package/src/component/earnings/reads.ts +41 -0
  124. package/src/component/earnings/schema.ts +11 -0
  125. package/src/component/earnings/triggers.ts +91 -0
  126. package/src/component/products/queries.ts +74 -0
  127. package/src/component/public.test.ts +1 -1
  128. package/src/component/read-pagination.test.ts +193 -0
  129. package/src/component/schema.ts +2 -0
  130. package/src/react/components/EarningsSummary.tsx +5 -2
  131. package/src/react/hooks/useConfirmPayment.test.tsx +47 -0
  132. package/src/react/hooks/useConfirmPayment.ts +16 -2
  133. package/src/react/hooks/useCurrencyEarnings.test.ts +59 -0
  134. package/src/react/hooks/useCurrencyEarnings.ts +73 -0
  135. package/src/react/index.ts +15 -0
  136. package/src/react/read-contracts.render.test.tsx +20 -0
  137. package/src/react/workflows/SaveCard.test.tsx +127 -0
  138. package/src/react/workflows/SaveCard.tsx +117 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StripeProviderProps } from "../components/StripeProvider.js";
3
+ export type SaveCardFormState = {
4
+ paymentElement: ReactNode;
5
+ consent: boolean;
6
+ setConsent: (value: boolean) => void;
7
+ submit: () => Promise<void>;
8
+ isPending: boolean;
9
+ error: string | null;
10
+ };
11
+ export type SaveCardProps = Pick<StripeProviderProps, "publishableKey" | "appearance"> & {
12
+ scopeKey: string;
13
+ requestId?: string;
14
+ returnUrl: string;
15
+ /** Authenticated callback authorizes customer, records usage consent and durable request identity. */
16
+ createSetupIntent: (request: {
17
+ requestId: string;
18
+ }) => Promise<{
19
+ setupIntentId: string;
20
+ clientSecret: string;
21
+ }>;
22
+ onSuccess?: (setupIntentId: string) => void;
23
+ renderForm?: (state: SaveCardFormState) => ReactNode;
24
+ };
25
+ /** Unstyled card setup. Host owns usage disclosures and authorization. */
26
+ export declare function SaveCard(props: SaveCardProps): import("react").JSX.Element;
27
+ //# sourceMappingURL=SaveCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SaveCard.d.ts","sourceRoot":"","sources":["../../../src/react/workflows/SaveCard.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAIzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,EAAE,SAAS,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,IAAI,CAC9B,mBAAmB,EACnB,gBAAgB,GAAG,YAAY,CAChC,GAAG;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAC3B,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;CACtD,CAAC;AACF,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,+BAO5C"}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState } from "react";
4
+ import { PaymentElement } from "@stripe/react-stripe-js";
5
+ import { StripeProvider, } from "../components/StripeProvider.js";
6
+ import { useConfirmPayment } from "../hooks/useConfirmPayment.js";
7
+ import { useWorkflowResource } from "./useWorkflowResource.js";
8
+ import { useWorkflowAction } from "./useWorkflowAction.js";
9
+ /** Unstyled card setup. Host owns usage disclosures and authorization. */
10
+ export function SaveCard(props) {
11
+ return (_jsx(SaveCardBody, { ...props }, `${props.scopeKey}:${props.requestId ?? ""}`));
12
+ }
13
+ function SaveCardBody(props) {
14
+ const [requestId] = useState(() => props.requestId ?? crypto.randomUUID());
15
+ const state = useWorkflowResource(() => props.createSetupIntent({ requestId }));
16
+ if (state.error)
17
+ return (_jsxs("div", { role: "alert", children: [state.error, _jsx("button", { type: "button", onClick: state.refresh, children: "Retry" })] }));
18
+ if (!state.data || state.isLoading)
19
+ return _jsx("p", { role: "status", children: "Preparing card setup\u2026" });
20
+ return (_jsx(StripeProvider, { publishableKey: props.publishableKey, appearance: props.appearance, elementsOptions: { clientSecret: state.data.clientSecret }, children: _jsx(SaveCardForm, { ...props, setupIntentId: state.data.setupIntentId }) }));
21
+ }
22
+ function SaveCardForm(props) {
23
+ const { confirmSetup, isReady, hasElements } = useConfirmPayment();
24
+ const [consent, setConsent] = useState(false);
25
+ const completed = useRef(false);
26
+ const action = useWorkflowAction(async () => {
27
+ if (completed.current)
28
+ return false;
29
+ if (!consent)
30
+ throw new Error("Consent is required to save this card.");
31
+ const result = await confirmSetup(props.returnUrl, "always");
32
+ if (!result.success)
33
+ throw new Error(result.error);
34
+ completed.current = true;
35
+ return true;
36
+ });
37
+ const state = {
38
+ paymentElement: _jsx(PaymentElement, {}),
39
+ consent,
40
+ setConsent,
41
+ submit: async () => {
42
+ const saved = await action.run();
43
+ if (saved)
44
+ props.onSuccess?.(props.setupIntentId);
45
+ },
46
+ isPending: action.isPending ||
47
+ !isReady ||
48
+ !hasElements ||
49
+ action.result !== undefined,
50
+ error: action.error,
51
+ };
52
+ if (props.renderForm)
53
+ return _jsx(_Fragment, { children: props.renderForm(state) });
54
+ return (_jsxs("div", { children: [state.paymentElement, _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: consent, onChange: (e) => setConsent(e.target.checked) }), "Save this card for future payments and show it at checkout."] }), state.error && _jsx("p", { role: "alert", children: state.error }), _jsx("button", { type: "button", disabled: !consent || state.isPending, onClick: () => void state.submit(), children: "Save card" })] }));
55
+ }
56
+ //# sourceMappingURL=SaveCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SaveCard.js","sourceRoot":"","sources":["../../../src/react/workflows/SaveCard.tsx"],"names":[],"mappings":"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"}
@@ -531,3 +531,50 @@ implementation PR. Version 0.13.0 does not contain them. Consumers must upgrade
531
531
  to that release before removing their navigation guard or using the upload API.
532
532
  Production storage, session authorization, styling, translations and extended
533
533
  hosted onboarding verification remain application responsibilities.
534
+
535
+ ## Save a card through SetupIntent confirmation
536
+
537
+ `SaveCard` provisions a customer-bound SetupIntent through an authenticated
538
+ `createSetupIntent({ requestId })` callback and mounts Payment Element. It keeps
539
+ creation retries on the same request, blocks duplicate confirmation, exposes
540
+ failures and only calls `onSuccess(setupIntentId)` after Stripe reports
541
+ `succeeded`. Stripe.js handles required authentication and redirects.
542
+
543
+ ```tsx
544
+ <SaveCard
545
+ publishableKey={publishableKey}
546
+ scopeKey={`${authenticatedUserId}:${customerAccount}:off_session`}
547
+ requestId={persistedSaveRequestId}
548
+ returnUrl="https://app.example/billing/cards"
549
+ createSetupIntent={createAuthorizedSetupIntent}
550
+ onSuccess={refreshPaymentMethods}
551
+ />
552
+ ```
553
+
554
+ The host callback calls `stripe.createSetupIntent(ctx, { userId,
555
+ customerAccount, usage: "off_session", consent: true, idempotencyKey })` with
556
+ server-resolved values. Record consent to the selected usage before mounting
557
+ this workflow. The default confirmation checkbox separately grants permission
558
+ to save and redisplay the card, sent as `allow_redisplay: "always"`. Use
559
+ `renderForm` for your own disclosures, consent copy, Payment Element, pending
560
+ state and errors. `useConfirmPayment().confirmSetup(returnUrl, allowRedisplay)`
561
+ supports `always`, `limited` and `unspecified` when a different redisplay policy
562
+ is required. Off-session usage does not guarantee that future payments will
563
+ never require authentication.
564
+
565
+ Persist the logical request and immutable customer/usage scope server-side.
566
+ `requestId` and `scopeKey` isolate UI lifecycles; they do not authorize access.
567
+ Handle redirect returns by retrieving the SetupIntent through an authenticated
568
+ server adapter and checking ownership and status before displaying completion.
569
+ Do not infer success from a URL parameter. Do not attach the confirmed method
570
+ again or treat confirmation as changing a subscription default.
571
+
572
+ Existing `AddCardForm` and `PaymentMethods` tokenization/attach integrations remain
573
+ available. Tokenization plus attachment does not complete SetupIntent or SCA
574
+ verification.
575
+
576
+ ## Component history and currency summaries
577
+
578
+ See the [read contract guide](read-contracts.md) for reactive product, price and payout
579
+ cursors, exact currency summaries, legacy backfill, payout failure details, and
580
+ published-version adoption requirements.
@@ -0,0 +1,136 @@
1
+ # Reactive history and currency totals
2
+
3
+ `BetterStripe.paginateProducts`, `paginatePrices`, and `paginatePayouts` read
4
+ webhook-synchronized component records. They return standard Convex
5
+ `{ page, isDone, continueCursor }` results. Pass `paginationOpts` with an opaque
6
+ `cursor` or `null` and an integer `numItems` between 1 and 100. These readers use
7
+ one indexed pagination call per invocation, ordered newest first. Existing array
8
+ methods remain available. Neither an array preview nor one cursor page is an
9
+ exact financial total.
10
+
11
+ Products support `accountId` and `active`. Prices support the component's
12
+ `productId` and `active`; this ID differs from `stripeProductId`. Authorize the
13
+ parent product before exposing price reads. Payout history requires `accountId`
14
+ and optionally accepts `status`. No filter means no restriction on that field.
15
+ An omitted product account filter includes all accounts, including platform
16
+ products. Host applications must derive allowed account and product scope from
17
+ the authenticated user. A client-supplied account ID is not authorization.
18
+
19
+ The Vite reference provides bindings in `example/convex/readContracts.ts` and
20
+ `example/src/lib/stripe-hooks.ts`. Its selectable personas are demo identities.
21
+ Production hosts must replace them with session authorization.
22
+
23
+ ## React pagination
24
+
25
+ Wrap the public client method in an authorized host Convex query with
26
+ `paginationOptsValidator` in its arguments. Return the package result unchanged.
27
+ Bind that query with `useComponentPaginatedQuery` from the package React export.
28
+ This uses the helper-compatible hook to pin end cursors when another page loads.
29
+ Native `convex/react` pagination does not pin component helper cursors and can
30
+ leave gaps when new rows arrive between loaded pages.
31
+
32
+ ```tsx
33
+ const products = useComponentPaginatedQuery(
34
+ api.catalog.paginateProducts,
35
+ {}, // Host derives account scope from the session.
36
+ { initialNumItems: 25 },
37
+ );
38
+ const manager = useProductManager({
39
+ scopeKey: authenticatedUser.id,
40
+ createProduct,
41
+ createPrice,
42
+ updateProduct,
43
+ deactivateProduct,
44
+ deactivatePrice,
45
+ });
46
+ // Render products.results with the existing Products/useProductManager callbacks.
47
+ // Expand each product with a separate useComponentPaginatedQuery for its component productId.
48
+ // Do not join unbounded prices onto every product in a page.
49
+ <button
50
+ disabled={products.status !== "CanLoadMore"}
51
+ onClick={() => products.loadMore(25)}
52
+ >
53
+ Load more products
54
+ </button>;
55
+ ```
56
+
57
+ Use the standard `LoadingFirstPage`, `LoadingMore`, `CanLoadMore`, and `Exhausted`
58
+ statuses for loading, empty, and terminal states. Queries react to writes
59
+ and reset when their arguments change. Keep price hooks in a child component
60
+ keyed by product ID. Do not invoke hooks inside a variable-length loop. A payout
61
+ history binding uses the same contract. Pinned ranges can grow beyond the requested
62
+ page size. The server caps each read at 200 rows or 1 MB and returns split hints
63
+ for the hook to split large ranges without dropping rows. The `Payouts` workflow's live snapshot
64
+ loader remains a separate Stripe action API, not a substitute for reactive
65
+ component history or exact paid totals.
66
+
67
+ ## Exact totals by currency
68
+
69
+ Use `getAccountEarningsByCurrency` and `getAccountPayoutsByCurrency` through the
70
+ client or component. They return `totals` sorted by currency and bounded
71
+ `transfers` or `payouts` previews. `previewLimit` is an integer from 0 to 100,
72
+ defaulting to 50. Earnings totals include `gross`, `reversed`, `net`, and
73
+ `transferCount`; payout totals include `paidOut` and `payoutCount`. Only payouts
74
+ with status `paid` contribute to `paidOut`. Reinstatement transfers contribute to
75
+ gross, while the original transfer's reversal remains recorded.
76
+
77
+ All amounts use the currency's original minor units. Never add different
78
+ currencies, or apply a fixed two-decimal conversion to JPY or KWD. The package
79
+ maintains summaries transactionally with every supported transfer and payout
80
+ mutation, including reversal mutations. Reads examine at most 501 summary rows,
81
+ one unbackfilled-row probe, and 100 preview rows, regardless of history size.
82
+ Accounts with more than 500 distinct currency strings fail explicitly. Summary
83
+ updates serialize per account/currency through Convex OCC.
84
+
85
+ Bind authorized wrappers to `createUseCurrencyEarnings(useQuery, earningsRef,
86
+ payoutsRef)`. Its `totals` joins the two summaries by currency and never sums the
87
+ previews. Render an `EarningsSummary` for each currency, passing that row's
88
+ `currency` and only that currency's payout preview. The default earnings formatter handles supported ordinary charge currencies.
89
+ Pass an explicit `formatAmount` callback for other observed currency precision,
90
+ including KWD, or render the raw typed totals with your own currency policy.
91
+ Use `getAccountBalance` or
92
+ `getPayoutSnapshot` for per-currency available/pending balances. The component's
93
+ earnings ledger does not determine settlement availability.
94
+
95
+ ### Existing installations
96
+
97
+ After deploying the optional marker fields and summary table, invoke
98
+ `components.betterStripe.earnings.mutations.backfillCurrencyTotals` from a trusted
99
+ server migration. Run separately for `kind: "earnings"` and `kind: "payouts"`,
100
+ starting with `cursor: null` and continuing with each `continueCursor` until
101
+ `isDone`. Each mutation visits at most 100 rows. Retrying a page is safe, and
102
+ ordinary package writes can continue during migration. A row's marker and its
103
+ summary contribution commit together. New readers throw while an account has
104
+ unbackfilled rows, so callers cannot mistake incomplete totals for zero.
105
+
106
+ Keep raw imports and direct database edits out of normal application writes.
107
+ If importing historical ledger rows, omit `totalsRecorded` and run the backfill.
108
+ Never copy markers without their matching summaries.
109
+
110
+ Legacy `getAccountEarnings`, `getAccountPayouts`, and `createUseEarnings` retain
111
+ their single-currency shapes. The legacy queries now reject mixed-currency
112
+ accounts and direct callers to the new methods. Their historical scans remain
113
+ subject to Convex read limits; migrate busy accounts to the maintained summaries.
114
+ Full referred-sale history uses `paginateTransfersByAccount`, not the preview.
115
+
116
+ ## Payout failures
117
+
118
+ Component payout rows and live `RecipientPayoutRow` snapshots expose optional
119
+ `failureCode` and `failureMessage`. Signed payout events write the provider's
120
+ current snapshot to the existing payout identity. A later snapshot with absent
121
+ or null provider failure fields clears stored details. Legacy rows remain valid.
122
+ Ordinary event replay is deduplicated. This is snapshot reconciliation and does
123
+ not guarantee convergence for globally out-of-order events.
124
+
125
+ Host apps should translate known codes into safe recovery instructions and a
126
+ settings link. Do not log or blindly render raw provider messages. Authorize
127
+ reads using the recipient's shared connected account.
128
+
129
+ ## Publication and consumer acceptance
130
+
131
+ A source merge does not activate an API in a published package. DOJO-908,
132
+ DOJO-1037, DOJO-1041, and DOJO-2257 require a verified published version and
133
+ explicit consumer adoption, authorized adapters, translated UI, and their own
134
+ acceptance evidence. These package changes do not complete those tickets or add
135
+ a second Dojo financial ledger, payout table, catalog store, or webhook processor.
136
+ Use the repository release workflow; do not publish npm manually.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fantastic.dev/better-stripe",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "A Convex component for Stripe V2 Accounts API integration.",
5
5
  "keywords": [
6
6
  "stripe",
@@ -62,7 +62,8 @@
62
62
  "docs/accounts-v2-preview.md",
63
63
  "docs/react-workflows-audit.md",
64
64
  "CHANGELOG.md",
65
- "MIGRATION.md"
65
+ "MIGRATION.md",
66
+ "docs/read-contracts.md"
66
67
  ],
67
68
  "scripts": {
68
69
  "build": "tsc --project ./tsconfig.build.json",
@@ -0,0 +1,27 @@
1
+ import type { SplitRecipient } from "../../component/lib/fees.js";
2
+ import type { FeeOverride } from "../types.js";
3
+
4
+ /** Host-authorized Checkout Session request. */
5
+ export type CreateCheckoutSessionOptions = {
6
+ idempotencyKey?: string;
7
+ userId: string;
8
+ orgId?: string;
9
+ stripePriceId: string;
10
+ mode: "payment" | "subscription" | "setup";
11
+ uiMode?: "embedded" | "redirect" | "custom" | "form";
12
+ quantity?: number;
13
+ returnUrl: string;
14
+ trialDays?: number;
15
+ accountId?: string;
16
+ customerEmail?: string;
17
+ /** Seller/recipient connected account to route funds to (destination charge). */
18
+ destinationAccountId?: string;
19
+ /** Multiple recipients (store + affiliate[s]); >1 routes via separate charges & transfers. */
20
+ split?: SplitRecipient[];
21
+ /** Per-call platform fee override; falls back to the configured default. */
22
+ fee?: FeeOverride;
23
+ /** Charge total (minor units) for one-time payment fee computation. */
24
+ amount?: number;
25
+ metadata?: Record<string, string>;
26
+ sessionOverrides?: Record<string, unknown>;
27
+ };
@@ -0,0 +1,216 @@
1
+ import { expect, it, vi } from "vitest";
2
+ import { BetterStripe } from "../index.js";
3
+ import { getStripeClient, type Component, type RunCtx } from "../helpers.js";
4
+
5
+ function fixture() {
6
+ const ref = { [Symbol.for("toReferencePath")]: "account" };
7
+ const component = {
8
+ core: { queries: { getAccountByStripeId: ref } },
9
+ } as unknown as Component;
10
+ const key = `sk_test_intents_${crypto.randomUUID()}`;
11
+ const client = new BetterStripe(component, { STRIPE_SECRET_KEY: key });
12
+ const sdk = getStripeClient(key);
13
+ const ctx = {
14
+ runQuery: vi
15
+ .fn()
16
+ .mockResolvedValue({ userId: "buyer", stripeAccountId: "acct_buyer" }),
17
+ } as unknown as RunCtx;
18
+ const price = vi.spyOn(sdk.prices, "retrieve").mockResolvedValue({
19
+ id: "price_one",
20
+ active: true,
21
+ type: "one_time",
22
+ unit_amount: 1200,
23
+ currency: "usd",
24
+ product: { id: "prod_one", active: true },
25
+ } as never);
26
+ const payment = vi.spyOn(sdk.paymentIntents, "create").mockResolvedValue({
27
+ id: "pi_one",
28
+ client_secret: "secret",
29
+ amount: 1200,
30
+ } as never);
31
+ const setup = vi.spyOn(sdk.setupIntents, "create").mockResolvedValue({
32
+ id: "seti_one",
33
+ client_secret: "setup_secret",
34
+ } as never);
35
+ const opts = {
36
+ userId: "buyer",
37
+ customerAccount: "acct_buyer",
38
+ idempotencyKey: "order:one",
39
+ };
40
+ return { client, ctx, price, payment, setup, opts };
41
+ }
42
+ it("creates a card-only intent with provider price economics and protected ownership metadata", async () => {
43
+ const { client, ctx, payment, opts } = fixture();
44
+ const args = {
45
+ ...opts,
46
+ stripePriceId: "price_one",
47
+ metadata: { userId: "forged", bsSplit: "forged", planId: "plan_one" },
48
+ };
49
+ expect(await client.createPaymentIntent(ctx, args)).toEqual({
50
+ paymentIntentId: "pi_one",
51
+ clientSecret: "secret",
52
+ });
53
+ await client.createPaymentIntent(ctx, args);
54
+ expect(payment).toHaveBeenNthCalledWith(
55
+ 2,
56
+ {
57
+ amount: 1200,
58
+ currency: "usd",
59
+ customer_account: "acct_buyer",
60
+ receipt_email: undefined,
61
+ allowed_payment_method_types: ["card"],
62
+ metadata: {
63
+ userId: "buyer",
64
+ planId: "plan_one",
65
+ stripePriceId: "price_one",
66
+ stripeProductId: "prod_one",
67
+ },
68
+ },
69
+ { idempotencyKey: "order:one" },
70
+ );
71
+ });
72
+ it.each([
73
+ { active: false },
74
+ { type: "recurring" },
75
+ { unit_amount: null },
76
+ { unit_amount: 0 },
77
+ { product: { id: "prod_one", active: false } },
78
+ ])("rejects unsupported price economics %j", async (override) => {
79
+ const { client, ctx, price, payment, opts } = fixture();
80
+ price.mockResolvedValue({
81
+ active: true,
82
+ type: "one_time",
83
+ unit_amount: 1200,
84
+ currency: "usd",
85
+ product: { id: "prod_one", active: true },
86
+ ...override,
87
+ } as never);
88
+ await expect(
89
+ client.createPaymentIntent(ctx, { ...opts, stripePriceId: "price_one" }),
90
+ ).rejects.toThrow();
91
+ expect(payment).not.toHaveBeenCalled();
92
+ });
93
+ it("rejects another customer's account before provider writes", async () => {
94
+ const { client, ctx, payment, setup, opts } = fixture();
95
+ await expect(
96
+ client.createPaymentIntent(ctx, {
97
+ ...opts,
98
+ userId: "attacker",
99
+ stripePriceId: "price_one",
100
+ }),
101
+ ).rejects.toThrow(/belong/);
102
+ await expect(
103
+ client.createSetupIntent(ctx, {
104
+ ...opts,
105
+ userId: "attacker",
106
+ usage: "off_session",
107
+ consent: true,
108
+ }),
109
+ ).rejects.toThrow(/belong/);
110
+ expect(payment).not.toHaveBeenCalled();
111
+ expect(setup).not.toHaveBeenCalled();
112
+ });
113
+ it("provisions consented card setup, preserves retry key after transport failure, and returns minimal secrets", async () => {
114
+ const { client, ctx, setup, opts } = fixture();
115
+ const args = {
116
+ ...opts,
117
+ usage: "off_session" as const,
118
+ consent: true as const,
119
+ };
120
+ setup.mockRejectedValueOnce(new Error("connection lost"));
121
+ await expect(client.createSetupIntent(ctx, args)).rejects.toThrow(
122
+ "connection lost",
123
+ );
124
+ expect(await client.createSetupIntent(ctx, args)).toEqual({
125
+ setupIntentId: "seti_one",
126
+ clientSecret: "setup_secret",
127
+ });
128
+ expect(setup).toHaveBeenNthCalledWith(
129
+ 2,
130
+ {
131
+ customer_account: "acct_buyer",
132
+ usage: "off_session",
133
+ allowed_payment_method_types: ["card"],
134
+ metadata: { userId: "buyer" },
135
+ },
136
+ { idempotencyKey: "order:one" },
137
+ );
138
+ });
139
+ it.each([undefined, "", "\n", "x".repeat(256)])(
140
+ "requires a valid explicit retry key %j",
141
+ async (key) => {
142
+ const { client, ctx, setup, payment, opts } = fixture();
143
+ const args = { ...opts, idempotencyKey: key as string };
144
+ await expect(
145
+ client.createSetupIntent(ctx, {
146
+ ...args,
147
+ usage: "on_session",
148
+ consent: true,
149
+ }),
150
+ ).rejects.toThrow();
151
+ await expect(
152
+ client.createPaymentIntent(ctx, { ...args, stripePriceId: "price_one" }),
153
+ ).rejects.toThrow();
154
+ expect(setup).not.toHaveBeenCalled();
155
+ expect(payment).not.toHaveBeenCalled();
156
+ },
157
+ );
158
+ it("requires setup consent and a supported usage", async () => {
159
+ const { client, ctx, setup, opts } = fixture();
160
+ await expect(
161
+ client.createSetupIntent(ctx, {
162
+ ...opts,
163
+ usage: "off_session",
164
+ consent: false as unknown as true,
165
+ }),
166
+ ).rejects.toThrow(/consent/);
167
+ await expect(
168
+ client.createSetupIntent(ctx, {
169
+ ...opts,
170
+ usage: "invalid" as "on_session",
171
+ consent: true,
172
+ }),
173
+ ).rejects.toThrow(/usage/);
174
+ expect(setup).not.toHaveBeenCalled();
175
+ });
176
+ it("rejects missing client secrets", async () => {
177
+ const { client, ctx, setup, payment, opts } = fixture();
178
+ setup.mockResolvedValue({ id: "seti_one", client_secret: null } as never);
179
+ payment.mockResolvedValue({ id: "pi_one", client_secret: null } as never);
180
+ await expect(
181
+ client.createSetupIntent(ctx, {
182
+ ...opts,
183
+ usage: "on_session",
184
+ consent: true,
185
+ }),
186
+ ).rejects.toThrow(/secret/);
187
+ await expect(
188
+ client.createPaymentIntent(ctx, { ...opts, stripePriceId: "price_one" }),
189
+ ).rejects.toThrow(/secret/);
190
+ });
191
+ it("preserves host-authorized guest purchases without inventing a customer account", async () => {
192
+ const { client, ctx, payment } = fixture();
193
+ await client.createPaymentIntent(ctx, {
194
+ userId: "",
195
+ stripePriceId: "price_one",
196
+ idempotencyKey: "guest:one",
197
+ });
198
+ expect(payment).toHaveBeenCalledWith(
199
+ expect.objectContaining({
200
+ customer_account: undefined,
201
+ metadata: expect.objectContaining({ userId: "" }),
202
+ }),
203
+ { idempotencyKey: "guest:one" },
204
+ );
205
+ });
206
+ it("preserves automatic-method checkout when the host selects it", async () => {
207
+ const { client, ctx, payment, opts } = fixture();
208
+ await client.createPaymentIntent(ctx, {
209
+ ...opts,
210
+ stripePriceId: "price_one",
211
+ paymentMethods: "automatic",
212
+ });
213
+ const request = payment.mock.calls[0][0];
214
+ expect(request.automatic_payment_methods).toEqual({ enabled: true });
215
+ expect(request.allowed_payment_method_types).toBeUndefined();
216
+ });