@fantastic.dev/better-stripe 0.23.0 → 0.23.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payoutAdoptionTestHelpers.d.ts","sourceRoot":"","sources":["../../../src/client/connect/payoutAdoptionTestHelpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ // @vitest-environment edge-runtime
2
+ import { convexTest } from "convex-test";
3
+ import { makeFunctionReference } from "convex/server";
4
+ import { vi } from "vitest";
5
+ import schema from "../../component/schema.js";
6
+ import { releaseRecipientPayout } from "./payoutExecution.js";
7
+ import { adoptRecipientPayouts } from "./payoutAdoption.js";
8
+ const modules = import.meta.glob("../../component/**/*.*s");
9
+ const symbol = Symbol.for("toReferencePath");
10
+ export const component = {
11
+ payoutPolicy: {
12
+ queries: { get: { [symbol]: "betterStripe/payoutPolicy/queries/get" } },
13
+ },
14
+ };
15
+ export const scope = {
16
+ accountId: "acct_recipient",
17
+ ownerKey: "owner",
18
+ operationKey: "release-1",
19
+ currency: "usd",
20
+ independentLiabilities: 0,
21
+ };
22
+ export async function setup(amounts = [600, 400], currency = "usd") {
23
+ const t = convexTest(schema, modules);
24
+ await t.run(async (ctx) => {
25
+ await ctx.db.insert("payoutPolicies", {
26
+ accountId: scope.accountId,
27
+ ownerKey: scope.ownerKey,
28
+ mode: "stripe_managed",
29
+ status: "ready",
30
+ revision: 0,
31
+ operationKey: "enable",
32
+ country: "US",
33
+ thresholds: [],
34
+ holdDeadline: Date.now() + 86400000,
35
+ });
36
+ for (let i = 0; i < amounts.length; i++)
37
+ await ctx.db.insert("transfers", {
38
+ stripeTransferId: `tr_${i}`,
39
+ destinationAccountId: scope.accountId,
40
+ sourceChargeId: `ch_${i}`,
41
+ amount: amounts[i],
42
+ currency,
43
+ status: "paid",
44
+ role: i % 2 ? "affiliate" : "store",
45
+ });
46
+ });
47
+ const path = (ref) => ref[symbol].replace("betterStripe/", "").replace(/\/(\w+)$/, ":$1");
48
+ const ctx = {
49
+ runQuery: (ref, args) => t.query(makeFunctionReference(path(ref)), args),
50
+ runMutation: (ref, args) => t.mutation(makeFunctionReference(path(ref)), args),
51
+ };
52
+ let transactions = amounts.map((amount, i) => ({
53
+ id: `txn_${i}`,
54
+ source: `py_${i}`,
55
+ amount,
56
+ fee: 0,
57
+ net: amount,
58
+ currency,
59
+ created: 100,
60
+ available_on: 200,
61
+ status: "available",
62
+ type: "payment",
63
+ exchange_rate: null,
64
+ }));
65
+ let payout;
66
+ const history = [];
67
+ const provider = {
68
+ accounts: {
69
+ listExternalAccounts: vi.fn(async () => ({
70
+ data: [
71
+ {
72
+ id: "ba_one",
73
+ object: "bank_account",
74
+ currency,
75
+ default_for_currency: true,
76
+ },
77
+ ],
78
+ has_more: false,
79
+ })),
80
+ },
81
+ transfers: {
82
+ retrieve: vi.fn(async (id) => {
83
+ const i = Number(id.slice(3));
84
+ return {
85
+ id,
86
+ destination: scope.accountId,
87
+ destination_payment: `py_${i}`,
88
+ source_transaction: `ch_${i}`,
89
+ amount: amounts[i],
90
+ currency,
91
+ amount_reversed: 0,
92
+ };
93
+ }),
94
+ listReversals: vi.fn(async () => ({ data: [], has_more: false })),
95
+ },
96
+ charges: {
97
+ retrieve: vi.fn(async (id) => {
98
+ const i = Number(id.slice(3));
99
+ return {
100
+ id,
101
+ source_transfer: `tr_${i}`,
102
+ amount: amounts[i],
103
+ currency,
104
+ paid: true,
105
+ status: "succeeded",
106
+ balance_transaction: `txn_${i}`,
107
+ };
108
+ }),
109
+ },
110
+ balanceTransactions: {
111
+ list: vi.fn(async (params) => ({
112
+ data: params.source
113
+ ? transactions.filter((t) => t.source === params.source)
114
+ : transactions.slice(0, params.limit),
115
+ has_more: false,
116
+ })),
117
+ retrieve: vi.fn(async (id) => transactions.find((t) => t.id === id)),
118
+ },
119
+ balance: {
120
+ retrieve: vi.fn(async () => ({
121
+ available: [
122
+ {
123
+ currency,
124
+ amount: transactions
125
+ .filter((t) => t.status === "available")
126
+ .reduce((a, t) => a + t.net, 0),
127
+ source_types: {
128
+ card: transactions
129
+ .filter((t) => t.status === "available")
130
+ .reduce((a, t) => a + t.net, 0),
131
+ },
132
+ },
133
+ ],
134
+ pending: [],
135
+ })),
136
+ },
137
+ payouts: {
138
+ list: vi.fn(async () => ({
139
+ data: [...history].reverse(),
140
+ has_more: false,
141
+ })),
142
+ retrieve: vi.fn(async (id) => id ? history.find((p) => p.id === id) : payout),
143
+ create: vi.fn(async (params) => {
144
+ const id = history.length ? `po_${history.length + 1}` : "po_one";
145
+ const debitId = history.length
146
+ ? `txn_debit_${history.length + 1}`
147
+ : "txn_debit";
148
+ payout = {
149
+ id,
150
+ ...params,
151
+ status: "paid",
152
+ automatic: false,
153
+ method: "standard",
154
+ type: "bank_account",
155
+ source_type: "card",
156
+ destination: "ba_one",
157
+ created: Math.floor(Date.now() / 1000),
158
+ arrival_date: Math.floor(Date.now() / 1000),
159
+ balance_transaction: debitId,
160
+ failure_balance_transaction: null,
161
+ reversed_by: null,
162
+ };
163
+ transactions = [
164
+ {
165
+ id: debitId,
166
+ source: id,
167
+ amount: -params.amount,
168
+ fee: 0,
169
+ net: -params.amount,
170
+ currency,
171
+ created: Math.floor(Date.now() / 1000),
172
+ available_on: Math.floor(Date.now() / 1000),
173
+ status: "available",
174
+ type: "payout",
175
+ exchange_rate: null,
176
+ },
177
+ ...transactions,
178
+ ];
179
+ history.push(payout);
180
+ return payout;
181
+ }),
182
+ },
183
+ };
184
+ const verify = vi.fn(async () => ({
185
+ status: "verified",
186
+ policy: (await t.run((ctx) => ctx.db.query("payoutPolicies").first())),
187
+ }));
188
+ const run = (extra = {}) => releaseRecipientPayout(provider, component, ctx, { ...scope, currency, ...extra }, verify);
189
+ return {
190
+ t,
191
+ ctx,
192
+ provider,
193
+ verify,
194
+ run,
195
+ setTransactions: (rows) => {
196
+ transactions = rows;
197
+ },
198
+ transactions: () => transactions,
199
+ };
200
+ }
201
+ export async function historical(currency = "usd") {
202
+ const amounts = [600, 400];
203
+ const s = await setup(amounts, currency);
204
+ await s.provider.payouts.create({
205
+ amount: 1000,
206
+ currency,
207
+ metadata: {},
208
+ });
209
+ s.provider.payouts.create.mockClear();
210
+ const opts = {
211
+ accountId: scope.accountId,
212
+ ownerKey: scope.ownerKey,
213
+ currency,
214
+ expectedRevision: 0,
215
+ payouts: [
216
+ {
217
+ stripePayoutId: "po_one",
218
+ allocations: [
219
+ { stripeTransferId: "tr_0", amount: 600 },
220
+ { stripeTransferId: "tr_1", amount: 400 },
221
+ ],
222
+ },
223
+ ],
224
+ };
225
+ const adopt = (extra = {}) => adoptRecipientPayouts(s.provider, component, s.ctx, {
226
+ ...opts,
227
+ ...extra,
228
+ });
229
+ return { ...s, adopt, opts, amounts };
230
+ }
231
+ //# sourceMappingURL=payoutAdoptionTestHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payoutAdoptionTestHelpers.js","sourceRoot":"","sources":["../../../src/client/connect/payoutAdoptionTestHelpers.ts"],"names":[],"mappings":"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"}
@@ -459,6 +459,12 @@ the currency. Existing package claims supply their immutable allocations on
459
459
  subsequent calls; use `payouts: []` to verify/reconcile already imported history.
460
460
  The same flow supports explicit accounting for automatic historical payouts.
461
461
 
462
+ For a verified bank return, holding-age proof retains the original paid
463
+ principal and adds the returned credit at its availability time. The returned
464
+ claim contributes no currently paid source allocation. Net provider fees still
465
+ reduce the combined unpaid balance. Replaying adoption preserves the same claim;
466
+ a later release can include returned cash and new funding exactly once.
467
+
462
468
  The package retrieves actual recipient funding and payout receipts, verifies
463
469
  full history against live available/pending cash, and validates each allocation
464
470
  against source maturity at the historical payout date. Future credits and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fantastic.dev/better-stripe",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "A Convex component for Stripe V2 Accounts API integration.",
5
5
  "keywords": [
6
6
  "stripe",
@@ -1,247 +1,12 @@
1
1
  // @vitest-environment edge-runtime
2
- import { convexTest } from "convex-test";
3
- import { makeFunctionReference } from "convex/server";
4
2
  import { expect, it, vi } from "vitest";
5
3
  import type Stripe from "stripe";
6
- import schema from "../../component/schema.js";
7
- import type { Component, RunCtx } from "../helpers.js";
8
- import { releaseRecipientPayout } from "./payoutExecution.js";
9
- import { adoptRecipientPayouts } from "./payoutAdoption.js";
10
- const modules = import.meta.glob("../../component/**/*.*s");
11
- const symbol = Symbol.for("toReferencePath");
12
- const component = {
13
- payoutPolicy: {
14
- queries: { get: { [symbol]: "betterStripe/payoutPolicy/queries/get" } },
15
- },
16
- } as unknown as Component;
17
- const scope = {
18
- accountId: "acct_recipient",
19
- ownerKey: "owner",
20
- operationKey: "release-1",
21
- currency: "usd",
22
- independentLiabilities: 0,
23
- };
24
- async function setup(amounts = [600, 400]) {
25
- const t = convexTest(schema, modules);
26
- await t.run(async (ctx) => {
27
- await ctx.db.insert("payoutPolicies", {
28
- accountId: scope.accountId,
29
- ownerKey: scope.ownerKey,
30
- mode: "stripe_managed",
31
- status: "ready",
32
- revision: 0,
33
- operationKey: "enable",
34
- country: "US",
35
- thresholds: [],
36
- holdDeadline: Date.now() + 86400000,
37
- });
38
- for (let i = 0; i < amounts.length; i++)
39
- await ctx.db.insert("transfers", {
40
- stripeTransferId: `tr_${i}`,
41
- destinationAccountId: scope.accountId,
42
- sourceChargeId: `ch_${i}`,
43
- amount: amounts[i],
44
- currency: "usd",
45
- status: "paid",
46
- role: i % 2 ? "affiliate" : "store",
47
- });
48
- });
49
- const path = (ref: Record<symbol, string>) =>
50
- ref[symbol].replace("betterStripe/", "").replace(/\/(\w+)$/, ":$1");
51
- const ctx = {
52
- runQuery: (ref: Record<symbol, string>, args: never) =>
53
- t.query(makeFunctionReference<"query">(path(ref)), args),
54
- runMutation: (ref: Record<symbol, string>, args: never) =>
55
- t.mutation(makeFunctionReference<"mutation">(path(ref)), args),
56
- } as unknown as RunCtx;
57
- let transactions = amounts.map((amount, i) => ({
58
- id: `txn_${i}`,
59
- source: `py_${i}`,
60
- amount,
61
- fee: 0,
62
- net: amount,
63
- currency: "usd",
64
- created: 100,
65
- available_on: 200,
66
- status: "available",
67
- type: "payment",
68
- exchange_rate: null,
69
- }));
70
- let payout: Record<string, unknown> | undefined;
71
- const provider = {
72
- accounts: {
73
- listExternalAccounts: vi.fn(async () => ({
74
- data: [
75
- {
76
- id: "ba_one",
77
- object: "bank_account",
78
- currency: "usd",
79
- default_for_currency: true,
80
- },
81
- ],
82
- has_more: false,
83
- })),
84
- },
85
- transfers: {
86
- retrieve: vi.fn(async (id: string) => {
87
- const i = Number(id.slice(3));
88
- return {
89
- id,
90
- destination: scope.accountId,
91
- destination_payment: `py_${i}`,
92
- source_transaction: `ch_${i}`,
93
- amount: amounts[i],
94
- currency: "usd",
95
- amount_reversed: 0,
96
- };
97
- }),
98
- listReversals: vi.fn(async () => ({ data: [], has_more: false })),
99
- },
100
- charges: {
101
- retrieve: vi.fn(async (id: string) => {
102
- const i = Number(id.slice(3));
103
- return {
104
- id,
105
- source_transfer: `tr_${i}`,
106
- amount: amounts[i],
107
- currency: "usd",
108
- paid: true,
109
- status: "succeeded",
110
- balance_transaction: `txn_${i}`,
111
- };
112
- }),
113
- },
114
- balanceTransactions: {
115
- list: vi.fn(async (params: { source?: string; limit?: number }) => ({
116
- data: params.source
117
- ? transactions.filter((t) => t.source === params.source)
118
- : transactions.slice(0, params.limit),
119
- has_more: false,
120
- })),
121
- retrieve: vi.fn(async (id: string) =>
122
- transactions.find((t) => t.id === id),
123
- ),
124
- },
125
- balance: {
126
- retrieve: vi.fn(async () => ({
127
- available: [
128
- {
129
- currency: "usd",
130
- amount: transactions
131
- .filter((t) => t.status === "available")
132
- .reduce((a, t) => a + t.net, 0),
133
- source_types: {
134
- card: transactions
135
- .filter((t) => t.status === "available")
136
- .reduce((a, t) => a + t.net, 0),
137
- },
138
- },
139
- ],
140
- pending: [],
141
- })),
142
- },
143
- payouts: {
144
- list: vi.fn(async () => ({
145
- data: payout ? [payout] : [],
146
- has_more: false,
147
- })),
148
- retrieve: vi.fn(async () => payout),
149
- create: vi.fn(
150
- async (params: {
151
- amount: number;
152
- currency: string;
153
- metadata: object;
154
- }) => {
155
- payout = {
156
- id: "po_one",
157
- ...params,
158
- status: "paid",
159
- automatic: false,
160
- method: "standard",
161
- type: "bank_account",
162
- source_type: "card",
163
- destination: "ba_one",
164
- created: Math.floor(Date.now() / 1000),
165
- arrival_date: Math.floor(Date.now() / 1000),
166
- balance_transaction: "txn_debit",
167
- failure_balance_transaction: null,
168
- reversed_by: null,
169
- };
170
- transactions = [
171
- {
172
- id: "txn_debit",
173
- source: "po_one",
174
- amount: -params.amount,
175
- fee: 0,
176
- net: -params.amount,
177
- currency: "usd",
178
- created: Math.floor(Date.now() / 1000),
179
- available_on: Math.floor(Date.now() / 1000),
180
- status: "available",
181
- type: "payout",
182
- exchange_rate: null,
183
- },
184
- ...transactions,
185
- ];
186
- return payout;
187
- },
188
- ),
189
- },
190
- };
191
- const verify = vi.fn(async () => ({
192
- status: "verified" as const,
193
- policy: (await t.run((ctx) => ctx.db.query("payoutPolicies").first()))!,
194
- }));
195
- const run = (extra = {}) =>
196
- releaseRecipientPayout(
197
- provider as unknown as Stripe,
198
- component,
199
- ctx,
200
- { ...scope, ...extra },
201
- verify,
202
- );
203
- return {
204
- t,
205
- ctx,
206
- provider,
207
- verify,
208
- run,
209
- setTransactions: (rows: typeof transactions) => {
210
- transactions = rows;
211
- },
212
- transactions: () => transactions,
213
- };
214
- }
215
- async function historical() {
216
- const s = await setup();
217
- await s.provider.payouts.create({
218
- amount: 1000,
219
- currency: "usd",
220
- metadata: {},
221
- });
222
- s.provider.payouts.create.mockClear();
223
- const opts = {
224
- accountId: scope.accountId,
225
- ownerKey: scope.ownerKey,
226
- currency: "usd",
227
- expectedRevision: 0,
228
- payouts: [
229
- {
230
- stripePayoutId: "po_one",
231
- allocations: [
232
- { stripeTransferId: "tr_0", amount: 600 },
233
- { stripeTransferId: "tr_1", amount: 400 },
234
- ],
235
- },
236
- ],
237
- };
238
- const adopt = (extra = {}) =>
239
- adoptRecipientPayouts(s.provider as unknown as Stripe, component, s.ctx, {
240
- ...opts,
241
- ...extra,
242
- });
243
- return { ...s, adopt, opts };
244
- }
4
+ import {
5
+ component,
6
+ scope,
7
+ setup,
8
+ historical,
9
+ } from "./payoutAdoptionTestHelpers.js";
245
10
  it("imports a verified historical manual payout with exact source allocations", async () => {
246
11
  const s = await historical();
247
12
  expect(await s.adopt()).toMatchObject({
@@ -303,8 +303,9 @@ export async function adoptRecipientPayouts(
303
303
  )) as { claims: Claim[] };
304
304
  claims.push(...result.claims);
305
305
  }
306
- const paid = claims
307
- .filter((c) => c.status === "paid")
306
+ // Returned cash gets a fresh holding age after consuming its original principal.
307
+ const originalPaid = claims
308
+ .filter((c) => c.status === "paid" || c.status === "returned")
308
309
  .flatMap((c) => c.allocations);
309
310
  const fees = claims.reduce((total, c) => add(total, c.providerFee ?? 0), 0);
310
311
  const returned = proofPayouts
@@ -315,7 +316,7 @@ export async function adoptRecipientPayouts(
315
316
  const oldestHeldAt = oldestUnpaidFunding(
316
317
  opts.currency,
317
318
  sources,
318
- paid,
319
+ originalPaid,
319
320
  fees,
320
321
  returned,
321
322
  );