@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
|
@@ -50,17 +50,21 @@ function makeComponent(): Component {
|
|
|
50
50
|
* product row (for updateProduct's account lookup); `account` is the store's
|
|
51
51
|
* account record; both default to null.
|
|
52
52
|
*/
|
|
53
|
-
function makeCtx(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
function makeCtx(
|
|
54
|
+
opts: {
|
|
55
|
+
product?: { _id?: string; accountId?: string } | null;
|
|
56
|
+
account?: { statementDescriptor?: string } | null;
|
|
57
|
+
mutation?: unknown;
|
|
58
|
+
} = {},
|
|
59
|
+
) {
|
|
60
|
+
const runQuery = vi
|
|
61
|
+
.fn()
|
|
62
|
+
.mockImplementation((refObj: Record<symbol, string>) => {
|
|
63
|
+
const path = refObj[TO_REF] ?? "";
|
|
64
|
+
if (path.includes("getAccountByStripeId")) return opts.account ?? null;
|
|
65
|
+
if (path.includes("getProductByStripeId")) return opts.product ?? null;
|
|
66
|
+
return null;
|
|
67
|
+
});
|
|
64
68
|
const runMutation = vi.fn().mockResolvedValue(opts.mutation ?? undefined);
|
|
65
69
|
return { runQuery, runMutation } as unknown as RunCtx & {
|
|
66
70
|
runQuery: ReturnType<typeof vi.fn>;
|
|
@@ -115,7 +119,10 @@ describe("createProduct", () => {
|
|
|
115
119
|
name: "Widget",
|
|
116
120
|
});
|
|
117
121
|
|
|
118
|
-
expect(result).toEqual({
|
|
122
|
+
expect(result).toEqual({
|
|
123
|
+
productId: "internal_1",
|
|
124
|
+
stripeProductId: "prod_1",
|
|
125
|
+
});
|
|
119
126
|
// active defaults to true when omitted
|
|
120
127
|
expect(stripe.products.create.mock.calls[0][0].active).toBe(true);
|
|
121
128
|
});
|
|
@@ -261,7 +268,9 @@ describe("createProduct — per-store statement descriptor (BTS-67)", () => {
|
|
|
261
268
|
).toBeUndefined();
|
|
262
269
|
// The account query is never made when there's no store.
|
|
263
270
|
expect(ctx.runQuery).not.toHaveBeenCalledWith(
|
|
264
|
-
expect.objectContaining({
|
|
271
|
+
expect.objectContaining({
|
|
272
|
+
[TO_REF]: "betterStripe/core/queries/getAccountByStripeId",
|
|
273
|
+
}),
|
|
265
274
|
expect.anything(),
|
|
266
275
|
);
|
|
267
276
|
});
|
|
@@ -312,7 +321,9 @@ describe("deactivateProduct / deleteProduct", () => {
|
|
|
312
321
|
|
|
313
322
|
describe("read delegators", () => {
|
|
314
323
|
it("getProduct returns the queried row", async () => {
|
|
315
|
-
const ctx = {
|
|
324
|
+
const ctx = {
|
|
325
|
+
runQuery: vi.fn().mockResolvedValue({ _id: "x" }),
|
|
326
|
+
} as unknown as RunCtx;
|
|
316
327
|
expect(await getProduct(makeComponent(), ctx, { productId: "x" })).toEqual({
|
|
317
328
|
_id: "x",
|
|
318
329
|
});
|
|
@@ -377,7 +388,13 @@ describe("syncAllProducts", () => {
|
|
|
377
388
|
const stripe = makeStripe();
|
|
378
389
|
stripe.products.list.mockReturnValue(
|
|
379
390
|
asyncIterable([
|
|
380
|
-
{
|
|
391
|
+
{
|
|
392
|
+
id: "prod_1",
|
|
393
|
+
name: "A",
|
|
394
|
+
description: null,
|
|
395
|
+
active: true,
|
|
396
|
+
metadata: {},
|
|
397
|
+
},
|
|
381
398
|
]),
|
|
382
399
|
);
|
|
383
400
|
// two prices for the SAME product → product should be looked up once (cache)
|
|
@@ -408,7 +425,11 @@ describe("syncAllProducts", () => {
|
|
|
408
425
|
runQuery,
|
|
409
426
|
} as unknown as RunCtx;
|
|
410
427
|
|
|
411
|
-
const result = await syncAllProducts(
|
|
428
|
+
const result = await syncAllProducts(
|
|
429
|
+
asStripe(stripe),
|
|
430
|
+
makeComponent(),
|
|
431
|
+
ctx,
|
|
432
|
+
);
|
|
412
433
|
|
|
413
434
|
expect(result).toEqual({
|
|
414
435
|
productsSynced: 1,
|
|
@@ -441,7 +462,11 @@ describe("syncAllProducts", () => {
|
|
|
441
462
|
runQuery: vi.fn().mockResolvedValue(null), // product not found
|
|
442
463
|
} as unknown as RunCtx;
|
|
443
464
|
|
|
444
|
-
const result = await syncAllProducts(
|
|
465
|
+
const result = await syncAllProducts(
|
|
466
|
+
asStripe(stripe),
|
|
467
|
+
makeComponent(),
|
|
468
|
+
ctx,
|
|
469
|
+
);
|
|
445
470
|
|
|
446
471
|
expect(result.pricesSynced).toBe(0);
|
|
447
472
|
expect(result.errorCount).toBe(0); // skipped, not errored
|
|
@@ -452,8 +477,20 @@ describe("syncAllProducts", () => {
|
|
|
452
477
|
const stripe = makeStripe();
|
|
453
478
|
stripe.products.list.mockReturnValue(
|
|
454
479
|
asyncIterable([
|
|
455
|
-
{
|
|
456
|
-
|
|
480
|
+
{
|
|
481
|
+
id: "prod_ok",
|
|
482
|
+
name: "A",
|
|
483
|
+
description: null,
|
|
484
|
+
active: true,
|
|
485
|
+
metadata: {},
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: "prod_bad",
|
|
489
|
+
name: "B",
|
|
490
|
+
description: null,
|
|
491
|
+
active: true,
|
|
492
|
+
metadata: {},
|
|
493
|
+
},
|
|
457
494
|
]),
|
|
458
495
|
);
|
|
459
496
|
stripe.prices.list.mockReturnValue(asyncIterable([]));
|
|
@@ -466,7 +503,11 @@ describe("syncAllProducts", () => {
|
|
|
466
503
|
runQuery: vi.fn(),
|
|
467
504
|
} as unknown as RunCtx;
|
|
468
505
|
|
|
469
|
-
const result = await syncAllProducts(
|
|
506
|
+
const result = await syncAllProducts(
|
|
507
|
+
asStripe(stripe),
|
|
508
|
+
makeComponent(),
|
|
509
|
+
ctx,
|
|
510
|
+
);
|
|
470
511
|
|
|
471
512
|
expect(result.productsSynced).toBe(1);
|
|
472
513
|
expect(result.errorCount).toBe(1);
|
|
@@ -79,7 +79,9 @@ export async function createProduct(
|
|
|
79
79
|
description: opts.description ?? undefined,
|
|
80
80
|
active: opts.active ?? true,
|
|
81
81
|
metadata: opts.metadata ?? undefined,
|
|
82
|
-
...(statementDescriptor
|
|
82
|
+
...(statementDescriptor
|
|
83
|
+
? { statement_descriptor: statementDescriptor }
|
|
84
|
+
: {}),
|
|
83
85
|
});
|
|
84
86
|
|
|
85
87
|
await runMutationOrThrow(
|
|
@@ -54,6 +54,8 @@ describe("clearAll", () => {
|
|
|
54
54
|
runMutation: vi.fn().mockRejectedValue(new Error("table locked")),
|
|
55
55
|
} as unknown as RunCtx;
|
|
56
56
|
|
|
57
|
-
await expect(clearAll(makeComponent(), ctx)).rejects.toThrow(
|
|
57
|
+
await expect(clearAll(makeComponent(), ctx)).rejects.toThrow(
|
|
58
|
+
"table locked",
|
|
59
|
+
);
|
|
58
60
|
});
|
|
59
61
|
});
|
|
@@ -17,7 +17,10 @@ describe("resolveOwnerAccount", () => {
|
|
|
17
17
|
|
|
18
18
|
it("falls back to customer.id for an expanded customer object", () => {
|
|
19
19
|
expect(
|
|
20
|
-
resolveOwnerAccount({
|
|
20
|
+
resolveOwnerAccount({
|
|
21
|
+
customer_account: null,
|
|
22
|
+
customer: { id: "cus_x" },
|
|
23
|
+
}),
|
|
21
24
|
).toBe("cus_x");
|
|
22
25
|
});
|
|
23
26
|
|
|
@@ -20,7 +20,11 @@ export type CustomerOwned = {
|
|
|
20
20
|
export function resolveOwnerAccount(obj: CustomerOwned): string | null {
|
|
21
21
|
if (obj.customer_account) return obj.customer_account;
|
|
22
22
|
if (typeof obj.customer === "string") return obj.customer;
|
|
23
|
-
if (
|
|
23
|
+
if (
|
|
24
|
+
obj.customer &&
|
|
25
|
+
typeof obj.customer === "object" &&
|
|
26
|
+
"id" in obj.customer
|
|
27
|
+
) {
|
|
24
28
|
return obj.customer.id ?? null;
|
|
25
29
|
}
|
|
26
30
|
return null;
|
|
@@ -8,10 +8,7 @@ export const StripeDashboardResourcePath = {
|
|
|
8
8
|
} as const;
|
|
9
9
|
|
|
10
10
|
export type StripeDashboardResourceType =
|
|
11
|
-
| "
|
|
12
|
-
| "price"
|
|
13
|
-
| "customer"
|
|
14
|
-
| "subscription";
|
|
11
|
+
"product" | "price" | "customer" | "subscription";
|
|
15
12
|
|
|
16
13
|
export type StripeMode = "test" | "live";
|
|
17
14
|
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
// @vitest-environment edge-runtime
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* and the success/failure HTTP contract.
|
|
7
|
-
*
|
|
8
|
-
* We exercise the function DIRECTLY (not via registerRoutes), mocking the
|
|
9
|
-
* Stripe SDK module so `getStripeClient()` yields a controllable client:
|
|
10
|
-
* - V1 verification → `webhooks.constructEventAsync`
|
|
11
|
-
* - V2 verification → `parseEventNotificationAsync` (NEVER constructEventAsync;
|
|
12
|
-
* stripe-node v22 rejects thin payloads passed to constructEventAsync)
|
|
13
|
-
* - V2 account re-fetch → `v2.core.accounts.retrieve`
|
|
14
|
-
* Every assertion is on the response status/body and on which component
|
|
15
|
-
* mutation/query refs were invoked per branch.
|
|
3
|
+
* Exercises handleWebhookRequest directly with a controllable Stripe mock.
|
|
4
|
+
* Checks V1/V2 signature routing, account re-fetch, deduplication, processor
|
|
5
|
+
* dispatch, and HTTP responses, including the invoked component references.
|
|
16
6
|
*/
|
|
17
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type Mock,
|
|
9
|
+
afterEach,
|
|
10
|
+
beforeEach,
|
|
11
|
+
describe,
|
|
12
|
+
expect,
|
|
13
|
+
it,
|
|
14
|
+
vi,
|
|
15
|
+
} from "vitest";
|
|
18
16
|
|
|
19
17
|
import type { Component } from "../helpers.js";
|
|
20
18
|
import type { RegisterRoutesConfig, WebhookActionCtx } from "../types.js";
|
|
@@ -97,7 +95,9 @@ function makeCtx(
|
|
|
97
95
|
} as unknown as WebhookActionCtx & { runMutation: Mock; runQuery: Mock };
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
function baseConfig(
|
|
98
|
+
function baseConfig(
|
|
99
|
+
over?: Partial<RegisterRoutesConfig>,
|
|
100
|
+
): RegisterRoutesConfig {
|
|
101
101
|
return {
|
|
102
102
|
stripeSecretKey: "sk_test_123",
|
|
103
103
|
webhookSecret: "whsec_test",
|
|
@@ -105,10 +105,7 @@ function baseConfig(over?: Partial<RegisterRoutesConfig>): RegisterRoutesConfig
|
|
|
105
105
|
} as RegisterRoutesConfig;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function makeRequest(
|
|
109
|
-
body: unknown,
|
|
110
|
-
headers?: Record<string, string>,
|
|
111
|
-
): Request {
|
|
108
|
+
function makeRequest(body: unknown, headers?: Record<string, string>): Request {
|
|
112
109
|
return new Request("https://example.com/stripe/webhook", {
|
|
113
110
|
method: "POST",
|
|
114
111
|
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
@@ -122,22 +122,29 @@ describe("deriveTrialFields", () => {
|
|
|
122
122
|
|
|
123
123
|
it("marks isTrialing true only when status is exactly 'trialing'", () => {
|
|
124
124
|
expect(
|
|
125
|
-
deriveTrialFields(
|
|
126
|
-
|
|
125
|
+
deriveTrialFields(
|
|
126
|
+
sub({ status: "trialing", trial_start: null, trial_end: null }),
|
|
127
|
+
).isTrialing,
|
|
127
128
|
).toBe(true);
|
|
128
129
|
expect(
|
|
129
|
-
deriveTrialFields(
|
|
130
|
-
|
|
130
|
+
deriveTrialFields(
|
|
131
|
+
sub({ status: "active", trial_start: null, trial_end: null }),
|
|
132
|
+
).isTrialing,
|
|
131
133
|
).toBe(false);
|
|
132
134
|
expect(
|
|
133
|
-
deriveTrialFields(
|
|
134
|
-
|
|
135
|
+
deriveTrialFields(
|
|
136
|
+
sub({ status: "past_due", trial_start: null, trial_end: null }),
|
|
137
|
+
).isTrialing,
|
|
135
138
|
).toBe(false);
|
|
136
139
|
});
|
|
137
140
|
|
|
138
141
|
it("converts trial_start/trial_end epochs to ISO strings", () => {
|
|
139
142
|
const result = deriveTrialFields(
|
|
140
|
-
sub({
|
|
143
|
+
sub({
|
|
144
|
+
status: "trialing",
|
|
145
|
+
trial_start: 1704067200,
|
|
146
|
+
trial_end: 1704153600,
|
|
147
|
+
}),
|
|
141
148
|
);
|
|
142
149
|
expect(result.trialStart).toBe("2024-01-01T00:00:00.000Z");
|
|
143
150
|
expect(result.trialEnd).toBe("2024-01-02T00:00:00.000Z");
|
|
@@ -154,7 +161,9 @@ describe("deriveTrialFields", () => {
|
|
|
154
161
|
|
|
155
162
|
describe("componentRef", () => {
|
|
156
163
|
it("returns the ref via direct property lookup when the path exists", () => {
|
|
157
|
-
const component = makeComponent({
|
|
164
|
+
const component = makeComponent({
|
|
165
|
+
paths: ["billing/queries/getSubscription"],
|
|
166
|
+
});
|
|
158
167
|
const result = componentRef(component, "billing/queries/getSubscription");
|
|
159
168
|
expect((result as any)[TO_REF]).toBe(
|
|
160
169
|
"betterStripe/billing/queries/getSubscription",
|
|
@@ -172,7 +181,9 @@ describe("componentRef", () => {
|
|
|
172
181
|
|
|
173
182
|
it("throws when neither the path nor an anchor ref is available", () => {
|
|
174
183
|
const component = makeComponent({ withAnchor: false });
|
|
175
|
-
expect(() =>
|
|
184
|
+
expect(() =>
|
|
185
|
+
componentRef(component, "billing/mutations/upsertInvoice"),
|
|
186
|
+
).toThrow(
|
|
176
187
|
/Cannot resolve component path: billing\/mutations\/upsertInvoice/,
|
|
177
188
|
);
|
|
178
189
|
});
|
|
@@ -208,7 +219,9 @@ describe("componentRef", () => {
|
|
|
208
219
|
|
|
209
220
|
describe("getComponentRef", () => {
|
|
210
221
|
it("resolves a mapped export via direct property lookup", () => {
|
|
211
|
-
const component = makeComponent({
|
|
222
|
+
const component = makeComponent({
|
|
223
|
+
paths: ["billing/queries/getSubscription"],
|
|
224
|
+
});
|
|
212
225
|
const result = getComponentRef(component, "billing", "getSubscription");
|
|
213
226
|
expect((result as any)[TO_REF]).toBe(
|
|
214
227
|
"betterStripe/billing/queries/getSubscription",
|
|
@@ -235,9 +248,9 @@ describe("getComponentRef", () => {
|
|
|
235
248
|
|
|
236
249
|
it("throws when no anchor ref exists on the component", () => {
|
|
237
250
|
const component = makeComponent({ withAnchor: false });
|
|
238
|
-
expect(() =>
|
|
239
|
-
|
|
240
|
-
);
|
|
251
|
+
expect(() =>
|
|
252
|
+
getComponentRef(component, "billing", "upsertInvoice"),
|
|
253
|
+
).toThrow(/Cannot resolve component ref for upsertInvoice/);
|
|
241
254
|
});
|
|
242
255
|
});
|
|
243
256
|
|
|
@@ -275,3 +275,26 @@ export type WebhookContext = {
|
|
|
275
275
|
stripe: Stripe;
|
|
276
276
|
webhookSecret: string;
|
|
277
277
|
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Fee/transfer fields (BTS-11) captured off a PaymentIntent for denormalization
|
|
281
|
+
* onto the `payments` row. Returns `{}` when the charge carried no fee/transfer.
|
|
282
|
+
*/
|
|
283
|
+
export function feeRoutingFromPaymentIntent(pi: Stripe.PaymentIntent): {
|
|
284
|
+
chargeType?: "destination";
|
|
285
|
+
destinationAccountId?: string;
|
|
286
|
+
applicationFeeAmount?: number;
|
|
287
|
+
feeCollectedAmount?: number;
|
|
288
|
+
} {
|
|
289
|
+
const dest = pi.transfer_data?.destination;
|
|
290
|
+
const destinationAccountId = typeof dest === "string" ? dest : dest?.id;
|
|
291
|
+
const fee = pi.application_fee_amount ?? undefined;
|
|
292
|
+
if (!destinationAccountId && fee === undefined) return {};
|
|
293
|
+
return {
|
|
294
|
+
chargeType: "destination",
|
|
295
|
+
...(destinationAccountId ? { destinationAccountId } : {}),
|
|
296
|
+
...(fee !== undefined
|
|
297
|
+
? { applicationFeeAmount: fee, feeCollectedAmount: fee }
|
|
298
|
+
: {}),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
@@ -40,8 +40,7 @@ function makeWhCtx(opts?: {
|
|
|
40
40
|
return {
|
|
41
41
|
ctx: {
|
|
42
42
|
runQuery: vi.fn().mockResolvedValue(opts?.doc ?? null),
|
|
43
|
-
scheduler:
|
|
44
|
-
opts?.withScheduler === false ? undefined : { runAfter },
|
|
43
|
+
scheduler: opts?.withScheduler === false ? undefined : { runAfter },
|
|
45
44
|
},
|
|
46
45
|
component: makeComponent(),
|
|
47
46
|
stripe: {} as never,
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
// @vitest-environment edge-runtime
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* correctness area in the library: a wrong default or a mishandled
|
|
6
|
-
* string-vs-object union silently corrupts a row with no error. We drive each
|
|
7
|
-
* event type through `processEvent` and assert the EXACT payload handed to the
|
|
8
|
-
* component mutation, covering every union branch, the `userId: ""` sentinel,
|
|
9
|
-
* trial derivation, the payment-status collapse, and the checkout metadata
|
|
10
|
-
* merge.
|
|
11
|
-
*
|
|
12
|
-
* `whCtx` is built WITHOUT `config.webhooks`, so `dispatchUpsert` routes through
|
|
13
|
-
* `ctx.runMutation(componentRef(component, <path>), data)` — letting us read the
|
|
14
|
-
* normalized payload straight off the runMutation mock.
|
|
3
|
+
* Drives Stripe event normalization through processEvent and checks the payloads
|
|
4
|
+
* passed to component mutations, including owner, fee, and trial fields.
|
|
15
5
|
*/
|
|
16
6
|
import type Stripe from "stripe";
|
|
17
7
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
@@ -24,34 +14,28 @@ const TO_REF = Symbol.for("toReferencePath");
|
|
|
24
14
|
|
|
25
15
|
/** Fake component exposing every query/mutation ref processors.ts resolves. */
|
|
26
16
|
function makeComponent() {
|
|
27
|
-
const
|
|
17
|
+
const refs = (namespace: string, names: string[]) =>
|
|
18
|
+
Object.fromEntries(
|
|
19
|
+
names.map((name) => [
|
|
20
|
+
name,
|
|
21
|
+
{ [TO_REF]: `betterStripe/${namespace}/${name}` },
|
|
22
|
+
]),
|
|
23
|
+
);
|
|
28
24
|
return {
|
|
29
25
|
products: {
|
|
30
|
-
queries:
|
|
31
|
-
|
|
32
|
-
},
|
|
33
|
-
mutations: {
|
|
34
|
-
upsertProduct: ref("products/mutations/upsertProduct"),
|
|
35
|
-
upsertPrice: ref("products/mutations/upsertPrice"),
|
|
36
|
-
},
|
|
26
|
+
queries: refs("products/queries", ["getProductByStripeId"]),
|
|
27
|
+
mutations: refs("products/mutations", ["upsertProduct", "upsertPrice"]),
|
|
37
28
|
},
|
|
38
29
|
billing: {
|
|
39
|
-
queries:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
upsertSubscription: ref("billing/mutations/upsertSubscription"),
|
|
46
|
-
upsertCheckoutSession: ref("billing/mutations/upsertCheckoutSession"),
|
|
47
|
-
upsertInvoice: ref("billing/mutations/upsertInvoice"),
|
|
48
|
-
},
|
|
30
|
+
queries: refs("billing/queries", ["getCheckoutSessionByStripeId"]),
|
|
31
|
+
mutations: refs("billing/mutations", [
|
|
32
|
+
"upsertSubscription",
|
|
33
|
+
"upsertCheckoutSession",
|
|
34
|
+
"upsertInvoice",
|
|
35
|
+
]),
|
|
49
36
|
},
|
|
50
37
|
connect: {
|
|
51
|
-
mutations:
|
|
52
|
-
upsertPayment: ref("connect/mutations/upsertPayment"),
|
|
53
|
-
upsertPayout: ref("connect/mutations/upsertPayout"),
|
|
54
|
-
},
|
|
38
|
+
mutations: refs("connect/mutations", ["upsertPayment", "upsertPayout"]),
|
|
55
39
|
},
|
|
56
40
|
};
|
|
57
41
|
}
|
|
@@ -15,6 +15,7 @@ import { resolveOwnerAccount } from "../utils/owner.js";
|
|
|
15
15
|
import {
|
|
16
16
|
type WebhookContext,
|
|
17
17
|
componentRef,
|
|
18
|
+
feeRoutingFromPaymentIntent,
|
|
18
19
|
deriveTrialFields,
|
|
19
20
|
dispatchUpsert,
|
|
20
21
|
epochToIso,
|
|
@@ -297,29 +298,6 @@ async function handleCheckoutEvent(
|
|
|
297
298
|
});
|
|
298
299
|
}
|
|
299
300
|
|
|
300
|
-
/**
|
|
301
|
-
* Fee/transfer fields (BTS-11) captured off a PaymentIntent for denormalization
|
|
302
|
-
* onto the `payments` row. Returns `{}` when the charge carried no fee/transfer.
|
|
303
|
-
*/
|
|
304
|
-
function feeRoutingFromPaymentIntent(pi: Stripe.PaymentIntent): {
|
|
305
|
-
chargeType?: "destination";
|
|
306
|
-
destinationAccountId?: string;
|
|
307
|
-
applicationFeeAmount?: number;
|
|
308
|
-
feeCollectedAmount?: number;
|
|
309
|
-
} {
|
|
310
|
-
const dest = pi.transfer_data?.destination;
|
|
311
|
-
const destinationAccountId = typeof dest === "string" ? dest : dest?.id;
|
|
312
|
-
const fee = pi.application_fee_amount ?? undefined;
|
|
313
|
-
if (!destinationAccountId && fee === undefined) return {};
|
|
314
|
-
return {
|
|
315
|
-
chargeType: "destination",
|
|
316
|
-
...(destinationAccountId ? { destinationAccountId } : {}),
|
|
317
|
-
...(fee !== undefined
|
|
318
|
-
? { applicationFeeAmount: fee, feeCollectedAmount: fee }
|
|
319
|
-
: {}),
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
301
|
/**
|
|
324
302
|
* Apply a fixed/tier platform fee to a subscription invoice before it finalizes
|
|
325
303
|
* (BTS-51). Percent-only fees ride on the subscription's `application_fee_percent`
|
|
@@ -37,8 +37,7 @@ vi.mock("stripe", () => {
|
|
|
37
37
|
|
|
38
38
|
// Mock httpActionGeneric to capture the raw handler
|
|
39
39
|
let _capturedHandler:
|
|
40
|
-
|
|
41
|
-
| undefined;
|
|
40
|
+
((ctx: any, request: Request) => Promise<Response>) | undefined;
|
|
42
41
|
|
|
43
42
|
vi.mock("convex/server", () => ({
|
|
44
43
|
httpActionGeneric: (fn: any) => fn,
|
|
@@ -19,6 +19,7 @@ import type * as core_queries from "../core/queries.js";
|
|
|
19
19
|
import type * as core_validators from "../core/validators.js";
|
|
20
20
|
import type * as lib_fees from "../lib/fees.js";
|
|
21
21
|
import type * as lib_idempotency from "../lib/idempotency.js";
|
|
22
|
+
import type * as lib_paymentSeller from "../lib/paymentSeller.js";
|
|
22
23
|
import type * as lib_reversals from "../lib/reversals.js";
|
|
23
24
|
import type * as lib_stripeFactory from "../lib/stripeFactory.js";
|
|
24
25
|
import type * as products_mutations from "../products/mutations.js";
|
|
@@ -47,6 +48,7 @@ const fullApi: ApiFromModules<{
|
|
|
47
48
|
"core/validators": typeof core_validators;
|
|
48
49
|
"lib/fees": typeof lib_fees;
|
|
49
50
|
"lib/idempotency": typeof lib_idempotency;
|
|
51
|
+
"lib/paymentSeller": typeof lib_paymentSeller;
|
|
50
52
|
"lib/reversals": typeof lib_reversals;
|
|
51
53
|
"lib/stripeFactory": typeof lib_stripeFactory;
|
|
52
54
|
"products/mutations": typeof products_mutations;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
2
|
|
|
3
3
|
import type { Doc, Id } from "../_generated/dataModel";
|
|
4
|
-
import { mutation
|
|
4
|
+
import { mutation } from "../_generated/server";
|
|
5
5
|
import { feeRoutingFields } from "../lib/fees";
|
|
6
|
-
import { computeReversalSlices } from "../lib/reversals";
|
|
6
|
+
import { computeReversalSlices, deriveReversalState } from "../lib/reversals";
|
|
7
|
+
import { paymentSeller } from "../lib/paymentSeller";
|
|
7
8
|
import {
|
|
8
9
|
disputeFields,
|
|
9
10
|
paymentStatusValidator,
|
|
@@ -12,23 +13,7 @@ import {
|
|
|
12
13
|
transferFields,
|
|
13
14
|
} from "./validators";
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
ctx: MutationCtx,
|
|
17
|
-
stripePaymentIntentId?: string,
|
|
18
|
-
): Promise<string | undefined> {
|
|
19
|
-
if (!stripePaymentIntentId) return undefined;
|
|
20
|
-
const payment = await ctx.db
|
|
21
|
-
.query("payments")
|
|
22
|
-
.withIndex("by_stripe_payment_intent_id", (q) =>
|
|
23
|
-
q.eq("stripePaymentIntentId", stripePaymentIntentId),
|
|
24
|
-
)
|
|
25
|
-
.first();
|
|
26
|
-
return payment?.destinationAccountId;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// =============================================================================
|
|
30
|
-
// PAYMENT MUTATIONS
|
|
31
|
-
// =============================================================================
|
|
16
|
+
// Payment mutations
|
|
32
17
|
|
|
33
18
|
export const upsertPayment = mutation({
|
|
34
19
|
args: {
|
|
@@ -125,9 +110,7 @@ export const upsertPayment = mutation({
|
|
|
125
110
|
},
|
|
126
111
|
});
|
|
127
112
|
|
|
128
|
-
//
|
|
129
|
-
// PAYOUT MUTATIONS
|
|
130
|
-
// =============================================================================
|
|
113
|
+
// Payout mutations
|
|
131
114
|
|
|
132
115
|
export const upsertPayout = mutation({
|
|
133
116
|
args: {
|
|
@@ -332,30 +315,6 @@ export const upsertDispute = mutation({
|
|
|
332
315
|
// TRANSFER MUTATIONS (ledger — BTS-12)
|
|
333
316
|
// =============================================================================
|
|
334
317
|
|
|
335
|
-
/**
|
|
336
|
-
* Derive reversal status + transfer status from a (transfer amount, cumulative
|
|
337
|
-
* reversed amount) pair. Shared by upsert and reversal so the three reversal
|
|
338
|
-
* fields can never drift.
|
|
339
|
-
*/
|
|
340
|
-
function deriveReversalState(
|
|
341
|
-
amount: number,
|
|
342
|
-
reversedAmount: number,
|
|
343
|
-
baseStatus: "pending" | "paid" | "failed" | "reversed",
|
|
344
|
-
) {
|
|
345
|
-
const reversalStatus =
|
|
346
|
-
reversedAmount <= 0
|
|
347
|
-
? undefined
|
|
348
|
-
: reversedAmount >= amount
|
|
349
|
-
? ("fully_reversed" as const)
|
|
350
|
-
: ("partially_reversed" as const);
|
|
351
|
-
return {
|
|
352
|
-
reversedAmount: reversedAmount > 0 ? reversedAmount : undefined,
|
|
353
|
-
reversalStatus,
|
|
354
|
-
status:
|
|
355
|
-
reversalStatus === "fully_reversed" ? ("reversed" as const) : baseStatus,
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
318
|
export const upsertTransfer = mutation({
|
|
360
319
|
args: transferFields,
|
|
361
320
|
returns: v.null(),
|
|
@@ -34,9 +34,10 @@ export const payoutsTable = defineTable(payoutFields)
|
|
|
34
34
|
* (BTS-103). One row per payment intent; consumed and deleted by
|
|
35
35
|
* `upsertPayment`'s insert branch.
|
|
36
36
|
*/
|
|
37
|
-
export const pendingFeeRefundsTable = defineTable(
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
export const pendingFeeRefundsTable = defineTable(pendingFeeRefundFields).index(
|
|
38
|
+
"by_stripe_payment_intent_id",
|
|
39
|
+
["stripePaymentIntentId"],
|
|
40
|
+
);
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Refunds. Compound status indexes follow Plan 002: filtered list queries
|
|
@@ -15,11 +15,7 @@ describe("connect — refunds", () => {
|
|
|
15
15
|
stripePaymentIntentId?: string;
|
|
16
16
|
accountId?: string;
|
|
17
17
|
status:
|
|
18
|
-
| "
|
|
19
|
-
| "requires_action"
|
|
20
|
-
| "succeeded"
|
|
21
|
-
| "failed"
|
|
22
|
-
| "canceled";
|
|
18
|
+
"pending" | "requires_action" | "succeeded" | "failed" | "canceled";
|
|
23
19
|
},
|
|
24
20
|
) => {
|
|
25
21
|
await t.mutation(api.connect.mutations.upsertRefund, {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MutationCtx } from "../_generated/server";
|
|
2
|
+
|
|
3
|
+
export async function paymentSeller(
|
|
4
|
+
ctx: MutationCtx,
|
|
5
|
+
stripePaymentIntentId?: string,
|
|
6
|
+
): Promise<string | undefined> {
|
|
7
|
+
if (!stripePaymentIntentId) return undefined;
|
|
8
|
+
const payment = await ctx.db
|
|
9
|
+
.query("payments")
|
|
10
|
+
.withIndex("by_stripe_payment_intent_id", (q) =>
|
|
11
|
+
q.eq("stripePaymentIntentId", stripePaymentIntentId),
|
|
12
|
+
)
|
|
13
|
+
.first();
|
|
14
|
+
return payment?.destinationAccountId;
|
|
15
|
+
}
|