@fantastic.dev/better-stripe 0.23.1 → 0.24.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 +26 -2
- package/README.md +40 -5
- package/dist/client/affiliateRecovery/disputes.d.ts +18 -0
- package/dist/client/affiliateRecovery/disputes.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/disputes.js +33 -0
- package/dist/client/affiliateRecovery/disputes.js.map +1 -0
- package/dist/client/affiliateRecovery/engine.d.ts +29 -0
- package/dist/client/affiliateRecovery/engine.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/engine.js +116 -0
- package/dist/client/affiliateRecovery/engine.js.map +1 -0
- package/dist/client/affiliateRecovery/payoutGate.d.ts +24 -0
- package/dist/client/affiliateRecovery/payoutGate.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/payoutGate.js +30 -0
- package/dist/client/affiliateRecovery/payoutGate.js.map +1 -0
- package/dist/client/affiliateRecovery/provider.d.ts +14 -0
- package/dist/client/affiliateRecovery/provider.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/provider.js +96 -0
- package/dist/client/affiliateRecovery/provider.js.map +1 -0
- package/dist/client/affiliateRecovery/review.d.ts +28 -0
- package/dist/client/affiliateRecovery/review.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/review.js +86 -0
- package/dist/client/affiliateRecovery/review.js.map +1 -0
- package/dist/client/affiliateRecovery/validators.d.ts +402 -0
- package/dist/client/affiliateRecovery/validators.d.ts.map +1 -0
- package/dist/client/affiliateRecovery/validators.js +27 -0
- package/dist/client/affiliateRecovery/validators.js.map +1 -0
- package/dist/client/affiliateRecovery.d.ts +131 -0
- package/dist/client/affiliateRecovery.d.ts.map +1 -0
- package/dist/client/affiliateRecovery.js +69 -0
- package/dist/client/affiliateRecovery.js.map +1 -0
- package/dist/client/connect/commissionReceipts.d.ts +4 -0
- package/dist/client/connect/commissionReceipts.d.ts.map +1 -0
- package/dist/client/connect/commissionReceipts.js +17 -0
- package/dist/client/connect/commissionReceipts.js.map +1 -0
- package/dist/client/connect/payoutAdoptionTestHelpers.d.ts +536 -2
- package/dist/client/connect/payoutAdoptionTestHelpers.d.ts.map +1 -1
- package/dist/client/connect/payoutSync.d.ts +32 -0
- package/dist/client/connect/payoutSync.d.ts.map +1 -0
- package/dist/client/connect/payoutSync.js +48 -0
- package/dist/client/connect/payoutSync.js.map +1 -0
- package/dist/client/connect/payoutSyncTestHelpers.d.ts +1509 -0
- package/dist/client/connect/payoutSyncTestHelpers.d.ts.map +1 -0
- package/dist/client/connect/payoutSyncTestHelpers.js +62 -0
- package/dist/client/connect/payoutSyncTestHelpers.js.map +1 -0
- package/dist/client/connect/payoutSyncValidation.d.ts +16 -0
- package/dist/client/connect/payoutSyncValidation.d.ts.map +1 -0
- package/dist/client/connect/payoutSyncValidation.js +54 -0
- package/dist/client/connect/payoutSyncValidation.js.map +1 -0
- package/dist/client/connect/payouts.d.ts +3 -1
- package/dist/client/connect/payouts.d.ts.map +1 -1
- package/dist/client/connect/payouts.js +45 -12
- package/dist/client/connect/payouts.js.map +1 -1
- package/dist/client/connect/refundPrincipal.d.ts +4 -0
- package/dist/client/connect/refundPrincipal.d.ts.map +1 -0
- package/dist/client/connect/refundPrincipal.js +21 -0
- package/dist/client/connect/refundPrincipal.js.map +1 -0
- package/dist/client/connect/reversalExecution.d.ts +47 -0
- package/dist/client/connect/reversalExecution.d.ts.map +1 -0
- package/dist/client/connect/reversalExecution.js +84 -0
- package/dist/client/connect/reversalExecution.js.map +1 -0
- package/dist/client/connect/transfers.d.ts +8 -4
- package/dist/client/connect/transfers.d.ts.map +1 -1
- package/dist/client/connect/transfers.js +52 -57
- package/dist/client/connect/transfers.js.map +1 -1
- package/dist/client/index.d.ts +29 -9
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -6
- package/dist/client/index.js.map +1 -1
- package/dist/client/readContracts.d.ts +8 -0
- package/dist/client/readContracts.d.ts.map +1 -1
- package/dist/client/readContracts.js +7 -0
- package/dist/client/readContracts.js.map +1 -1
- package/dist/client/settlement/execution.d.ts.map +1 -1
- package/dist/client/settlement/execution.js +30 -3
- package/dist/client/settlement/execution.js.map +1 -1
- package/dist/client/settlement/reconciliation.d.ts.map +1 -1
- package/dist/client/settlement/reconciliation.js +16 -2
- package/dist/client/settlement/reconciliation.js.map +1 -1
- package/dist/client/webhooks/disputeRecovery.d.ts +4 -0
- package/dist/client/webhooks/disputeRecovery.d.ts.map +1 -0
- package/dist/client/webhooks/disputeRecovery.js +40 -0
- package/dist/client/webhooks/disputeRecovery.js.map +1 -0
- package/dist/client/webhooks/processors.d.ts.map +1 -1
- package/dist/client/webhooks/processors.js +14 -29
- package/dist/client/webhooks/processors.js.map +1 -1
- package/dist/component/_generated/component.d.ts +560 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/affiliateEarnings/mutations.d.ts +22 -0
- package/dist/component/affiliateEarnings/mutations.d.ts.map +1 -0
- package/dist/component/affiliateEarnings/mutations.js +160 -0
- package/dist/component/affiliateEarnings/mutations.js.map +1 -0
- package/dist/component/affiliatePayouts/mutations.d.ts +25 -0
- package/dist/component/affiliatePayouts/mutations.d.ts.map +1 -0
- package/dist/component/affiliatePayouts/mutations.js +104 -0
- package/dist/component/affiliatePayouts/mutations.js.map +1 -0
- package/dist/component/affiliatePayouts/queries.d.ts +23 -0
- package/dist/component/affiliatePayouts/queries.d.ts.map +1 -0
- package/dist/component/affiliatePayouts/queries.js +23 -0
- package/dist/component/affiliatePayouts/queries.js.map +1 -0
- package/dist/component/affiliatePayouts/recipientGuard.d.ts +12 -0
- package/dist/component/affiliatePayouts/recipientGuard.d.ts.map +1 -0
- package/dist/component/affiliatePayouts/recipientGuard.js +26 -0
- package/dist/component/affiliatePayouts/recipientGuard.js.map +1 -0
- package/dist/component/affiliateRecovery/accounting.d.ts +28 -0
- package/dist/component/affiliateRecovery/accounting.d.ts.map +1 -0
- package/dist/component/affiliateRecovery/accounting.js +71 -0
- package/dist/component/affiliateRecovery/accounting.js.map +1 -0
- package/dist/component/affiliateRecovery/mutations.d.ts +65 -0
- package/dist/component/affiliateRecovery/mutations.d.ts.map +1 -0
- package/dist/component/affiliateRecovery/mutations.js +250 -0
- package/dist/component/affiliateRecovery/mutations.js.map +1 -0
- package/dist/component/affiliateRecovery/queries.d.ts +305 -0
- package/dist/component/affiliateRecovery/queries.d.ts.map +1 -0
- package/dist/component/affiliateRecovery/queries.js +113 -0
- package/dist/component/affiliateRecovery/queries.js.map +1 -0
- package/dist/component/affiliateRecovery/schema.d.ts +171 -0
- package/dist/component/affiliateRecovery/schema.d.ts.map +1 -0
- package/dist/component/affiliateRecovery/schema.js +71 -0
- package/dist/component/affiliateRecovery/schema.js.map +1 -0
- package/dist/component/connect/mutations.d.ts +36 -23
- package/dist/component/connect/mutations.d.ts.map +1 -1
- package/dist/component/connect/mutations.js +11 -151
- package/dist/component/connect/mutations.js.map +1 -1
- package/dist/component/connect/payoutWrites.d.ts +21 -0
- package/dist/component/connect/payoutWrites.d.ts.map +1 -0
- package/dist/component/connect/payoutWrites.js +19 -0
- package/dist/component/connect/payoutWrites.js.map +1 -0
- package/dist/component/connect/queries.d.ts +10 -0
- package/dist/component/connect/queries.d.ts.map +1 -1
- package/dist/component/connect/queries.js +3 -40
- package/dist/component/connect/queries.js.map +1 -1
- package/dist/component/connect/reversalClaims.d.ts +169 -0
- package/dist/component/connect/reversalClaims.d.ts.map +1 -0
- package/dist/component/connect/reversalClaims.js +176 -0
- package/dist/component/connect/reversalClaims.js.map +1 -0
- package/dist/component/connect/reversalDiagnostics.d.ts +59 -0
- package/dist/component/connect/reversalDiagnostics.d.ts.map +1 -0
- package/dist/component/connect/reversalDiagnostics.js +50 -0
- package/dist/component/connect/reversalDiagnostics.js.map +1 -0
- package/dist/component/connect/reversalReceipts.d.ts +27 -0
- package/dist/component/connect/reversalReceipts.d.ts.map +1 -0
- package/dist/component/connect/reversalReceipts.js +85 -0
- package/dist/component/connect/reversalReceipts.js.map +1 -0
- package/dist/component/connect/schema.d.ts +31 -1
- package/dist/component/connect/schema.d.ts.map +1 -1
- package/dist/component/connect/schema.js +3 -1
- package/dist/component/connect/schema.js.map +1 -1
- package/dist/component/connect/validators.d.ts +49 -1
- package/dist/component/connect/validators.d.ts.map +1 -1
- package/dist/component/connect/validators.js +2 -0
- package/dist/component/connect/validators.js.map +1 -1
- package/dist/component/earnings/triggers.d.ts +454 -2
- package/dist/component/earnings/triggers.d.ts.map +1 -1
- package/dist/component/payoutRelease/mutations.d.ts.map +1 -1
- package/dist/component/payoutRelease/mutations.js +6 -0
- package/dist/component/payoutRelease/mutations.js.map +1 -1
- package/dist/component/payoutRelease/queries.d.ts.map +1 -1
- package/dist/component/payoutRelease/queries.js +3 -1
- package/dist/component/payoutRelease/queries.js.map +1 -1
- package/dist/component/payoutSync/helpers.d.ts +44 -0
- package/dist/component/payoutSync/helpers.d.ts.map +1 -0
- package/dist/component/payoutSync/helpers.js +60 -0
- package/dist/component/payoutSync/helpers.js.map +1 -0
- package/dist/component/payoutSync/mutations.d.ts +51 -0
- package/dist/component/payoutSync/mutations.d.ts.map +1 -0
- package/dist/component/payoutSync/mutations.js +88 -0
- package/dist/component/payoutSync/mutations.js.map +1 -0
- package/dist/component/payoutSync/queries.d.ts +16 -0
- package/dist/component/payoutSync/queries.d.ts.map +1 -0
- package/dist/component/payoutSync/queries.js +11 -0
- package/dist/component/payoutSync/queries.js.map +1 -0
- package/dist/component/payoutSync/schema.d.ts +46 -0
- package/dist/component/payoutSync/schema.d.ts.map +1 -0
- package/dist/component/payoutSync/schema.js +22 -0
- package/dist/component/payoutSync/schema.js.map +1 -0
- package/dist/component/payoutSync/validators.d.ts +137 -0
- package/dist/component/payoutSync/validators.d.ts.map +1 -0
- package/dist/component/payoutSync/validators.js +19 -0
- package/dist/component/payoutSync/validators.js.map +1 -0
- package/dist/component/schema.d.ts +268 -1
- package/dist/component/schema.js +11 -0
- package/dist/component/schema.js.map +1 -1
- package/dist/component/settlement/clawbackSchema.d.ts +161 -0
- package/dist/component/settlement/clawbackSchema.d.ts.map +1 -0
- package/dist/component/settlement/clawbackSchema.js +73 -0
- package/dist/component/settlement/clawbackSchema.js.map +1 -0
- package/dist/component/settlement/clawbacks.d.ts +55 -0
- package/dist/component/settlement/clawbacks.d.ts.map +1 -0
- package/dist/component/settlement/clawbacks.js +146 -0
- package/dist/component/settlement/clawbacks.js.map +1 -0
- package/dist/component/settlement/mutations.d.ts +16 -0
- package/dist/component/settlement/mutations.d.ts.map +1 -1
- package/dist/component/settlement/mutations.js +48 -0
- package/dist/component/settlement/mutations.js.map +1 -1
- package/dist/component/settlement/queries.d.ts +53 -0
- package/dist/component/settlement/queries.d.ts.map +1 -1
- package/dist/component/settlement/queries.js +26 -0
- package/dist/component/settlement/queries.js.map +1 -1
- package/dist/react/workflows/AffiliateRecovery.d.ts +46 -0
- package/dist/react/workflows/AffiliateRecovery.d.ts.map +1 -0
- package/dist/react/workflows/AffiliateRecovery.js +84 -0
- package/dist/react/workflows/AffiliateRecovery.js.map +1 -0
- package/dist/react/workflows/index.d.ts +2 -0
- package/dist/react/workflows/index.d.ts.map +1 -1
- package/dist/react/workflows/index.js +1 -0
- package/dist/react/workflows/index.js.map +1 -1
- package/dist/testing/affiliate-recovery.d.ts +10 -0
- package/dist/testing/affiliate-recovery.d.ts.map +1 -0
- package/dist/testing/affiliate-recovery.js +126 -0
- package/dist/testing/affiliate-recovery.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/docs/affiliate-clawbacks.md +306 -0
- package/docs/read-contracts.md +40 -5
- package/docs/settlement.md +17 -0
- package/package.json +2 -1
- package/src/client/affiliateRecovery/disputes.ts +62 -0
- package/src/client/affiliateRecovery/engine.ts +196 -0
- package/src/client/affiliateRecovery/payoutGate.ts +65 -0
- package/src/client/affiliateRecovery/provider.ts +127 -0
- package/src/client/affiliateRecovery/review.ts +117 -0
- package/src/client/affiliateRecovery/validators.ts +38 -0
- package/src/client/affiliateRecovery.ts +173 -0
- package/src/client/connect/commissionReceipts.test.ts +157 -0
- package/src/client/connect/commissionReceipts.ts +23 -0
- package/src/client/connect/payoutSync.test.ts +313 -0
- package/src/client/connect/payoutSync.ts +93 -0
- package/src/client/connect/payoutSyncTestHelpers.ts +82 -0
- package/src/client/connect/payoutSyncValidation.ts +74 -0
- package/src/client/connect/payouts.ts +75 -15
- package/src/client/connect/refundPrincipal.test.ts +46 -0
- package/src/client/connect/refundPrincipal.ts +26 -0
- package/src/client/connect/reversalExecution.ts +130 -0
- package/src/client/connect/transfers.test.ts +3 -3
- package/src/client/connect/transfers.ts +93 -92
- package/src/client/index.ts +20 -18
- package/src/client/readContracts.ts +12 -0
- package/src/client/settlement/execution.test.ts +5 -0
- package/src/client/settlement/execution.ts +50 -3
- package/src/client/settlement/reconciliation.ts +20 -2
- package/src/client/webhooks/disputeRecovery.ts +74 -0
- package/src/client/webhooks/processors.test.ts +36 -53
- package/src/client/webhooks/processors.ts +18 -31
- package/src/component/_generated/api.ts +34 -0
- package/src/component/_generated/component.ts +696 -1
- package/src/component/affiliate-clawbacks.test.ts +370 -0
- package/src/component/affiliate-component-pagination.test.ts +83 -0
- package/src/component/affiliate-direct-payout.test.ts +160 -0
- package/src/component/affiliate-recovery-lifecycle.test.ts +587 -0
- package/src/component/affiliateEarnings/mutations.ts +196 -0
- package/src/component/affiliatePayouts/mutations.ts +121 -0
- package/src/component/affiliatePayouts/queries.ts +28 -0
- package/src/component/affiliatePayouts/recipientGuard.ts +42 -0
- package/src/component/affiliateRecovery/accounting.ts +111 -0
- package/src/component/affiliateRecovery/mutations.ts +275 -0
- package/src/component/affiliateRecovery/queries.ts +138 -0
- package/src/component/affiliateRecovery/schema.ts +86 -0
- package/src/component/connect/mutations.ts +13 -190
- package/src/component/connect/payoutWrites.ts +25 -0
- package/src/component/connect/queries.ts +4 -47
- package/src/component/connect/reversalClaims.ts +232 -0
- package/src/component/connect/reversalDiagnostics.ts +61 -0
- package/src/component/connect/reversalReceipts.ts +147 -0
- package/src/component/connect/schema.ts +3 -3
- package/src/component/connect/validators.ts +2 -0
- package/src/component/payout-recovery-integration.test.ts +172 -0
- package/src/component/payout-sync.test.ts +117 -0
- package/src/component/payoutRelease/mutations.ts +9 -0
- package/src/component/payoutRelease/queries.ts +3 -1
- package/src/component/payoutSync/helpers.ts +86 -0
- package/src/component/payoutSync/mutations.ts +102 -0
- package/src/component/payoutSync/queries.ts +10 -0
- package/src/component/payoutSync/schema.ts +25 -0
- package/src/component/payoutSync/validators.ts +21 -0
- package/src/component/schema.ts +20 -0
- package/src/component/settlement/clawbackSchema.ts +78 -0
- package/src/component/settlement/clawbacks.ts +233 -0
- package/src/component/settlement/mutations.ts +54 -0
- package/src/component/settlement/queries.ts +35 -0
- package/src/component/summary-arithmetic.test.ts +9 -4
- package/src/react/workflows/AffiliateRecovery.test.tsx +106 -0
- package/src/react/workflows/AffiliateRecovery.tsx +273 -0
- package/src/react/workflows/index.ts +9 -0
- package/src/testing/affiliate-recovery.ts +137 -0
- package/src/testing/index.ts +2 -0
|
@@ -96,6 +96,13 @@ type MockReversal = {
|
|
|
96
96
|
|
|
97
97
|
function makeStripe() {
|
|
98
98
|
return {
|
|
99
|
+
refunds: {
|
|
100
|
+
list: vi.fn(() =>
|
|
101
|
+
(async function* () {
|
|
102
|
+
yield { amount: 5000, status: "succeeded" };
|
|
103
|
+
})(),
|
|
104
|
+
),
|
|
105
|
+
},
|
|
99
106
|
products: { retrieve: vi.fn() },
|
|
100
107
|
subscriptions: {
|
|
101
108
|
retrieve: vi.fn(),
|
|
@@ -1396,7 +1403,7 @@ describe("processEvent — split transfer engine (BTS-22)", () => {
|
|
|
1396
1403
|
expect(data.statementDescriptor).toBe("ACME STORE");
|
|
1397
1404
|
});
|
|
1398
1405
|
|
|
1399
|
-
it("
|
|
1406
|
+
it("records the dispute but retries recovery when the charge basis is unavailable", async () => {
|
|
1400
1407
|
const stripe = makeStripe();
|
|
1401
1408
|
stripe.charges.retrieve.mockRejectedValue(new Error("stripe down"));
|
|
1402
1409
|
const ctx = makeCtx({
|
|
@@ -1411,26 +1418,28 @@ describe("processEvent — split transfer engine (BTS-22)", () => {
|
|
|
1411
1418
|
});
|
|
1412
1419
|
const whCtx = makeWhCtx({ stripe, ctx });
|
|
1413
1420
|
|
|
1414
|
-
await
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1421
|
+
await expect(
|
|
1422
|
+
processEvent(
|
|
1423
|
+
whCtx,
|
|
1424
|
+
event("charge.dispute.created", {
|
|
1425
|
+
id: "dp_bf",
|
|
1426
|
+
charge: "ch_1",
|
|
1427
|
+
amount: 9000,
|
|
1428
|
+
currency: "usd",
|
|
1429
|
+
status: "needs_response",
|
|
1430
|
+
reason: "fraudulent",
|
|
1431
|
+
is_charge_refundable: true,
|
|
1432
|
+
evidence_details: { due_by: 1700000000 },
|
|
1433
|
+
metadata: {},
|
|
1434
|
+
}),
|
|
1435
|
+
),
|
|
1436
|
+
).rejects.toThrow("stripe down");
|
|
1428
1437
|
|
|
1429
|
-
//
|
|
1438
|
+
// Record the dispute, but require an authoritative basis before recovery.
|
|
1430
1439
|
const { path, data } = dispatchedPayload(whCtx.ctx);
|
|
1431
1440
|
expect(path).toBe("betterStripe/connect/mutations/upsertDispute");
|
|
1432
1441
|
expect(data.statementDescriptor).toBeUndefined();
|
|
1433
|
-
expect(stripe.transfers.createReversal).
|
|
1442
|
+
expect(stripe.transfers.createReversal).not.toHaveBeenCalled();
|
|
1434
1443
|
});
|
|
1435
1444
|
|
|
1436
1445
|
it("[BTS-27] claws back a single-recipient transfer on dispute", async () => {
|
|
@@ -2338,7 +2347,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2338
2347
|
: {}),
|
|
2339
2348
|
},
|
|
2340
2349
|
];
|
|
2341
|
-
|
|
2342
2350
|
const refundEvent = (overrides: Record<string, unknown> = {}) =>
|
|
2343
2351
|
event("refund.created", {
|
|
2344
2352
|
id: "re_1",
|
|
@@ -2350,7 +2358,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2350
2358
|
metadata: {},
|
|
2351
2359
|
...overrides,
|
|
2352
2360
|
});
|
|
2353
|
-
|
|
2354
2361
|
it("reverses split-sale transfers pro-rata on a succeeded partial refund", async () => {
|
|
2355
2362
|
const ctx = makeCtx({ query: ledgerLegs() });
|
|
2356
2363
|
const stripe = makeStripe();
|
|
@@ -2360,10 +2367,7 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2360
2367
|
amount_refunded: 5000,
|
|
2361
2368
|
});
|
|
2362
2369
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2363
|
-
|
|
2364
2370
|
await processEvent(whCtx, refundEvent());
|
|
2365
|
-
|
|
2366
|
-
// 50% refunded → each leg reversed to 50% of its original amount.
|
|
2367
2371
|
expect(stripe.transfers.createReversal).toHaveBeenCalledTimes(2);
|
|
2368
2372
|
expect(stripe.transfers.createReversal).toHaveBeenCalledWith(
|
|
2369
2373
|
"tr_s",
|
|
@@ -2376,19 +2380,21 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2376
2380
|
{ idempotencyKey: "bs_rev_re_1_tr_a" },
|
|
2377
2381
|
);
|
|
2378
2382
|
});
|
|
2379
|
-
|
|
2380
2383
|
it("reverses the full transfer amounts on a full refund", async () => {
|
|
2381
2384
|
const ctx = makeCtx({ query: ledgerLegs() });
|
|
2382
2385
|
const stripe = makeStripe();
|
|
2386
|
+
stripe.refunds.list.mockImplementation(() =>
|
|
2387
|
+
(async function* () {
|
|
2388
|
+
yield { amount: 10000, status: "succeeded" };
|
|
2389
|
+
})(),
|
|
2390
|
+
);
|
|
2383
2391
|
stripe.charges.retrieve.mockResolvedValue({
|
|
2384
2392
|
id: "ch_1",
|
|
2385
2393
|
amount: 10000,
|
|
2386
2394
|
amount_refunded: 10000,
|
|
2387
2395
|
});
|
|
2388
2396
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2389
|
-
|
|
2390
2397
|
await processEvent(whCtx, refundEvent({ amount: 10000 }));
|
|
2391
|
-
|
|
2392
2398
|
expect(stripe.transfers.createReversal).toHaveBeenCalledWith(
|
|
2393
2399
|
"tr_s",
|
|
2394
2400
|
{ amount: 7000 },
|
|
@@ -2400,23 +2406,23 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2400
2406
|
{ idempotencyKey: "bs_rev_re_1_tr_a" },
|
|
2401
2407
|
);
|
|
2402
2408
|
});
|
|
2403
|
-
|
|
2404
2409
|
it("a second partial refund reverses only the delta up to the new cumulative target", async () => {
|
|
2405
|
-
// First refund (5000) already clawed back 3500/500 and was recorded.
|
|
2406
2410
|
const ctx = makeCtx({
|
|
2407
2411
|
query: ledgerLegs({ reversedStore: 3500, reversedAff: 500 }),
|
|
2408
2412
|
});
|
|
2409
2413
|
const stripe = makeStripe();
|
|
2414
|
+
stripe.refunds.list.mockImplementation(() =>
|
|
2415
|
+
(async function* () {
|
|
2416
|
+
yield { amount: 7000, status: "succeeded" };
|
|
2417
|
+
})(),
|
|
2418
|
+
);
|
|
2410
2419
|
stripe.charges.retrieve.mockResolvedValue({
|
|
2411
2420
|
id: "ch_1",
|
|
2412
2421
|
amount: 10000,
|
|
2413
2422
|
amount_refunded: 7000, // cumulative: 5000 + this refund's 2000
|
|
2414
2423
|
});
|
|
2415
2424
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2416
|
-
|
|
2417
2425
|
await processEvent(whCtx, refundEvent({ id: "re_2", amount: 2000 }));
|
|
2418
|
-
|
|
2419
|
-
// Target 70% of each leg minus what's already reversed: 4900-3500, 700-500.
|
|
2420
2426
|
expect(stripe.transfers.createReversal).toHaveBeenCalledWith(
|
|
2421
2427
|
"tr_s",
|
|
2422
2428
|
{ amount: 1400 },
|
|
@@ -2428,7 +2434,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2428
2434
|
{ idempotencyKey: "bs_rev_re_2_tr_a" },
|
|
2429
2435
|
);
|
|
2430
2436
|
});
|
|
2431
|
-
|
|
2432
2437
|
it("is idempotent — a redelivered refund event whose reversals are recorded reverses nothing", async () => {
|
|
2433
2438
|
const ctx = makeCtx({
|
|
2434
2439
|
query: ledgerLegs({ reversedStore: 3500, reversedAff: 500 }),
|
|
@@ -2440,36 +2445,24 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2440
2445
|
amount_refunded: 5000,
|
|
2441
2446
|
});
|
|
2442
2447
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2443
|
-
|
|
2444
2448
|
await processEvent(whCtx, refundEvent());
|
|
2445
|
-
|
|
2446
|
-
// Cumulative target (50%) already met on both legs — no delta, no calls.
|
|
2447
2449
|
expect(stripe.transfers.createReversal).not.toHaveBeenCalled();
|
|
2448
2450
|
});
|
|
2449
|
-
|
|
2450
2451
|
it("skips clawback quietly for a refund on a non-split sale", async () => {
|
|
2451
2452
|
const ctx = makeCtx({ query: [] });
|
|
2452
2453
|
const stripe = makeStripe();
|
|
2453
2454
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2454
|
-
|
|
2455
2455
|
await processEvent(whCtx, refundEvent());
|
|
2456
|
-
|
|
2457
|
-
// No ledger legs → not a split sale (Stripe-native reverse_transfer covers
|
|
2458
|
-
// destination charges). No charge lookup, no reversals, no throw.
|
|
2459
2456
|
expect(stripe.charges.retrieve).not.toHaveBeenCalled();
|
|
2460
2457
|
expect(stripe.transfers.createReversal).not.toHaveBeenCalled();
|
|
2461
2458
|
});
|
|
2462
|
-
|
|
2463
2459
|
it("does not claw back for a pending refund", async () => {
|
|
2464
2460
|
const ctx = makeCtx({ query: ledgerLegs() });
|
|
2465
2461
|
const stripe = makeStripe();
|
|
2466
2462
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2467
|
-
|
|
2468
2463
|
await processEvent(whCtx, refundEvent({ status: "pending" }));
|
|
2469
|
-
|
|
2470
2464
|
expect(stripe.transfers.createReversal).not.toHaveBeenCalled();
|
|
2471
2465
|
});
|
|
2472
|
-
|
|
2473
2466
|
it("propagates a reversal failure so the event is marked failed and Stripe retries", async () => {
|
|
2474
2467
|
const ctx = makeCtx({ query: ledgerLegs() });
|
|
2475
2468
|
const stripe = makeStripe();
|
|
@@ -2480,12 +2473,10 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2480
2473
|
});
|
|
2481
2474
|
stripe.transfers.createReversal.mockRejectedValue(new Error("stripe down"));
|
|
2482
2475
|
const whCtx = makeWhCtx({ ctx, stripe });
|
|
2483
|
-
|
|
2484
2476
|
await expect(processEvent(whCtx, refundEvent())).rejects.toThrow(
|
|
2485
2477
|
"stripe down",
|
|
2486
2478
|
);
|
|
2487
2479
|
});
|
|
2488
|
-
|
|
2489
2480
|
it("application_fee.refunded sets absolute fee totals on the linked payment", async () => {
|
|
2490
2481
|
const stripe = makeStripe();
|
|
2491
2482
|
stripe.charges.retrieve.mockResolvedValue({
|
|
@@ -2493,7 +2484,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2493
2484
|
payment_intent: "pi_1",
|
|
2494
2485
|
});
|
|
2495
2486
|
const whCtx = makeWhCtx({ stripe });
|
|
2496
|
-
|
|
2497
2487
|
const feeEvent = event("application_fee.refunded", {
|
|
2498
2488
|
id: "fee_1",
|
|
2499
2489
|
charge: "ch_1",
|
|
@@ -2502,7 +2492,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2502
2492
|
currency: "usd",
|
|
2503
2493
|
});
|
|
2504
2494
|
await processEvent(whCtx, feeEvent);
|
|
2505
|
-
|
|
2506
2495
|
const calls = whCtx.ctx.runMutation.mock.calls;
|
|
2507
2496
|
const feeCall = calls.find(
|
|
2508
2497
|
(c) =>
|
|
@@ -2510,15 +2499,11 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2510
2499
|
"betterStripe/connect/mutations/recordPaymentFeeRefund",
|
|
2511
2500
|
);
|
|
2512
2501
|
expect(feeCall).toBeDefined();
|
|
2513
|
-
// Absolute cumulative values straight from Stripe — never incremented, so
|
|
2514
|
-
// a redelivery (or a createRefund-triggered duplicate) can't double-count.
|
|
2515
2502
|
expect(feeCall![1]).toEqual({
|
|
2516
2503
|
stripePaymentIntentId: "pi_1",
|
|
2517
2504
|
feeCollectedAmount: 160,
|
|
2518
2505
|
feeRefundedAmount: 160,
|
|
2519
2506
|
});
|
|
2520
|
-
|
|
2521
|
-
// Redelivery writes the identical absolute state.
|
|
2522
2507
|
await processEvent(whCtx, feeEvent);
|
|
2523
2508
|
const feeCalls = whCtx.ctx.runMutation.mock.calls.filter(
|
|
2524
2509
|
(c) =>
|
|
@@ -2528,7 +2513,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2528
2513
|
expect(feeCalls).toHaveLength(2);
|
|
2529
2514
|
expect(feeCalls[1][1]).toEqual(feeCalls[0][1]);
|
|
2530
2515
|
});
|
|
2531
|
-
|
|
2532
2516
|
it("application_fee.refunded skips quietly when the charge has no payment intent", async () => {
|
|
2533
2517
|
const stripe = makeStripe();
|
|
2534
2518
|
stripe.charges.retrieve.mockResolvedValue({
|
|
@@ -2536,7 +2520,6 @@ describe("processEvent — refunds: split clawback + fee-refund denormalization
|
|
|
2536
2520
|
payment_intent: null,
|
|
2537
2521
|
});
|
|
2538
2522
|
const whCtx = makeWhCtx({ stripe });
|
|
2539
|
-
|
|
2540
2523
|
await expect(
|
|
2541
2524
|
processEvent(
|
|
2542
2525
|
whCtx,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { reconcileDisputeMoney } from "./disputeRecovery.js";
|
|
2
|
+
import { successfulRefundPrincipal } from "../connect/refundPrincipal.js";
|
|
1
3
|
import { subscriptionRouting } from "../billing/subscriptionRouting.js";
|
|
2
4
|
import { cancelsAtPeriodEnd } from "../billing/subscriptionCancellation.js";
|
|
3
5
|
import type Stripe from "stripe";
|
|
@@ -5,8 +7,6 @@ import type Stripe from "stripe";
|
|
|
5
7
|
import type { SplitRecipient } from "../../component/lib/fees.js";
|
|
6
8
|
import {
|
|
7
9
|
createSplitTransfers,
|
|
8
|
-
reinstateTransfers,
|
|
9
|
-
reverseTransfers,
|
|
10
10
|
reverseTransfersForRefund,
|
|
11
11
|
} from "../connect/transfers.js";
|
|
12
12
|
import { validateStatementDescriptorSuffix } from "../core/descriptors.js";
|
|
@@ -989,15 +989,24 @@ async function handleRefundEvent(
|
|
|
989
989
|
componentRef(whCtx.component, "connect/queries/listTransfersByCharge"),
|
|
990
990
|
{ sourceChargeId: chargeId },
|
|
991
991
|
)) ?? []) as unknown[];
|
|
992
|
-
if (legs.length === 0)
|
|
992
|
+
if (legs.length === 0) {
|
|
993
|
+
const obligations = (await whCtx.ctx.runQuery(
|
|
994
|
+
componentRef(
|
|
995
|
+
whCtx.component,
|
|
996
|
+
"affiliateRecovery/queries/chargeObligations",
|
|
997
|
+
),
|
|
998
|
+
{ sourceChargeId: chargeId },
|
|
999
|
+
)) as unknown[] | null;
|
|
1000
|
+
if (!obligations?.length) return;
|
|
1001
|
+
}
|
|
993
1002
|
|
|
994
|
-
//
|
|
1003
|
+
// Fetch succeeded refunds: charge.amount_refunded can include pending funds.
|
|
995
1004
|
const charge = await whCtx.stripe.charges.retrieve(chargeId);
|
|
996
1005
|
await reverseTransfersForRefund(whCtx.stripe, whCtx.component, whCtx.ctx, {
|
|
997
1006
|
sourceChargeId: chargeId,
|
|
998
1007
|
refundId: refund.id,
|
|
999
1008
|
chargeAmount: charge.amount,
|
|
1000
|
-
amountRefunded:
|
|
1009
|
+
amountRefunded: await successfulRefundPrincipal(whCtx.stripe, chargeId),
|
|
1001
1010
|
});
|
|
1002
1011
|
}
|
|
1003
1012
|
|
|
@@ -1054,9 +1063,11 @@ async function handleDisputeEvent(
|
|
|
1054
1063
|
// best-effort: this is optional metadata, so a transient charge-lookup failure
|
|
1055
1064
|
// must never abort the critical dispute side effects (upsert, clawback, cancel).
|
|
1056
1065
|
let statementDescriptor: string | undefined;
|
|
1066
|
+
let originalCharge: Stripe.Charge | undefined;
|
|
1057
1067
|
if (lastEvent === "created" && chargeId) {
|
|
1058
1068
|
try {
|
|
1059
|
-
const charge =
|
|
1069
|
+
const charge = (originalCharge =
|
|
1070
|
+
await whCtx.stripe.charges.retrieve(chargeId));
|
|
1060
1071
|
statementDescriptor =
|
|
1061
1072
|
charge?.calculated_statement_descriptor ??
|
|
1062
1073
|
charge?.statement_descriptor ??
|
|
@@ -1082,31 +1093,7 @@ async function handleDisputeEvent(
|
|
|
1082
1093
|
metadata: dispute.metadata ?? undefined,
|
|
1083
1094
|
});
|
|
1084
1095
|
|
|
1085
|
-
|
|
1086
|
-
// funded by the disputed charge pro-rata to the disputed amount, so recipients
|
|
1087
|
-
// bear their share (losses_collector=application leaves the platform with the
|
|
1088
|
-
// rest). Idempotent via the dispute id; reinstated by BTS-29 if the dispute is
|
|
1089
|
-
// won. Only on `created` — other dispute events don't move funds here.
|
|
1090
|
-
if (lastEvent === "created" && chargeId) {
|
|
1091
|
-
await reverseTransfers(whCtx.stripe, whCtx.component, whCtx.ctx, {
|
|
1092
|
-
sourceChargeId: chargeId,
|
|
1093
|
-
amount: dispute.amount,
|
|
1094
|
-
operationId: dispute.id,
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
// Reinstatement (BTS-29): when the dispute is won (or funds are explicitly
|
|
1099
|
-
// reinstated), pay recipients back the amounts clawed back on `created`.
|
|
1100
|
-
const isWon =
|
|
1101
|
-
lastEvent === "funds_reinstated" ||
|
|
1102
|
-
(lastEvent === "closed" && dispute.status === "won");
|
|
1103
|
-
if (isWon && chargeId) {
|
|
1104
|
-
await reinstateTransfers(whCtx.stripe, whCtx.component, whCtx.ctx, {
|
|
1105
|
-
sourceChargeId: chargeId,
|
|
1106
|
-
operationId: dispute.id,
|
|
1107
|
-
currency: dispute.currency,
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1096
|
+
await reconcileDisputeMoney(whCtx, dispute, lastEvent, originalCharge);
|
|
1110
1097
|
|
|
1111
1098
|
// Auto-cancel the disputed subscription (BTS-28, default on). Resolve the
|
|
1112
1099
|
// subscription from the dispute's PaymentIntent (invoice payment → invoice →
|
|
@@ -8,11 +8,22 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type * as affiliateEarnings_mutations from "../affiliateEarnings/mutations.js";
|
|
12
|
+
import type * as affiliatePayouts_mutations from "../affiliatePayouts/mutations.js";
|
|
13
|
+
import type * as affiliatePayouts_queries from "../affiliatePayouts/queries.js";
|
|
14
|
+
import type * as affiliatePayouts_recipientGuard from "../affiliatePayouts/recipientGuard.js";
|
|
15
|
+
import type * as affiliateRecovery_accounting from "../affiliateRecovery/accounting.js";
|
|
16
|
+
import type * as affiliateRecovery_mutations from "../affiliateRecovery/mutations.js";
|
|
17
|
+
import type * as affiliateRecovery_queries from "../affiliateRecovery/queries.js";
|
|
11
18
|
import type * as billing_mutations from "../billing/mutations.js";
|
|
12
19
|
import type * as billing_queries from "../billing/queries.js";
|
|
13
20
|
import type * as billing_validators from "../billing/validators.js";
|
|
14
21
|
import type * as connect_mutations from "../connect/mutations.js";
|
|
22
|
+
import type * as connect_payoutWrites from "../connect/payoutWrites.js";
|
|
15
23
|
import type * as connect_queries from "../connect/queries.js";
|
|
24
|
+
import type * as connect_reversalClaims from "../connect/reversalClaims.js";
|
|
25
|
+
import type * as connect_reversalDiagnostics from "../connect/reversalDiagnostics.js";
|
|
26
|
+
import type * as connect_reversalReceipts from "../connect/reversalReceipts.js";
|
|
16
27
|
import type * as connect_validators from "../connect/validators.js";
|
|
17
28
|
import type * as core_mutations from "../core/mutations.js";
|
|
18
29
|
import type * as core_queries from "../core/queries.js";
|
|
@@ -45,10 +56,16 @@ import type * as payoutRelease_helpers from "../payoutRelease/helpers.js";
|
|
|
45
56
|
import type * as payoutRelease_mutations from "../payoutRelease/mutations.js";
|
|
46
57
|
import type * as payoutRelease_observation from "../payoutRelease/observation.js";
|
|
47
58
|
import type * as payoutRelease_queries from "../payoutRelease/queries.js";
|
|
59
|
+
import type * as payoutSync_helpers from "../payoutSync/helpers.js";
|
|
60
|
+
import type * as payoutSync_mutations from "../payoutSync/mutations.js";
|
|
61
|
+
import type * as payoutSync_queries from "../payoutSync/queries.js";
|
|
62
|
+
import type * as payoutSync_validators from "../payoutSync/validators.js";
|
|
48
63
|
import type * as products_mutations from "../products/mutations.js";
|
|
49
64
|
import type * as products_queries from "../products/queries.js";
|
|
50
65
|
import type * as products_validators from "../products/validators.js";
|
|
51
66
|
import type * as refundHistory_queries from "../refundHistory/queries.js";
|
|
67
|
+
import type * as settlement_clawbackSchema from "../settlement/clawbackSchema.js";
|
|
68
|
+
import type * as settlement_clawbacks from "../settlement/clawbacks.js";
|
|
52
69
|
import type * as settlement_mutations from "../settlement/mutations.js";
|
|
53
70
|
import type * as settlement_queries from "../settlement/queries.js";
|
|
54
71
|
import type * as webhooks_mutations from "../webhooks/mutations.js";
|
|
@@ -63,11 +80,22 @@ import type {
|
|
|
63
80
|
import { anyApi, componentsGeneric } from "convex/server";
|
|
64
81
|
|
|
65
82
|
const fullApi: ApiFromModules<{
|
|
83
|
+
"affiliateEarnings/mutations": typeof affiliateEarnings_mutations;
|
|
84
|
+
"affiliatePayouts/mutations": typeof affiliatePayouts_mutations;
|
|
85
|
+
"affiliatePayouts/queries": typeof affiliatePayouts_queries;
|
|
86
|
+
"affiliatePayouts/recipientGuard": typeof affiliatePayouts_recipientGuard;
|
|
87
|
+
"affiliateRecovery/accounting": typeof affiliateRecovery_accounting;
|
|
88
|
+
"affiliateRecovery/mutations": typeof affiliateRecovery_mutations;
|
|
89
|
+
"affiliateRecovery/queries": typeof affiliateRecovery_queries;
|
|
66
90
|
"billing/mutations": typeof billing_mutations;
|
|
67
91
|
"billing/queries": typeof billing_queries;
|
|
68
92
|
"billing/validators": typeof billing_validators;
|
|
69
93
|
"connect/mutations": typeof connect_mutations;
|
|
94
|
+
"connect/payoutWrites": typeof connect_payoutWrites;
|
|
70
95
|
"connect/queries": typeof connect_queries;
|
|
96
|
+
"connect/reversalClaims": typeof connect_reversalClaims;
|
|
97
|
+
"connect/reversalDiagnostics": typeof connect_reversalDiagnostics;
|
|
98
|
+
"connect/reversalReceipts": typeof connect_reversalReceipts;
|
|
71
99
|
"connect/validators": typeof connect_validators;
|
|
72
100
|
"core/mutations": typeof core_mutations;
|
|
73
101
|
"core/queries": typeof core_queries;
|
|
@@ -100,10 +128,16 @@ const fullApi: ApiFromModules<{
|
|
|
100
128
|
"payoutRelease/mutations": typeof payoutRelease_mutations;
|
|
101
129
|
"payoutRelease/observation": typeof payoutRelease_observation;
|
|
102
130
|
"payoutRelease/queries": typeof payoutRelease_queries;
|
|
131
|
+
"payoutSync/helpers": typeof payoutSync_helpers;
|
|
132
|
+
"payoutSync/mutations": typeof payoutSync_mutations;
|
|
133
|
+
"payoutSync/queries": typeof payoutSync_queries;
|
|
134
|
+
"payoutSync/validators": typeof payoutSync_validators;
|
|
103
135
|
"products/mutations": typeof products_mutations;
|
|
104
136
|
"products/queries": typeof products_queries;
|
|
105
137
|
"products/validators": typeof products_validators;
|
|
106
138
|
"refundHistory/queries": typeof refundHistory_queries;
|
|
139
|
+
"settlement/clawbackSchema": typeof settlement_clawbackSchema;
|
|
140
|
+
"settlement/clawbacks": typeof settlement_clawbacks;
|
|
107
141
|
"settlement/mutations": typeof settlement_mutations;
|
|
108
142
|
"settlement/queries": typeof settlement_queries;
|
|
109
143
|
"webhooks/mutations": typeof webhooks_mutations;
|