@fantastic.dev/better-stripe 0.17.1 → 0.19.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +10 -1
  3. package/dist/client/connect/payouts.d.ts +3 -2
  4. package/dist/client/connect/payouts.d.ts.map +1 -1
  5. package/dist/client/connect/payouts.js.map +1 -1
  6. package/dist/client/connect/transferReceipt.d.ts +48 -0
  7. package/dist/client/connect/transferReceipt.d.ts.map +1 -0
  8. package/dist/client/connect/transferReceipt.js +296 -0
  9. package/dist/client/connect/transferReceipt.js.map +1 -0
  10. package/dist/client/index.d.ts +4 -8
  11. package/dist/client/index.d.ts.map +1 -1
  12. package/dist/client/index.js +5 -0
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/client/payoutPolicy.d.ts +84 -0
  15. package/dist/client/payoutPolicy.d.ts.map +1 -0
  16. package/dist/client/payoutPolicy.js +266 -0
  17. package/dist/client/payoutPolicy.js.map +1 -0
  18. package/dist/component/_generated/component.d.ts +273 -0
  19. package/dist/component/_generated/component.d.ts.map +1 -1
  20. package/dist/component/earnings/triggers.d.ts +150 -0
  21. package/dist/component/earnings/triggers.d.ts.map +1 -1
  22. package/dist/component/payoutPolicy/helpers.d.ts +51 -0
  23. package/dist/component/payoutPolicy/helpers.d.ts.map +1 -0
  24. package/dist/component/payoutPolicy/helpers.js +68 -0
  25. package/dist/component/payoutPolicy/helpers.js.map +1 -0
  26. package/dist/component/payoutPolicy/mutations.d.ts +172 -0
  27. package/dist/component/payoutPolicy/mutations.d.ts.map +1 -0
  28. package/dist/component/payoutPolicy/mutations.js +191 -0
  29. package/dist/component/payoutPolicy/mutations.js.map +1 -0
  30. package/dist/component/payoutPolicy/queries.d.ts +97 -0
  31. package/dist/component/payoutPolicy/queries.d.ts.map +1 -0
  32. package/dist/component/payoutPolicy/queries.js +51 -0
  33. package/dist/component/payoutPolicy/queries.js.map +1 -0
  34. package/dist/component/payoutPolicy/schema.d.ts +396 -0
  35. package/dist/component/payoutPolicy/schema.d.ts.map +1 -0
  36. package/dist/component/payoutPolicy/schema.js +54 -0
  37. package/dist/component/payoutPolicy/schema.js.map +1 -0
  38. package/dist/component/schema.d.ts +131 -0
  39. package/dist/component/schema.js +3 -0
  40. package/dist/component/schema.js.map +1 -1
  41. package/docs/settlement.md +112 -0
  42. package/package.json +1 -1
  43. package/src/client/connect/payouts.ts +10 -8
  44. package/src/client/connect/transferReceipt.pagination.test.ts +209 -0
  45. package/src/client/connect/transferReceipt.test.ts +520 -0
  46. package/src/client/connect/transferReceipt.ts +422 -0
  47. package/src/client/index.ts +7 -11
  48. package/src/client/payoutPolicy.test.ts +320 -0
  49. package/src/client/payoutPolicy.ts +422 -0
  50. package/src/component/_generated/api.ts +6 -0
  51. package/src/component/_generated/component.ts +280 -0
  52. package/src/component/payout-policy.test.ts +248 -0
  53. package/src/component/payoutPolicy/helpers.ts +88 -0
  54. package/src/component/payoutPolicy/mutations.ts +225 -0
  55. package/src/component/payoutPolicy/queries.ts +56 -0
  56. package/src/component/payoutPolicy/schema.ts +67 -0
  57. package/src/component/schema.ts +3 -0
@@ -0,0 +1,54 @@
1
+ import { defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+ export const scopeFields = { accountId: v.string(), ownerKey: v.string() };
4
+ export const schedule = v.object({
5
+ interval: v.union(v.literal("daily"), v.literal("weekly"), v.literal("monthly"), v.literal("manual")),
6
+ weeklyDays: v.optional(v.array(v.string())),
7
+ monthlyDays: v.optional(v.array(v.number())),
8
+ });
9
+ export const mode = v.union(v.literal("stripe_managed"), v.literal("custom"));
10
+ export const thresholds = v.array(v.object({ currency: v.string(), amount: v.number() }));
11
+ export const prepareFields = {
12
+ ...scopeFields,
13
+ operationKey: v.string(),
14
+ expectedRevision: v.number(),
15
+ mode,
16
+ thresholds,
17
+ country: v.string(),
18
+ observedSchedule: schedule,
19
+ observedAt: v.optional(v.number()),
20
+ };
21
+ export const policyFields = {
22
+ ...scopeFields,
23
+ revision: v.number(),
24
+ mode,
25
+ thresholds,
26
+ status: v.union(v.literal("changing"), v.literal("ready")),
27
+ operationKey: v.string(),
28
+ country: v.string(),
29
+ previousSchedule: v.optional(schedule),
30
+ holdDeadline: v.optional(v.number()),
31
+ };
32
+ export const operationFields = {
33
+ ...prepareFields,
34
+ revision: v.number(),
35
+ previousSchedule: schedule,
36
+ targetSchedule: schedule,
37
+ status: v.union(v.literal("pending"), v.literal("complete"), v.literal("canceled")),
38
+ attempted: v.optional(v.boolean()),
39
+ };
40
+ export const policyDoc = v.object({
41
+ ...policyFields,
42
+ _id: v.id("payoutPolicies"),
43
+ _creationTime: v.number(),
44
+ });
45
+ export const operationDoc = v.object({
46
+ ...operationFields,
47
+ _id: v.id("payoutPolicyOperations"),
48
+ _creationTime: v.number(),
49
+ });
50
+ export const policiesTable = defineTable(policyFields)
51
+ .index("by_account", ["accountId"])
52
+ .index("by_deadline", ["holdDeadline"]);
53
+ export const operationsTable = defineTable(operationFields).index("by_operation", ["operationKey"]);
54
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/component/payoutPolicy/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;AAC3E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAClB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EACnB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EACpB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CACpB;IACD,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;CAC7C,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAC/B,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvD,CAAC;AACF,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,GAAG,WAAW;IACd,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC5B,IAAI;IACJ,UAAU;IACV,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,gBAAgB,EAAE,QAAQ;IAC1B,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACnC,CAAC;AACF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,WAAW;IACd,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,IAAI;IACJ,UAAU;IACV,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,gBAAgB,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACtC,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACrC,CAAC;AACF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,GAAG,aAAa;IAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,gBAAgB,EAAE,QAAQ;IAC1B,cAAc,EAAE,QAAQ;IACxB,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EACpB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EACrB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CACtB;IACD,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CACnC,CAAC;AACF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,YAAY;IACf,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC;IAC3B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;CAC1B,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,GAAG,eAAe;IAClB,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,wBAAwB,CAAC;IACnC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;CAC1B,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC,YAAY,CAAC;KACnD,KAAK,CAAC,YAAY,EAAE,CAAC,WAAW,CAAC,CAAC;KAClC,KAAK,CAAC,aAAa,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC,KAAK,CAC/D,cAAc,EACd,CAAC,cAAc,CAAC,CACjB,CAAC"}
@@ -1,4 +1,135 @@
1
1
  declare const _default: import("convex/server").SchemaDefinition<{
2
+ payoutPolicies: import("convex/server").TableDefinition<import("convex/values").VObject<{
3
+ accountId: string;
4
+ country: string;
5
+ holdDeadline?: number | undefined;
6
+ mode: "custom" | "stripe_managed";
7
+ operationKey: string;
8
+ ownerKey: string;
9
+ previousSchedule?: {
10
+ interval: "daily" | "manual" | "monthly" | "weekly";
11
+ monthlyDays?: number[] | undefined;
12
+ weeklyDays?: string[] | undefined;
13
+ } | undefined;
14
+ revision: number;
15
+ status: "changing" | "ready";
16
+ thresholds: {
17
+ amount: number;
18
+ currency: string;
19
+ }[];
20
+ }, {
21
+ accountId: import("convex/values").VString<string, "required">;
22
+ ownerKey: import("convex/values").VString<string, "required">;
23
+ revision: import("convex/values").VFloat64<number, "required">;
24
+ mode: import("convex/values").VUnion<"custom" | "stripe_managed", [import("convex/values").VLiteral<"stripe_managed", "required">, import("convex/values").VLiteral<"custom", "required">], "required", never>;
25
+ thresholds: import("convex/values").VArray<{
26
+ amount: number;
27
+ currency: string;
28
+ }[], import("convex/values").VObject<{
29
+ amount: number;
30
+ currency: string;
31
+ }, {
32
+ currency: import("convex/values").VString<string, "required">;
33
+ amount: import("convex/values").VFloat64<number, "required">;
34
+ }, "required", "amount" | "currency">, "required">;
35
+ status: import("convex/values").VUnion<"changing" | "ready", [import("convex/values").VLiteral<"changing", "required">, import("convex/values").VLiteral<"ready", "required">], "required", never>;
36
+ operationKey: import("convex/values").VString<string, "required">;
37
+ country: import("convex/values").VString<string, "required">;
38
+ previousSchedule: import("convex/values").VObject<{
39
+ interval: "daily" | "manual" | "monthly" | "weekly";
40
+ monthlyDays?: number[] | undefined;
41
+ weeklyDays?: string[] | undefined;
42
+ } | undefined, {
43
+ interval: import("convex/values").VUnion<"daily" | "manual" | "monthly" | "weekly", [import("convex/values").VLiteral<"daily", "required">, import("convex/values").VLiteral<"weekly", "required">, import("convex/values").VLiteral<"monthly", "required">, import("convex/values").VLiteral<"manual", "required">], "required", never>;
44
+ weeklyDays: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
45
+ monthlyDays: import("convex/values").VArray<number[] | undefined, import("convex/values").VFloat64<number, "required">, "optional">;
46
+ }, "optional", "interval" | "monthlyDays" | "weeklyDays">;
47
+ holdDeadline: import("convex/values").VFloat64<number | undefined, "optional">;
48
+ }, "required", "accountId" | "country" | "holdDeadline" | "mode" | "operationKey" | "ownerKey" | "previousSchedule" | "previousSchedule.interval" | "previousSchedule.monthlyDays" | "previousSchedule.weeklyDays" | "revision" | "status" | "thresholds">, {
49
+ by_account: ["accountId", "_creationTime"];
50
+ by_deadline: ["holdDeadline", "_creationTime"];
51
+ }, {}, {}>;
52
+ payoutPolicyOperations: import("convex/server").TableDefinition<import("convex/values").VObject<{
53
+ accountId: string;
54
+ attempted?: boolean | undefined;
55
+ country: string;
56
+ expectedRevision: number;
57
+ mode: "custom" | "stripe_managed";
58
+ observedAt?: number | undefined;
59
+ observedSchedule: {
60
+ interval: "daily" | "manual" | "monthly" | "weekly";
61
+ monthlyDays?: number[] | undefined;
62
+ weeklyDays?: string[] | undefined;
63
+ };
64
+ operationKey: string;
65
+ ownerKey: string;
66
+ previousSchedule: {
67
+ interval: "daily" | "manual" | "monthly" | "weekly";
68
+ monthlyDays?: number[] | undefined;
69
+ weeklyDays?: string[] | undefined;
70
+ };
71
+ revision: number;
72
+ status: "canceled" | "complete" | "pending";
73
+ targetSchedule: {
74
+ interval: "daily" | "manual" | "monthly" | "weekly";
75
+ monthlyDays?: number[] | undefined;
76
+ weeklyDays?: string[] | undefined;
77
+ };
78
+ thresholds: {
79
+ amount: number;
80
+ currency: string;
81
+ }[];
82
+ }, {
83
+ accountId: import("convex/values").VString<string, "required">;
84
+ ownerKey: import("convex/values").VString<string, "required">;
85
+ operationKey: import("convex/values").VString<string, "required">;
86
+ expectedRevision: import("convex/values").VFloat64<number, "required">;
87
+ mode: import("convex/values").VUnion<"custom" | "stripe_managed", [import("convex/values").VLiteral<"stripe_managed", "required">, import("convex/values").VLiteral<"custom", "required">], "required", never>;
88
+ thresholds: import("convex/values").VArray<{
89
+ amount: number;
90
+ currency: string;
91
+ }[], import("convex/values").VObject<{
92
+ amount: number;
93
+ currency: string;
94
+ }, {
95
+ currency: import("convex/values").VString<string, "required">;
96
+ amount: import("convex/values").VFloat64<number, "required">;
97
+ }, "required", "amount" | "currency">, "required">;
98
+ country: import("convex/values").VString<string, "required">;
99
+ observedSchedule: import("convex/values").VObject<{
100
+ interval: "daily" | "manual" | "monthly" | "weekly";
101
+ monthlyDays?: number[] | undefined;
102
+ weeklyDays?: string[] | undefined;
103
+ }, {
104
+ interval: import("convex/values").VUnion<"daily" | "manual" | "monthly" | "weekly", [import("convex/values").VLiteral<"daily", "required">, import("convex/values").VLiteral<"weekly", "required">, import("convex/values").VLiteral<"monthly", "required">, import("convex/values").VLiteral<"manual", "required">], "required", never>;
105
+ weeklyDays: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
106
+ monthlyDays: import("convex/values").VArray<number[] | undefined, import("convex/values").VFloat64<number, "required">, "optional">;
107
+ }, "required", "interval" | "monthlyDays" | "weeklyDays">;
108
+ observedAt: import("convex/values").VFloat64<number | undefined, "optional">;
109
+ revision: import("convex/values").VFloat64<number, "required">;
110
+ previousSchedule: import("convex/values").VObject<{
111
+ interval: "daily" | "manual" | "monthly" | "weekly";
112
+ monthlyDays?: number[] | undefined;
113
+ weeklyDays?: string[] | undefined;
114
+ }, {
115
+ interval: import("convex/values").VUnion<"daily" | "manual" | "monthly" | "weekly", [import("convex/values").VLiteral<"daily", "required">, import("convex/values").VLiteral<"weekly", "required">, import("convex/values").VLiteral<"monthly", "required">, import("convex/values").VLiteral<"manual", "required">], "required", never>;
116
+ weeklyDays: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
117
+ monthlyDays: import("convex/values").VArray<number[] | undefined, import("convex/values").VFloat64<number, "required">, "optional">;
118
+ }, "required", "interval" | "monthlyDays" | "weeklyDays">;
119
+ targetSchedule: import("convex/values").VObject<{
120
+ interval: "daily" | "manual" | "monthly" | "weekly";
121
+ monthlyDays?: number[] | undefined;
122
+ weeklyDays?: string[] | undefined;
123
+ }, {
124
+ interval: import("convex/values").VUnion<"daily" | "manual" | "monthly" | "weekly", [import("convex/values").VLiteral<"daily", "required">, import("convex/values").VLiteral<"weekly", "required">, import("convex/values").VLiteral<"monthly", "required">, import("convex/values").VLiteral<"manual", "required">], "required", never>;
125
+ weeklyDays: import("convex/values").VArray<string[] | undefined, import("convex/values").VString<string, "required">, "optional">;
126
+ monthlyDays: import("convex/values").VArray<number[] | undefined, import("convex/values").VFloat64<number, "required">, "optional">;
127
+ }, "required", "interval" | "monthlyDays" | "weeklyDays">;
128
+ status: import("convex/values").VUnion<"canceled" | "complete" | "pending", [import("convex/values").VLiteral<"pending", "required">, import("convex/values").VLiteral<"complete", "required">, import("convex/values").VLiteral<"canceled", "required">], "required", never>;
129
+ attempted: import("convex/values").VBoolean<boolean | undefined, "optional">;
130
+ }, "required", "accountId" | "attempted" | "country" | "expectedRevision" | "mode" | "observedAt" | "observedSchedule" | "observedSchedule.interval" | "observedSchedule.monthlyDays" | "observedSchedule.weeklyDays" | "operationKey" | "ownerKey" | "previousSchedule" | "previousSchedule.interval" | "previousSchedule.monthlyDays" | "previousSchedule.weeklyDays" | "revision" | "status" | "targetSchedule" | "targetSchedule.interval" | "targetSchedule.monthlyDays" | "targetSchedule.weeklyDays" | "thresholds">, {
131
+ by_operation: ["operationKey", "_creationTime"];
132
+ }, {}, {}>;
2
133
  currencyTotals: import("convex/server").TableDefinition<import("convex/values").VObject<{
3
134
  accountId: string;
4
135
  count: number;
@@ -1,3 +1,4 @@
1
+ import { policiesTable, operationsTable } from "./payoutPolicy/schema.js";
1
2
  import { currencyTotalsTable } from "./earnings/schema.js";
2
3
  import { claimsTable, balancesTable, groupsTable, reservationsTable, } from "./costRecovery/schema";
3
4
  import { allocationsTable, commissionTermsTable, commissionDecisionsTable, } from "./settlement/schema";
@@ -8,6 +9,8 @@ import { accountsTable } from "./core/schema";
8
9
  import { pricesTable, productsTable } from "./products/schema";
9
10
  import { webhookEventsTable } from "./webhooks/schema";
10
11
  export default defineSchema({
12
+ payoutPolicies: policiesTable,
13
+ payoutPolicyOperations: operationsTable,
11
14
  currencyTotals: currencyTotalsTable,
12
15
  recoveryClaims: claimsTable,
13
16
  recoveryBalances: balancesTable,
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/component/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,sBAAsB,EACtB,YAAY,EACZ,wBAAwB,EACxB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,eAAe,YAAY,CAAC;IAC1B,cAAc,EAAE,mBAAmB;IACnC,cAAc,EAAE,WAAW;IAC3B,gBAAgB,EAAE,aAAa;IAC/B,cAAc,EAAE,WAAW;IAC3B,oBAAoB,EAAE,iBAAiB;IACvC,qBAAqB,EAAE,gBAAgB;IACvC,eAAe,EAAE,oBAAoB;IACrC,mBAAmB,EAAE,wBAAwB;IAC7C,OAAO;IACP,QAAQ,EAAE,aAAa;IAEvB,WAAW;IACX,QAAQ,EAAE,aAAa;IACvB,MAAM,EAAE,WAAW;IAEnB,UAAU;IACV,aAAa,EAAE,kBAAkB;IACjC,gBAAgB,EAAE,qBAAqB;IACvC,QAAQ,EAAE,aAAa;IAEvB,UAAU;IACV,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,YAAY;IACrB,iBAAiB,EAAE,sBAAsB;IACzC,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,cAAc;IACzB,mBAAmB,EAAE,wBAAwB;IAE7C,WAAW;IACX,aAAa,EAAE,kBAAkB;CAClC,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/component/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,sBAAsB,EACtB,YAAY,EACZ,wBAAwB,EACxB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,eAAe,YAAY,CAAC;IAC1B,cAAc,EAAE,aAAa;IAC7B,sBAAsB,EAAE,eAAe;IACvC,cAAc,EAAE,mBAAmB;IACnC,cAAc,EAAE,WAAW;IAC3B,gBAAgB,EAAE,aAAa;IAC/B,cAAc,EAAE,WAAW;IAC3B,oBAAoB,EAAE,iBAAiB;IACvC,qBAAqB,EAAE,gBAAgB;IACvC,eAAe,EAAE,oBAAoB;IACrC,mBAAmB,EAAE,wBAAwB;IAC7C,OAAO;IACP,QAAQ,EAAE,aAAa;IAEvB,WAAW;IACX,QAAQ,EAAE,aAAa;IACvB,MAAM,EAAE,WAAW;IAEnB,UAAU;IACV,aAAa,EAAE,kBAAkB;IACjC,gBAAgB,EAAE,qBAAqB;IACvC,QAAQ,EAAE,aAAa;IAEvB,UAAU;IACV,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,YAAY;IACrB,iBAAiB,EAAE,sBAAsB;IACzC,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,cAAc;IACzB,mBAAmB,EAAE,wBAAwB;IAE7C,WAAW;IACX,aAAa,EAAE,kBAAkB;CAClC,CAAC,CAAC"}
@@ -156,3 +156,115 @@ It requires a selected retention boundary and migration policy. Applications mus
156
156
  not describe a displayed threshold as enforced while Stripe controls automatic
157
157
  payouts. A numeric below/exact/above provider-floor demonstration is unavailable
158
158
  without a supported provider floor read; surface this capability limitation.
159
+
160
+ ## Recipient-held payout policy controls
161
+
162
+ `stripe.payoutPolicy` stages versioned, account-wide schedule changes for a
163
+ user-level recipient. The host authenticates `ownerKey`, authorizes the policy,
164
+ and records explicit consent. Thresholds use integer minor units per currency;
165
+ they combine creator and affiliate funds rather than creating category gates.
166
+ The untouched default remains Stripe-managed automatic payouts.
167
+
168
+ Call `prepare(ctx, { accountId, ownerKey, operationKey, expectedRevision, mode:
169
+ "custom", thresholds: [{ currency: "usd", amount: 1000 }], consent: true })` in
170
+ an authorized action. Revision `-1` means no existing policy. Persisted preparation
171
+ captures the original payout schedule and country holding deadline before any
172
+ provider update. If preparation returns `prepared`, call `apply` with the same
173
+ account, owner and operation key. Replays preserve the immutable request;
174
+ competing operations and stale revisions are rejected. A provider timeout or
175
+ failed confirmation stays pending for reconciliation, rather than being treated
176
+ as a completed schedule change.
177
+
178
+ These APIs currently require a verified `dashboard: none` account and enabled
179
+ payouts for custom activation. Accounts with Dashboard withdrawal access return
180
+ an explicit control limitation: restricting schedule edits alone cannot prevent
181
+ manual withdrawals. The API never changes dashboard access or loss liability to
182
+ obtain control. Stripe also validates update permissions. Before each subsequent
183
+ payout attempt, `verify` must confirm live manual settings, unchanged country,
184
+ and an unexpired policy; stored `ready` state alone is insufficient.
185
+
186
+ Initial activation rejects existing nonzero available or pending balances,
187
+ in-flight payouts, and existing manual schedules with `migration_required`.
188
+ The preflight is repeated before an initial schedule update; a delayed activation
189
+ with newly arrived funds is canceled before changing Stripe. This is a bounded
190
+ preflight, not an ownership inference or automatic adoption.
191
+ Already-mixed funds need a reconciled source ledger and explicit adoption before
192
+ custom release can be enabled. Zero-balance activation does not classify later
193
+ unrelated deposits as earnings. Payout claims must use that source ledger and a
194
+ fresh provider balance when the separate release boundary is integrated.
195
+
196
+ The host must schedule a recurring server job that pages `due(ctx,
197
+ { cursor: null, numItems: 100 })`, follows its cursor, and calls `expire` for each
198
+ returned account/owner. `expire` reconciles pending operations and restores the
199
+ original automatic schedule when the holding deadline is reached. Unattempted
200
+ expired enables are canceled without changing Stripe. Failed/unknown restores
201
+ remain visible and retryable; alert operators if they cannot complete. Editing
202
+ thresholds never extends the original deadline. Current limits are 10 days for
203
+ Thailand, a conservative 730 days for the US, and 90 days elsewhere; verify
204
+ current provider rules before enabling the feature in production.
205
+
206
+ For opt-out, prepare a new operation with the latest revision,
207
+ `mode: "stripe_managed"`, and `thresholds: []`, then apply it. Restoration remains
208
+ available after Dashboard control is lost, subject to Stripe permissions. It
209
+ restores only the captured schedule; other balance settings remain untouched.
210
+
211
+ This is the durable policy/control boundary. Automatic combined payout execution,
212
+ adoption of existing balances, and its UI integration are separate work and are
213
+ not supplied by these schedule APIs. Do not expose a custom minimum as fully
214
+ enforced until those boundaries and the recurring expiry job are installed.
215
+ `verify.providerMinimum` is `null`: the Balance Settings API does not expose a
216
+ numeric payout-floor field, and retained balance reserves are not payout triggers.
217
+
218
+ Sources: [payout schedules](https://docs.stripe.com/connect/manage-payout-schedule),
219
+ [manual payout holding limits](https://docs.stripe.com/connect/manual-payouts), and
220
+ [Dashboard control limits](https://docs.stripe.com/connect/platform-controls-for-stripe-dashboard-accounts#control-payout-timing).
221
+
222
+ ## Verified recipient funding receipts
223
+
224
+ Use `stripe.payoutPolicy.transferReceipt(ctx, { accountId, stripeTransferId })`
225
+ in an authorized action to read a transfer's actual recipient funding. The
226
+ reader binds the component ledger to the provider transfer, destination payment,
227
+ original balance transaction and each transfer reversal's recipient refund.
228
+ It verifies account, amount, currency and source identities, and rechecks the
229
+ transfer and ledger before publishing a result. It performs no financial writes.
230
+
231
+ A `ready` result contains exact signed recipient settlement transactions and
232
+ `totalNet`, `matureNet`, and `pendingCredits`. `sourceCurrency` describes the
233
+ original transfer; `settlementCurrency` describes the actual recipient balance.
234
+ For supported FX receipts, amounts come directly from Stripe's settlement
235
+ transactions; the reader does not calculate exchange-rate conversions. Mixed
236
+ settlement currencies or missing conversion evidence require explicit allocation.
237
+ Transaction `created` and `availableOn` values are Unix seconds; the snapshot's
238
+ `observedAt` is milliseconds.
239
+
240
+ Positive credits qualify as mature only when their provider status is available
241
+ and their availability date has passed. Pending debits first cancel the matching
242
+ pending original credit; uncovered pending debits reduce mature credit. Other
243
+ positive pending adjustments never qualify early. Signed mature debt is preserved.
244
+ For example, in one settlement currency's minor units:
245
+
246
+ | Original credit | Reversal/debit | Total net | Mature net | Pending credits |
247
+ | --------------- | -------------- | --------: | ---------: | --------------: |
248
+ | 1000 pending | 250 pending | 750 | 0 | 750 |
249
+ | 1000 available | 250 pending | 750 | 750 | 0 |
250
+ | 1000 pending | 1000 pending | 0 | 0 | 0 |
251
+ | 1000 pending | 250 available | 750 | -250 | 1000 |
252
+
253
+ This is **received credit, not still-unpaid entitlement**. Previous bank payouts,
254
+ source allocations, recovery liabilities and outstanding reservations must be
255
+ reconciled before any release. Do not pay `matureNet` directly or infer ownership
256
+ from unrelated account-wide available funds. Stripe's balance-transaction
257
+ `payout` filter maps automatic payouts only; application-created manual payouts
258
+ need a maintained allocation ledger.
259
+
260
+ The default budget is 20 provider requests; `maxRequests` accepts 1–100. Exhausted
261
+ or incomplete history returns `not_ready` without partial financial totals.
262
+ Reserved reversal claims, changing provider/ledger snapshots and unavailable
263
+ receipts also fail closed. Identity mismatches and invalid financial arithmetic
264
+ throw. Larger histories need continued reconciliation rather than treating the
265
+ missing amount as zero or silently truncating it. The original transfer ledger
266
+ remains intact for retries.
267
+
268
+ Sources: [transfer provenance](https://docs.stripe.com/api/transfers/object),
269
+ [receipt maturity](https://docs.stripe.com/api/balance_transactions/object), and
270
+ [history pagination and payout-filter limits](https://docs.stripe.com/api/balance_transactions/list).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fantastic.dev/better-stripe",
3
- "version": "0.17.1",
3
+ "version": "0.19.0",
4
4
  "description": "A Convex component for Stripe V2 Accounts API integration.",
5
5
  "keywords": [
6
6
  "stripe",
@@ -53,17 +53,19 @@ export async function getAccountBalance(
53
53
  return [...byCurrency.values()];
54
54
  }
55
55
 
56
+ export type CreatePayoutOptions = {
57
+ stripeAccountId: string;
58
+ amount: number;
59
+ currency?: string;
60
+ metadata?: Record<string, string>;
61
+ /** Stripe idempotency key for retry-safe payout creation. */
62
+ idempotencyKey?: string;
63
+ };
64
+
56
65
  export async function createPayout(
57
66
  stripe: Stripe,
58
67
  _ctx: RunCtx,
59
- opts: {
60
- stripeAccountId: string;
61
- amount: number;
62
- currency?: string;
63
- metadata?: Record<string, string>;
64
- /** Stripe idempotency key for retry-safe payout creation. */
65
- idempotencyKey?: string;
66
- },
68
+ opts: CreatePayoutOptions,
67
69
  ) {
68
70
  const payout = await stripe.payouts.create(
69
71
  {
@@ -0,0 +1,209 @@
1
+ import type Stripe from "stripe";
2
+ import { expect, it, vi } from "vitest";
3
+ import type { Component, RunCtx } from "../helpers.js";
4
+ import { getTransferFundingReceipt } from "./transferReceipt.js";
5
+ const symbol = Symbol.for("toReferencePath");
6
+ const component = {
7
+ connect: {
8
+ queries: {
9
+ getTransferByStripeId: {
10
+ [symbol]: "betterStripe/connect/queries/getTransferByStripeId",
11
+ },
12
+ },
13
+ },
14
+ } as unknown as Component;
15
+ const ledger = {
16
+ stripeTransferId: "tr_1",
17
+ destinationAccountId: "acct_owner",
18
+ amount: 1000,
19
+ currency: "usd",
20
+ status: "paid",
21
+ };
22
+ const tx = (id: string, amount: number, source = "py_1") => ({
23
+ id,
24
+ source,
25
+ amount,
26
+ net: amount,
27
+ fee: 0,
28
+ currency: "usd",
29
+ created: 1,
30
+ available_on: 2,
31
+ status: "available",
32
+ type: amount > 0 ? "payment" : "adjustment",
33
+ exchange_rate: null,
34
+ });
35
+ function setup() {
36
+ const ctx = {
37
+ runQuery: vi.fn().mockResolvedValue(ledger),
38
+ } as unknown as RunCtx;
39
+ const provider = {
40
+ transfers: {
41
+ retrieve: vi.fn().mockResolvedValue({
42
+ id: "tr_1",
43
+ destination: "acct_owner",
44
+ destination_payment: "py_1",
45
+ source_transaction: null,
46
+ amount: 1000,
47
+ amount_reversed: 0,
48
+ currency: "usd",
49
+ }),
50
+ listReversals: vi.fn().mockResolvedValue({ data: [], has_more: false }),
51
+ },
52
+ charges: {
53
+ retrieve: vi.fn().mockResolvedValue({
54
+ id: "py_1",
55
+ source_transfer: "tr_1",
56
+ amount: 1000,
57
+ currency: "usd",
58
+ paid: true,
59
+ status: "succeeded",
60
+ balance_transaction: "txn_credit",
61
+ }),
62
+ },
63
+ refunds: { retrieve: vi.fn() },
64
+ balanceTransactions: {
65
+ list: vi
66
+ .fn()
67
+ .mockResolvedValue({ data: [tx("txn_credit", 1000)], has_more: false }),
68
+ },
69
+ };
70
+ return {
71
+ ctx,
72
+ provider,
73
+ run: (extra: object = {}) =>
74
+ getTransferFundingReceipt(provider as unknown as Stripe, component, ctx, {
75
+ accountId: "acct_owner",
76
+ stripeTransferId: "tr_1",
77
+ ...extra,
78
+ }),
79
+ };
80
+ }
81
+ it("reads all 205 source receipts instead of truncating at the first provider page", async () => {
82
+ const { provider, run } = setup();
83
+ const rows = [
84
+ tx("txn_credit", 1000),
85
+ ...Array.from({ length: 204 }, (_, i) => tx(`txn_fee_${i}`, -1)),
86
+ ];
87
+ provider.balanceTransactions.list.mockImplementation(
88
+ async (args: { starting_after?: string }) => {
89
+ const start = args.starting_after
90
+ ? rows.findIndex((t) => t.id === args.starting_after) + 1
91
+ : 0;
92
+ return {
93
+ data: rows.slice(start, start + 100),
94
+ has_more: start + 100 < rows.length,
95
+ };
96
+ },
97
+ );
98
+ const result = await run();
99
+ expect(result).toMatchObject({
100
+ status: "ready",
101
+ totalNet: 796,
102
+ matureNet: 796,
103
+ pendingCredits: 0,
104
+ });
105
+ if (result.status === "ready") expect(result.transactions).toHaveLength(205);
106
+ expect(provider.balanceTransactions.list).toHaveBeenCalledTimes(3);
107
+ const limited = await run({ maxRequests: 6 });
108
+ expect(limited).toMatchObject({
109
+ status: "not_ready",
110
+ reason: "history_incomplete",
111
+ });
112
+ expect(limited).not.toHaveProperty("totalNet");
113
+ });
114
+ it("does not publish a source snapshot if its authoritative ledger changed during the read", async () => {
115
+ const { ctx, run } = setup();
116
+ (ctx.runQuery as ReturnType<typeof vi.fn>)
117
+ .mockReset()
118
+ .mockResolvedValueOnce(ledger)
119
+ .mockResolvedValueOnce({ ...ledger, reversedAmount: 1 });
120
+ expect(await run()).toMatchObject({
121
+ status: "not_ready",
122
+ reason: "ledger_changed",
123
+ });
124
+ });
125
+ it("propagates genuine provider failures and detects aggregate overflow", async () => {
126
+ const { provider, run } = setup();
127
+ provider.charges.retrieve.mockRejectedValueOnce(
128
+ new Error("provider unavailable"),
129
+ );
130
+ await expect(run()).rejects.toThrow("provider unavailable");
131
+ provider.balanceTransactions.list.mockResolvedValue({
132
+ data: [
133
+ tx("txn_credit", 1000),
134
+ { ...tx("txn_extra", Number.MAX_SAFE_INTEGER), type: "adjustment" },
135
+ ],
136
+ has_more: false,
137
+ });
138
+ await expect(run()).rejects.toThrow("safe integers");
139
+ });
140
+ it("never uses a pending fee credit to qualify a fully returned mature transfer", async () => {
141
+ const { ctx, provider, run } = setup();
142
+ (ctx.runQuery as ReturnType<typeof vi.fn>).mockResolvedValue({
143
+ ...ledger,
144
+ reversedAmount: 1000,
145
+ });
146
+ provider.transfers.retrieve.mockResolvedValue({
147
+ id: "tr_1",
148
+ destination: "acct_owner",
149
+ destination_payment: "py_1",
150
+ source_transaction: null,
151
+ amount: 1000,
152
+ amount_reversed: 1000,
153
+ currency: "usd",
154
+ });
155
+ provider.transfers.listReversals.mockResolvedValue({
156
+ data: [
157
+ {
158
+ id: "trr_1",
159
+ transfer: "tr_1",
160
+ amount: 1000,
161
+ currency: "usd",
162
+ destination_payment_refund: "pyr_1",
163
+ },
164
+ ],
165
+ has_more: false,
166
+ } as never);
167
+ provider.refunds.retrieve.mockResolvedValue({
168
+ id: "pyr_1",
169
+ charge: "py_1",
170
+ amount: 1000,
171
+ currency: "usd",
172
+ status: "succeeded",
173
+ balance_transaction: "txn_debit",
174
+ });
175
+ provider.balanceTransactions.list.mockImplementation(
176
+ async (args: { source: string }) => ({
177
+ data:
178
+ args.source === "py_1"
179
+ ? [
180
+ tx("txn_credit", 1000),
181
+ { ...tx("txn_fee_credit", 100), status: "pending" },
182
+ ]
183
+ : [{ ...tx("txn_debit", -1000, "pyr_1"), status: "pending" }],
184
+ has_more: false,
185
+ }),
186
+ );
187
+ expect(await run()).toMatchObject({
188
+ status: "ready",
189
+ totalNet: 100,
190
+ matureNet: 0,
191
+ pendingCredits: 100,
192
+ });
193
+ });
194
+ it("preserves an already available debit while its original credit is pending", async () => {
195
+ const { provider, run } = setup();
196
+ provider.balanceTransactions.list.mockResolvedValue({
197
+ data: [
198
+ { ...tx("txn_credit", 1000), status: "pending" },
199
+ tx("txn_fee", -200),
200
+ ],
201
+ has_more: false,
202
+ });
203
+ expect(await run()).toMatchObject({
204
+ status: "ready",
205
+ totalNet: 800,
206
+ matureNet: -200,
207
+ pendingCredits: 1000,
208
+ });
209
+ });