@fantastic.dev/better-stripe 0.21.0 → 0.22.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 +6 -0
- package/README.md +6 -5
- package/dist/client/connect/payoutExecution.d.ts +34 -0
- package/dist/client/connect/payoutExecution.d.ts.map +1 -0
- package/dist/client/connect/payoutExecution.js +189 -0
- package/dist/client/connect/payoutExecution.js.map +1 -0
- package/dist/client/connect/payoutHistory.d.ts +22 -0
- package/dist/client/connect/payoutHistory.d.ts.map +1 -0
- package/dist/client/connect/payoutHistory.js +122 -0
- package/dist/client/connect/payoutHistory.js.map +1 -0
- package/dist/client/connect/payoutReconciliation.d.ts.map +1 -1
- package/dist/client/connect/payoutReconciliation.js +2 -0
- package/dist/client/connect/payoutReconciliation.js.map +1 -1
- package/dist/client/payoutPolicy.d.ts +3 -0
- package/dist/client/payoutPolicy.d.ts.map +1 -1
- package/dist/client/payoutPolicy.js +4 -0
- package/dist/client/payoutPolicy.js.map +1 -1
- package/dist/component/_generated/component.d.ts +73 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/earnings/triggers.d.ts +4 -2
- package/dist/component/earnings/triggers.d.ts.map +1 -1
- package/dist/component/payoutRelease/helpers.d.ts +1 -0
- package/dist/component/payoutRelease/helpers.d.ts.map +1 -1
- package/dist/component/payoutRelease/mutations.d.ts +6 -0
- package/dist/component/payoutRelease/mutations.d.ts.map +1 -1
- package/dist/component/payoutRelease/mutations.js +7 -1
- package/dist/component/payoutRelease/mutations.js.map +1 -1
- package/dist/component/payoutRelease/queries.d.ts +71 -0
- package/dist/component/payoutRelease/queries.d.ts.map +1 -1
- package/dist/component/payoutRelease/queries.js +29 -0
- package/dist/component/payoutRelease/queries.js.map +1 -1
- package/dist/component/payoutRelease/schema.d.ts +7 -2
- package/dist/component/payoutRelease/schema.d.ts.map +1 -1
- package/dist/component/payoutRelease/schema.js +1 -0
- package/dist/component/payoutRelease/schema.js.map +1 -1
- package/dist/component/schema.d.ts +3 -1
- package/docs/settlement.md +64 -4
- package/package.json +1 -1
- package/src/client/connect/payoutExecution.test.ts +504 -0
- package/src/client/connect/payoutExecution.ts +265 -0
- package/src/client/connect/payoutHistory.test.ts +145 -0
- package/src/client/connect/payoutHistory.ts +149 -0
- package/src/client/connect/payoutReconciliation.test.ts +13 -0
- package/src/client/connect/payoutReconciliation.ts +4 -0
- package/src/client/payoutPolicy.ts +14 -0
- package/src/component/_generated/component.ts +80 -0
- package/src/component/payout-release-snapshot.test.ts +57 -0
- package/src/component/payoutRelease/mutations.ts +9 -1
- package/src/component/payoutRelease/queries.ts +35 -0
- package/src/component/payoutRelease/schema.ts +1 -0
|
@@ -2494,6 +2494,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2494
2494
|
currency: string;
|
|
2495
2495
|
debitFee?: number;
|
|
2496
2496
|
debitTransactionId?: string;
|
|
2497
|
+
destinationId?: string;
|
|
2497
2498
|
expectedRevision: number;
|
|
2498
2499
|
feeRevision?: number;
|
|
2499
2500
|
observations: Array<{
|
|
@@ -2542,6 +2543,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2542
2543
|
currency: string;
|
|
2543
2544
|
debitFee?: number;
|
|
2544
2545
|
debitTransactionId?: string;
|
|
2546
|
+
destinationId?: string;
|
|
2545
2547
|
expectedRevision: number;
|
|
2546
2548
|
feeRevision?: number;
|
|
2547
2549
|
observations: Array<{
|
|
@@ -2578,6 +2580,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2578
2580
|
{
|
|
2579
2581
|
accountId: string;
|
|
2580
2582
|
currency: string;
|
|
2583
|
+
destinationId?: string;
|
|
2581
2584
|
expectedRevision: number;
|
|
2582
2585
|
independentLiabilities: number;
|
|
2583
2586
|
observations: Array<{
|
|
@@ -2607,6 +2610,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2607
2610
|
currency: string;
|
|
2608
2611
|
debitFee?: number;
|
|
2609
2612
|
debitTransactionId?: string;
|
|
2613
|
+
destinationId?: string;
|
|
2610
2614
|
expectedRevision: number;
|
|
2611
2615
|
feeRevision?: number;
|
|
2612
2616
|
observations: Array<{
|
|
@@ -2678,6 +2682,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2678
2682
|
currency: string;
|
|
2679
2683
|
debitFee?: number;
|
|
2680
2684
|
debitTransactionId?: string;
|
|
2685
|
+
destinationId?: string;
|
|
2681
2686
|
expectedRevision: number;
|
|
2682
2687
|
feeRevision?: number;
|
|
2683
2688
|
observations: Array<{
|
|
@@ -2732,6 +2737,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2732
2737
|
currency: string;
|
|
2733
2738
|
debitFee?: number;
|
|
2734
2739
|
debitTransactionId?: string;
|
|
2740
|
+
destinationId?: string;
|
|
2735
2741
|
expectedRevision: number;
|
|
2736
2742
|
feeRevision?: number;
|
|
2737
2743
|
observations: Array<{
|
|
@@ -2789,6 +2795,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2789
2795
|
currency: string;
|
|
2790
2796
|
debitFee?: number;
|
|
2791
2797
|
debitTransactionId?: string;
|
|
2798
|
+
destinationId?: string;
|
|
2792
2799
|
expectedRevision: number;
|
|
2793
2800
|
feeRevision?: number;
|
|
2794
2801
|
observations: Array<{
|
|
@@ -2851,6 +2858,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2851
2858
|
currency: string;
|
|
2852
2859
|
debitFee?: number;
|
|
2853
2860
|
debitTransactionId?: string;
|
|
2861
|
+
destinationId?: string;
|
|
2854
2862
|
expectedRevision: number;
|
|
2855
2863
|
feeRevision?: number;
|
|
2856
2864
|
observations: Array<{
|
|
@@ -2882,6 +2890,78 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
2882
2890
|
},
|
|
2883
2891
|
Name
|
|
2884
2892
|
>;
|
|
2893
|
+
snapshot: FunctionReference<
|
|
2894
|
+
"query",
|
|
2895
|
+
"internal",
|
|
2896
|
+
{ accountId: string; ownerKey: string },
|
|
2897
|
+
| { reason: "snapshot_limit"; status: "held" }
|
|
2898
|
+
| {
|
|
2899
|
+
claims: Array<{
|
|
2900
|
+
_creationTime: number;
|
|
2901
|
+
_id: string;
|
|
2902
|
+
accountId: string;
|
|
2903
|
+
allocations: Array<{
|
|
2904
|
+
amount: number;
|
|
2905
|
+
role?: "store" | "affiliate" | "other";
|
|
2906
|
+
stripeTransferId: string;
|
|
2907
|
+
}>;
|
|
2908
|
+
amount: number;
|
|
2909
|
+
attemptedAt?: number;
|
|
2910
|
+
currency: string;
|
|
2911
|
+
debitFee?: number;
|
|
2912
|
+
debitTransactionId?: string;
|
|
2913
|
+
destinationId?: string;
|
|
2914
|
+
expectedRevision: number;
|
|
2915
|
+
feeRevision?: number;
|
|
2916
|
+
observations: Array<{
|
|
2917
|
+
currency: string;
|
|
2918
|
+
ledgerFingerprint: string;
|
|
2919
|
+
matureNet: number;
|
|
2920
|
+
observedAt: number;
|
|
2921
|
+
pendingCredits: number;
|
|
2922
|
+
stripeTransferId: string;
|
|
2923
|
+
transactionIds: Array<string>;
|
|
2924
|
+
}>;
|
|
2925
|
+
operationKey: string;
|
|
2926
|
+
ownerKey: string;
|
|
2927
|
+
providerFee?: number;
|
|
2928
|
+
rejection?: {
|
|
2929
|
+
code: "balance_insufficient" | "amount_too_small";
|
|
2930
|
+
requestId: string;
|
|
2931
|
+
};
|
|
2932
|
+
returnTransactionId?: string;
|
|
2933
|
+
status:
|
|
2934
|
+
| "reserved"
|
|
2935
|
+
| "submitted"
|
|
2936
|
+
| "paid"
|
|
2937
|
+
| "returned"
|
|
2938
|
+
| "canceled"
|
|
2939
|
+
| "rejected";
|
|
2940
|
+
stripePayoutId?: string;
|
|
2941
|
+
}>;
|
|
2942
|
+
status: "ready";
|
|
2943
|
+
transfers: Array<{
|
|
2944
|
+
_creationTime: number;
|
|
2945
|
+
_id: string;
|
|
2946
|
+
amount: number;
|
|
2947
|
+
currency: string;
|
|
2948
|
+
destinationAccountId: string;
|
|
2949
|
+
metadata?: any;
|
|
2950
|
+
paymentId?: string;
|
|
2951
|
+
reinstatement?: boolean;
|
|
2952
|
+
reversalClaimedAmount?: number;
|
|
2953
|
+
reversalStatus?: "partially_reversed" | "fully_reversed";
|
|
2954
|
+
reversedAmount?: number;
|
|
2955
|
+
role?: "store" | "affiliate" | "other";
|
|
2956
|
+
sourceChargeId?: string;
|
|
2957
|
+
sourceInvoiceId?: string;
|
|
2958
|
+
status: "pending" | "paid" | "failed" | "reversed";
|
|
2959
|
+
stripeTransferId: string;
|
|
2960
|
+
totalsRecorded?: true;
|
|
2961
|
+
}>;
|
|
2962
|
+
},
|
|
2963
|
+
Name
|
|
2964
|
+
>;
|
|
2885
2965
|
};
|
|
2886
2966
|
};
|
|
2887
2967
|
products: {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// @vitest-environment edge-runtime
|
|
2
|
+
import { convexTest } from "convex-test";
|
|
3
|
+
import { makeFunctionReference } from "convex/server";
|
|
4
|
+
import { expect, it } from "vitest";
|
|
5
|
+
import schema from "./schema.js";
|
|
6
|
+
const modules = import.meta.glob("./**/*.*s");
|
|
7
|
+
const snapshot = makeFunctionReference<"query">(
|
|
8
|
+
"payoutRelease/queries:snapshot",
|
|
9
|
+
);
|
|
10
|
+
const scope = { accountId: "acct_recipient", ownerKey: "owner" };
|
|
11
|
+
async function setup(count = 2) {
|
|
12
|
+
const t = convexTest(schema, modules);
|
|
13
|
+
await t.run(async (ctx) => {
|
|
14
|
+
await ctx.db.insert("payoutPolicies", {
|
|
15
|
+
...scope,
|
|
16
|
+
mode: "custom",
|
|
17
|
+
status: "ready",
|
|
18
|
+
revision: 0,
|
|
19
|
+
operationKey: "enable",
|
|
20
|
+
thresholds: [{ currency: "usd", amount: 1000 }],
|
|
21
|
+
country: "US",
|
|
22
|
+
previousSchedule: { interval: "daily" },
|
|
23
|
+
holdDeadline: Date.now() + 86400000,
|
|
24
|
+
});
|
|
25
|
+
for (let i = 0; i < count; i++)
|
|
26
|
+
await ctx.db.insert("transfers", {
|
|
27
|
+
stripeTransferId: `tr_${i}`,
|
|
28
|
+
destinationAccountId: scope.accountId,
|
|
29
|
+
amount: 500,
|
|
30
|
+
currency: "usd",
|
|
31
|
+
status: "paid",
|
|
32
|
+
role: i % 2 ? "affiliate" : "store",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
return t;
|
|
36
|
+
}
|
|
37
|
+
it("returns complete source membership and historical claims for the owned account", async () => {
|
|
38
|
+
const t = await setup();
|
|
39
|
+
expect(await t.query(snapshot, scope)).toMatchObject({
|
|
40
|
+
status: "ready",
|
|
41
|
+
transfers: [{ stripeTransferId: "tr_0" }, { stripeTransferId: "tr_1" }],
|
|
42
|
+
claims: [],
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("holds an account exceeding the atomic claim bound rather than qualifying a partial page", async () => {
|
|
46
|
+
const t = await setup(101);
|
|
47
|
+
expect(await t.query(snapshot, scope)).toEqual({
|
|
48
|
+
status: "held",
|
|
49
|
+
reason: "snapshot_limit",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it("rejects another owner", async () => {
|
|
53
|
+
const t = await setup();
|
|
54
|
+
await expect(
|
|
55
|
+
t.query(snapshot, { ...scope, ownerKey: "other" }),
|
|
56
|
+
).rejects.toThrow(/owner/i);
|
|
57
|
+
});
|
|
@@ -31,6 +31,7 @@ export const claim = mutation({
|
|
|
31
31
|
...identity,
|
|
32
32
|
expectedRevision: v.number(),
|
|
33
33
|
currency: v.string(),
|
|
34
|
+
destinationId: v.optional(v.string()),
|
|
34
35
|
providerAvailable: v.number(),
|
|
35
36
|
independentLiabilities: v.number(),
|
|
36
37
|
observations: v.array(observation),
|
|
@@ -38,11 +39,17 @@ export const claim = mutation({
|
|
|
38
39
|
returns: v.union(claimDoc, held),
|
|
39
40
|
handler: async (ctx, args) => {
|
|
40
41
|
validateIdentity(args);
|
|
42
|
+
if (
|
|
43
|
+
args.destinationId !== undefined &&
|
|
44
|
+
!/^ba_\w+$/.test(args.destinationId)
|
|
45
|
+
)
|
|
46
|
+
throw new Error("Invalid payout bank destination.");
|
|
41
47
|
const existing = await claimByKey(ctx, args);
|
|
42
48
|
if (existing) {
|
|
43
49
|
if (
|
|
44
50
|
existing.currency !== args.currency ||
|
|
45
|
-
existing.expectedRevision !== args.expectedRevision
|
|
51
|
+
existing.expectedRevision !== args.expectedRevision ||
|
|
52
|
+
existing.destinationId !== args.destinationId
|
|
46
53
|
)
|
|
47
54
|
throw new Error("Payout claim identity cannot be reused.");
|
|
48
55
|
return existing;
|
|
@@ -122,6 +129,7 @@ export const claim = mutation({
|
|
|
122
129
|
expectedRevision: args.expectedRevision,
|
|
123
130
|
currency: args.currency,
|
|
124
131
|
amount: plan.amount,
|
|
132
|
+
...(args.destinationId ? { destinationId: args.destinationId } : {}),
|
|
125
133
|
allocations: plan.allocations,
|
|
126
134
|
observations: args.observations,
|
|
127
135
|
feeRevision: fees?.revision ?? 0,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { transferDocValidator } from "../connect/validators.js";
|
|
1
2
|
import { readFees } from "./fees.js";
|
|
2
3
|
import { paginationOptsValidator } from "convex/server";
|
|
3
4
|
import { v } from "convex/values";
|
|
@@ -65,3 +66,37 @@ export const fees = query({
|
|
|
65
66
|
return { amount: balance?.amount ?? 0, revision: balance?.revision ?? 0 };
|
|
66
67
|
},
|
|
67
68
|
});
|
|
69
|
+
|
|
70
|
+
/** Complete bounded input for package qualification. Partial membership never qualifies. */
|
|
71
|
+
export const snapshot = query({
|
|
72
|
+
args: { accountId: v.string(), ownerKey: v.string() },
|
|
73
|
+
returns: v.union(
|
|
74
|
+
v.object({
|
|
75
|
+
status: v.literal("held"),
|
|
76
|
+
reason: v.literal("snapshot_limit"),
|
|
77
|
+
}),
|
|
78
|
+
v.object({
|
|
79
|
+
status: v.literal("ready"),
|
|
80
|
+
transfers: v.array(transferDocValidator),
|
|
81
|
+
claims: v.array(claimDoc),
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
handler: async (ctx, args) => {
|
|
85
|
+
await readPolicy(ctx, args);
|
|
86
|
+
const transfers = await ctx.db
|
|
87
|
+
.query("transfers")
|
|
88
|
+
.withIndex("by_destination_account_id", (q) =>
|
|
89
|
+
q.eq("destinationAccountId", args.accountId),
|
|
90
|
+
)
|
|
91
|
+
.take(101);
|
|
92
|
+
const claims = await ctx.db
|
|
93
|
+
.query("payoutReleaseClaims")
|
|
94
|
+
.withIndex("by_account_status", (q) => q.eq("accountId", args.accountId))
|
|
95
|
+
.take(101);
|
|
96
|
+
if (transfers.length > 100 || claims.length > 100)
|
|
97
|
+
return { status: "held" as const, reason: "snapshot_limit" as const };
|
|
98
|
+
if (claims.some((c) => c.ownerKey !== args.ownerKey))
|
|
99
|
+
throw new Error("Payout history owner does not match.");
|
|
100
|
+
return { status: "ready" as const, transfers, claims };
|
|
101
|
+
},
|
|
102
|
+
});
|