@fantastic.dev/better-stripe 0.10.2 → 0.12.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 (140) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +220 -4
  3. package/dist/client/billing/balanceFunding.d.ts +36 -0
  4. package/dist/client/billing/balanceFunding.d.ts.map +1 -0
  5. package/dist/client/billing/balanceFunding.js +107 -0
  6. package/dist/client/billing/balanceFunding.js.map +1 -0
  7. package/dist/client/billing/invoicePreview.d.ts +28 -0
  8. package/dist/client/billing/invoicePreview.d.ts.map +1 -0
  9. package/dist/client/billing/invoicePreview.js +56 -0
  10. package/dist/client/billing/invoicePreview.js.map +1 -0
  11. package/dist/client/charge-currency-format.d.ts +3 -0
  12. package/dist/client/charge-currency-format.d.ts.map +1 -0
  13. package/dist/client/charge-currency-format.js +22 -0
  14. package/dist/client/charge-currency-format.js.map +1 -0
  15. package/dist/client/charge-currency.d.ts +14 -0
  16. package/dist/client/charge-currency.d.ts.map +1 -0
  17. package/dist/client/charge-currency.js +85 -0
  18. package/dist/client/charge-currency.js.map +1 -0
  19. package/dist/client/costRecovery.d.ts +115 -0
  20. package/dist/client/costRecovery.d.ts.map +1 -0
  21. package/dist/client/costRecovery.js +50 -0
  22. package/dist/client/costRecovery.js.map +1 -0
  23. package/dist/client/index.d.ts +28 -64
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +25 -0
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/products/prices.d.ts +4 -2
  28. package/dist/client/products/prices.d.ts.map +1 -1
  29. package/dist/client/products/prices.js +5 -4
  30. package/dist/client/products/prices.js.map +1 -1
  31. package/dist/client/products/products.d.ts +4 -2
  32. package/dist/client/products/products.d.ts.map +1 -1
  33. package/dist/client/products/products.js +7 -5
  34. package/dist/client/products/products.js.map +1 -1
  35. package/dist/client/settlement/processingCosts.d.ts +56 -0
  36. package/dist/client/settlement/processingCosts.d.ts.map +1 -0
  37. package/dist/client/settlement/processingCosts.js +173 -0
  38. package/dist/client/settlement/processingCosts.js.map +1 -0
  39. package/dist/client/utils/webhookEndpoints.d.ts +1 -1
  40. package/dist/client/utils/webhookEndpoints.d.ts.map +1 -1
  41. package/dist/client/utils/webhookEndpoints.js +1 -0
  42. package/dist/client/utils/webhookEndpoints.js.map +1 -1
  43. package/dist/client/webhooks/processors.d.ts.map +1 -1
  44. package/dist/client/webhooks/processors.js +1 -0
  45. package/dist/client/webhooks/processors.js.map +1 -1
  46. package/dist/component/_generated/component.d.ts +244 -0
  47. package/dist/component/_generated/component.d.ts.map +1 -1
  48. package/dist/component/costRecovery/claims.d.ts +68 -0
  49. package/dist/component/costRecovery/claims.d.ts.map +1 -0
  50. package/dist/component/costRecovery/claims.js +85 -0
  51. package/dist/component/costRecovery/claims.js.map +1 -0
  52. package/dist/component/costRecovery/completion.d.ts +33 -0
  53. package/dist/component/costRecovery/completion.d.ts.map +1 -0
  54. package/dist/component/costRecovery/completion.js +69 -0
  55. package/dist/component/costRecovery/completion.js.map +1 -0
  56. package/dist/component/costRecovery/helpers.d.ts +62 -0
  57. package/dist/component/costRecovery/helpers.d.ts.map +1 -0
  58. package/dist/component/costRecovery/helpers.js +71 -0
  59. package/dist/component/costRecovery/helpers.js.map +1 -0
  60. package/dist/component/costRecovery/mutations.d.ts +138 -0
  61. package/dist/component/costRecovery/mutations.d.ts.map +1 -0
  62. package/dist/component/costRecovery/mutations.js +36 -0
  63. package/dist/component/costRecovery/mutations.js.map +1 -0
  64. package/dist/component/costRecovery/queries.d.ts +102 -0
  65. package/dist/component/costRecovery/queries.d.ts.map +1 -0
  66. package/dist/component/costRecovery/queries.js +73 -0
  67. package/dist/component/costRecovery/queries.js.map +1 -0
  68. package/dist/component/costRecovery/reservations.d.ts +83 -0
  69. package/dist/component/costRecovery/reservations.d.ts.map +1 -0
  70. package/dist/component/costRecovery/reservations.js +134 -0
  71. package/dist/component/costRecovery/reservations.js.map +1 -0
  72. package/dist/component/costRecovery/schema.d.ts +340 -0
  73. package/dist/component/costRecovery/schema.d.ts.map +1 -0
  74. package/dist/component/costRecovery/schema.js +109 -0
  75. package/dist/component/costRecovery/schema.js.map +1 -0
  76. package/dist/component/schema.d.ts +119 -0
  77. package/dist/component/schema.js +5 -0
  78. package/dist/component/schema.js.map +1 -1
  79. package/dist/react/components/PriceCard.d.ts +3 -1
  80. package/dist/react/components/PriceCard.d.ts.map +1 -1
  81. package/dist/react/components/PriceCard.js +2 -2
  82. package/dist/react/components/PriceCard.js.map +1 -1
  83. package/dist/react/index.d.ts +1 -0
  84. package/dist/react/index.d.ts.map +1 -1
  85. package/dist/react/index.js +1 -0
  86. package/dist/react/index.js.map +1 -1
  87. package/dist/react/lib/price-helpers.d.ts +4 -6
  88. package/dist/react/lib/price-helpers.d.ts.map +1 -1
  89. package/dist/react/lib/price-helpers.js +16 -9
  90. package/dist/react/lib/price-helpers.js.map +1 -1
  91. package/dist/react/workflows/PlanPricingEditor.d.ts +4 -0
  92. package/dist/react/workflows/PlanPricingEditor.d.ts.map +1 -1
  93. package/dist/react/workflows/PlanPricingEditor.js +12 -9
  94. package/dist/react/workflows/PlanPricingEditor.js.map +1 -1
  95. package/docs/react-workflows.md +91 -0
  96. package/package.json +2 -2
  97. package/src/client/billing/balanceFunding.test.ts +257 -0
  98. package/src/client/billing/balanceFunding.ts +183 -0
  99. package/src/client/billing/balanceFundingTransport.test.ts +89 -0
  100. package/src/client/billing/invoicePreview.test.ts +123 -0
  101. package/src/client/billing/invoicePreview.ts +94 -0
  102. package/src/client/billing/invoicePreviewTransport.test.ts +70 -0
  103. package/src/client/charge-currency-format.ts +29 -0
  104. package/src/client/charge-currency.test.ts +113 -0
  105. package/src/client/charge-currency.ts +119 -0
  106. package/src/client/costRecovery.test.ts +76 -0
  107. package/src/client/costRecovery.ts +149 -0
  108. package/src/client/index.ts +76 -73
  109. package/src/client/products/catalog-idempotency.test.ts +116 -0
  110. package/src/client/products/prices.ts +18 -11
  111. package/src/client/products/products.ts +17 -11
  112. package/src/client/settlement/processingCosts.test.ts +349 -0
  113. package/src/client/settlement/processingCosts.ts +315 -0
  114. package/src/client/settlement/processingCostsTransport.test.ts +135 -0
  115. package/src/client/utils/webhookEndpoints.test.ts +5 -0
  116. package/src/client/utils/webhookEndpoints.ts +1 -0
  117. package/src/client/webhooks/hooks.test.ts +9 -0
  118. package/src/client/webhooks/payout-canceled.integration.test.ts +192 -0
  119. package/src/client/webhooks/processors.ts +1 -0
  120. package/src/component/_generated/api.ts +12 -0
  121. package/src/component/_generated/component.ts +308 -0
  122. package/src/component/cost-recovery.test.ts +344 -0
  123. package/src/component/costRecovery/claims.ts +109 -0
  124. package/src/component/costRecovery/completion.ts +92 -0
  125. package/src/component/costRecovery/helpers.ts +83 -0
  126. package/src/component/costRecovery/mutations.ts +43 -0
  127. package/src/component/costRecovery/queries.ts +88 -0
  128. package/src/component/costRecovery/reservations.ts +164 -0
  129. package/src/component/costRecovery/schema.ts +127 -0
  130. package/src/component/schema.ts +10 -0
  131. package/src/react/components/PayoutSchedule.render.test.tsx +15 -0
  132. package/src/react/components/PriceCard.render.test.tsx +37 -0
  133. package/src/react/components/PriceCard.tsx +9 -1
  134. package/src/react/index.test.ts +5 -0
  135. package/src/react/index.ts +7 -0
  136. package/src/react/lib/price-helpers.test.ts +15 -2
  137. package/src/react/lib/price-helpers.ts +25 -8
  138. package/src/react/workflows/Billing.test.tsx +1 -1
  139. package/src/react/workflows/PlanPricingEditor.currency.test.tsx +164 -0
  140. package/src/react/workflows/PlanPricingEditor.tsx +26 -10
@@ -0,0 +1,71 @@
1
+ export function integer(value) {
2
+ if (!Number.isSafeInteger(value))
3
+ throw new Error("Recovery amounts must be safe integers.");
4
+ return value;
5
+ }
6
+ export function validateScope(scope) {
7
+ if (!scope.accountId || !scope.scopeKey || !/^[a-z]{3}$/.test(scope.currency))
8
+ throw new Error("Recovery account, scope and currency are required.");
9
+ }
10
+ export const sign = (value) => value > 0
11
+ ? "positive"
12
+ : value < 0
13
+ ? "negative"
14
+ : "zero";
15
+ export const scopeOnly = (scope) => ({
16
+ accountId: scope.accountId,
17
+ scopeKey: scope.scopeKey,
18
+ currency: scope.currency,
19
+ });
20
+ export const zeroTotals = {
21
+ amountDue: 0,
22
+ collectedAmount: 0,
23
+ reservedAmount: 0,
24
+ outstanding: 0,
25
+ };
26
+ export function readBalance(ctx, scope) {
27
+ return ctx.db
28
+ .query("recoveryBalances")
29
+ .withIndex("by_scope", (q) => q
30
+ .eq("accountId", scope.accountId)
31
+ .eq("scopeKey", scope.scopeKey)
32
+ .eq("currency", scope.currency))
33
+ .unique();
34
+ }
35
+ export async function balance(ctx, scope) {
36
+ const found = await readBalance(ctx, scope);
37
+ if (found)
38
+ return found;
39
+ const id = await ctx.db.insert("recoveryBalances", {
40
+ ...scopeOnly(scope),
41
+ ...zeroTotals,
42
+ });
43
+ return (await ctx.db.get("recoveryBalances", id));
44
+ }
45
+ export async function adjustTotals(ctx, scope, delta) {
46
+ const stored = await balance(ctx, scope);
47
+ const next = { ...zeroTotals };
48
+ for (const key of Object.keys(zeroTotals))
49
+ next[key] = integer(stored[key] + (delta[key] ?? 0));
50
+ if (next.outstanding !==
51
+ integer(next.amountDue - next.collectedAmount - next.reservedAmount))
52
+ throw new Error("Recovery totals do not reconcile.");
53
+ await ctx.db.patch("recoveryBalances", stored._id, next);
54
+ }
55
+ export function groupTarget(group, outstanding) {
56
+ return Math.max(-group.maxCredit, Math.min(group.maxDebit, integer(outstanding + group.amount)));
57
+ }
58
+ export async function getGroup(ctx, key) {
59
+ const group = await ctx.db
60
+ .query("recoveryGroups")
61
+ .withIndex("by_key", (q) => q.eq("groupKey", key))
62
+ .unique();
63
+ if (!group)
64
+ throw new Error("Recovery group is missing.");
65
+ return group;
66
+ }
67
+ export function batchLimit(limit, max = 100) {
68
+ if (!Number.isInteger(limit) || limit < 1 || limit > max)
69
+ throw new Error(`Recovery batch limit must be between 1 and ${max}.`);
70
+ }
71
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/component/costRecovery/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ export declare const record: import("convex/server").RegisteredMutation<"public", {
2
+ accountId: string;
3
+ amountDue: number;
4
+ claimKey: string;
5
+ currency: string;
6
+ revision: number;
7
+ scopeKey: string;
8
+ sourceChargeId: string;
9
+ sourceGross: number;
10
+ sourceProcessingFee: number;
11
+ }, Promise<{
12
+ _creationTime: number;
13
+ _id: import("convex/values").GenericId<"recoveryClaims">;
14
+ accountId: string;
15
+ amountDue: number;
16
+ claimKey: string;
17
+ collectedAmount: number;
18
+ currency: string;
19
+ outstanding: number;
20
+ reservedAmount: number;
21
+ revision: number;
22
+ scopeKey: string;
23
+ sign: "negative" | "positive" | "zero";
24
+ sourceChargeId: string;
25
+ sourceGross: number;
26
+ sourceProcessingFee: number;
27
+ }>>;
28
+ export declare const rollback: import("convex/server").RegisteredMutation<"public", {
29
+ groupKey: string;
30
+ returnedPrincipal: number;
31
+ revision: number;
32
+ }, Promise<{
33
+ _creationTime: number;
34
+ _id: import("convex/values").GenericId<"recoveryClaims">;
35
+ accountId: string;
36
+ amountDue: number;
37
+ claimKey: string;
38
+ collectedAmount: number;
39
+ currency: string;
40
+ outstanding: number;
41
+ reservedAmount: number;
42
+ revision: number;
43
+ scopeKey: string;
44
+ sign: "negative" | "positive" | "zero";
45
+ sourceChargeId: string;
46
+ sourceGross: number;
47
+ sourceProcessingFee: number;
48
+ }>>;
49
+ export declare const reserve: import("convex/server").RegisteredMutation<"public", {
50
+ accountId: string;
51
+ currency: string;
52
+ groupKey: string;
53
+ limit?: number | undefined;
54
+ maxCredit: number;
55
+ maxDebit: number;
56
+ operationKey: string;
57
+ scopeKey: string;
58
+ sourceGross?: number | undefined;
59
+ sourceId: string;
60
+ sourceKind: "invoice" | "settlement";
61
+ sourceProcessingFee?: number | undefined;
62
+ }, Promise<{
63
+ reservation: {
64
+ _creationTime: number;
65
+ _id: import("convex/values").GenericId<"recoveryReservations">;
66
+ allocations: {
67
+ amount: number;
68
+ claimId: import("convex/values").GenericId<"recoveryClaims">;
69
+ claimKey: string;
70
+ }[];
71
+ amount: number;
72
+ groupKey: string;
73
+ limit: number;
74
+ operationKey: string;
75
+ status: "collected" | "empty" | "released" | "reserved";
76
+ };
77
+ group: {
78
+ _creationTime: number;
79
+ _id: import("convex/values").GenericId<"recoveryGroups">;
80
+ accountId: string;
81
+ amount: number;
82
+ currency: string;
83
+ groupKey: string;
84
+ maxCredit: number;
85
+ maxDebit: number;
86
+ pendingOperations: number;
87
+ scopeKey: string;
88
+ sourceGross?: number | undefined;
89
+ sourceId: string;
90
+ sourceKind: "invoice" | "settlement";
91
+ sourceProcessingFee?: number | undefined;
92
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
93
+ };
94
+ }>>;
95
+ export declare const seal: import("convex/server").RegisteredMutation<"public", {
96
+ groupKey: string;
97
+ }, Promise<{
98
+ _creationTime: number;
99
+ _id: import("convex/values").GenericId<"recoveryGroups">;
100
+ accountId: string;
101
+ amount: number;
102
+ currency: string;
103
+ groupKey: string;
104
+ maxCredit: number;
105
+ maxDebit: number;
106
+ pendingOperations: number;
107
+ scopeKey: string;
108
+ sourceGross?: number | undefined;
109
+ sourceId: string;
110
+ sourceKind: "invoice" | "settlement";
111
+ sourceProcessingFee?: number | undefined;
112
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
113
+ }>>;
114
+ export declare const finish: import("convex/server").RegisteredMutation<"public", {
115
+ groupKey: string;
116
+ limit?: number | undefined;
117
+ outcome: "collect" | "release";
118
+ }, Promise<{
119
+ group: {
120
+ _creationTime: number;
121
+ _id: import("convex/values").GenericId<"recoveryGroups">;
122
+ accountId: string;
123
+ amount: number;
124
+ currency: string;
125
+ groupKey: string;
126
+ maxCredit: number;
127
+ maxDebit: number;
128
+ pendingOperations: number;
129
+ scopeKey: string;
130
+ sourceGross?: number | undefined;
131
+ sourceId: string;
132
+ sourceKind: "invoice" | "settlement";
133
+ sourceProcessingFee?: number | undefined;
134
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
135
+ };
136
+ isDone: boolean;
137
+ }>>;
138
+ //# sourceMappingURL=mutations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../../src/component/costRecovery/mutations.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;GAQjB,CAAC;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;GAInB,CAAC;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAIlB,CAAC;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;GAIf,CAAC;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAIjB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { v } from "convex/values";
2
+ import { mutation } from "../_generated/server.js";
3
+ import { recordClaim, rollbackClaim } from "./claims.js";
4
+ import { reserveGroup, sealGroup } from "./reservations.js";
5
+ import { finishGroup } from "./completion.js";
6
+ import { observationFields, claimDoc, groupDoc, reservationDoc, reserveArgs, rollbackArgs, finishArgs, } from "./schema.js";
7
+ export const record = mutation({
8
+ args: observationFields,
9
+ returns: claimDoc,
10
+ handler: async (ctx, args) => {
11
+ if (args.claimKey.startsWith("rollback:"))
12
+ throw new Error("Rollback claim keys are reserved.");
13
+ return recordClaim(ctx, args);
14
+ },
15
+ });
16
+ export const rollback = mutation({
17
+ args: rollbackArgs,
18
+ returns: claimDoc,
19
+ handler: rollbackClaim,
20
+ });
21
+ export const reserve = mutation({
22
+ args: reserveArgs,
23
+ returns: v.object({ reservation: reservationDoc, group: groupDoc }),
24
+ handler: reserveGroup,
25
+ });
26
+ export const seal = mutation({
27
+ args: { groupKey: v.string() },
28
+ returns: groupDoc,
29
+ handler: sealGroup,
30
+ });
31
+ export const finish = mutation({
32
+ args: finishArgs,
33
+ returns: v.object({ group: groupDoc, isDone: v.boolean() }),
34
+ handler: finishGroup,
35
+ });
36
+ //# sourceMappingURL=mutations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutations.js","sourceRoot":"","sources":["../../../src/component/costRecovery/mutations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,MAAM,CAAC,MAAM,MAAM,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,OAAO,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;CACF,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC/B,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,aAAa;CACvB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACnE,OAAO,EAAE,YAAY;CACtB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC;IAC3B,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE;IAC9B,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;IAC3D,OAAO,EAAE,WAAW;CACrB,CAAC,CAAC"}
@@ -0,0 +1,102 @@
1
+ export declare const balance: import("convex/server").RegisteredQuery<"public", {
2
+ accountId: string;
3
+ currency: string;
4
+ scopeKey: string;
5
+ }, Promise<{
6
+ accountId: string;
7
+ scopeKey: string;
8
+ currency: string;
9
+ amountDue: number;
10
+ collectedAmount: number;
11
+ reservedAmount: number;
12
+ outstanding: number;
13
+ activeGroupKey?: string | undefined;
14
+ }>>;
15
+ export declare const listClaims: import("convex/server").RegisteredQuery<"public", {
16
+ accountId: string;
17
+ currency: string;
18
+ paginationOpts: {
19
+ id?: number;
20
+ endCursor?: string | null;
21
+ maximumRowsRead?: number;
22
+ maximumBytesRead?: number;
23
+ numItems: number;
24
+ cursor: string | null;
25
+ };
26
+ scopeKey: string;
27
+ }, Promise<{
28
+ page: {
29
+ _creationTime: number;
30
+ _id: import("convex/values").GenericId<"recoveryClaims">;
31
+ accountId: string;
32
+ amountDue: number;
33
+ claimKey: string;
34
+ collectedAmount: number;
35
+ currency: string;
36
+ outstanding: number;
37
+ reservedAmount: number;
38
+ revision: number;
39
+ scopeKey: string;
40
+ sign: "negative" | "positive" | "zero";
41
+ sourceChargeId: string;
42
+ sourceGross: number;
43
+ sourceProcessingFee: number;
44
+ }[];
45
+ continueCursor: string;
46
+ isDone: boolean;
47
+ }>>;
48
+ export declare const group: import("convex/server").RegisteredQuery<"public", {
49
+ groupKey: string;
50
+ }, Promise<{
51
+ _creationTime: number;
52
+ _id: import("convex/values").GenericId<"recoveryGroups">;
53
+ accountId: string;
54
+ amount: number;
55
+ currency: string;
56
+ groupKey: string;
57
+ maxCredit: number;
58
+ maxDebit: number;
59
+ pendingOperations: number;
60
+ scopeKey: string;
61
+ sourceGross?: number | undefined;
62
+ sourceId: string;
63
+ sourceKind: "invoice" | "settlement";
64
+ sourceProcessingFee?: number | undefined;
65
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
66
+ } | null>>;
67
+ export declare const reservation: import("convex/server").RegisteredQuery<"public", {
68
+ operationKey: string;
69
+ }, Promise<{
70
+ _creationTime: number;
71
+ _id: import("convex/values").GenericId<"recoveryReservations">;
72
+ allocations: {
73
+ amount: number;
74
+ claimId: import("convex/values").GenericId<"recoveryClaims">;
75
+ claimKey: string;
76
+ }[];
77
+ amount: number;
78
+ groupKey: string;
79
+ limit: number;
80
+ operationKey: string;
81
+ status: "collected" | "empty" | "released" | "reserved";
82
+ } | null>>;
83
+ export declare const claim: import("convex/server").RegisteredQuery<"public", {
84
+ claimKey: string;
85
+ }, Promise<{
86
+ _creationTime: number;
87
+ _id: import("convex/values").GenericId<"recoveryClaims">;
88
+ accountId: string;
89
+ amountDue: number;
90
+ claimKey: string;
91
+ collectedAmount: number;
92
+ currency: string;
93
+ outstanding: number;
94
+ reservedAmount: number;
95
+ revision: number;
96
+ scopeKey: string;
97
+ sign: "negative" | "positive" | "zero";
98
+ sourceChargeId: string;
99
+ sourceGross: number;
100
+ sourceProcessingFee: number;
101
+ } | null>>;
102
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/component/costRecovery/queries.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,OAAO;;;;;;;;;;;;;GAiBlB,CAAC;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BrB,CAAC;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;UAQhB,CAAC;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;UAQtB,CAAC;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;UAQhB,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { paginationOptsValidator } from "convex/server";
2
+ import { v } from "convex/values";
3
+ import { query } from "../_generated/server.js";
4
+ import { balanceFields, claimDoc, groupDoc, scopeFields, reservationDoc, } from "./schema.js";
5
+ import { readBalance, scopeOnly, validateScope, batchLimit, } from "./helpers.js";
6
+ export const balance = query({
7
+ args: scopeFields,
8
+ returns: v.object(balanceFields),
9
+ handler: async (ctx, args) => {
10
+ validateScope(args);
11
+ const stored = await readBalance(ctx, args);
12
+ return {
13
+ ...scopeOnly(args),
14
+ amountDue: stored?.amountDue ?? 0,
15
+ collectedAmount: stored?.collectedAmount ?? 0,
16
+ reservedAmount: stored?.reservedAmount ?? 0,
17
+ outstanding: stored?.outstanding ?? 0,
18
+ ...(stored?.activeGroupKey
19
+ ? { activeGroupKey: stored.activeGroupKey }
20
+ : {}),
21
+ };
22
+ },
23
+ });
24
+ export const listClaims = query({
25
+ args: { ...scopeFields, paginationOpts: paginationOptsValidator },
26
+ returns: v.object({
27
+ page: v.array(claimDoc),
28
+ continueCursor: v.string(),
29
+ isDone: v.boolean(),
30
+ }),
31
+ handler: async (ctx, args) => {
32
+ validateScope(args);
33
+ batchLimit(args.paginationOpts.numItems);
34
+ const result = await ctx.db
35
+ .query("recoveryClaims")
36
+ .withIndex("by_scope", (q) => q
37
+ .eq("accountId", args.accountId)
38
+ .eq("scopeKey", args.scopeKey)
39
+ .eq("currency", args.currency))
40
+ .order("desc")
41
+ .paginate(args.paginationOpts);
42
+ return {
43
+ page: result.page,
44
+ continueCursor: result.continueCursor,
45
+ isDone: result.isDone,
46
+ };
47
+ },
48
+ });
49
+ export const group = query({
50
+ args: { groupKey: v.string() },
51
+ returns: v.union(groupDoc, v.null()),
52
+ handler: (ctx, args) => ctx.db
53
+ .query("recoveryGroups")
54
+ .withIndex("by_key", (q) => q.eq("groupKey", args.groupKey))
55
+ .unique(),
56
+ });
57
+ export const reservation = query({
58
+ args: { operationKey: v.string() },
59
+ returns: v.union(reservationDoc, v.null()),
60
+ handler: (ctx, args) => ctx.db
61
+ .query("recoveryReservations")
62
+ .withIndex("by_key", (q) => q.eq("operationKey", args.operationKey))
63
+ .unique(),
64
+ });
65
+ export const claim = query({
66
+ args: { claimKey: v.string() },
67
+ returns: v.union(claimDoc, v.null()),
68
+ handler: (ctx, args) => ctx.db
69
+ .query("recoveryClaims")
70
+ .withIndex("by_key", (q) => q.eq("claimKey", args.claimKey))
71
+ .unique(),
72
+ });
73
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../src/component/costRecovery/queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { MutationCtx } from "../_generated/server.js";
2
+ import type { Infer } from "convex/values";
3
+ declare const _reserveArgs: import("convex/values").VObject<{
4
+ accountId: string;
5
+ currency: string;
6
+ groupKey: string;
7
+ limit?: number | undefined;
8
+ maxCredit: number;
9
+ maxDebit: number;
10
+ operationKey: string;
11
+ scopeKey: string;
12
+ sourceGross?: number | undefined;
13
+ sourceId: string;
14
+ sourceKind: "invoice" | "settlement";
15
+ sourceProcessingFee?: number | undefined;
16
+ }, {
17
+ accountId: import("convex/values").VString<string, "required">;
18
+ scopeKey: import("convex/values").VString<string, "required">;
19
+ currency: import("convex/values").VString<string, "required">;
20
+ groupKey: import("convex/values").VString<string, "required">;
21
+ sourceKind: import("convex/values").VUnion<"invoice" | "settlement", [import("convex/values").VLiteral<"settlement", "required">, import("convex/values").VLiteral<"invoice", "required">], "required", never>;
22
+ sourceId: import("convex/values").VString<string, "required">;
23
+ sourceGross: import("convex/values").VFloat64<number | undefined, "optional">;
24
+ sourceProcessingFee: import("convex/values").VFloat64<number | undefined, "optional">;
25
+ maxDebit: import("convex/values").VFloat64<number, "required">;
26
+ maxCredit: import("convex/values").VFloat64<number, "required">;
27
+ operationKey: import("convex/values").VString<string, "required">;
28
+ limit: import("convex/values").VFloat64<number | undefined, "optional">;
29
+ }, "required", "accountId" | "currency" | "groupKey" | "limit" | "maxCredit" | "maxDebit" | "operationKey" | "scopeKey" | "sourceGross" | "sourceId" | "sourceKind" | "sourceProcessingFee">;
30
+ export declare function reserveGroup(ctx: MutationCtx, args: Infer<typeof _reserveArgs>): Promise<{
31
+ reservation: {
32
+ _creationTime: number;
33
+ _id: import("convex/values").GenericId<"recoveryReservations">;
34
+ allocations: {
35
+ amount: number;
36
+ claimId: import("convex/values").GenericId<"recoveryClaims">;
37
+ claimKey: string;
38
+ }[];
39
+ amount: number;
40
+ groupKey: string;
41
+ limit: number;
42
+ operationKey: string;
43
+ status: "collected" | "empty" | "released" | "reserved";
44
+ };
45
+ group: {
46
+ _creationTime: number;
47
+ _id: import("convex/values").GenericId<"recoveryGroups">;
48
+ accountId: string;
49
+ amount: number;
50
+ currency: string;
51
+ groupKey: string;
52
+ maxCredit: number;
53
+ maxDebit: number;
54
+ pendingOperations: number;
55
+ scopeKey: string;
56
+ sourceGross?: number | undefined;
57
+ sourceId: string;
58
+ sourceKind: "invoice" | "settlement";
59
+ sourceProcessingFee?: number | undefined;
60
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
61
+ };
62
+ }>;
63
+ export declare function sealGroup(ctx: MutationCtx, args: {
64
+ groupKey: string;
65
+ }): Promise<{
66
+ _creationTime: number;
67
+ _id: import("convex/values").GenericId<"recoveryGroups">;
68
+ accountId: string;
69
+ amount: number;
70
+ currency: string;
71
+ groupKey: string;
72
+ maxCredit: number;
73
+ maxDebit: number;
74
+ pendingOperations: number;
75
+ scopeKey: string;
76
+ sourceGross?: number | undefined;
77
+ sourceId: string;
78
+ sourceKind: "invoice" | "settlement";
79
+ sourceProcessingFee?: number | undefined;
80
+ status: "collected" | "collecting" | "open" | "released" | "releasing" | "sealed";
81
+ }>;
82
+ export {};
83
+ //# sourceMappingURL=reservations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reservations.d.ts","sourceRoot":"","sources":["../../../src/component/costRecovery/reservations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;4LAAwB,CAAC;AAC3C,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,KAAK,CAAC,OAAO,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyIjC;AACD,wBAAsB,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;GAQ3E"}
@@ -0,0 +1,134 @@
1
+ import { v } from "convex/values";
2
+ import { reserveArgs } from "./schema.js";
3
+ import { balance, batchLimit, getGroup, groupTarget, integer, sign, validateScope, } from "./helpers.js";
4
+ const _reserveArgs = v.object(reserveArgs);
5
+ export async function reserveGroup(ctx, args) {
6
+ validateScope(args);
7
+ const limit = args.limit ?? 100;
8
+ batchLimit(limit);
9
+ [args.maxDebit, args.maxCredit].forEach(integer);
10
+ if (!args.groupKey.trim() ||
11
+ !args.operationKey.trim() ||
12
+ !args.sourceId ||
13
+ args.maxDebit < 0 ||
14
+ args.maxCredit < 0)
15
+ throw new Error("Invalid recovery operation identity or limits.");
16
+ if (args.sourceGross !== undefined && integer(args.sourceGross) <= 0)
17
+ throw new Error("Funding gross must be positive.");
18
+ if (args.sourceProcessingFee !== undefined &&
19
+ integer(args.sourceProcessingFee) < 0)
20
+ throw new Error("Funding processing fee must be nonnegative.");
21
+ if (args.sourceKind === "settlement" &&
22
+ (!args.sourceGross || args.sourceProcessingFee === undefined))
23
+ throw new Error("Settlement recovery requires original funding gross.");
24
+ let group = await ctx.db
25
+ .query("recoveryGroups")
26
+ .withIndex("by_key", (q) => q.eq("groupKey", args.groupKey))
27
+ .unique();
28
+ const existing = await ctx.db
29
+ .query("recoveryReservations")
30
+ .withIndex("by_key", (q) => q.eq("operationKey", args.operationKey))
31
+ .unique();
32
+ if (group)
33
+ for (const key of [
34
+ "accountId",
35
+ "scopeKey",
36
+ "currency",
37
+ "sourceKind",
38
+ "sourceId",
39
+ "sourceGross",
40
+ "sourceProcessingFee",
41
+ "maxDebit",
42
+ "maxCredit",
43
+ ])
44
+ if (group[key] !== args[key])
45
+ throw new Error("Recovery group identity and limits cannot change.");
46
+ if (existing) {
47
+ if (existing.groupKey !== args.groupKey ||
48
+ existing.limit !== limit ||
49
+ !group)
50
+ throw new Error("Recovery operation identity cannot change.");
51
+ return { reservation: existing, group };
52
+ }
53
+ const ledger = await balance(ctx, args);
54
+ if (ledger.activeGroupKey && ledger.activeGroupKey !== args.groupKey)
55
+ throw new Error("Another recovery boundary is pending.");
56
+ if (group && group.status !== "open")
57
+ throw new Error("Recovery group is sealed; replay its existing operations.");
58
+ if (!group) {
59
+ const { operationKey: _operationKey, limit: _limit, ...input } = args;
60
+ const id = await ctx.db.insert("recoveryGroups", {
61
+ ...input,
62
+ amount: 0,
63
+ pendingOperations: 0,
64
+ status: "open",
65
+ });
66
+ group = (await ctx.db.get("recoveryGroups", id));
67
+ }
68
+ const delta = integer(groupTarget(group, ledger.outstanding) - group.amount);
69
+ const direction = sign(delta);
70
+ let remaining = Math.abs(delta), amount = 0;
71
+ const allocations = [];
72
+ if (direction !== "zero") {
73
+ const claims = await ctx.db
74
+ .query("recoveryClaims")
75
+ .withIndex("by_sign", (q) => q
76
+ .eq("accountId", args.accountId)
77
+ .eq("scopeKey", args.scopeKey)
78
+ .eq("currency", args.currency)
79
+ .eq("sign", direction))
80
+ .take(limit);
81
+ for (const claim of claims) {
82
+ if (!remaining)
83
+ break;
84
+ const take = Math.min(remaining, Math.abs(claim.outstanding)) * (delta < 0 ? -1 : 1);
85
+ const reservedAmount = integer(claim.reservedAmount + take), outstanding = integer(claim.outstanding - take);
86
+ await ctx.db.patch("recoveryClaims", claim._id, {
87
+ reservedAmount,
88
+ outstanding,
89
+ sign: sign(outstanding),
90
+ });
91
+ allocations.push({
92
+ claimId: claim._id,
93
+ claimKey: claim.claimKey,
94
+ amount: take,
95
+ });
96
+ amount = integer(amount + take);
97
+ remaining -= Math.abs(take);
98
+ }
99
+ }
100
+ const reservationId = await ctx.db.insert("recoveryReservations", {
101
+ operationKey: args.operationKey,
102
+ groupKey: args.groupKey,
103
+ amount,
104
+ limit,
105
+ allocations,
106
+ status: allocations.length ? "reserved" : "empty",
107
+ });
108
+ if (allocations.length) {
109
+ await ctx.db.patch("recoveryBalances", ledger._id, {
110
+ reservedAmount: integer(ledger.reservedAmount + amount),
111
+ outstanding: integer(ledger.outstanding - amount),
112
+ activeGroupKey: group.groupKey,
113
+ });
114
+ await ctx.db.patch("recoveryGroups", group._id, {
115
+ amount: integer(group.amount + amount),
116
+ pendingOperations: group.pendingOperations + 1,
117
+ });
118
+ }
119
+ return {
120
+ reservation: (await ctx.db.get("recoveryReservations", reservationId)),
121
+ group: (await ctx.db.get("recoveryGroups", group._id)),
122
+ };
123
+ }
124
+ export async function sealGroup(ctx, args) {
125
+ const group = await getGroup(ctx, args.groupKey);
126
+ if (group.status !== "open")
127
+ return group;
128
+ const ledger = await balance(ctx, group);
129
+ if (groupTarget(group, ledger.outstanding) !== group.amount)
130
+ throw new Error("Recovery needs another reservation batch before sealing.");
131
+ await ctx.db.patch("recoveryGroups", group._id, { status: "sealed" });
132
+ return (await ctx.db.get("recoveryGroups", group._id));
133
+ }
134
+ //# sourceMappingURL=reservations.js.map