@fantastic.dev/better-stripe 0.7.0 → 0.8.1
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 +29 -18
- package/README.md +123 -91
- 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/disputes.d.ts +1 -1
- package/dist/client/connect/disputes.d.ts.map +1 -1
- package/dist/client/connect/disputes.js +12 -6
- package/dist/client/connect/disputes.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/DisputeDetail.d.ts +4 -1
- package/dist/react/components/DisputeDetail.d.ts.map +1 -1
- package/dist/react/components/DisputeDetail.js +12 -6
- package/dist/react/components/DisputeDetail.js.map +1 -1
- package/dist/react/components/DisputesList.d.ts.map +1 -1
- package/dist/react/components/DisputesList.js +2 -1
- package/dist/react/components/DisputesList.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 +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- 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 +48 -0
- package/dist/react/workflows/Payouts.d.ts.map +1 -0
- package/dist/react/workflows/Payouts.js +88 -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 +88 -0
- package/docs/react-workflows.md +234 -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/disputes.test.ts +15 -0
- package/src/client/connect/disputes.ts +13 -7
- 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/DisputeDetail.tsx +14 -5
- package/src/react/components/DisputesList.render.test.tsx +21 -0
- package/src/react/components/DisputesList.tsx +2 -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 +3 -10
- 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 +7 -1
- 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 +64 -0
- package/src/react/workflows/Disputes.tsx +173 -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 +112 -0
- package/src/react/workflows/Payouts.tsx +215 -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
|
@@ -64,10 +64,14 @@ export function computeReversalSlices(
|
|
|
64
64
|
);
|
|
65
65
|
break;
|
|
66
66
|
case "fraction":
|
|
67
|
-
to = Math.min(
|
|
67
|
+
to = Math.min(
|
|
68
|
+
leg.amount,
|
|
69
|
+
Math.max(from, Math.round(leg.amount * fraction)),
|
|
70
|
+
);
|
|
68
71
|
break;
|
|
69
72
|
case "amount": {
|
|
70
|
-
const prorata =
|
|
73
|
+
const prorata =
|
|
74
|
+
total > 0 ? Math.round((mode.amount * leg.amount) / total) : 0;
|
|
71
75
|
const take = Math.max(0, Math.min(prorata, budget, leg.amount - from));
|
|
72
76
|
budget -= take;
|
|
73
77
|
to = from + take;
|
|
@@ -111,3 +115,27 @@ export function computeReversalSlices(
|
|
|
111
115
|
|
|
112
116
|
return slices;
|
|
113
117
|
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Derive reversal status + transfer status from a (transfer amount, cumulative
|
|
121
|
+
* reversed amount) pair. Shared by upsert and reversal so the three reversal
|
|
122
|
+
* fields can never drift.
|
|
123
|
+
*/
|
|
124
|
+
export function deriveReversalState(
|
|
125
|
+
amount: number,
|
|
126
|
+
reversedAmount: number,
|
|
127
|
+
baseStatus: "pending" | "paid" | "failed" | "reversed",
|
|
128
|
+
) {
|
|
129
|
+
const reversalStatus =
|
|
130
|
+
reversedAmount <= 0
|
|
131
|
+
? undefined
|
|
132
|
+
: reversedAmount >= amount
|
|
133
|
+
? ("fully_reversed" as const)
|
|
134
|
+
: ("partially_reversed" as const);
|
|
135
|
+
return {
|
|
136
|
+
reversedAmount: reversedAmount > 0 ? reversedAmount : undefined,
|
|
137
|
+
reversalStatus,
|
|
138
|
+
status:
|
|
139
|
+
reversalStatus === "fully_reversed" ? ("reversed" as const) : baseStatus,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -64,7 +64,11 @@ describe("per-store subscription scoping (BTS-19)", () => {
|
|
|
64
64
|
|
|
65
65
|
const active = await t.query(
|
|
66
66
|
api.billing.queries.listSubscriptionsByUserAndStore,
|
|
67
|
-
{
|
|
67
|
+
{
|
|
68
|
+
userId: "buyer_1",
|
|
69
|
+
destinationAccountId: "acct_store_a",
|
|
70
|
+
status: "active",
|
|
71
|
+
},
|
|
68
72
|
);
|
|
69
73
|
expect(active.map((s) => s.stripeSubscriptionId)).toEqual(["sub_active"]);
|
|
70
74
|
});
|
|
@@ -236,10 +236,13 @@ describe("webhook event ledger", () => {
|
|
|
236
236
|
it("fresh processing dedupes", async () => {
|
|
237
237
|
const t = convexTest(schema, modules);
|
|
238
238
|
|
|
239
|
-
const first = await t.mutation(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
const first = await t.mutation(
|
|
240
|
+
api.webhooks.mutations.insertWebhookEvent,
|
|
241
|
+
{
|
|
242
|
+
stripeEventId: "evt_stale_001",
|
|
243
|
+
eventType: "invoice.paid",
|
|
244
|
+
},
|
|
245
|
+
);
|
|
243
246
|
expect(first).toBe("inserted");
|
|
244
247
|
|
|
245
248
|
// Immediate second insert — row is fresh processing, must dedupe.
|
|
@@ -69,7 +69,10 @@ describe("BuyerBillingView", () => {
|
|
|
69
69
|
|
|
70
70
|
it("renders the empty label with no subscriptions and no saved card", () => {
|
|
71
71
|
const html = renderToStaticMarkup(
|
|
72
|
-
createElement(BuyerBillingView, {
|
|
72
|
+
createElement(BuyerBillingView, {
|
|
73
|
+
subscriptions: [],
|
|
74
|
+
paymentMethod: null,
|
|
75
|
+
}),
|
|
73
76
|
);
|
|
74
77
|
expect(html).toContain("No billing yet");
|
|
75
78
|
});
|
|
@@ -122,7 +125,9 @@ describe("BuyerBillingView", () => {
|
|
|
122
125
|
createElement(BuyerBillingView, {
|
|
123
126
|
subscriptions: [activeSub],
|
|
124
127
|
paymentMethod,
|
|
125
|
-
updateCardSlot: createElement("div", {
|
|
128
|
+
updateCardSlot: createElement("div", {
|
|
129
|
+
"data-testid": "embedded-card",
|
|
130
|
+
}),
|
|
126
131
|
}),
|
|
127
132
|
);
|
|
128
133
|
expect(html).toContain('data-testid="embedded-card"');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { cleanup, render } from "@testing-library/react";
|
|
3
|
+
import { afterEach, expect, it, vi } from "vitest";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
const stripe = vi.hoisted(() => ({ initialize: vi.fn(), update: vi.fn() }));
|
|
6
|
+
vi.mock("@stripe/connect-js/pure.js", () => ({
|
|
7
|
+
loadConnectAndInitialize: stripe.initialize,
|
|
8
|
+
}));
|
|
9
|
+
vi.mock("@stripe/connect-js", () => ({
|
|
10
|
+
loadConnectAndInitialize: stripe.initialize,
|
|
11
|
+
}));
|
|
12
|
+
vi.mock("@stripe/react-connect-js", () => ({
|
|
13
|
+
ConnectComponentsProvider: ({ children }: { children: ReactNode }) =>
|
|
14
|
+
children,
|
|
15
|
+
}));
|
|
16
|
+
import { ConnectProvider } from "./ConnectProvider.js";
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
cleanup();
|
|
19
|
+
vi.clearAllMocks();
|
|
20
|
+
});
|
|
21
|
+
it("updates appearance and locale while retaining the active Connect session", async () => {
|
|
22
|
+
stripe.initialize.mockReturnValue({ update: stripe.update });
|
|
23
|
+
const first = vi.fn(async () => "first_session");
|
|
24
|
+
const next = vi.fn(async () => "refreshed_session");
|
|
25
|
+
const { rerender } = render(
|
|
26
|
+
<ConnectProvider
|
|
27
|
+
publishableKey="pk_test_one"
|
|
28
|
+
fetchClientSecret={first}
|
|
29
|
+
locale="en"
|
|
30
|
+
/>,
|
|
31
|
+
);
|
|
32
|
+
const fetch = stripe.initialize.mock.calls[0][0].fetchClientSecret;
|
|
33
|
+
expect(await fetch()).toBe("first_session");
|
|
34
|
+
rerender(
|
|
35
|
+
<ConnectProvider
|
|
36
|
+
publishableKey="pk_test_one"
|
|
37
|
+
fetchClientSecret={next}
|
|
38
|
+
locale="fr"
|
|
39
|
+
appearance={{ variables: { colorPrimary: "#123456" } }}
|
|
40
|
+
/>,
|
|
41
|
+
);
|
|
42
|
+
expect(stripe.initialize).toHaveBeenCalledTimes(1);
|
|
43
|
+
expect(stripe.update).toHaveBeenLastCalledWith({
|
|
44
|
+
locale: "fr",
|
|
45
|
+
appearance: { variables: { colorPrimary: "#123456" } },
|
|
46
|
+
});
|
|
47
|
+
expect(await fetch()).toBe("refreshed_session");
|
|
48
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
49
|
+
});
|
|
50
|
+
it("creates a separate Connect instance when the publishable key changes", () => {
|
|
51
|
+
stripe.initialize.mockReturnValue({ update: stripe.update });
|
|
52
|
+
const fetch = async () => "session";
|
|
53
|
+
const { rerender } = render(
|
|
54
|
+
<ConnectProvider publishableKey="pk_test_one" fetchClientSecret={fetch} />,
|
|
55
|
+
);
|
|
56
|
+
rerender(
|
|
57
|
+
<ConnectProvider publishableKey="pk_test_two" fetchClientSecret={fetch} />,
|
|
58
|
+
);
|
|
59
|
+
expect(stripe.initialize).toHaveBeenCalledTimes(2);
|
|
60
|
+
expect(stripe.initialize.mock.calls[1][0].publishableKey).toBe("pk_test_two");
|
|
61
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { type ReactNode, useState } from "react";
|
|
3
|
+
import { type ReactNode, useEffect, useRef, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import type { IStripeConnectInitParams } from "@stripe/connect-js";
|
|
6
|
-
import { loadConnectAndInitialize } from "@stripe/connect-js";
|
|
6
|
+
import { loadConnectAndInitialize } from "@stripe/connect-js/pure.js";
|
|
7
7
|
import { ConnectComponentsProvider } from "@stripe/react-connect-js";
|
|
8
8
|
|
|
9
9
|
export type ConnectProviderProps = {
|
|
@@ -32,24 +32,38 @@ export type ConnectProviderProps = {
|
|
|
32
32
|
* ConnectJS renders client-only iframes; on the server this component renders
|
|
33
33
|
* its tree but the embedded surfaces only materialize in the browser.
|
|
34
34
|
*/
|
|
35
|
-
export function ConnectProvider({
|
|
35
|
+
export function ConnectProvider(props: ConnectProviderProps) {
|
|
36
|
+
return <ConnectInstanceProvider key={props.publishableKey} {...props} />;
|
|
37
|
+
}
|
|
38
|
+
function ConnectInstanceProvider({
|
|
36
39
|
publishableKey,
|
|
37
40
|
fetchClientSecret,
|
|
38
41
|
appearance,
|
|
39
42
|
locale,
|
|
40
43
|
children,
|
|
41
44
|
}: ConnectProviderProps) {
|
|
45
|
+
const fetcher = useRef(fetchClientSecret);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
fetcher.current = fetchClientSecret;
|
|
48
|
+
}, [fetchClientSecret]);
|
|
42
49
|
// Lazy useState initializer: exactly one Connect instance per mount, stable
|
|
43
50
|
// across re-renders (loadConnectAndInitialize returns synchronously).
|
|
44
51
|
const [connectInstance] = useState(() =>
|
|
45
52
|
loadConnectAndInitialize({
|
|
46
53
|
publishableKey,
|
|
47
|
-
fetchClientSecret,
|
|
54
|
+
fetchClientSecret: () => fetcher.current(),
|
|
48
55
|
...(appearance !== undefined ? { appearance } : {}),
|
|
49
56
|
...(locale !== undefined ? { locale } : {}),
|
|
50
57
|
}),
|
|
51
58
|
);
|
|
52
59
|
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (appearance !== undefined || locale !== undefined)
|
|
62
|
+
connectInstance.update({
|
|
63
|
+
...(appearance !== undefined ? { appearance } : {}),
|
|
64
|
+
...(locale !== undefined ? { locale } : {}),
|
|
65
|
+
});
|
|
66
|
+
}, [connectInstance, appearance, locale]);
|
|
53
67
|
return (
|
|
54
68
|
<ConnectComponentsProvider connectInstance={connectInstance}>
|
|
55
69
|
{children}
|
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
export type ConnectStatus =
|
|
6
|
-
| "
|
|
7
|
-
| "in_progress"
|
|
8
|
-
| "complete"
|
|
9
|
-
| "restricted";
|
|
6
|
+
"pending" | "in_progress" | "complete" | "restricted";
|
|
10
7
|
|
|
11
8
|
export type ConnectStatusDetails = {
|
|
12
9
|
detailsSubmitted: boolean;
|
|
@@ -74,7 +74,11 @@ describe("DisputeDetail", () => {
|
|
|
74
74
|
createElement(DisputeDetail, {
|
|
75
75
|
dispute: {
|
|
76
76
|
...dispute,
|
|
77
|
-
countdown: {
|
|
77
|
+
countdown: {
|
|
78
|
+
dueBy: dispute.evidenceDueBy,
|
|
79
|
+
daysRemaining: -2,
|
|
80
|
+
isOverdue: true,
|
|
81
|
+
},
|
|
78
82
|
},
|
|
79
83
|
}),
|
|
80
84
|
);
|
|
@@ -5,6 +5,11 @@ import type { ReactNode } from "react";
|
|
|
5
5
|
import type { DisputeWithCountdown } from "../hooks/useDisputeWithCountdown.js";
|
|
6
6
|
import { formatPrice } from "../lib/price-helpers.js";
|
|
7
7
|
|
|
8
|
+
/** Stripe only accepts responses while the dispute requires one. */
|
|
9
|
+
export function canRespondToDispute(status: string | undefined) {
|
|
10
|
+
return status === "needs_response" || status === "warning_needs_response";
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
export type DisputeDetailRenderProps = {
|
|
9
14
|
dispute: DisputeWithCountdown;
|
|
10
15
|
/** Formatted dispute amount, e.g. "$120.50". */
|
|
@@ -26,6 +31,7 @@ export type DisputeDetailProps = {
|
|
|
26
31
|
dueLabel?: string;
|
|
27
32
|
overdueLabel?: string;
|
|
28
33
|
noDeadlineLabel?: string;
|
|
34
|
+
responseClosedLabel?: string;
|
|
29
35
|
/** Field captions in the default rendering. */
|
|
30
36
|
labels?: {
|
|
31
37
|
amount?: string;
|
|
@@ -53,6 +59,7 @@ export function DisputeDetail({
|
|
|
53
59
|
dueLabel = "Evidence due in {days} days",
|
|
54
60
|
overdueLabel = "Evidence overdue",
|
|
55
61
|
noDeadlineLabel = "No evidence deadline",
|
|
62
|
+
responseClosedLabel = "Evidence submission closed",
|
|
56
63
|
labels,
|
|
57
64
|
locale,
|
|
58
65
|
className,
|
|
@@ -70,11 +77,13 @@ export function DisputeDetail({
|
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
const { countdown } = dispute;
|
|
73
|
-
const countdownMessage =
|
|
74
|
-
?
|
|
75
|
-
: countdown.
|
|
76
|
-
?
|
|
77
|
-
:
|
|
80
|
+
const countdownMessage = !canRespondToDispute(dispute.status)
|
|
81
|
+
? responseClosedLabel
|
|
82
|
+
: countdown.isOverdue
|
|
83
|
+
? overdueLabel
|
|
84
|
+
: countdown.daysRemaining !== undefined
|
|
85
|
+
? dueLabel.replace("{days}", String(countdown.daysRemaining))
|
|
86
|
+
: noDeadlineLabel;
|
|
78
87
|
|
|
79
88
|
const renderProps: DisputeDetailRenderProps = {
|
|
80
89
|
dispute,
|
|
@@ -242,3 +242,24 @@ describe("DisputesList", () => {
|
|
|
242
242
|
expect(html).not.toContain("No disputes");
|
|
243
243
|
});
|
|
244
244
|
});
|
|
245
|
+
it.each(["won", "lost", "under_review"] as const)(
|
|
246
|
+
"hides stale evidence deadlines for %s disputes",
|
|
247
|
+
(status) => {
|
|
248
|
+
const html = renderToStaticMarkup(
|
|
249
|
+
createElement(DisputesList, {
|
|
250
|
+
disputes: [
|
|
251
|
+
makeDispute({
|
|
252
|
+
status,
|
|
253
|
+
countdown: {
|
|
254
|
+
dueBy: "2030-01-01T00:00:00Z",
|
|
255
|
+
daysRemaining: 9,
|
|
256
|
+
isOverdue: false,
|
|
257
|
+
},
|
|
258
|
+
}),
|
|
259
|
+
],
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
expect(html).toContain(status);
|
|
263
|
+
expect(html).not.toContain("Evidence due in");
|
|
264
|
+
},
|
|
265
|
+
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
|
+
import { canRespondToDispute } from "./DisputeDetail.js";
|
|
4
5
|
|
|
5
6
|
import type { StripeComponentDispute } from "../types.js";
|
|
6
7
|
import type { DisputeEvidenceCountdown } from "../hooks/useDisputeWithCountdown.js";
|
|
@@ -78,7 +79,7 @@ export function DisputesList({
|
|
|
78
79
|
|
|
79
80
|
const badgeTextFor = (row: DisputesListRow): string | undefined => {
|
|
80
81
|
const countdown = row.countdown;
|
|
81
|
-
if (!countdown?.dueBy) return undefined;
|
|
82
|
+
if (!canRespondToDispute(row.status) || !countdown?.dueBy) return undefined;
|
|
82
83
|
if (countdown.isOverdue) return overdueLabel;
|
|
83
84
|
return dueLabel.replace("{days}", String(countdown.daysRemaining));
|
|
84
85
|
};
|
|
@@ -73,9 +73,7 @@ describe("EarningsSummary", () => {
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
it("renders gross, reversals, net, and paid-out amounts", () => {
|
|
76
|
-
const html = renderToStaticMarkup(
|
|
77
|
-
createElement(EarningsSummary, earnings),
|
|
78
|
-
);
|
|
76
|
+
const html = renderToStaticMarkup(createElement(EarningsSummary, earnings));
|
|
79
77
|
expect(html).toContain("Gross");
|
|
80
78
|
expect(html).toContain("$105.00");
|
|
81
79
|
// Honest label per the BTS-36 review — NOT "Fees".
|
|
@@ -89,9 +87,7 @@ describe("EarningsSummary", () => {
|
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
it("announces the latest payout with amount and status", () => {
|
|
92
|
-
const html = renderToStaticMarkup(
|
|
93
|
-
createElement(EarningsSummary, earnings),
|
|
94
|
-
);
|
|
90
|
+
const html = renderToStaticMarkup(createElement(EarningsSummary, earnings));
|
|
95
91
|
// po_latest (newest _creationTime) wins over po_old.
|
|
96
92
|
expect(html).toContain("Latest payout of $40.00 — in_transit");
|
|
97
93
|
});
|
|
@@ -21,12 +21,15 @@ const loadConnectAndInitialize = vi.fn(
|
|
|
21
21
|
}),
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
-
vi.mock("@stripe/connect-js", () => ({
|
|
24
|
+
vi.mock("@stripe/connect-js/pure.js", () => ({
|
|
25
25
|
loadConnectAndInitialize: (initParams: unknown) =>
|
|
26
26
|
loadConnectAndInitialize(initParams as Record<string, unknown>),
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
|
-
vi.mock("@stripe/react-connect-js", () => ({
|
|
29
|
+
vi.mock("@stripe/react-connect-js", async () => ({
|
|
30
|
+
...(await vi.importActual<typeof import("@stripe/react-connect-js")>(
|
|
31
|
+
"@stripe/react-connect-js",
|
|
32
|
+
)),
|
|
30
33
|
ConnectComponentsProvider: ({
|
|
31
34
|
connectInstance,
|
|
32
35
|
children,
|
|
@@ -73,9 +76,8 @@ describe("ConnectProvider", () => {
|
|
|
73
76
|
expect(loadConnectAndInitialize).toHaveBeenCalledTimes(1);
|
|
74
77
|
const initParams = loadConnectAndInitialize.mock.calls[0][0];
|
|
75
78
|
expect(initParams.publishableKey).toBe("pk_test_123");
|
|
76
|
-
// The
|
|
77
|
-
|
|
78
|
-
expect(initParams.fetchClientSecret).toBe(fetchClientSecret);
|
|
79
|
+
// The Connect runtime receives a lazy fetcher. Rendering does not request a session.
|
|
80
|
+
expect(initParams.fetchClientSecret).toEqual(expect.any(Function));
|
|
79
81
|
expect(fetchClientSecret).not.toHaveBeenCalled();
|
|
80
82
|
// Provider rendered with an instance, children inside.
|
|
81
83
|
expect(html).toContain('data-testid="connect-provider"');
|
|
@@ -25,7 +25,10 @@ export type EmbeddedDisputesProps = {
|
|
|
25
25
|
* Must be rendered inside a {@link ConnectProvider} whose `fetchClientSecret`
|
|
26
26
|
* returns that session's `clientSecret`.
|
|
27
27
|
*/
|
|
28
|
-
export function EmbeddedDisputes({
|
|
28
|
+
export function EmbeddedDisputes({
|
|
29
|
+
payment,
|
|
30
|
+
className,
|
|
31
|
+
}: EmbeddedDisputesProps) {
|
|
29
32
|
return (
|
|
30
33
|
<div className={className}>
|
|
31
34
|
{payment ? (
|
|
@@ -19,10 +19,7 @@ import { renderToStaticMarkup } from "react-dom/server";
|
|
|
19
19
|
import { describe, expect, it, vi } from "vitest";
|
|
20
20
|
|
|
21
21
|
import { EvidenceForm as BarrelExport } from "../index.js";
|
|
22
|
-
import {
|
|
23
|
-
EvidenceForm,
|
|
24
|
-
type EvidenceFormRenderProps,
|
|
25
|
-
} from "./EvidenceForm.js";
|
|
22
|
+
import { EvidenceForm, type EvidenceFormRenderProps } from "./EvidenceForm.js";
|
|
26
23
|
|
|
27
24
|
const allFields = {
|
|
28
25
|
productDescription: "Ceramics Masterclass, lifetime access",
|
|
@@ -33,10 +30,7 @@ const allFields = {
|
|
|
33
30
|
|
|
34
31
|
/** Render via the children prop and hand back the captured render props. */
|
|
35
32
|
function capture(
|
|
36
|
-
props: Omit<
|
|
37
|
-
Parameters<typeof EvidenceForm>[0],
|
|
38
|
-
"children"
|
|
39
|
-
>,
|
|
33
|
+
props: Omit<Parameters<typeof EvidenceForm>[0], "children">,
|
|
40
34
|
): EvidenceFormRenderProps {
|
|
41
35
|
let received: EvidenceFormRenderProps | undefined;
|
|
42
36
|
renderToStaticMarkup(
|
|
@@ -85,8 +79,7 @@ describe("EvidenceForm", () => {
|
|
|
85
79
|
expect(args.evidence).toEqual({
|
|
86
80
|
product_description: allFields.productDescription,
|
|
87
81
|
access_activity_log: allFields.accessActivity,
|
|
88
|
-
uncategorized_text: allFields.additionalInfo
|
|
89
|
-
customer_communication: allFields.customerCommunication,
|
|
82
|
+
uncategorized_text: `${allFields.additionalInfo}\n\nCustomer communication:\n${allFields.customerCommunication}`,
|
|
90
83
|
});
|
|
91
84
|
// Stripe's dispute-update `submit` DEFAULTS TO TRUE when omitted, so a
|
|
92
85
|
// draft must send an explicit false — omission would submit to the bank.
|
|
@@ -34,8 +34,18 @@ const leg = (over: Record<string, unknown>) => ({
|
|
|
34
34
|
|
|
35
35
|
const totals = {
|
|
36
36
|
legs: [
|
|
37
|
-
leg({
|
|
38
|
-
|
|
37
|
+
leg({
|
|
38
|
+
stripeTransferId: "tr_store",
|
|
39
|
+
role: "store",
|
|
40
|
+
amount: 8000,
|
|
41
|
+
net: 8000,
|
|
42
|
+
}),
|
|
43
|
+
leg({
|
|
44
|
+
stripeTransferId: "tr_aff",
|
|
45
|
+
role: "affiliate",
|
|
46
|
+
amount: 1000,
|
|
47
|
+
net: 1000,
|
|
48
|
+
}),
|
|
39
49
|
],
|
|
40
50
|
store: 8000,
|
|
41
51
|
affiliate: 1000,
|
|
@@ -79,7 +89,10 @@ describe("SplitBreakdown", () => {
|
|
|
79
89
|
createElement(SplitBreakdown, {
|
|
80
90
|
breakdown: {
|
|
81
91
|
...totals,
|
|
82
|
-
legs: [
|
|
92
|
+
legs: [
|
|
93
|
+
...totals.legs,
|
|
94
|
+
leg({ stripeTransferId: "tr_misc", amount: 500, net: 500 }),
|
|
95
|
+
],
|
|
83
96
|
other: 500,
|
|
84
97
|
recipientsTotal: 9500,
|
|
85
98
|
platform: 500,
|
|
@@ -136,8 +149,18 @@ describe("SplitBreakdown", () => {
|
|
|
136
149
|
expect(received!.breakdown).toBe(totals);
|
|
137
150
|
expect(received!.lines).toEqual([
|
|
138
151
|
{ key: "store", label: "Store", amount: 8000, amountLabel: "$80.00" },
|
|
139
|
-
{
|
|
140
|
-
|
|
152
|
+
{
|
|
153
|
+
key: "affiliate",
|
|
154
|
+
label: "Affiliate",
|
|
155
|
+
amount: 1000,
|
|
156
|
+
amountLabel: "$10.00",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: "platform",
|
|
160
|
+
label: "Platform",
|
|
161
|
+
amount: 1000,
|
|
162
|
+
amountLabel: "$10.00",
|
|
163
|
+
},
|
|
141
164
|
]);
|
|
142
165
|
expect(html).toBe("<p>3</p>");
|
|
143
166
|
});
|
|
@@ -75,7 +75,11 @@ export function SplitBreakdown({
|
|
|
75
75
|
const money = (amount: number) => formatPrice(amount, currency, locale);
|
|
76
76
|
|
|
77
77
|
const lines: SplitBreakdownLine[] = [
|
|
78
|
-
{
|
|
78
|
+
{
|
|
79
|
+
key: "store" as const,
|
|
80
|
+
label: labels?.store ?? "Store",
|
|
81
|
+
amount: breakdown.store,
|
|
82
|
+
},
|
|
79
83
|
{
|
|
80
84
|
key: "affiliate" as const,
|
|
81
85
|
label: labels?.affiliate ?? "Affiliate",
|
|
@@ -83,7 +87,13 @@ export function SplitBreakdown({
|
|
|
83
87
|
},
|
|
84
88
|
// The catch-all bucket only earns a line when it carries money.
|
|
85
89
|
...(breakdown.other > 0
|
|
86
|
-
? [
|
|
90
|
+
? [
|
|
91
|
+
{
|
|
92
|
+
key: "other" as const,
|
|
93
|
+
label: labels?.other ?? "Other",
|
|
94
|
+
amount: breakdown.other,
|
|
95
|
+
},
|
|
96
|
+
]
|
|
87
97
|
: []),
|
|
88
98
|
{
|
|
89
99
|
key: "platform" as const,
|
|
@@ -14,8 +14,7 @@ function extractErrorMessage(error: unknown, fallback: string): string {
|
|
|
14
14
|
* Result of a checkout confirmation attempt.
|
|
15
15
|
*/
|
|
16
16
|
export type CheckoutConfirmResult =
|
|
17
|
-
| { type: "
|
|
18
|
-
| { type: "error"; message: string };
|
|
17
|
+
{ type: "success" } | { type: "error"; message: string };
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Checkout session state and actions.
|
|
@@ -8,8 +8,7 @@ import { useElements, useStripe } from "@stripe/react-stripe-js";
|
|
|
8
8
|
* Normalized result from a Stripe confirmation operation.
|
|
9
9
|
*/
|
|
10
10
|
export type StripeConfirmResult =
|
|
11
|
-
| { success:
|
|
12
|
-
| { success: false; error: string };
|
|
11
|
+
{ success: true } | { success: false; error: string };
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Hook for confirming a PaymentIntent (immediate charge) or SetupIntent (future charge / trial).
|
|
@@ -48,8 +48,20 @@ const earningsResult = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const previewPayouts = [
|
|
51
|
-
{
|
|
52
|
-
|
|
51
|
+
{
|
|
52
|
+
stripePayoutId: "po_1",
|
|
53
|
+
accountId: "acct_store",
|
|
54
|
+
amount: 4000,
|
|
55
|
+
currency: "usd",
|
|
56
|
+
status: "paid",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
stripePayoutId: "po_2",
|
|
60
|
+
accountId: "acct_store",
|
|
61
|
+
amount: 1500,
|
|
62
|
+
currency: "usd",
|
|
63
|
+
status: "pending",
|
|
64
|
+
},
|
|
53
65
|
];
|
|
54
66
|
|
|
55
67
|
const payoutsResult = {
|
|
@@ -70,7 +82,11 @@ describe("createUseEarnings", () => {
|
|
|
70
82
|
[payoutsRef, payoutsResult],
|
|
71
83
|
]),
|
|
72
84
|
);
|
|
73
|
-
const useEarnings = createUseEarnings(
|
|
85
|
+
const useEarnings = createUseEarnings(
|
|
86
|
+
mockUseQuery,
|
|
87
|
+
earningsRef,
|
|
88
|
+
payoutsRef,
|
|
89
|
+
);
|
|
74
90
|
|
|
75
91
|
const result = useEarnings("acct_store");
|
|
76
92
|
|
|
@@ -92,7 +108,11 @@ describe("createUseEarnings", () => {
|
|
|
92
108
|
[payoutsRef, payoutsResult],
|
|
93
109
|
]),
|
|
94
110
|
);
|
|
95
|
-
const useEarnings = createUseEarnings(
|
|
111
|
+
const useEarnings = createUseEarnings(
|
|
112
|
+
mockUseQuery,
|
|
113
|
+
earningsRef,
|
|
114
|
+
payoutsRef,
|
|
115
|
+
);
|
|
96
116
|
|
|
97
117
|
const result = useEarnings("acct_store");
|
|
98
118
|
|
|
@@ -111,7 +131,11 @@ describe("createUseEarnings", () => {
|
|
|
111
131
|
[payoutsRef, undefined],
|
|
112
132
|
]),
|
|
113
133
|
);
|
|
114
|
-
const useEarnings = createUseEarnings(
|
|
134
|
+
const useEarnings = createUseEarnings(
|
|
135
|
+
mockUseQuery,
|
|
136
|
+
earningsRef,
|
|
137
|
+
payoutsRef,
|
|
138
|
+
);
|
|
115
139
|
|
|
116
140
|
const result = useEarnings("acct_store");
|
|
117
141
|
|
|
@@ -125,7 +149,11 @@ describe("createUseEarnings", () => {
|
|
|
125
149
|
|
|
126
150
|
it("skips both queries when the account id is undefined", () => {
|
|
127
151
|
const mockUseQuery = useQueryFor(new Map());
|
|
128
|
-
const useEarnings = createUseEarnings(
|
|
152
|
+
const useEarnings = createUseEarnings(
|
|
153
|
+
mockUseQuery,
|
|
154
|
+
earningsRef,
|
|
155
|
+
payoutsRef,
|
|
156
|
+
);
|
|
129
157
|
|
|
130
158
|
const result = useEarnings(undefined);
|
|
131
159
|
|
|
@@ -143,11 +171,18 @@ describe("createUseEarnings", () => {
|
|
|
143
171
|
it("treats empty ledgers as loaded with zero totals", () => {
|
|
144
172
|
const mockUseQuery = useQueryFor(
|
|
145
173
|
new Map<unknown, unknown>([
|
|
146
|
-
[
|
|
174
|
+
[
|
|
175
|
+
earningsRef,
|
|
176
|
+
{ gross: 0, reversed: 0, transferCount: 0, transfers: [] },
|
|
177
|
+
],
|
|
147
178
|
[payoutsRef, { paidOut: 0, payoutCount: 0, payouts: [] }],
|
|
148
179
|
]),
|
|
149
180
|
);
|
|
150
|
-
const useEarnings = createUseEarnings(
|
|
181
|
+
const useEarnings = createUseEarnings(
|
|
182
|
+
mockUseQuery,
|
|
183
|
+
earningsRef,
|
|
184
|
+
payoutsRef,
|
|
185
|
+
);
|
|
151
186
|
|
|
152
187
|
const result = useEarnings("acct_new");
|
|
153
188
|
|
|
@@ -28,16 +28,13 @@ describe("useStripeMode", () => {
|
|
|
28
28
|
expect(useQueryMock).toHaveBeenCalledWith(mockQueryRef, {});
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it.each(["test", "live"] as const)(
|
|
32
|
-
|
|
33
|
-
(mode) => {
|
|
34
|
-
useQueryMock.mockReturnValue(mode);
|
|
31
|
+
it.each(["test", "live"] as const)("returns the resolved %s mode", (mode) => {
|
|
32
|
+
useQueryMock.mockReturnValue(mode);
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
const result = useStripeMode(mockQueryRef);
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
36
|
+
expect(result).toBe(mode);
|
|
37
|
+
});
|
|
41
38
|
|
|
42
39
|
it("returns undefined while the query is still loading", () => {
|
|
43
40
|
useQueryMock.mockReturnValue(undefined);
|