@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
|
@@ -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
|
+
}
|
|
@@ -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
|
);
|
|
@@ -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(
|
|
@@ -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
|
|