@fantastic.dev/better-stripe 0.7.0 → 0.8.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/CHANGELOG.md +23 -18
- package/README.md +122 -90
- package/dist/client/billing/checkout.d.ts +1 -1
- package/dist/client/billing/checkout.d.ts.map +1 -1
- package/dist/client/billing/checkout.js +7 -2
- package/dist/client/billing/checkout.js.map +1 -1
- package/dist/client/connect/paymentMethods.d.ts.map +1 -1
- package/dist/client/connect/paymentMethods.js.map +1 -1
- package/dist/client/connect/refundActor.d.ts.map +1 -1
- package/dist/client/connect/refundActor.js.map +1 -1
- package/dist/client/connect/refunds.d.ts.map +1 -1
- package/dist/client/connect/refunds.js.map +1 -1
- package/dist/client/connect/transfers.d.ts.map +1 -1
- package/dist/client/connect/transfers.js +15 -4
- package/dist/client/connect/transfers.js.map +1 -1
- package/dist/client/core/accountDefaults.d.ts +6 -0
- package/dist/client/core/accountDefaults.d.ts.map +1 -0
- package/dist/client/core/accountDefaults.js +15 -0
- package/dist/client/core/accountDefaults.js.map +1 -0
- package/dist/client/core/accounts.d.ts +1 -5
- package/dist/client/core/accounts.d.ts.map +1 -1
- package/dist/client/core/accounts.js +2 -14
- package/dist/client/core/accounts.js.map +1 -1
- package/dist/client/core/fees.d.ts.map +1 -1
- package/dist/client/core/fees.js +7 -2
- package/dist/client/core/fees.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/products/prices.d.ts.map +1 -1
- package/dist/client/products/prices.js.map +1 -1
- package/dist/client/products/products.d.ts.map +1 -1
- package/dist/client/products/products.js +3 -1
- package/dist/client/products/products.js.map +1 -1
- package/dist/client/utils/owner.d.ts.map +1 -1
- package/dist/client/utils/owner.js +3 -1
- package/dist/client/utils/owner.js.map +1 -1
- package/dist/client/utils/stripeDashboardUrl.d.ts.map +1 -1
- package/dist/client/utils/stripeDashboardUrl.js.map +1 -1
- package/dist/client/webhooks/helpers.d.ts +10 -0
- package/dist/client/webhooks/helpers.d.ts.map +1 -1
- package/dist/client/webhooks/helpers.js +18 -0
- package/dist/client/webhooks/helpers.js.map +1 -1
- package/dist/client/webhooks/processors.d.ts.map +1 -1
- package/dist/client/webhooks/processors.js +1 -19
- package/dist/client/webhooks/processors.js.map +1 -1
- package/dist/component/connect/mutations.d.ts.map +1 -1
- package/dist/component/connect/mutations.js +4 -33
- package/dist/component/connect/mutations.js.map +1 -1
- package/dist/component/connect/schema.d.ts.map +1 -1
- package/dist/component/connect/schema.js.map +1 -1
- package/dist/component/lib/paymentSeller.d.ts +3 -0
- package/dist/component/lib/paymentSeller.d.ts.map +1 -0
- package/dist/component/lib/paymentSeller.js +10 -0
- package/dist/component/lib/paymentSeller.js.map +1 -0
- package/dist/component/lib/reversals.d.ts +10 -0
- package/dist/component/lib/reversals.d.ts.map +1 -1
- package/dist/component/lib/reversals.js +17 -0
- package/dist/component/lib/reversals.js.map +1 -1
- package/dist/react/components/ConnectProvider.d.ts +1 -1
- package/dist/react/components/ConnectProvider.d.ts.map +1 -1
- package/dist/react/components/ConnectProvider.js +18 -4
- package/dist/react/components/ConnectProvider.js.map +1 -1
- package/dist/react/components/ConnectStatusBadge.d.ts.map +1 -1
- package/dist/react/components/ConnectStatusBadge.js.map +1 -1
- package/dist/react/components/EmbeddedDisputes.d.ts +1 -1
- package/dist/react/components/EmbeddedDisputes.d.ts.map +1 -1
- package/dist/react/components/EmbeddedDisputes.js +1 -1
- package/dist/react/components/EmbeddedDisputes.js.map +1 -1
- package/dist/react/components/SplitBreakdown.d.ts.map +1 -1
- package/dist/react/components/SplitBreakdown.js +12 -2
- package/dist/react/components/SplitBreakdown.js.map +1 -1
- package/dist/react/hooks/useCheckoutSession.d.ts.map +1 -1
- package/dist/react/hooks/useCheckoutSession.js.map +1 -1
- package/dist/react/hooks/useConfirmPayment.d.ts.map +1 -1
- package/dist/react/hooks/useConfirmPayment.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/workflows/Account.d.ts +67 -0
- package/dist/react/workflows/Account.d.ts.map +1 -0
- package/dist/react/workflows/Account.js +74 -0
- package/dist/react/workflows/Account.js.map +1 -0
- package/dist/react/workflows/Billing.d.ts +60 -0
- package/dist/react/workflows/Billing.d.ts.map +1 -0
- package/dist/react/workflows/Billing.js +84 -0
- package/dist/react/workflows/Billing.js.map +1 -0
- package/dist/react/workflows/BillingPlans.d.ts +45 -0
- package/dist/react/workflows/BillingPlans.d.ts.map +1 -0
- package/dist/react/workflows/BillingPlans.js +51 -0
- package/dist/react/workflows/BillingPlans.js.map +1 -0
- package/dist/react/workflows/Checkout.d.ts +46 -0
- package/dist/react/workflows/Checkout.d.ts.map +1 -0
- package/dist/react/workflows/Checkout.js +61 -0
- package/dist/react/workflows/Checkout.js.map +1 -0
- package/dist/react/workflows/CheckoutChooser.d.ts +20 -0
- package/dist/react/workflows/CheckoutChooser.d.ts.map +1 -0
- package/dist/react/workflows/CheckoutChooser.js +24 -0
- package/dist/react/workflows/CheckoutChooser.js.map +1 -0
- package/dist/react/workflows/CheckoutForms.d.ts +26 -0
- package/dist/react/workflows/CheckoutForms.d.ts.map +1 -0
- package/dist/react/workflows/CheckoutForms.js +126 -0
- package/dist/react/workflows/CheckoutForms.js.map +1 -0
- package/dist/react/workflows/ClipboardButton.d.ts +17 -0
- package/dist/react/workflows/ClipboardButton.d.ts.map +1 -0
- package/dist/react/workflows/ClipboardButton.js +35 -0
- package/dist/react/workflows/ClipboardButton.js.map +1 -0
- package/dist/react/workflows/Collection.d.ts +30 -0
- package/dist/react/workflows/Collection.d.ts.map +1 -0
- package/dist/react/workflows/Collection.js +25 -0
- package/dist/react/workflows/Collection.js.map +1 -0
- package/dist/react/workflows/Collections.d.ts +56 -0
- package/dist/react/workflows/Collections.d.ts.map +1 -0
- package/dist/react/workflows/Collections.js +111 -0
- package/dist/react/workflows/Collections.js.map +1 -0
- package/dist/react/workflows/Connect.d.ts +19 -0
- package/dist/react/workflows/Connect.d.ts.map +1 -0
- package/dist/react/workflows/Connect.js +43 -0
- package/dist/react/workflows/Connect.js.map +1 -0
- package/dist/react/workflows/Costs.d.ts +48 -0
- package/dist/react/workflows/Costs.d.ts.map +1 -0
- package/dist/react/workflows/Costs.js +66 -0
- package/dist/react/workflows/Costs.js.map +1 -0
- package/dist/react/workflows/Disputes.d.ts +36 -0
- package/dist/react/workflows/Disputes.d.ts.map +1 -0
- package/dist/react/workflows/Disputes.js +74 -0
- package/dist/react/workflows/Disputes.js.map +1 -0
- package/dist/react/workflows/Earnings.d.ts +42 -0
- package/dist/react/workflows/Earnings.d.ts.map +1 -0
- package/dist/react/workflows/Earnings.js +38 -0
- package/dist/react/workflows/Earnings.js.map +1 -0
- package/dist/react/workflows/Financials.d.ts +43 -0
- package/dist/react/workflows/Financials.d.ts.map +1 -0
- package/dist/react/workflows/Financials.js +57 -0
- package/dist/react/workflows/Financials.js.map +1 -0
- package/dist/react/workflows/IntegrationSwitch.d.ts +23 -0
- package/dist/react/workflows/IntegrationSwitch.d.ts.map +1 -0
- package/dist/react/workflows/IntegrationSwitch.js +31 -0
- package/dist/react/workflows/IntegrationSwitch.js.map +1 -0
- package/dist/react/workflows/Onboarding.d.ts +56 -0
- package/dist/react/workflows/Onboarding.d.ts.map +1 -0
- package/dist/react/workflows/Onboarding.js +86 -0
- package/dist/react/workflows/Onboarding.js.map +1 -0
- package/dist/react/workflows/Overview.d.ts +51 -0
- package/dist/react/workflows/Overview.d.ts.map +1 -0
- package/dist/react/workflows/Overview.js +63 -0
- package/dist/react/workflows/Overview.js.map +1 -0
- package/dist/react/workflows/PaymentMethods.d.ts +35 -0
- package/dist/react/workflows/PaymentMethods.d.ts.map +1 -0
- package/dist/react/workflows/PaymentMethods.js +63 -0
- package/dist/react/workflows/PaymentMethods.js.map +1 -0
- package/dist/react/workflows/PaymentReceipt.d.ts +20 -0
- package/dist/react/workflows/PaymentReceipt.d.ts.map +1 -0
- package/dist/react/workflows/PaymentReceipt.js +24 -0
- package/dist/react/workflows/PaymentReceipt.js.map +1 -0
- package/dist/react/workflows/Payouts.d.ts +47 -0
- package/dist/react/workflows/Payouts.d.ts.map +1 -0
- package/dist/react/workflows/Payouts.js +87 -0
- package/dist/react/workflows/Payouts.js.map +1 -0
- package/dist/react/workflows/Products.d.ts +92 -0
- package/dist/react/workflows/Products.d.ts.map +1 -0
- package/dist/react/workflows/Products.js +179 -0
- package/dist/react/workflows/Products.js.map +1 -0
- package/dist/react/workflows/ReferralLinks.d.ts +26 -0
- package/dist/react/workflows/ReferralLinks.d.ts.map +1 -0
- package/dist/react/workflows/ReferralLinks.js +30 -0
- package/dist/react/workflows/ReferralLinks.js.map +1 -0
- package/dist/react/workflows/Refunds.d.ts +72 -0
- package/dist/react/workflows/Refunds.d.ts.map +1 -0
- package/dist/react/workflows/Refunds.js +95 -0
- package/dist/react/workflows/Refunds.js.map +1 -0
- package/dist/react/workflows/Setup.d.ts +50 -0
- package/dist/react/workflows/Setup.d.ts.map +1 -0
- package/dist/react/workflows/Setup.js +59 -0
- package/dist/react/workflows/Setup.js.map +1 -0
- package/dist/react/workflows/Storefront.d.ts +53 -0
- package/dist/react/workflows/Storefront.d.ts.map +1 -0
- package/dist/react/workflows/Storefront.js +44 -0
- package/dist/react/workflows/Storefront.js.map +1 -0
- package/dist/react/workflows/SubscriptionOverview.d.ts +13 -0
- package/dist/react/workflows/SubscriptionOverview.d.ts.map +1 -0
- package/dist/react/workflows/SubscriptionOverview.js +26 -0
- package/dist/react/workflows/SubscriptionOverview.js.map +1 -0
- package/dist/react/workflows/Subscriptions.d.ts +92 -0
- package/dist/react/workflows/Subscriptions.d.ts.map +1 -0
- package/dist/react/workflows/Subscriptions.js +149 -0
- package/dist/react/workflows/Subscriptions.js.map +1 -0
- package/dist/react/workflows/Tasks.d.ts +40 -0
- package/dist/react/workflows/Tasks.d.ts.map +1 -0
- package/dist/react/workflows/Tasks.js +113 -0
- package/dist/react/workflows/Tasks.js.map +1 -0
- package/dist/react/workflows/checkout-options.d.ts +37 -0
- package/dist/react/workflows/checkout-options.d.ts.map +1 -0
- package/dist/react/workflows/checkout-options.js +43 -0
- package/dist/react/workflows/checkout-options.js.map +1 -0
- package/dist/react/workflows/current-subscription.d.ts +11 -0
- package/dist/react/workflows/current-subscription.d.ts.map +1 -0
- package/dist/react/workflows/current-subscription.js +14 -0
- package/dist/react/workflows/current-subscription.js.map +1 -0
- package/dist/react/workflows/filters.d.ts +17 -0
- package/dist/react/workflows/filters.d.ts.map +1 -0
- package/dist/react/workflows/filters.js +23 -0
- package/dist/react/workflows/filters.js.map +1 -0
- package/dist/react/workflows/index.d.ts +70 -0
- package/dist/react/workflows/index.d.ts.map +1 -0
- package/dist/react/workflows/index.js +38 -0
- package/dist/react/workflows/index.js.map +1 -0
- package/dist/react/workflows/useWorkflowAction.d.ts +11 -0
- package/dist/react/workflows/useWorkflowAction.d.ts.map +1 -0
- package/dist/react/workflows/useWorkflowAction.js +68 -0
- package/dist/react/workflows/useWorkflowAction.js.map +1 -0
- package/dist/react/workflows/useWorkflowResource.d.ts +9 -0
- package/dist/react/workflows/useWorkflowResource.d.ts.map +1 -0
- package/dist/react/workflows/useWorkflowResource.js +67 -0
- package/dist/react/workflows/useWorkflowResource.js.map +1 -0
- package/docs/react-workflows-audit.md +66 -0
- package/docs/react-workflows.md +231 -0
- package/package.json +19 -3
- package/src/client/billing/checkout-ui-modes.test.ts +49 -0
- package/src/client/billing/checkout.ts +8 -3
- package/src/client/billing/invoices.test.ts +12 -6
- package/src/client/connect/paymentMethods.test.ts +3 -1
- package/src/client/connect/paymentMethods.ts +4 -1
- package/src/client/connect/refundActor.ts +1 -2
- package/src/client/connect/refunds.test.ts +35 -10
- package/src/client/connect/refunds.ts +4 -1
- package/src/client/connect/transfers.test.ts +66 -12
- package/src/client/connect/transfers.ts +32 -8
- package/src/client/core/accountDefaults.ts +15 -0
- package/src/client/core/accounts.ts +14 -17
- package/src/client/core/config.test.ts +2 -1
- package/src/client/core/descriptors.test.ts +6 -4
- package/src/client/core/fees.test.ts +20 -28
- package/src/client/core/fees.ts +9 -2
- package/src/client/index.test.ts +22 -22
- package/src/client/index.ts +1 -1
- package/src/client/products/prices.test.ts +8 -2
- package/src/client/products/prices.ts +4 -1
- package/src/client/products/products.test.ts +61 -20
- package/src/client/products/products.ts +3 -1
- package/src/client/utils/data.test.ts +3 -1
- package/src/client/utils/owner.test.ts +4 -1
- package/src/client/utils/owner.ts +5 -1
- package/src/client/utils/stripeDashboardUrl.ts +1 -4
- package/src/client/webhooks/handler.test.ts +16 -19
- package/src/client/webhooks/helpers.test.ts +26 -13
- package/src/client/webhooks/helpers.ts +23 -0
- package/src/client/webhooks/hooks.test.ts +1 -2
- package/src/client/webhooks/processors.test.ts +18 -34
- package/src/client/webhooks/processors.ts +1 -23
- package/src/client/webhooks.test.ts +1 -2
- package/src/component/_generated/api.ts +2 -0
- package/src/component/connect/mutations.ts +5 -46
- package/src/component/connect/schema.ts +4 -3
- package/src/component/connect.test.ts +1 -5
- package/src/component/lib/paymentSeller.ts +15 -0
- package/src/component/lib/reversals.ts +30 -2
- package/src/component/subscription-scoping.test.ts +5 -1
- package/src/component/triggers.test.ts +7 -4
- package/src/react/components/BuyerBillingView.render.test.tsx +7 -2
- package/src/react/components/ConnectProvider.live.test.tsx +61 -0
- package/src/react/components/ConnectProvider.tsx +18 -4
- package/src/react/components/ConnectStatusBadge.tsx +1 -4
- package/src/react/components/DisputeDetail.render.test.tsx +5 -1
- package/src/react/components/EarningsSummary.render.test.tsx +2 -6
- package/src/react/components/EmbeddedDisputes.render.test.tsx +7 -5
- package/src/react/components/EmbeddedDisputes.tsx +4 -1
- package/src/react/components/EvidenceForm.render.test.tsx +2 -8
- package/src/react/components/SplitBreakdown.render.test.tsx +28 -5
- package/src/react/components/SplitBreakdown.tsx +12 -2
- package/src/react/hooks/useCheckoutSession.ts +1 -2
- package/src/react/hooks/useConfirmPayment.ts +1 -2
- package/src/react/hooks/useEarnings.test.ts +43 -8
- package/src/react/hooks/useStripeMode.test.ts +5 -8
- package/src/react/index.ts +3 -0
- package/src/react/workflows/Account.test.tsx +31 -0
- package/src/react/workflows/Account.tsx +183 -0
- package/src/react/workflows/Billing.test.tsx +45 -0
- package/src/react/workflows/Billing.tsx +213 -0
- package/src/react/workflows/BillingPlans.test.tsx +31 -0
- package/src/react/workflows/BillingPlans.tsx +130 -0
- package/src/react/workflows/Checkout.test.tsx +118 -0
- package/src/react/workflows/Checkout.tsx +135 -0
- package/src/react/workflows/CheckoutChooser.test.tsx +44 -0
- package/src/react/workflows/CheckoutChooser.tsx +77 -0
- package/src/react/workflows/CheckoutForm.test.tsx +66 -0
- package/src/react/workflows/CheckoutForms.test.tsx +123 -0
- package/src/react/workflows/CheckoutForms.tsx +247 -0
- package/src/react/workflows/ClipboardButton.test.tsx +34 -0
- package/src/react/workflows/ClipboardButton.tsx +67 -0
- package/src/react/workflows/Collection.tsx +76 -0
- package/src/react/workflows/Collections.test.tsx +86 -0
- package/src/react/workflows/Collections.tsx +213 -0
- package/src/react/workflows/Connect.test.tsx +50 -0
- package/src/react/workflows/Connect.tsx +107 -0
- package/src/react/workflows/Costs.test.tsx +41 -0
- package/src/react/workflows/Costs.tsx +133 -0
- package/src/react/workflows/Disputes.test.tsx +25 -0
- package/src/react/workflows/Disputes.tsx +164 -0
- package/src/react/workflows/Earnings.test.ts +19 -0
- package/src/react/workflows/Earnings.tsx +84 -0
- package/src/react/workflows/Financials.test.tsx +35 -0
- package/src/react/workflows/Financials.tsx +117 -0
- package/src/react/workflows/IntegrationSwitch.test.tsx +36 -0
- package/src/react/workflows/IntegrationSwitch.tsx +63 -0
- package/src/react/workflows/Onboarding.test.tsx +75 -0
- package/src/react/workflows/Onboarding.tsx +171 -0
- package/src/react/workflows/Overview.test.tsx +57 -0
- package/src/react/workflows/Overview.tsx +123 -0
- package/src/react/workflows/PaymentMethods.test.tsx +60 -0
- package/src/react/workflows/PaymentMethods.tsx +117 -0
- package/src/react/workflows/PaymentReceipt.test.tsx +24 -0
- package/src/react/workflows/PaymentReceipt.tsx +46 -0
- package/src/react/workflows/Payouts.test.tsx +82 -0
- package/src/react/workflows/Payouts.tsx +212 -0
- package/src/react/workflows/Products.test.tsx +90 -0
- package/src/react/workflows/Products.tsx +377 -0
- package/src/react/workflows/ReferralLinks.test.tsx +39 -0
- package/src/react/workflows/ReferralLinks.tsx +75 -0
- package/src/react/workflows/Refunds.test.tsx +75 -0
- package/src/react/workflows/Refunds.tsx +223 -0
- package/src/react/workflows/Setup.test.tsx +25 -0
- package/src/react/workflows/Setup.tsx +166 -0
- package/src/react/workflows/Storefront.test.tsx +82 -0
- package/src/react/workflows/Storefront.tsx +138 -0
- package/src/react/workflows/SubscriptionOverview.test.tsx +27 -0
- package/src/react/workflows/SubscriptionOverview.tsx +48 -0
- package/src/react/workflows/Subscriptions.test.tsx +61 -0
- package/src/react/workflows/Subscriptions.tsx +282 -0
- package/src/react/workflows/Tasks.test.tsx +23 -0
- package/src/react/workflows/Tasks.tsx +199 -0
- package/src/react/workflows/checkout-options.ts +42 -0
- package/src/react/workflows/current-subscription.test.ts +29 -0
- package/src/react/workflows/current-subscription.ts +25 -0
- package/src/react/workflows/filters.test.ts +12 -0
- package/src/react/workflows/filters.ts +22 -0
- package/src/react/workflows/index.ts +238 -0
- package/src/react/workflows/native-interactions.test.tsx +73 -0
- package/src/react/workflows/public-workflows.test.tsx +216 -0
- package/src/react/workflows/useWorkflowAction.test.tsx +54 -0
- package/src/react/workflows/useWorkflowAction.ts +75 -0
- package/src/react/workflows/useWorkflowResource.test.tsx +41 -0
- package/src/react/workflows/useWorkflowResource.ts +78 -0
|
@@ -114,7 +114,7 @@ export async function createCheckoutSession(
|
|
|
114
114
|
orgId?: string;
|
|
115
115
|
stripePriceId: string;
|
|
116
116
|
mode: "payment" | "subscription" | "setup";
|
|
117
|
-
uiMode?: "embedded" | "redirect";
|
|
117
|
+
uiMode?: "embedded" | "redirect" | "custom" | "form";
|
|
118
118
|
quantity?: number;
|
|
119
119
|
returnUrl: string;
|
|
120
120
|
trialDays?: number;
|
|
@@ -295,8 +295,13 @@ export async function createCheckoutSession(
|
|
|
295
295
|
sessionParams.payment_intent_data = paymentIntentData;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
if (uiMode === "embedded") {
|
|
299
|
-
sessionParams.ui_mode =
|
|
298
|
+
if (uiMode === "embedded" || uiMode === "custom" || uiMode === "form") {
|
|
299
|
+
sessionParams.ui_mode =
|
|
300
|
+
uiMode === "custom"
|
|
301
|
+
? "elements"
|
|
302
|
+
: uiMode === "form"
|
|
303
|
+
? "form"
|
|
304
|
+
: "embedded_page";
|
|
300
305
|
sessionParams.return_url = opts.returnUrl;
|
|
301
306
|
} else {
|
|
302
307
|
sessionParams.success_url = `${opts.returnUrl}?session_id={CHECKOUT_SESSION_ID}`;
|
|
@@ -75,7 +75,9 @@ describe("listInvoices", () => {
|
|
|
75
75
|
|
|
76
76
|
expect(result).toEqual([]);
|
|
77
77
|
const [ref, args] = ctx.runQuery.mock.calls[0];
|
|
78
|
-
expect(ref[TO_REF]).toBe(
|
|
78
|
+
expect(ref[TO_REF]).toBe(
|
|
79
|
+
refPath(component, "billing/queries/listInvoices"),
|
|
80
|
+
);
|
|
79
81
|
expect(args).toEqual({});
|
|
80
82
|
});
|
|
81
83
|
|
|
@@ -125,7 +127,9 @@ describe("listInvoicesByUser", () => {
|
|
|
125
127
|
|
|
126
128
|
expect(result).toBe(rows);
|
|
127
129
|
const [ref, args] = ctx.runQuery.mock.calls[0];
|
|
128
|
-
expect(ref[TO_REF]).toBe(
|
|
130
|
+
expect(ref[TO_REF]).toBe(
|
|
131
|
+
refPath(component, "billing/queries/listInvoices"),
|
|
132
|
+
);
|
|
129
133
|
expect(args).toEqual({ userId: "u_9" });
|
|
130
134
|
});
|
|
131
135
|
});
|
|
@@ -177,9 +181,9 @@ describe("upsertInvoice", () => {
|
|
|
177
181
|
it("throws when ctx has no runMutation (runMutationOrThrow guard)", async () => {
|
|
178
182
|
const ctx = { runQuery: vi.fn() } as unknown as RunCtx;
|
|
179
183
|
|
|
180
|
-
await expect(
|
|
181
|
-
|
|
182
|
-
)
|
|
184
|
+
await expect(upsertInvoice(makeComponent(), ctx, baseOpts)).rejects.toThrow(
|
|
185
|
+
/requires a Convex ctx with runMutation/,
|
|
186
|
+
);
|
|
183
187
|
});
|
|
184
188
|
});
|
|
185
189
|
|
|
@@ -189,7 +193,9 @@ describe("getInvoice", () => {
|
|
|
189
193
|
const row = { stripeInvoiceId: "in_4", status: "paid" };
|
|
190
194
|
const ctx = makeCtx(row);
|
|
191
195
|
|
|
192
|
-
const result = await getInvoice(component, ctx, {
|
|
196
|
+
const result = await getInvoice(component, ctx, {
|
|
197
|
+
stripeInvoiceId: "in_4",
|
|
198
|
+
});
|
|
193
199
|
|
|
194
200
|
expect(result).toBe(row);
|
|
195
201
|
const [ref, args] = ctx.runQuery.mock.calls[0];
|
|
@@ -60,7 +60,9 @@ describe("payment methods are keyed to the customer_account (BTS-19 reuse)", ()
|
|
|
60
60
|
|
|
61
61
|
it("detaches a payment method by id", async () => {
|
|
62
62
|
const stripe = makeStripe();
|
|
63
|
-
await detachPaymentMethod(asStripe(stripe), ctx, {
|
|
63
|
+
await detachPaymentMethod(asStripe(stripe), ctx, {
|
|
64
|
+
paymentMethodId: "pm_1",
|
|
65
|
+
});
|
|
64
66
|
expect(stripe.paymentMethods.detach).toHaveBeenCalledWith("pm_1");
|
|
65
67
|
});
|
|
66
68
|
});
|
|
@@ -51,5 +51,8 @@ export async function setDefaultPaymentMethod(
|
|
|
51
51
|
_ctx: RunCtx,
|
|
52
52
|
_opts: { stripeAccountId: string; paymentMethodId: string },
|
|
53
53
|
) {
|
|
54
|
-
throwStripeError(
|
|
54
|
+
throwStripeError(
|
|
55
|
+
"PAYMENT_METHOD_FAILED",
|
|
56
|
+
"[better-stripe] setDefaultPaymentMethod is not yet implemented for V2 Accounts. Use createBillingPortalSession() to let users manage payment methods.",
|
|
57
|
+
);
|
|
55
58
|
}
|
|
@@ -19,8 +19,7 @@ import type { SplitRecipientRole } from "../../component/lib/fees.js";
|
|
|
19
19
|
|
|
20
20
|
/** Who is initiating a refund. `accountId` is the seller's `acct_…` id. */
|
|
21
21
|
export type RefundActor =
|
|
22
|
-
| { type: "
|
|
23
|
-
| { type: "seller"; accountId: string };
|
|
22
|
+
{ type: "admin" } | { type: "seller"; accountId: string };
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* The subset of a payment's routing needed to authorize a seller refund, read
|
|
@@ -53,7 +53,9 @@ function makeStripe(charge: Record<string, unknown>) {
|
|
|
53
53
|
.mockResolvedValue({ id: "ch_1", payment_intent: "pi_1", ...charge }),
|
|
54
54
|
},
|
|
55
55
|
paymentIntents: {
|
|
56
|
-
retrieve: vi
|
|
56
|
+
retrieve: vi
|
|
57
|
+
.fn()
|
|
58
|
+
.mockResolvedValue({ id: "pi_1", latest_charge: "ch_1" }),
|
|
57
59
|
},
|
|
58
60
|
};
|
|
59
61
|
}
|
|
@@ -62,12 +64,20 @@ const asStripe = (s: ReturnType<typeof makeStripe>) => s as unknown as Stripe;
|
|
|
62
64
|
|
|
63
65
|
describe("createRefund — fee & transfer flags (BTS-34)", () => {
|
|
64
66
|
it("defaults both flags on for a destination charge with an application fee", async () => {
|
|
65
|
-
const stripe = makeStripe({
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
stripeChargeId: "ch_1",
|
|
67
|
+
const stripe = makeStripe({
|
|
68
|
+
transfer: "tr_auto",
|
|
69
|
+
application_fee: "fee_1",
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
const result = await createRefund(
|
|
73
|
+
asStripe(stripe),
|
|
74
|
+
makeComponent(),
|
|
75
|
+
noCtx,
|
|
76
|
+
{
|
|
77
|
+
stripeChargeId: "ch_1",
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
|
|
71
81
|
expect(result).toEqual({ stripeRefundId: "re_new" });
|
|
72
82
|
expect(stripe.refunds.create).toHaveBeenCalledWith(
|
|
73
83
|
{
|
|
@@ -80,7 +90,10 @@ describe("createRefund — fee & transfer flags (BTS-34)", () => {
|
|
|
80
90
|
});
|
|
81
91
|
|
|
82
92
|
it("keeps the flags on a partial refund (Stripe pro-rates both natively)", async () => {
|
|
83
|
-
const stripe = makeStripe({
|
|
93
|
+
const stripe = makeStripe({
|
|
94
|
+
transfer: "tr_auto",
|
|
95
|
+
application_fee: "fee_1",
|
|
96
|
+
});
|
|
84
97
|
|
|
85
98
|
await createRefund(asStripe(stripe), makeComponent(), noCtx, {
|
|
86
99
|
stripeChargeId: "ch_1",
|
|
@@ -124,7 +137,10 @@ describe("createRefund — fee & transfer flags (BTS-34)", () => {
|
|
|
124
137
|
});
|
|
125
138
|
|
|
126
139
|
it("honours an explicit false even when the charge is capable", async () => {
|
|
127
|
-
const stripe = makeStripe({
|
|
140
|
+
const stripe = makeStripe({
|
|
141
|
+
transfer: "tr_auto",
|
|
142
|
+
application_fee: "fee_1",
|
|
143
|
+
});
|
|
128
144
|
|
|
129
145
|
await createRefund(asStripe(stripe), makeComponent(), noCtx, {
|
|
130
146
|
stripeChargeId: "ch_1",
|
|
@@ -217,7 +233,10 @@ describe("createRefund — fee & transfer flags (BTS-34)", () => {
|
|
|
217
233
|
|
|
218
234
|
describe("createRefund — actor scoping (BTS-35)", () => {
|
|
219
235
|
it("lets a platform admin refund any sale without a ledger lookup", async () => {
|
|
220
|
-
const stripe = makeStripe({
|
|
236
|
+
const stripe = makeStripe({
|
|
237
|
+
transfer: "tr_auto",
|
|
238
|
+
application_fee: "fee_1",
|
|
239
|
+
});
|
|
221
240
|
const ctx = makeCtx();
|
|
222
241
|
|
|
223
242
|
await createRefund(asStripe(stripe), makeComponent(), ctx, {
|
|
@@ -250,7 +269,10 @@ describe("createRefund — actor scoping (BTS-35)", () => {
|
|
|
250
269
|
});
|
|
251
270
|
|
|
252
271
|
it("rejects a seller refunding another seller's destination charge", async () => {
|
|
253
|
-
const stripe = makeStripe({
|
|
272
|
+
const stripe = makeStripe({
|
|
273
|
+
transfer: "tr_auto",
|
|
274
|
+
application_fee: "fee_1",
|
|
275
|
+
});
|
|
254
276
|
const ctx = makeCtx({
|
|
255
277
|
stripePaymentIntentId: "pi_1",
|
|
256
278
|
destinationAccountId: "acct_other",
|
|
@@ -307,7 +329,10 @@ describe("createRefund — actor scoping (BTS-35)", () => {
|
|
|
307
329
|
});
|
|
308
330
|
|
|
309
331
|
it("rejects a seller when no matching payment exists in the ledger (fail closed)", async () => {
|
|
310
|
-
const stripe = makeStripe({
|
|
332
|
+
const stripe = makeStripe({
|
|
333
|
+
transfer: "tr_auto",
|
|
334
|
+
application_fee: "fee_1",
|
|
335
|
+
});
|
|
311
336
|
const ctx = makeCtx(null);
|
|
312
337
|
|
|
313
338
|
await expect(
|
|
@@ -3,7 +3,10 @@ import type Stripe from "stripe";
|
|
|
3
3
|
import type { RefundStatus } from "../../component/connect/validators.js";
|
|
4
4
|
import { throwStripeError } from "../errors.js";
|
|
5
5
|
import type { Component, RunCtx } from "../helpers.js";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
StripeComponentPayment,
|
|
8
|
+
StripeComponentRefund,
|
|
9
|
+
} from "../types.js";
|
|
7
10
|
import { componentRef } from "../webhooks/helpers.js";
|
|
8
11
|
import { type RefundActor, isRefundAuthorized } from "./refundActor.js";
|
|
9
12
|
|
|
@@ -14,7 +14,9 @@ function makeComponent(): Component {
|
|
|
14
14
|
const ref = (path: string) => ({ [TO_REF]: `betterStripe/${path}` });
|
|
15
15
|
return {
|
|
16
16
|
connect: {
|
|
17
|
-
queries: {
|
|
17
|
+
queries: {
|
|
18
|
+
listTransfersByCharge: ref("connect/queries/listTransfersByCharge"),
|
|
19
|
+
},
|
|
18
20
|
mutations: {
|
|
19
21
|
recordTransferReversal: ref("connect/mutations/recordTransferReversal"),
|
|
20
22
|
upsertTransfer: ref("connect/mutations/upsertTransfer"),
|
|
@@ -56,7 +58,10 @@ function makeCtx(transfers: unknown[]) {
|
|
|
56
58
|
legs.map((t) => ({
|
|
57
59
|
stripeTransferId: t.stripeTransferId,
|
|
58
60
|
amount: t.amount,
|
|
59
|
-
frontier: Math.max(
|
|
61
|
+
frontier: Math.max(
|
|
62
|
+
t.reversedAmount ?? 0,
|
|
63
|
+
t.reversalClaimedAmount ?? 0,
|
|
64
|
+
),
|
|
60
65
|
})),
|
|
61
66
|
args.mode as Parameters<typeof computeReversalSlices>[1],
|
|
62
67
|
);
|
|
@@ -117,7 +122,9 @@ describe("reverseTransfers (BTS-25)", () => {
|
|
|
117
122
|
undefined,
|
|
118
123
|
);
|
|
119
124
|
// ledger updated with cumulative reversed amounts
|
|
120
|
-
const reversedAmounts = ctx.runMutation.mock.calls.map(
|
|
125
|
+
const reversedAmounts = ctx.runMutation.mock.calls.map(
|
|
126
|
+
(c) => c[1].reversedAmount,
|
|
127
|
+
);
|
|
121
128
|
expect(reversedAmounts).toEqual([8000, 1000]);
|
|
122
129
|
});
|
|
123
130
|
|
|
@@ -391,7 +398,12 @@ describe("createSplitTransfers idempotency (BTS-24)", () => {
|
|
|
391
398
|
const stripe = makeStripe();
|
|
392
399
|
// The store transfer already exists from a prior (partial) run.
|
|
393
400
|
const ctx = makeCtx([
|
|
394
|
-
{
|
|
401
|
+
{
|
|
402
|
+
stripeTransferId: "tr_store",
|
|
403
|
+
destinationAccountId: "acct_store",
|
|
404
|
+
role: "store",
|
|
405
|
+
amount: 8000,
|
|
406
|
+
},
|
|
395
407
|
]);
|
|
396
408
|
|
|
397
409
|
await createSplitTransfers(asStripe(stripe), makeComponent(), ctx, {
|
|
@@ -414,8 +426,18 @@ describe("createSplitTransfers idempotency (BTS-24)", () => {
|
|
|
414
426
|
it("creates nothing when all recipients are already in the ledger (full replay)", async () => {
|
|
415
427
|
const stripe = makeStripe();
|
|
416
428
|
const ctx = makeCtx([
|
|
417
|
-
{
|
|
418
|
-
|
|
429
|
+
{
|
|
430
|
+
stripeTransferId: "tr_store",
|
|
431
|
+
destinationAccountId: "acct_store",
|
|
432
|
+
role: "store",
|
|
433
|
+
amount: 8000,
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
stripeTransferId: "tr_aff",
|
|
437
|
+
destinationAccountId: "acct_aff",
|
|
438
|
+
role: "affiliate",
|
|
439
|
+
amount: 1000,
|
|
440
|
+
},
|
|
419
441
|
]);
|
|
420
442
|
|
|
421
443
|
await createSplitTransfers(asStripe(stripe), makeComponent(), ctx, {
|
|
@@ -436,8 +458,22 @@ describe("reinstateTransfers (BTS-29)", () => {
|
|
|
436
458
|
it("re-creates transfers for the reversed amounts (dispute won)", async () => {
|
|
437
459
|
const stripe = makeStripe();
|
|
438
460
|
const ctx = makeCtx([
|
|
439
|
-
{
|
|
440
|
-
|
|
461
|
+
{
|
|
462
|
+
stripeTransferId: "tr_store",
|
|
463
|
+
destinationAccountId: "acct_store",
|
|
464
|
+
role: "store",
|
|
465
|
+
amount: 8000,
|
|
466
|
+
currency: "usd",
|
|
467
|
+
reversedAmount: 8000,
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
stripeTransferId: "tr_aff",
|
|
471
|
+
destinationAccountId: "acct_aff",
|
|
472
|
+
role: "affiliate",
|
|
473
|
+
amount: 1000,
|
|
474
|
+
currency: "usd",
|
|
475
|
+
reversedAmount: 1000,
|
|
476
|
+
},
|
|
441
477
|
]);
|
|
442
478
|
|
|
443
479
|
await reinstateTransfers(asStripe(stripe), makeComponent(), ctx, {
|
|
@@ -448,7 +484,11 @@ describe("reinstateTransfers (BTS-29)", () => {
|
|
|
448
484
|
|
|
449
485
|
expect(stripe.transfers.create).toHaveBeenCalledTimes(2);
|
|
450
486
|
expect(stripe.transfers.create).toHaveBeenCalledWith(
|
|
451
|
-
expect.objectContaining({
|
|
487
|
+
expect.objectContaining({
|
|
488
|
+
amount: 8000,
|
|
489
|
+
currency: "usd",
|
|
490
|
+
destination: "acct_store",
|
|
491
|
+
}),
|
|
452
492
|
{ idempotencyKey: "bs_reinstate_dp_1_acct_store_store" },
|
|
453
493
|
);
|
|
454
494
|
expect(stripe.transfers.create).toHaveBeenCalledWith(
|
|
@@ -457,14 +497,22 @@ describe("reinstateTransfers (BTS-29)", () => {
|
|
|
457
497
|
);
|
|
458
498
|
// Ledger rows are tagged reinstatement so they stay out of the charge's
|
|
459
499
|
// split-leg view and can't be re-reversed.
|
|
460
|
-
expect(ctx.runMutation.mock.calls[0][1]).toMatchObject({
|
|
500
|
+
expect(ctx.runMutation.mock.calls[0][1]).toMatchObject({
|
|
501
|
+
reinstatement: true,
|
|
502
|
+
});
|
|
461
503
|
});
|
|
462
504
|
|
|
463
505
|
it("uses a legacy row's fallback currency from the dispute, not USD", async () => {
|
|
464
506
|
const stripe = makeStripe();
|
|
465
507
|
const ctx = makeCtx([
|
|
466
508
|
// Legacy row with no stored currency.
|
|
467
|
-
{
|
|
509
|
+
{
|
|
510
|
+
stripeTransferId: "tr_x",
|
|
511
|
+
destinationAccountId: "acct_x",
|
|
512
|
+
role: "store",
|
|
513
|
+
amount: 5000,
|
|
514
|
+
reversedAmount: 5000,
|
|
515
|
+
},
|
|
468
516
|
]);
|
|
469
517
|
await reinstateTransfers(asStripe(stripe), makeComponent(), ctx, {
|
|
470
518
|
sourceChargeId: "ch_1",
|
|
@@ -480,7 +528,13 @@ describe("reinstateTransfers (BTS-29)", () => {
|
|
|
480
528
|
it("skips transfers that were never reversed", async () => {
|
|
481
529
|
const stripe = makeStripe();
|
|
482
530
|
const ctx = makeCtx([
|
|
483
|
-
{
|
|
531
|
+
{
|
|
532
|
+
stripeTransferId: "tr_store",
|
|
533
|
+
destinationAccountId: "acct_store",
|
|
534
|
+
role: "store",
|
|
535
|
+
amount: 8000,
|
|
536
|
+
currency: "usd",
|
|
537
|
+
},
|
|
484
538
|
]);
|
|
485
539
|
await reinstateTransfers(asStripe(stripe), makeComponent(), ctx, {
|
|
486
540
|
sourceChargeId: "ch_1",
|
|
@@ -78,7 +78,10 @@ async function executeClaimedSlices(
|
|
|
78
78
|
await runMutationOrThrow(
|
|
79
79
|
ctx,
|
|
80
80
|
componentRef(component, "connect/mutations/recordTransferReversal"),
|
|
81
|
-
{
|
|
81
|
+
{
|
|
82
|
+
stripeTransferId: slice.stripeTransferId,
|
|
83
|
+
reversedAmount: slice.to,
|
|
84
|
+
},
|
|
82
85
|
);
|
|
83
86
|
continue;
|
|
84
87
|
}
|
|
@@ -242,7 +245,10 @@ export async function reclaimReversalClaim(
|
|
|
242
245
|
ctx,
|
|
243
246
|
componentRef(component, "connect/mutations/releaseReversalClaim"),
|
|
244
247
|
{ operationId: op.operationId, verified },
|
|
245
|
-
)) as {
|
|
248
|
+
)) as {
|
|
249
|
+
released: boolean;
|
|
250
|
+
rewound: { stripeTransferId: string; to: number }[];
|
|
251
|
+
};
|
|
246
252
|
return { mode: "release", ...res };
|
|
247
253
|
}
|
|
248
254
|
|
|
@@ -312,7 +318,13 @@ export async function reverseTransfers(
|
|
|
312
318
|
: { kind: "full" as const },
|
|
313
319
|
},
|
|
314
320
|
)) as ReversalClaim;
|
|
315
|
-
return executeClaimedSlices(
|
|
321
|
+
return executeClaimedSlices(
|
|
322
|
+
stripe,
|
|
323
|
+
component,
|
|
324
|
+
ctx,
|
|
325
|
+
opts.operationId,
|
|
326
|
+
claim,
|
|
327
|
+
);
|
|
316
328
|
}
|
|
317
329
|
|
|
318
330
|
// Legacy one-shot path (no operationId): size against the claim frontier so
|
|
@@ -330,7 +342,10 @@ export async function reverseTransfers(
|
|
|
330
342
|
const reversals: { stripeTransferId: string; amount: number }[] = [];
|
|
331
343
|
|
|
332
344
|
for (const t of transfers) {
|
|
333
|
-
const already = Math.max(
|
|
345
|
+
const already = Math.max(
|
|
346
|
+
t.reversedAmount ?? 0,
|
|
347
|
+
t.reversalClaimedAmount ?? 0,
|
|
348
|
+
);
|
|
334
349
|
const remaining = t.amount - already;
|
|
335
350
|
if (remaining <= 0) continue; // already fully reversed — idempotent
|
|
336
351
|
|
|
@@ -338,7 +353,8 @@ export async function reverseTransfers(
|
|
|
338
353
|
if (opts.percent !== undefined) {
|
|
339
354
|
reversalAmt = Math.round((t.amount * opts.percent) / 100);
|
|
340
355
|
} else if (amountBudget !== undefined) {
|
|
341
|
-
const prorata =
|
|
356
|
+
const prorata =
|
|
357
|
+
total > 0 ? Math.round((opts.amount! * t.amount) / total) : 0;
|
|
342
358
|
reversalAmt = Math.min(prorata, amountBudget);
|
|
343
359
|
} else {
|
|
344
360
|
reversalAmt = remaining; // full reversal
|
|
@@ -355,9 +371,15 @@ export async function reverseTransfers(
|
|
|
355
371
|
await runMutationOrThrow(
|
|
356
372
|
ctx,
|
|
357
373
|
componentRef(component, "connect/mutations/recordTransferReversal"),
|
|
358
|
-
{
|
|
374
|
+
{
|
|
375
|
+
stripeTransferId: t.stripeTransferId,
|
|
376
|
+
reversedAmount: already + reversalAmt,
|
|
377
|
+
},
|
|
359
378
|
);
|
|
360
|
-
reversals.push({
|
|
379
|
+
reversals.push({
|
|
380
|
+
stripeTransferId: t.stripeTransferId,
|
|
381
|
+
amount: reversalAmt,
|
|
382
|
+
});
|
|
361
383
|
}
|
|
362
384
|
|
|
363
385
|
return { reversals };
|
|
@@ -461,7 +483,9 @@ export async function createSplitTransfers(
|
|
|
461
483
|
metadata: { bsRole: t.role, bsSourceCharge: opts.sourceChargeId },
|
|
462
484
|
},
|
|
463
485
|
// Deterministic key so a webhook retry can't double-transfer (BTS-24).
|
|
464
|
-
{
|
|
486
|
+
{
|
|
487
|
+
idempotencyKey: `bs_split_${opts.sourceChargeId}_${t.destinationAccountId}_${t.role}`,
|
|
488
|
+
},
|
|
465
489
|
);
|
|
466
490
|
await runMutationOrThrow(
|
|
467
491
|
ctx,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default V2 account configuration: merchant with card_payments.
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_ACCOUNT_CONFIGURATION: Record<string, unknown> = {
|
|
5
|
+
merchant: {
|
|
6
|
+
capabilities: { card_payments: { requested: true } },
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_ACCOUNT_DEFAULTS: Record<string, unknown> = {
|
|
11
|
+
responsibilities: {
|
|
12
|
+
losses_collector: "application",
|
|
13
|
+
fees_collector: "application",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -14,6 +14,15 @@ import { componentRef } from "../webhooks/helpers.js";
|
|
|
14
14
|
import { validateStatementDescriptorSuffix } from "./descriptors.js";
|
|
15
15
|
import { deriveAccountStatus } from "./accountStatus.js";
|
|
16
16
|
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_ACCOUNT_CONFIGURATION,
|
|
19
|
+
DEFAULT_ACCOUNT_DEFAULTS,
|
|
20
|
+
} from "./accountDefaults.js";
|
|
21
|
+
export {
|
|
22
|
+
DEFAULT_ACCOUNT_CONFIGURATION,
|
|
23
|
+
DEFAULT_ACCOUNT_DEFAULTS,
|
|
24
|
+
} from "./accountDefaults.js";
|
|
25
|
+
|
|
17
26
|
// =============================================================================
|
|
18
27
|
// Account CRUD
|
|
19
28
|
// =============================================================================
|
|
@@ -76,22 +85,6 @@ export async function createAccount(
|
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
87
|
|
|
79
|
-
/**
|
|
80
|
-
* Default V2 account configuration: merchant with card_payments.
|
|
81
|
-
*/
|
|
82
|
-
export const DEFAULT_ACCOUNT_CONFIGURATION: Record<string, unknown> = {
|
|
83
|
-
merchant: {
|
|
84
|
-
capabilities: { card_payments: { requested: true } },
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export const DEFAULT_ACCOUNT_DEFAULTS: Record<string, unknown> = {
|
|
89
|
-
responsibilities: {
|
|
90
|
-
losses_collector: "application",
|
|
91
|
-
fees_collector: "application",
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
|
|
95
88
|
export async function createAccountWithOnboarding(
|
|
96
89
|
stripe: Stripe,
|
|
97
90
|
component: Component,
|
|
@@ -145,7 +138,11 @@ export async function createAccountWithOnboarding(
|
|
|
145
138
|
} catch {
|
|
146
139
|
// Best effort cleanup
|
|
147
140
|
}
|
|
148
|
-
throwStripeError(
|
|
141
|
+
throwStripeError(
|
|
142
|
+
"ACCOUNT_CREATE_FAILED",
|
|
143
|
+
"Failed to apply account configuration after create; account was rolled back",
|
|
144
|
+
configError,
|
|
145
|
+
);
|
|
149
146
|
}
|
|
150
147
|
|
|
151
148
|
// Re-fetch account to get actual applied_configurations from Stripe
|
|
@@ -37,7 +37,8 @@ describe("getPublishableKey", () => {
|
|
|
37
37
|
const result = await getPublishableKey(component, ctx);
|
|
38
38
|
|
|
39
39
|
expect(result).toBe("pk_test_123");
|
|
40
|
-
const [ref, args] = (ctx.runQuery as ReturnType<typeof vi.fn>).mock
|
|
40
|
+
const [ref, args] = (ctx.runQuery as ReturnType<typeof vi.fn>).mock
|
|
41
|
+
.calls[0];
|
|
41
42
|
expect(ref[TO_REF]).toBe("betterStripe/core/queries/getPublishableKey");
|
|
42
43
|
expect(args).toEqual({});
|
|
43
44
|
});
|
|
@@ -38,15 +38,17 @@ describe("validateStatementDescriptorSuffix", () => {
|
|
|
38
38
|
it.each(["<", ">", "\\", "'", '"', "*"])(
|
|
39
39
|
"rejects the disallowed character %s",
|
|
40
40
|
(char) => {
|
|
41
|
-
expect(() =>
|
|
42
|
-
|
|
43
|
-
);
|
|
41
|
+
expect(() =>
|
|
42
|
+
validateStatementDescriptorSuffix(`STORE${char}NAME`),
|
|
43
|
+
).toThrow(/character/i);
|
|
44
44
|
},
|
|
45
45
|
);
|
|
46
46
|
|
|
47
47
|
it("rejects a value with no letter (digits/punctuation only)", () => {
|
|
48
48
|
expect(() => validateStatementDescriptorSuffix("12345")).toThrow(/letter/i);
|
|
49
|
-
expect(() => validateStatementDescriptorSuffix("--- 9")).toThrow(
|
|
49
|
+
expect(() => validateStatementDescriptorSuffix("--- 9")).toThrow(
|
|
50
|
+
/letter/i,
|
|
51
|
+
);
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
it("rejects non-ASCII (non-Latin) characters", () => {
|
|
@@ -14,7 +14,9 @@ describe("validatePlatformFee (BTS-13)", () => {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
it("accepts percent + fixed", () => {
|
|
17
|
-
expect(() =>
|
|
17
|
+
expect(() =>
|
|
18
|
+
validatePlatformFee({ percent: 2.9, fixed: 30 }),
|
|
19
|
+
).not.toThrow();
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
it("accepts ascending tiers with a null final upper bound", () => {
|
|
@@ -219,14 +221,10 @@ describe("computeSplit (BTS-23)", () => {
|
|
|
219
221
|
const aff = "acct_aff";
|
|
220
222
|
|
|
221
223
|
it("splits with fixed recipient amounts; platform retains the rest", () => {
|
|
222
|
-
const r = computeSplit(
|
|
223
|
-
|
|
224
|
-
{
|
|
225
|
-
|
|
226
|
-
{ destinationAccountId: store, role: "store", amount: 8000 },
|
|
227
|
-
{ destinationAccountId: aff, role: "affiliate", amount: 1000 },
|
|
228
|
-
],
|
|
229
|
-
);
|
|
224
|
+
const r = computeSplit(10000, { percent: 10 }, [
|
|
225
|
+
{ destinationAccountId: store, role: "store", amount: 8000 },
|
|
226
|
+
{ destinationAccountId: aff, role: "affiliate", amount: 1000 },
|
|
227
|
+
]);
|
|
230
228
|
expect(r.transfers).toEqual([
|
|
231
229
|
{ destinationAccountId: store, role: "store", amount: 8000 },
|
|
232
230
|
{ destinationAccountId: aff, role: "affiliate", amount: 1000 },
|
|
@@ -239,14 +237,10 @@ describe("computeSplit (BTS-23)", () => {
|
|
|
239
237
|
});
|
|
240
238
|
|
|
241
239
|
it("supports percent-of-gross recipients", () => {
|
|
242
|
-
const r = computeSplit(
|
|
243
|
-
|
|
244
|
-
{ percent:
|
|
245
|
-
|
|
246
|
-
{ destinationAccountId: store, role: "store", percent: 80 },
|
|
247
|
-
{ destinationAccountId: aff, role: "affiliate", percent: 5 },
|
|
248
|
-
],
|
|
249
|
-
);
|
|
240
|
+
const r = computeSplit(10000, { percent: 10 }, [
|
|
241
|
+
{ destinationAccountId: store, role: "store", percent: 80 },
|
|
242
|
+
{ destinationAccountId: aff, role: "affiliate", percent: 5 },
|
|
243
|
+
]);
|
|
250
244
|
expect(r.transfers.map((t) => t.amount)).toEqual([8000, 500]);
|
|
251
245
|
expect(r.platformRetained).toBe(1500); // 10000 - 8500
|
|
252
246
|
});
|
|
@@ -269,15 +263,11 @@ describe("computeSplit (BTS-23)", () => {
|
|
|
269
263
|
|
|
270
264
|
it("assigns the rounding remainder deterministically to the platform", () => {
|
|
271
265
|
// 33% + 33% + 33% of 100 → 33 + 33 + 33 = 99; platform keeps the 1 remainder.
|
|
272
|
-
const r = computeSplit(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
{ destinationAccountId: aff, role: "affiliate", percent: 33 },
|
|
278
|
-
{ destinationAccountId: "acct_c", role: "other", percent: 33 },
|
|
279
|
-
],
|
|
280
|
-
);
|
|
266
|
+
const r = computeSplit(100, undefined, [
|
|
267
|
+
{ destinationAccountId: store, role: "store", percent: 33 },
|
|
268
|
+
{ destinationAccountId: aff, role: "affiliate", percent: 33 },
|
|
269
|
+
{ destinationAccountId: "acct_c", role: "other", percent: 33 },
|
|
270
|
+
]);
|
|
281
271
|
const sum = r.transfers.reduce((s, t) => s + t.amount, 0);
|
|
282
272
|
expect(sum).toBe(99);
|
|
283
273
|
expect(r.platformRetained).toBe(1);
|
|
@@ -300,7 +290,7 @@ describe("computeSplit (BTS-23)", () => {
|
|
|
300
290
|
expect(r.platformFee).toBe(0);
|
|
301
291
|
expect(r.platformRetained).toBe(0);
|
|
302
292
|
});
|
|
303
|
-
})
|
|
293
|
+
});
|
|
304
294
|
|
|
305
295
|
describe("validateSplit (BTS-21)", () => {
|
|
306
296
|
const r = (over = {}) => ({
|
|
@@ -354,7 +344,9 @@ describe("validateSplit (BTS-21)", () => {
|
|
|
354
344
|
});
|
|
355
345
|
|
|
356
346
|
it("rejects an empty destinationAccountId", () => {
|
|
357
|
-
expect(() =>
|
|
347
|
+
expect(() =>
|
|
348
|
+
validateSplit([r({ destinationAccountId: "", amount: 100 })]),
|
|
349
|
+
).toThrow();
|
|
358
350
|
});
|
|
359
351
|
|
|
360
352
|
it("rejects duplicate destination+role legs (idempotency-key collision)", () => {
|
package/src/client/core/fees.ts
CHANGED
|
@@ -201,7 +201,11 @@ export function computeSplit(
|
|
|
201
201
|
`Invalid split amount for ${r.destinationAccountId}: ${amt}`,
|
|
202
202
|
);
|
|
203
203
|
}
|
|
204
|
-
return {
|
|
204
|
+
return {
|
|
205
|
+
destinationAccountId: r.destinationAccountId,
|
|
206
|
+
role: r.role,
|
|
207
|
+
amount: amt,
|
|
208
|
+
};
|
|
205
209
|
});
|
|
206
210
|
|
|
207
211
|
const sumTransfers = transfers.reduce((s, t) => s + t.amount, 0);
|
|
@@ -247,7 +251,10 @@ export function validateSplit(recipients: SplitRecipient[]): void {
|
|
|
247
251
|
`split recipient ${r.destinationAccountId} must set exactly one of amount or percent`,
|
|
248
252
|
);
|
|
249
253
|
}
|
|
250
|
-
if (
|
|
254
|
+
if (
|
|
255
|
+
hasAmount &&
|
|
256
|
+
(!Number.isInteger(r.amount) || (r.amount as number) < 0)
|
|
257
|
+
) {
|
|
251
258
|
throw new Error(
|
|
252
259
|
`split recipient ${r.destinationAccountId} amount must be a non-negative integer (minor units)`,
|
|
253
260
|
);
|