@fantastic.dev/better-stripe 0.9.0 → 0.10.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 +12 -0
- package/MIGRATION.md +26 -0
- package/README.md +38 -1
- package/dist/client/apiVersion.d.ts +6 -0
- package/dist/client/apiVersion.d.ts.map +1 -0
- package/dist/client/apiVersion.js +13 -0
- package/dist/client/apiVersion.js.map +1 -0
- package/dist/client/billing/checkout.d.ts +1 -0
- package/dist/client/billing/checkout.d.ts.map +1 -1
- package/dist/client/billing/checkout.js +5 -1
- package/dist/client/billing/checkout.js.map +1 -1
- package/dist/client/billing/providerReads.d.ts +18 -0
- package/dist/client/billing/providerReads.d.ts.map +1 -0
- package/dist/client/billing/providerReads.js +15 -0
- package/dist/client/billing/providerReads.js.map +1 -0
- package/dist/client/billing/subscriptions.d.ts +1 -1
- package/dist/client/billing/subscriptions.d.ts.map +1 -1
- package/dist/client/billing/subscriptions.js.map +1 -1
- package/dist/client/connect/payoutSnapshot.d.ts +28 -0
- package/dist/client/connect/payoutSnapshot.d.ts.map +1 -0
- package/dist/client/connect/payoutSnapshot.js +81 -0
- package/dist/client/connect/payoutSnapshot.js.map +1 -0
- package/dist/client/connect/transfers.d.ts.map +1 -1
- package/dist/client/connect/transfers.js +13 -32
- package/dist/client/connect/transfers.js.map +1 -1
- package/dist/client/core/accounts.d.ts +12 -23
- package/dist/client/core/accounts.d.ts.map +1 -1
- package/dist/client/core/accounts.js +5 -1
- package/dist/client/core/accounts.js.map +1 -1
- package/dist/client/core/idempotency.d.ts +5 -0
- package/dist/client/core/idempotency.d.ts.map +1 -0
- package/dist/client/core/idempotency.js +10 -0
- package/dist/client/core/idempotency.js.map +1 -0
- package/dist/client/index.d.ts +39 -37
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +47 -56
- package/dist/client/index.js.map +1 -1
- package/dist/client/previewRoutes.d.ts +9 -0
- package/dist/client/previewRoutes.d.ts.map +1 -0
- package/dist/client/previewRoutes.js +15 -0
- package/dist/client/previewRoutes.js.map +1 -0
- package/dist/client/settlement/allocation.d.ts +30 -0
- package/dist/client/settlement/allocation.d.ts.map +1 -0
- package/dist/client/settlement/allocation.js +71 -0
- package/dist/client/settlement/allocation.js.map +1 -0
- package/dist/client/settlement/commissions.d.ts +27 -0
- package/dist/client/settlement/commissions.d.ts.map +1 -0
- package/dist/client/settlement/commissions.js +69 -0
- package/dist/client/settlement/commissions.js.map +1 -0
- package/dist/client/settlement/execution.d.ts +27 -0
- package/dist/client/settlement/execution.d.ts.map +1 -0
- package/dist/client/settlement/execution.js +70 -0
- package/dist/client/settlement/execution.js.map +1 -0
- package/dist/client/settlement/reconciliation.d.ts +10 -0
- package/dist/client/settlement/reconciliation.d.ts.map +1 -0
- package/dist/client/settlement/reconciliation.js +73 -0
- package/dist/client/settlement/reconciliation.js.map +1 -0
- package/dist/client/settlement/settleSale.d.ts +21 -0
- package/dist/client/settlement/settleSale.d.ts.map +1 -0
- package/dist/client/settlement/settleSale.js +27 -0
- package/dist/client/settlement/settleSale.js.map +1 -0
- package/dist/client/settlement/verifiedSale.d.ts +25 -0
- package/dist/client/settlement/verifiedSale.d.ts.map +1 -0
- package/dist/client/settlement/verifiedSale.js +100 -0
- package/dist/client/settlement/verifiedSale.js.map +1 -0
- package/dist/client/types/options.d.ts +3 -0
- package/dist/client/types/options.d.ts.map +1 -1
- package/dist/client/utils/webhookEndpoints.d.ts +12 -8
- package/dist/client/utils/webhookEndpoints.d.ts.map +1 -1
- package/dist/client/utils/webhookEndpoints.js +41 -2
- package/dist/client/utils/webhookEndpoints.js.map +1 -1
- package/dist/client/webhooks/asyncHooks.d.ts +5 -0
- package/dist/client/webhooks/asyncHooks.d.ts.map +1 -0
- package/dist/client/webhooks/asyncHooks.js +20 -0
- package/dist/client/webhooks/asyncHooks.js.map +1 -0
- package/dist/client/webhooks/v2.d.ts.map +1 -1
- package/dist/client/webhooks/v2.js +10 -7
- package/dist/client/webhooks/v2.js.map +1 -1
- package/dist/component/_generated/component.d.ts +80 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/lib/stripeFactory.d.ts +3 -2
- package/dist/component/lib/stripeFactory.d.ts.map +1 -1
- package/dist/component/lib/stripeFactory.js +6 -5
- package/dist/component/lib/stripeFactory.js.map +1 -1
- package/dist/component/schema.d.ts +67 -0
- package/dist/component/schema.js +4 -0
- package/dist/component/schema.js.map +1 -1
- package/dist/component/settlement/mutations.d.ts +47 -0
- package/dist/component/settlement/mutations.d.ts.map +1 -0
- package/dist/component/settlement/mutations.js +104 -0
- package/dist/component/settlement/mutations.js.map +1 -0
- package/dist/component/settlement/queries.d.ts +29 -0
- package/dist/component/settlement/queries.d.ts.map +1 -0
- package/dist/component/settlement/queries.js +35 -0
- package/dist/component/settlement/queries.js.map +1 -0
- package/dist/component/settlement/schema.d.ts +99 -0
- package/dist/component/settlement/schema.d.ts.map +1 -0
- package/dist/component/settlement/schema.js +35 -0
- package/dist/component/settlement/schema.js.map +1 -0
- package/dist/react/workflows/Checkout.d.ts +3 -1
- package/dist/react/workflows/Checkout.d.ts.map +1 -1
- package/dist/react/workflows/Checkout.js +3 -2
- package/dist/react/workflows/Checkout.js.map +1 -1
- package/dist/react/workflows/Payouts.d.ts +3 -18
- package/dist/react/workflows/Payouts.d.ts.map +1 -1
- package/dist/react/workflows/Payouts.js +2 -2
- package/dist/react/workflows/Payouts.js.map +1 -1
- package/docs/accounts-v2-preview.md +132 -0
- package/docs/react-workflows.md +5 -2
- package/docs/settlement.md +158 -0
- package/package.json +3 -1
- package/src/client/apiVersion.ts +20 -0
- package/src/client/billing/checkout-idempotency.test.ts +82 -0
- package/src/client/billing/checkout.ts +6 -1
- package/src/client/billing/providerReads.test.ts +60 -0
- package/src/client/billing/providerReads.ts +30 -0
- package/src/client/billing/subscriptions.ts +1 -8
- package/src/client/connect/payoutSnapshot.test.ts +91 -0
- package/src/client/connect/payoutSnapshot.ts +130 -0
- package/src/client/connect/transfers.test.ts +15 -4
- package/src/client/connect/transfers.ts +13 -42
- package/src/client/core/account-idempotency.test.ts +78 -0
- package/src/client/core/accounts.ts +18 -25
- package/src/client/core/idempotency.test.ts +10 -0
- package/src/client/core/idempotency.ts +11 -0
- package/src/client/customer-preview-thin.test.ts +41 -0
- package/src/client/customer-preview-webhooks.test.ts +150 -0
- package/src/client/customer-preview.test.ts +185 -0
- package/src/client/index.ts +115 -119
- package/src/client/previewRoutes.ts +26 -0
- package/src/client/settlement/allocation.test.ts +98 -0
- package/src/client/settlement/allocation.ts +110 -0
- package/src/client/settlement/commissions.test.ts +103 -0
- package/src/client/settlement/commissions.ts +125 -0
- package/src/client/settlement/execution.test.ts +186 -0
- package/src/client/settlement/execution.ts +134 -0
- package/src/client/settlement/reconciliation.ts +119 -0
- package/src/client/settlement/settleSale.ts +59 -0
- package/src/client/settlement/verifiedSale.test.ts +139 -0
- package/src/client/settlement/verifiedSale.ts +159 -0
- package/src/client/stripe-version.test.ts +10 -4
- package/src/client/types/options.ts +3 -0
- package/src/client/utils/webhookEndpoints.ts +70 -15
- package/src/client/webhooks/asyncHooks.ts +38 -0
- package/src/client/webhooks/processors.test.ts +9 -9
- package/src/client/webhooks/v2.ts +14 -10
- package/src/component/_generated/api.ts +4 -0
- package/src/component/_generated/component.ts +100 -0
- package/src/component/lib/stripeFactory.ts +13 -5
- package/src/component/schema.ts +8 -0
- package/src/component/settlement/mutations.ts +119 -0
- package/src/component/settlement/queries.ts +38 -0
- package/src/component/settlement/schema.ts +45 -0
- package/src/component/settlement-claims.test.ts +77 -0
- package/src/react/components/EmbeddedDisputes.render.test.tsx +2 -2
- package/src/react/index.payment-element.test.ts +22 -37
- package/src/react/workflows/Checkout.test.tsx +30 -0
- package/src/react/workflows/Checkout.tsx +10 -3
- package/src/react/workflows/Payouts.test.tsx +33 -0
- package/src/react/workflows/Payouts.tsx +21 -15
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @vitest-environment edge-runtime
|
|
2
|
+
import { convexTest } from "convex-test";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import { api } from "./_generated/api.js";
|
|
5
|
+
import schema from "./schema.js";
|
|
6
|
+
const modules = import.meta.glob("./**/*.*s");
|
|
7
|
+
it("keeps a claimed allocation immutable and isolated per source charge", async () => {
|
|
8
|
+
const t = convexTest(schema, modules);
|
|
9
|
+
const opts = {
|
|
10
|
+
sourceChargeId: "ch_1",
|
|
11
|
+
paymentIntentId: "pi_1",
|
|
12
|
+
gross: 100,
|
|
13
|
+
currency: "usd",
|
|
14
|
+
processingFee: 3,
|
|
15
|
+
platformNet: 7,
|
|
16
|
+
transfers: [
|
|
17
|
+
{ destinationAccountId: "acct_c", amount: 90, role: "store" as const },
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
const first = await t.mutation(
|
|
21
|
+
api.settlement.mutations.reserveSettlementAllocation,
|
|
22
|
+
opts,
|
|
23
|
+
);
|
|
24
|
+
const replay = await t.mutation(
|
|
25
|
+
api.settlement.mutations.reserveSettlementAllocation,
|
|
26
|
+
{
|
|
27
|
+
...opts,
|
|
28
|
+
transfers: [{ ...opts.transfers[0], amount: 80 }],
|
|
29
|
+
platformNet: 17,
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
expect(replay).toEqual(first);
|
|
33
|
+
await expect(
|
|
34
|
+
t.mutation(api.settlement.mutations.reserveSettlementAllocation, {
|
|
35
|
+
...opts,
|
|
36
|
+
sourceChargeId: "ch_invalid",
|
|
37
|
+
gross: 1,
|
|
38
|
+
}),
|
|
39
|
+
).rejects.toThrow();
|
|
40
|
+
});
|
|
41
|
+
it("persists the term, identity and earliest invoice despite replay and term changes", async () => {
|
|
42
|
+
const t = convexTest(schema, modules);
|
|
43
|
+
const opts = {
|
|
44
|
+
subscriptionId: "sub_1",
|
|
45
|
+
invoiceId: "in_renewal",
|
|
46
|
+
firstPaidInvoiceId: "in_first",
|
|
47
|
+
term: "one_time" as const,
|
|
48
|
+
affiliateAccountId: "acct_a",
|
|
49
|
+
};
|
|
50
|
+
expect(
|
|
51
|
+
await t.mutation(api.settlement.mutations.claimCommissionInvoice, opts),
|
|
52
|
+
).toMatchObject({
|
|
53
|
+
eligible: false,
|
|
54
|
+
firstPaidInvoiceId: "in_first",
|
|
55
|
+
term: "one_time",
|
|
56
|
+
replay: false,
|
|
57
|
+
});
|
|
58
|
+
expect(
|
|
59
|
+
await t.mutation(api.settlement.mutations.claimCommissionInvoice, {
|
|
60
|
+
...opts,
|
|
61
|
+
invoiceId: "in_first",
|
|
62
|
+
term: "forever",
|
|
63
|
+
}),
|
|
64
|
+
).toMatchObject({ eligible: true, term: "one_time" });
|
|
65
|
+
expect(
|
|
66
|
+
await t.mutation(api.settlement.mutations.claimCommissionInvoice, {
|
|
67
|
+
...opts,
|
|
68
|
+
invoiceId: "in_first",
|
|
69
|
+
}),
|
|
70
|
+
).toMatchObject({ eligible: true, replay: true });
|
|
71
|
+
await expect(
|
|
72
|
+
t.mutation(api.settlement.mutations.claimCommissionInvoice, {
|
|
73
|
+
...opts,
|
|
74
|
+
affiliateAccountId: "acct_other",
|
|
75
|
+
}),
|
|
76
|
+
).rejects.toThrow();
|
|
77
|
+
});
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* tree rendering without throwing via react-dom/server, the disputes_list vs
|
|
11
11
|
* payment_disputes mount switch, and the headless overrides.
|
|
12
12
|
*/
|
|
13
|
+
import * as barrel from "../index.js";
|
|
13
14
|
import { createElement } from "react";
|
|
14
15
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
15
16
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
@@ -136,8 +137,7 @@ describe("EmbeddedDisputes", () => {
|
|
|
136
137
|
});
|
|
137
138
|
|
|
138
139
|
describe("react barrel exports", () => {
|
|
139
|
-
it("exports ConnectProvider and EmbeddedDisputes",
|
|
140
|
-
const barrel = await import("../index.js");
|
|
140
|
+
it("exports ConnectProvider and EmbeddedDisputes", () => {
|
|
141
141
|
expect(barrel.ConnectProvider).toBe(ConnectProvider);
|
|
142
142
|
expect(barrel.EmbeddedDisputes).toBe(EmbeddedDisputes);
|
|
143
143
|
});
|
|
@@ -1,41 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const elements = vi.hoisted(() => ({
|
|
4
|
+
checkout: Symbol("checkout-payment-element"),
|
|
5
|
+
paymentIntent: Symbol("elements-payment-element"),
|
|
6
|
+
}));
|
|
7
|
+
vi.mock("@stripe/react-stripe-js", async () => ({
|
|
8
|
+
...(await vi.importActual<typeof import("@stripe/react-stripe-js")>(
|
|
9
|
+
"@stripe/react-stripe-js",
|
|
10
|
+
)),
|
|
11
|
+
PaymentElement: elements.paymentIntent,
|
|
12
|
+
}));
|
|
13
|
+
vi.mock("@stripe/react-stripe-js/checkout", async () => ({
|
|
14
|
+
...(await vi.importActual<typeof import("@stripe/react-stripe-js/checkout")>(
|
|
15
|
+
"@stripe/react-stripe-js/checkout",
|
|
16
|
+
)),
|
|
17
|
+
PaymentElement: elements.checkout,
|
|
18
|
+
}));
|
|
19
|
+
import * as reactExports from "./index.js";
|
|
2
20
|
|
|
3
21
|
describe("better-stripe/react PaymentElement exports", () => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
vi.doUnmock("@stripe/react-stripe-js/checkout");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("uses checkout PaymentElement as the default export and keeps the Elements variant aliased", async () => {
|
|
11
|
-
const checkoutPaymentElement = Symbol("checkout-payment-element");
|
|
12
|
-
const elementsPaymentElement = Symbol("elements-payment-element");
|
|
13
|
-
|
|
14
|
-
vi.doMock("@stripe/react-stripe-js", async () => {
|
|
15
|
-
const actual = await vi.importActual<
|
|
16
|
-
typeof import("@stripe/react-stripe-js")
|
|
17
|
-
>("@stripe/react-stripe-js");
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
...actual,
|
|
21
|
-
PaymentElement: elementsPaymentElement,
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
vi.doMock("@stripe/react-stripe-js/checkout", async () => {
|
|
26
|
-
const actual = await vi.importActual<
|
|
27
|
-
typeof import("@stripe/react-stripe-js/checkout")
|
|
28
|
-
>("@stripe/react-stripe-js/checkout");
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
...actual,
|
|
32
|
-
PaymentElement: checkoutPaymentElement,
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const reactExports = await import("./index.js");
|
|
37
|
-
|
|
38
|
-
expect(reactExports.PaymentElement).toBe(checkoutPaymentElement);
|
|
39
|
-
expect(reactExports.ElementsPaymentElement).toBe(elementsPaymentElement);
|
|
22
|
+
it("uses checkout PaymentElement as the default export and keeps the Elements variant aliased", () => {
|
|
23
|
+
expect(reactExports.PaymentElement).toBe(elements.checkout);
|
|
24
|
+
expect(reactExports.ElementsPaymentElement).toBe(elements.paymentIntent);
|
|
40
25
|
});
|
|
41
26
|
});
|
|
@@ -83,6 +83,9 @@ it("exposes creation errors and retries without losing the selected integration"
|
|
|
83
83
|
await userEvent.click(screen.getByRole("button", { name: "Retry" }));
|
|
84
84
|
await screen.findByText("Stripe iframe");
|
|
85
85
|
await waitFor(() => expect(createSession).toHaveBeenCalledTimes(2));
|
|
86
|
+
expect(createSession.mock.calls[1][0].requestId).toBe(
|
|
87
|
+
createSession.mock.calls[0][0].requestId,
|
|
88
|
+
);
|
|
86
89
|
});
|
|
87
90
|
it("rejects missing session secrets and initializes Payment Intents through their own callback", async () => {
|
|
88
91
|
const { unmount } = render(
|
|
@@ -116,3 +119,30 @@ it("rejects missing session secrets and initializes Payment Intents through thei
|
|
|
116
119
|
requestId: expect.any(String),
|
|
117
120
|
});
|
|
118
121
|
});
|
|
122
|
+
|
|
123
|
+
it("keeps an explicit request identity through rerenders and replaces generated identity for a new order", async () => {
|
|
124
|
+
const createSession = vi
|
|
125
|
+
.fn()
|
|
126
|
+
.mockResolvedValue({ stripeSessionId: "cs_test", clientSecret: "secret" });
|
|
127
|
+
const props = {
|
|
128
|
+
mode: "embedded" as const,
|
|
129
|
+
orderKey: "order_one",
|
|
130
|
+
publishableKey: "pk_test",
|
|
131
|
+
returnUrl: "https://app.test/receipt",
|
|
132
|
+
createSession,
|
|
133
|
+
};
|
|
134
|
+
const { rerender } = render(
|
|
135
|
+
<Checkout {...props} requestId="persisted-order" />,
|
|
136
|
+
);
|
|
137
|
+
await screen.findByText("Stripe iframe");
|
|
138
|
+
expect(createSession.mock.calls[0][0].requestId).toBe("persisted-order");
|
|
139
|
+
rerender(<Checkout {...props} requestId="persisted-order" />);
|
|
140
|
+
expect(createSession).toHaveBeenCalledTimes(1);
|
|
141
|
+
rerender(<Checkout {...props} orderKey="order_two" />);
|
|
142
|
+
await waitFor(() => expect(createSession).toHaveBeenCalledTimes(2));
|
|
143
|
+
const second = createSession.mock.calls[1][0].requestId;
|
|
144
|
+
expect(second).not.toBe("persisted-order");
|
|
145
|
+
rerender(<Checkout {...props} orderKey="order_three" />);
|
|
146
|
+
await waitFor(() => expect(createSession).toHaveBeenCalledTimes(3));
|
|
147
|
+
expect(createSession.mock.calls[2][0].requestId).not.toBe(second);
|
|
148
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import type
|
|
2
|
+
import { useState, type ReactNode } from "react";
|
|
3
3
|
import { EmbeddedCheckout } from "../components/EmbeddedCheckout.js";
|
|
4
4
|
import { useWorkflowResource } from "./useWorkflowResource.js";
|
|
5
5
|
import {
|
|
@@ -36,6 +36,8 @@ type Common = CheckoutFormOptions & {
|
|
|
36
36
|
publishableKey: string;
|
|
37
37
|
/** Changes when the price, signed-in customer, or referral attribution changes. */
|
|
38
38
|
orderKey: string;
|
|
39
|
+
/** Optional durable logical-order identity, persisted by the host app. */
|
|
40
|
+
requestId?: string;
|
|
39
41
|
returnUrl: string;
|
|
40
42
|
onNavigate?: (url: string) => void;
|
|
41
43
|
children?: (state: CheckoutRenderProps) => ReactNode;
|
|
@@ -57,11 +59,16 @@ export type CheckoutProps = Common &
|
|
|
57
59
|
);
|
|
58
60
|
/** All checkout integrations share one unstyled lifecycle. Callbacks must authenticate and validate prices on the server. */
|
|
59
61
|
export function Checkout(props: CheckoutProps) {
|
|
60
|
-
return
|
|
62
|
+
return (
|
|
63
|
+
<CheckoutBody
|
|
64
|
+
key={`${props.orderKey}:${props.mode}:${props.requestId ?? ""}`}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
61
68
|
}
|
|
62
69
|
function CheckoutBody(props: CheckoutProps) {
|
|
70
|
+
const [requestId] = useState(() => props.requestId ?? crypto.randomUUID());
|
|
63
71
|
const state = useWorkflowResource(async () => {
|
|
64
|
-
const requestId = crypto.randomUUID();
|
|
65
72
|
const result =
|
|
66
73
|
props.mode === "payment-intent"
|
|
67
74
|
? await props.createPaymentIntent({ requestId })
|
|
@@ -110,3 +110,36 @@ it("keeps completed payouts out of upcoming and sorts the remaining arrivals", a
|
|
|
110
110
|
);
|
|
111
111
|
expect(screen.getAllByText("po_paid")).toHaveLength(1);
|
|
112
112
|
});
|
|
113
|
+
it("distinguishes an unavailable account from an empty balance and labels upcoming previews", async () => {
|
|
114
|
+
const load = vi.fn().mockResolvedValue({
|
|
115
|
+
status: "not_ready",
|
|
116
|
+
reason: "account_unavailable",
|
|
117
|
+
balances: [],
|
|
118
|
+
upcoming: [],
|
|
119
|
+
payouts: [],
|
|
120
|
+
nextCursor: null,
|
|
121
|
+
upcomingComplete: true,
|
|
122
|
+
});
|
|
123
|
+
const { unmount } = render(<Payouts scopeKey="unavailable" load={load} />);
|
|
124
|
+
expect((await screen.findByRole("alert")).textContent).toContain(
|
|
125
|
+
"not available",
|
|
126
|
+
);
|
|
127
|
+
expect(screen.queryByText("Not scheduled")).toBeNull();
|
|
128
|
+
unmount();
|
|
129
|
+
render(
|
|
130
|
+
<Payouts
|
|
131
|
+
scopeKey="preview"
|
|
132
|
+
load={async () => ({
|
|
133
|
+
status: "ready",
|
|
134
|
+
balances: [],
|
|
135
|
+
upcoming: [],
|
|
136
|
+
payouts: [],
|
|
137
|
+
nextCursor: null,
|
|
138
|
+
upcomingComplete: false,
|
|
139
|
+
})}
|
|
140
|
+
/>,
|
|
141
|
+
);
|
|
142
|
+
expect(
|
|
143
|
+
await screen.findByText(/Upcoming payouts are a bounded preview/),
|
|
144
|
+
).toBeTruthy();
|
|
145
|
+
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import type {
|
|
3
|
+
RecipientPayoutSnapshot,
|
|
4
|
+
RecipientPayoutRow,
|
|
5
|
+
} from "../../client/connect/payoutSnapshot.js";
|
|
2
6
|
import { useState, type ReactNode } from "react";
|
|
3
7
|
import { Connect, type ConnectProps } from "./Connect.js";
|
|
4
8
|
import { useWorkflowAction } from "./useWorkflowAction.js";
|
|
@@ -9,20 +13,14 @@ import {
|
|
|
9
13
|
type WorkflowColumn,
|
|
10
14
|
} from "./Collection.js";
|
|
11
15
|
import { formatPrice } from "../lib/price-helpers.js";
|
|
12
|
-
export type PayoutRow =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export type PayoutSnapshot = {
|
|
21
|
-
balances: { currency: string; available: number; pending: number }[];
|
|
22
|
-
payouts: PayoutRow[];
|
|
23
|
-
upcoming: PayoutRow[];
|
|
24
|
-
nextCursor: string | null;
|
|
25
|
-
};
|
|
16
|
+
export type PayoutRow = RecipientPayoutRow;
|
|
17
|
+
export type PayoutSnapshot = Pick<
|
|
18
|
+
RecipientPayoutSnapshot,
|
|
19
|
+
"balances" | "payouts" | "upcoming" | "nextCursor"
|
|
20
|
+
> &
|
|
21
|
+
Partial<
|
|
22
|
+
Pick<RecipientPayoutSnapshot, "status" | "reason" | "upcomingComplete">
|
|
23
|
+
>;
|
|
26
24
|
export type PayoutsRenderProps = {
|
|
27
25
|
data: PayoutSnapshot | undefined;
|
|
28
26
|
error: string | null;
|
|
@@ -169,7 +167,10 @@ function NativePayouts({
|
|
|
169
167
|
</button>
|
|
170
168
|
{error && <p role="alert">{error}</p>}
|
|
171
169
|
{isLoading && !data && <p role="status">Loading payouts…</p>}
|
|
172
|
-
{data && (
|
|
170
|
+
{data?.status === "not_ready" && (
|
|
171
|
+
<p role="alert">Stripe account data is not available yet.</p>
|
|
172
|
+
)}
|
|
173
|
+
{data && data.status !== "not_ready" && (
|
|
173
174
|
<>
|
|
174
175
|
<dl>
|
|
175
176
|
{data.balances.map((balance) => (
|
|
@@ -188,6 +189,11 @@ function NativePayouts({
|
|
|
188
189
|
</dd>
|
|
189
190
|
</dl>
|
|
190
191
|
<h3>Upcoming payouts</h3>
|
|
192
|
+
{data.upcomingComplete === false && (
|
|
193
|
+
<p>
|
|
194
|
+
Upcoming payouts are a bounded preview. More may be scheduled.
|
|
195
|
+
</p>
|
|
196
|
+
)}
|
|
191
197
|
<PayoutHistory
|
|
192
198
|
rows={upcomingPayouts}
|
|
193
199
|
emptyLabel="No upcoming payouts scheduled."
|