@fantastic.dev/better-stripe 0.23.2 → 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 +20 -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 +494 -2
- package/dist/client/connect/payoutAdoptionTestHelpers.d.ts.map +1 -1
- package/dist/client/connect/payoutSyncTestHelpers.d.ts +247 -1
- package/dist/client/connect/payoutSyncTestHelpers.d.ts.map +1 -1
- 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 +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -1
- package/dist/client/index.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 +491 -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 +7 -124
- package/dist/component/connect/mutations.js.map +1 -1
- 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 +410 -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/schema.d.ts +247 -1
- package/dist/component/schema.js +9 -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/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/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 +6 -7
- 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 +24 -0
- package/src/component/_generated/component.ts +614 -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 +9 -160
- 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/payoutRelease/mutations.ts +9 -0
- package/src/component/payoutRelease/queries.ts +3 -1
- package/src/component/schema.ts +18 -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
package/CHANGELOG.md
CHANGED
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Add original affiliate commission obligations, exact reversal receipts,
|
|
6
|
+
currency-scoped debt offsets before future transfers, receipt-backed dispute
|
|
7
|
+
restoration, payout release reservations and paginated audit/migration APIs.
|
|
8
|
+
Add authenticated example views, reusable React recovery components, scheduled
|
|
9
|
+
reconciliation and simulated scenario/video evidence. Document actual provider
|
|
10
|
+
restrictions and sandbox verification limits in the [clawback guide](docs/affiliate-clawbacks.md).
|
|
11
|
+
- Base split refund recovery on cumulative succeeded refunds, excluding pending
|
|
12
|
+
and failed refunds from finalized obligations. Calculate affiliate dispute
|
|
13
|
+
exposure from original gross charge principal, with deterministic integer
|
|
14
|
+
rounding and an original-commission cap.
|
|
15
|
+
|
|
5
16
|
- Return every original split leg by default from `listTransfersByCharge`,
|
|
6
17
|
excluding reinstatement rows before applying any explicit preview limit.
|
|
7
18
|
Split breakdowns and reversal claims now cover sales with more than 50 legs.
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
Atomic claims fail explicitly above 1,000 charge-linked transfer rows;
|
|
20
|
+
queries also fail on Convex resource limits rather than silently truncate
|
|
21
|
+
financial data. `listTransfersByAccount` remains a preview API;
|
|
10
22
|
earnings hooks use exact server-side aggregates.
|
|
11
23
|
- Update the former roadmap to describe the existing product-level first-charge
|
|
12
24
|
descriptors, first-invoice fee recovery, retry-safe reversal claims, and exact
|
|
@@ -14,6 +26,12 @@
|
|
|
14
26
|
- Remove internal tracker references from public documentation and add a
|
|
15
27
|
regression check that prevents their reintroduction.
|
|
16
28
|
|
|
29
|
+
## [0.24.0](https://github.com/FantasticDevHQ/better-stripe/compare/v0.23.2...v0.24.0) (2026-10-15)
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
- add affiliate commission recovery and future earnings offsets ([#191](https://github.com/FantasticDevHQ/better-stripe/issues/191)) ([49f38ac](https://github.com/FantasticDevHQ/better-stripe/commit/49f38ac78f12415d6a330b4729ade56950ee2ad5))
|
|
34
|
+
|
|
17
35
|
## [0.23.2](https://github.com/FantasticDevHQ/better-stripe/compare/v0.23.1...v0.23.2) (2026-10-15)
|
|
18
36
|
|
|
19
37
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ schedule controls alone do not enforce a minimum.
|
|
|
55
55
|
|
|
56
56
|
## Status
|
|
57
57
|
|
|
58
|
-
**Version**: `0.
|
|
58
|
+
**Version**: `0.24.0` <!-- x-release-please-version --> (pre-1.0). API may change between minor versions until 1.0.
|
|
59
59
|
|
|
60
60
|
Used in production by its authors. Webhook pipeline is covered by unit tests and a live E2E harness (`npm run e2e:webhooks`) that fires real Stripe-signed events and asserts the ledger.
|
|
61
61
|
|
|
@@ -194,7 +194,8 @@ The marketplace economics layer includes platform fees, split transfers, rolling
|
|
|
194
194
|
- **First subscription charge descriptor** — `createProduct` and `updateProduct` stamp the store's descriptor (or platform fallback) onto store-owned Stripe products. The product descriptor covers the first invoice for direct subscriptions and Checkout subscription mode; the invoice-time descriptor still takes precedence on later invoices. Existing products need `updateProduct` to receive the descriptor, and a later store descriptor change requires refreshing its products. Shared products have one descriptor and should not be reused across stores that need different descriptors.
|
|
195
195
|
- **First-invoice fees** — finalized first invoices bypass the draft update window. At `invoice.paid`, the processor collects the fixed/tiered fee through a partial reversal of the destination transfer. Invoice markers and invoice-tagged reversals prevent duplicate collection, including recovery after a failed marker write. Renewals whose fees were set on the draft invoice are skipped.
|
|
196
196
|
- **Dispute reversal retries** — reversal slices are claimed atomically before any Stripe call. Partial-success retries reuse the recorded amounts and idempotency keys, skip confirmed legs, and reconcile money already moved before a ledger-write failure.
|
|
197
|
-
- **
|
|
197
|
+
- **Affiliate commission recovery** — original commission snapshots, shared refund/dispute exposure, reversal receipts, and a resumable backfill are available. Currency-scoped future-commission offsets, receipt-backed dispute restoration, payout reservations, and reusable recovery views are included. See [affiliate commission clawbacks](docs/affiliate-clawbacks.md).
|
|
198
|
+
- **Earnings and split totals** — `useEarnings` reads exact server-side aggregates with bounded drill-down previews. `listTransfersByAccount` remains a preview API; do not sum it for financial totals. `listTransfersByCharge` now returns every original split leg by default, and reversal claims cover up to 1,000 charge-linked transfer rows, failing explicitly above that bound. An explicit list limit requests a preview. Convex read/write/response limits fail loudly rather than silently truncate financial data.
|
|
198
199
|
|
|
199
200
|
## Installation
|
|
200
201
|
|
|
@@ -1085,8 +1086,8 @@ Payouts are **automatic and rolling** (Stripe-managed schedule, via hosted Expre
|
|
|
1085
1086
|
|
|
1086
1087
|
The platform is merchant of record, so a chargeback lands on the platform's Stripe account even for a seller's sale. The dispute pipeline (all webhook-driven, on `charge.dispute.*`):
|
|
1087
1088
|
|
|
1088
|
-
1. **Clawback** —
|
|
1089
|
-
2. **Reinstatement** — if the dispute is won (`.closed` with `status: "won"`, or `.funds_reinstated`), `reinstateTransfers`
|
|
1089
|
+
1. **Clawback** — a monetary `charge.dispute.created` event uses the disputed charge's original gross amount to calculate affiliate commission exposure. Inquiry/warning, prevented, and already-won payloads do not trigger recovery. Affiliate legs share the original-principal exposure axis with refunds; seller and other legs retain the existing reversal allocation. The dispute ID identifies the operation, and a missing authoritative charge amount causes recovery to retry.
|
|
1090
|
+
2. **Reinstatement** — if the dispute is won (`.closed` with `status: "won"`, or `.funds_reinstated`), the existing `reinstateTransfers` path creates new transfers from the platform balance. Affiliate restoration uses exact reversal/offset receipts, preserves independent successful refunds, and cancels unpaid dispute debt. Failed or uncertain restoration remains visible until reconciled. See [recovery limitations](docs/affiliate-clawbacks.md#verification-evidence).
|
|
1090
1091
|
3. **Auto-cancel** — on dispute open, the disputed subscription (resolved PaymentIntent → invoice → subscription) is canceled by default (`registerRoutes`'s `autoCancelOnDispute`, default `true`; pairs with `cancelDisputeAtPeriodEnd`, default `false` → cancel-at-period-end, matching the Skool model of "cancel now, remove access at cycle end").
|
|
1091
1092
|
|
|
1092
1093
|
Sellers respond to their own disputes without platform staff involvement, via an embedded Stripe Connect session:
|
|
@@ -1103,6 +1104,40 @@ export const createDisputeSession = action({
|
|
|
1103
1104
|
|
|
1104
1105
|
`stripe.reverseTransfers(ctx, { sourceChargeId, percent?, amount? })` is also exposed directly, for manual clawback outside the automatic dispute flow (e.g. a support-initiated partial reversal). The internal engine additionally accepts an `operationId` for a stable Stripe idempotency key (used by the automatic dispute flow); the public method doesn't expose it yet, so a manual call relies on Stripe's default request idempotency.
|
|
1105
1106
|
|
|
1107
|
+
For commissions tracked by the recovery ledger, generic manual reversal is
|
|
1108
|
+
rejected. Use `createRefund` for a real refund or `stripe.affiliateRecovery.retry`
|
|
1109
|
+
for a recovery operation, so existing offsets and uncertain receipts remain
|
|
1110
|
+
coordinated.
|
|
1111
|
+
|
|
1112
|
+
### Affiliate commission accounting
|
|
1113
|
+
|
|
1114
|
+
The component stores each original affiliate transfer's commission and currency,
|
|
1115
|
+
links available payment/invoice references, and separates finalized obligations,
|
|
1116
|
+
provisional dispute exposure, reserved slices, and confirmed reversals. New
|
|
1117
|
+
commission reversal operations reconcile exact Stripe receipts after interrupted
|
|
1118
|
+
ledger writes. Percentage and fixed commissions use the original allocated
|
|
1119
|
+
amount, with deterministic rounding in integer currency units.
|
|
1120
|
+
|
|
1121
|
+
Trusted host APIs under `components.betterStripe.settlement` provide
|
|
1122
|
+
`queries.listAffiliateObligations` and
|
|
1123
|
+
`mutations.migrateAffiliateObligations`. Authorize account ownership before
|
|
1124
|
+
exposing snapshots and restrict migration to operators. Existing rows with
|
|
1125
|
+
unattributed reversal balances require reconciliation. See the
|
|
1126
|
+
[API and migration guide](docs/affiliate-clawbacks.md#migration-and-operator-reconciliation).
|
|
1127
|
+
|
|
1128
|
+
Insufficient-balance debt is offset atomically against eligible future commissions
|
|
1129
|
+
in the same currency before transfer creation. Dispute wins restore only actual
|
|
1130
|
+
reversals/offsets and retain independent refunds. `stripe.affiliateRecovery`
|
|
1131
|
+
provides snapshots, history, operator retry and legacy review; the reusable
|
|
1132
|
+
`AffiliateRecovery`/`AffiliateRecoveryHistory` views consume those records.
|
|
1133
|
+
|
|
1134
|
+
Affiliate payouts require a stable idempotency key and pass an atomic debt and
|
|
1135
|
+
reservation gate. An optional `minimumAmount` applies a host-authorized currency
|
|
1136
|
+
minimum; it is not a discovered provider floor. Transfer reversal does not recall
|
|
1137
|
+
a completed bank payout, Stripe-managed automatic bank payouts remain outside
|
|
1138
|
+
this gate, and Global Payouts is unsupported. See the guide for account capability
|
|
1139
|
+
checks, maintenance jobs and live sandbox verification requirements.
|
|
1140
|
+
|
|
1106
1141
|
### Refunds and Statement Descriptors
|
|
1107
1142
|
|
|
1108
1143
|
`createRefund(ctx, { stripePaymentIntentId?, stripeChargeId?, amount?, reason?, refundApplicationFee?, reverseTransfer?, ... })` issues a Stripe refund with marketplace semantics, both defaulting **on**:
|
|
@@ -1110,7 +1145,7 @@ export const createDisputeSession = action({
|
|
|
1110
1145
|
- `refundApplicationFee` — return the platform's application fee pro-rata with the refund (destination charges only; Stripe pro-rates partial refunds automatically).
|
|
1111
1146
|
- `reverseTransfer` — pull the refunded amount back from the destination account (destination charges only; also pro-rated for partials).
|
|
1112
1147
|
|
|
1113
|
-
Stripe hard-errors if either flag is sent for a charge that doesn't carry an application fee / destination transfer, so `createRefund` resolves the charge first and only sends each flag when the charge can honor it. **Split (separate-charges) sales carry neither** — those go through the same charge, so their clawback is webhook-driven: on `refund.created`/`.updated`, `reverseTransfersForRefund` reverses each recipient's ledger leg pro-rata to
|
|
1148
|
+
Stripe hard-errors if either flag is sent for a charge that doesn't carry an application fee / destination transfer, so `createRefund` resolves the charge first and only sends each flag when the charge can honor it. **Split (separate-charges) sales carry neither** — those go through the same charge, so their clawback is webhook-driven: on `refund.created`/`.updated`, `reverseTransfersForRefund` reverses each recipient's ledger leg pro-rata to the cumulative amount of live refunds whose status is `succeeded`. Pending, action-required, failed, and canceled refunds are excluded; `charge.amount_refunded` alone can include pending refunds. Affiliate legs converge on the larger successful-refund or dispute exposure, capped at the original commission, so the same principal is not recovered twice. A separate `application_fee.refunded` handler keeps the linked payment's `feeCollectedAmount`/`feeRefundedAmount` in sync. The `refunds` table and the linked `payments` row update as the corresponding webhooks arrive; `createRefund` itself returns only the new refund id.
|
|
1114
1149
|
|
|
1115
1150
|
Per-store statement descriptors make destination charges recognizable on a buyer's card statement (`PLATFORMPREFIX* SUFFIX`):
|
|
1116
1151
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { Component, RunCtx } from "../helpers.js";
|
|
3
|
+
/** Record proportional original commission exposure for a monetary dispute. */
|
|
4
|
+
export declare function reverseTransfersForDispute(stripe: Stripe, component: Component, ctx: RunCtx, opts: {
|
|
5
|
+
sourceChargeId: string;
|
|
6
|
+
operationId: string;
|
|
7
|
+
chargeAmount: number;
|
|
8
|
+
amount: number;
|
|
9
|
+
finalized: boolean;
|
|
10
|
+
disputeState?: "open" | "lost" | "won";
|
|
11
|
+
refundedPrincipal?: number;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
reversals: {
|
|
14
|
+
stripeTransferId: string;
|
|
15
|
+
amount: number;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=disputes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disputes.d.ts","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/disputes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAUvD,+EAA+E;AAC/E,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IACJ,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;;;;;GAqCF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { runMutationOrThrow } from "../helpers.js";
|
|
2
|
+
import { componentRef } from "../webhooks/helpers.js";
|
|
3
|
+
import { recoverSettlementTransfers } from "../settlement/reconciliation.js";
|
|
4
|
+
import { executeClaimedSlices, } from "../connect/reversalExecution.js";
|
|
5
|
+
import { reconcileAffiliateCharge } from "./engine.js";
|
|
6
|
+
/** Record proportional original commission exposure for a monetary dispute. */
|
|
7
|
+
export async function reverseTransfersForDispute(stripe, component, ctx, opts) {
|
|
8
|
+
await recoverSettlementTransfers(stripe, component, ctx, opts.sourceChargeId);
|
|
9
|
+
const claim = (await runMutationOrThrow(ctx, componentRef(component, "connect/mutations/claimReversalSlices"), {
|
|
10
|
+
sourceChargeId: opts.sourceChargeId,
|
|
11
|
+
operationId: opts.operationId,
|
|
12
|
+
mode: opts.disputeState === "won"
|
|
13
|
+
? {
|
|
14
|
+
kind: "fraction",
|
|
15
|
+
chargeAmount: opts.chargeAmount,
|
|
16
|
+
amountRefunded: 0,
|
|
17
|
+
}
|
|
18
|
+
: { kind: "amount", amount: opts.amount },
|
|
19
|
+
commissionExposure: {
|
|
20
|
+
kind: "dispute",
|
|
21
|
+
chargeAmount: opts.chargeAmount,
|
|
22
|
+
principal: opts.amount,
|
|
23
|
+
finalized: opts.finalized,
|
|
24
|
+
disputeState: opts.disputeState,
|
|
25
|
+
refundedPrincipal: opts.refundedPrincipal,
|
|
26
|
+
disputeId: opts.operationId,
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
const result = await executeClaimedSlices(stripe, component, ctx, opts.operationId, { ...claim, slices: claim.slices.filter((s) => !s.commissionTracked) });
|
|
30
|
+
await reconcileAffiliateCharge(stripe, component, ctx, opts.sourceChargeId);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=disputes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disputes.js","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/disputes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EACL,oBAAoB,GAErB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEvD,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,MAAc,EACd,SAAoB,EACpB,GAAW,EACX,IAQC;IAED,MAAM,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,CAAC,MAAM,kBAAkB,CACrC,GAAG,EACH,YAAY,CAAC,SAAS,EAAE,uCAAuC,CAAC,EAChE;QACE,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,IAAI,EACF,IAAI,CAAC,YAAY,KAAK,KAAK;YACzB,CAAC,CAAC;gBACE,IAAI,EAAE,UAAU;gBAChB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,cAAc,EAAE,CAAC;aAClB;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QAC7C,kBAAkB,EAAE;YAClB,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,MAAM;YACtB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,SAAS,EAAE,IAAI,CAAC,WAAW;SAC5B;KACF,CACF,CAAkB,CAAC;IACpB,MAAM,MAAM,GAAG,MAAM,oBAAoB,CACvC,MAAM,EACN,SAAS,EACT,GAAG,EACH,IAAI,CAAC,WAAW,EAChB,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,CACvE,CAAC;IACF,MAAM,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC5E,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { Component, RunCtx } from "../helpers.js";
|
|
3
|
+
export declare function reconcileObligation(stripe: Stripe, component: Component, ctx: RunCtx, obligationId: string): Promise<{
|
|
4
|
+
state: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
error?: undefined;
|
|
7
|
+
} | {
|
|
8
|
+
reason?: undefined;
|
|
9
|
+
state: string;
|
|
10
|
+
error: string;
|
|
11
|
+
} | {
|
|
12
|
+
reason?: undefined;
|
|
13
|
+
error?: undefined;
|
|
14
|
+
state: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function reconcileAffiliateCharge(stripe: Stripe, component: Component, ctx: RunCtx, sourceChargeId: string): Promise<({
|
|
17
|
+
state: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
error?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
reason?: undefined;
|
|
22
|
+
state: string;
|
|
23
|
+
error: string;
|
|
24
|
+
} | {
|
|
25
|
+
reason?: undefined;
|
|
26
|
+
error?: undefined;
|
|
27
|
+
state: string;
|
|
28
|
+
})[]>;
|
|
29
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAUvD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM;;;;;;;;;;;;GAiKrB;AACD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM;;;;;;;;;;;;MAcvB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { runMutationOrThrow } from "../helpers.js";
|
|
2
|
+
import { componentRef } from "../webhooks/helpers.js";
|
|
3
|
+
import { classifyRecoveryError, findReceipt, verifyConnectPath, } from "./provider.js";
|
|
4
|
+
export async function reconcileObligation(stripe, component, ctx, obligationId) {
|
|
5
|
+
let row = (await ctx.runQuery(componentRef(component, "affiliateRecovery/queries/getObligation"), { obligationId }));
|
|
6
|
+
if (!row || row.needsReconciliation)
|
|
7
|
+
return {
|
|
8
|
+
state: "unsupported",
|
|
9
|
+
reason: "Original commission provenance requires reconciliation.",
|
|
10
|
+
};
|
|
11
|
+
// Finish uncertain recovery before restoring its actual proceeds. Each pass
|
|
12
|
+
// claims one immutable operation atomically with debt/offset reservations.
|
|
13
|
+
let lastState = "settled";
|
|
14
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
15
|
+
const net = row.recoveredAmount + (row.offsetAmount ?? 0) - (row.restoredAmount ?? 0);
|
|
16
|
+
const kind = net > row.finalizedAmount + row.provisionalAmount
|
|
17
|
+
? "restoration"
|
|
18
|
+
: "recovery";
|
|
19
|
+
const op = (await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/reserveOperation"), { obligationId, kind, now: Date.now() }));
|
|
20
|
+
if (!op) {
|
|
21
|
+
if ((row.debtAmount ?? 0) > 0)
|
|
22
|
+
lastState = "offset_required";
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const receiptId = op.attempted
|
|
27
|
+
? await findReceipt(stripe, op, row)
|
|
28
|
+
: null;
|
|
29
|
+
if (!receiptId && op.attempted && op.kind === "recovery") {
|
|
30
|
+
const original = await stripe.transfers.retrieve(row.stripeTransferId);
|
|
31
|
+
if (original.amount_reversed >
|
|
32
|
+
(op.providerBaseline ?? row.recoveredAmount)) {
|
|
33
|
+
const error = "Provider reversal aggregate changed without an attributable operation receipt; operator receipt review is required.";
|
|
34
|
+
await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/completeOperation"), { operationId: op._id, state: "unknown", error });
|
|
35
|
+
return { state: "unknown", error };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let confirmed = receiptId;
|
|
39
|
+
if (!confirmed) {
|
|
40
|
+
const started = (await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/beginOperation"), { operationId: op._id }));
|
|
41
|
+
if (!["pending", "unknown"].includes(started.state)) {
|
|
42
|
+
lastState = started.state;
|
|
43
|
+
if (started.state === "canceled") {
|
|
44
|
+
row = (await ctx.runQuery(componentRef(component, "affiliateRecovery/queries/getObligation"), { obligationId }));
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
const capability = await verifyConnectPath(stripe, row, op.kind);
|
|
50
|
+
if (!capability.supported) {
|
|
51
|
+
await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/completeOperation"), {
|
|
52
|
+
operationId: op._id,
|
|
53
|
+
state: op.attempted ? "unknown" : "unsupported",
|
|
54
|
+
error: capability.reason,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
state: op.attempted ? "unknown" : "unsupported",
|
|
58
|
+
reason: capability.reason,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const metadata = {
|
|
62
|
+
bsRecoveryOperation: op._id,
|
|
63
|
+
bsOriginalCharge: row.sourceChargeId,
|
|
64
|
+
};
|
|
65
|
+
if (op.kind === "recovery") {
|
|
66
|
+
const receipt = await stripe.transfers.createReversal(row.stripeTransferId, { amount: op.amount, metadata }, { idempotencyKey: `bs_affiliate_${op._id}` });
|
|
67
|
+
if (receipt.amount !== op.amount)
|
|
68
|
+
throw new Error("Affiliate provider reversal amount mismatch.");
|
|
69
|
+
confirmed = receipt.id;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const receipt = await stripe.transfers.create({
|
|
73
|
+
amount: op.amount,
|
|
74
|
+
currency: row.currency,
|
|
75
|
+
destination: row.affiliateAccountId,
|
|
76
|
+
transfer_group: `bs_restore_${row._id}`,
|
|
77
|
+
metadata,
|
|
78
|
+
}, { idempotencyKey: `bs_affiliate_${op._id}` });
|
|
79
|
+
if (receipt.amount !== op.amount || receipt.currency !== row.currency)
|
|
80
|
+
throw new Error("Affiliate provider restoration amount mismatch.");
|
|
81
|
+
confirmed = receipt.id;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const confirmedTransferReversed = op.kind === "recovery"
|
|
85
|
+
? (await stripe.transfers.retrieve(row.stripeTransferId))
|
|
86
|
+
.amount_reversed
|
|
87
|
+
: undefined;
|
|
88
|
+
await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/completeOperation"), {
|
|
89
|
+
operationId: op._id,
|
|
90
|
+
state: "succeeded",
|
|
91
|
+
receiptId: confirmed,
|
|
92
|
+
confirmedTransferReversed,
|
|
93
|
+
});
|
|
94
|
+
lastState = "succeeded";
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const outcome = classifyRecoveryError(error);
|
|
98
|
+
// This write deliberately occurs outside the provider/receipt transaction.
|
|
99
|
+
// Unknown outcomes retain the reservation and block commission release.
|
|
100
|
+
await runMutationOrThrow(ctx, componentRef(component, "affiliateRecovery/mutations/completeOperation"), { operationId: op._id, ...outcome });
|
|
101
|
+
return outcome;
|
|
102
|
+
}
|
|
103
|
+
row = (await ctx.runQuery(componentRef(component, "affiliateRecovery/queries/getObligation"), { obligationId }));
|
|
104
|
+
}
|
|
105
|
+
return { state: lastState };
|
|
106
|
+
}
|
|
107
|
+
export async function reconcileAffiliateCharge(stripe, component, ctx, sourceChargeId) {
|
|
108
|
+
const rows = (await ctx.runQuery(componentRef(component, "affiliateRecovery/queries/chargeObligations"), { sourceChargeId }));
|
|
109
|
+
const results = [];
|
|
110
|
+
for (const row of rows ?? [])
|
|
111
|
+
if (typeof row._id === "string" &&
|
|
112
|
+
typeof row.originalCommission === "number")
|
|
113
|
+
results.push(await reconcileObligation(stripe, component, ctx, row._id));
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAc,EACd,SAAoB,EACpB,GAAW,EACX,YAAoB;IAEpB,IAAI,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAC3B,YAAY,CAAC,SAAS,EAAE,yCAAyC,CAAC,EAClE,EAAE,YAAY,EAAE,CACjB,CAA8B,CAAC;IAChC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,mBAAmB;QACjC,OAAO;YACL,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,yDAAyD;SAClE,CAAC;IACJ,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,SAAS,GAAG,SAAS,CAAC;IAC1B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,GAAG,GACP,GAAG,CAAC,eAAe,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,CAAC,CAAC;QAC5E,MAAM,IAAI,GACR,GAAG,GAAG,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,iBAAiB;YAC/C,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,UAAU,CAAC;QACjB,MAAM,EAAE,GAAG,CAAC,MAAM,kBAAkB,CAClC,GAAG,EACH,YAAY,CAAC,SAAS,EAAE,8CAA8C,CAAC,EACvE,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CACxC,CAA6B,CAAC;QAC/B,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC;gBAAE,SAAS,GAAG,iBAAiB,CAAC;YAC7D,MAAM;QACR,CAAC;QACD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,EAAE,CAAC,SAAS;gBAC5B,CAAC,CAAC,MAAM,WAAW,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC;gBACpC,CAAC,CAAC,IAAI,CAAC;YACT,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACzD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAiB,CAAC,CAAC;gBACxE,IACE,QAAQ,CAAC,eAAe;oBACxB,CAAC,EAAE,CAAC,gBAAgB,IAAI,GAAG,CAAC,eAAe,CAAC,EAC5C,CAAC;oBACD,MAAM,KAAK,GACT,qHAAqH,CAAC;oBACxH,MAAM,kBAAkB,CACtB,GAAG,EACH,YAAY,CACV,SAAS,EACT,+CAA+C,CAChD,EACD,EAAE,WAAW,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CACjD,CAAC;oBACF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;gBACrC,CAAC;YACH,CAAC;YACD,IAAI,SAAS,GAAG,SAAS,CAAC;YAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,CAAC,MAAM,kBAAkB,CACvC,GAAG,EACH,YAAY,CAAC,SAAS,EAAE,4CAA4C,CAAC,EACrE,EAAE,WAAW,EAAE,EAAE,CAAC,GAAG,EAAE,CACxB,CAAsB,CAAC;gBACxB,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpD,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC;oBAC1B,IAAI,OAAO,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;wBACjC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CACvB,YAAY,CACV,SAAS,EACT,yCAAyC,CAC1C,EACD,EAAE,YAAY,EAAE,CACjB,CAAuB,CAAC;wBACzB,SAAS;oBACX,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;gBACjE,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;oBAC1B,MAAM,kBAAkB,CACtB,GAAG,EACH,YAAY,CACV,SAAS,EACT,+CAA+C,CAChD,EACD;wBACE,WAAW,EAAE,EAAE,CAAC,GAAG;wBACnB,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;wBAC/C,KAAK,EAAE,UAAU,CAAC,MAAM;qBACzB,CACF,CAAC;oBACF,OAAO;wBACL,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;wBAC/C,MAAM,EAAE,UAAU,CAAC,MAAM;qBAC1B,CAAC;gBACJ,CAAC;gBACD,MAAM,QAAQ,GAAG;oBACf,mBAAmB,EAAE,EAAE,CAAC,GAAG;oBAC3B,gBAAgB,EAAE,GAAG,CAAC,cAAc;iBACrC,CAAC;gBACF,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBAC3B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,cAAc,CACnD,GAAG,CAAC,gBAAiB,EACrB,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAC/B,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE,CAC7C,CAAC;oBACF,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;wBAC9B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;oBAClE,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAC3C;wBACE,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,QAAQ,EAAE,GAAG,CAAC,QAAQ;wBACtB,WAAW,EAAE,GAAG,CAAC,kBAAkB;wBACnC,cAAc,EAAE,cAAc,GAAG,CAAC,GAAG,EAAE;wBACvC,QAAQ;qBACT,EACD,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE,CAC7C,CAAC;oBACF,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ;wBACnE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;oBACrE,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,MAAM,yBAAyB,GAC7B,EAAE,CAAC,IAAI,KAAK,UAAU;gBACpB,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAiB,CAAC,CAAC;qBACrD,eAAe;gBACpB,CAAC,CAAC,SAAS,CAAC;YAChB,MAAM,kBAAkB,CACtB,GAAG,EACH,YAAY,CACV,SAAS,EACT,+CAA+C,CAChD,EACD;gBACE,WAAW,EAAE,EAAE,CAAC,GAAG;gBACnB,KAAK,EAAE,WAAW;gBAClB,SAAS,EAAE,SAAS;gBACpB,yBAAyB;aAC1B,CACF,CAAC;YACF,SAAS,GAAG,WAAW,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC7C,2EAA2E;YAC3E,wEAAwE;YACxE,MAAM,kBAAkB,CACtB,GAAG,EACH,YAAY,CACV,SAAS,EACT,+CAA+C,CAChD,EACD,EAAE,WAAW,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CACpC,CAAC;YACF,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CACvB,YAAY,CAAC,SAAS,EAAE,yCAAyC,CAAC,EAClE,EAAE,YAAY,EAAE,CACjB,CAAuB,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAc,EACd,SAAoB,EACpB,GAAW,EACX,cAAsB;IAEtB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAC9B,YAAY,CAAC,SAAS,EAAE,6CAA6C,CAAC,EACtE,EAAE,cAAc,EAAE,CACnB,CAAgC,CAAC;IAClC,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE;QAC1B,IACE,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ;YAC3B,OAAO,GAAG,CAAC,kBAAkB,KAAK,QAAQ;YAE1C,OAAO,CAAC,IAAI,CAAC,MAAM,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { Component, RunCtx } from "../helpers.js";
|
|
3
|
+
import type { Doc } from "../../component/_generated/dataModel.js";
|
|
4
|
+
export declare function reserveAffiliatePayout(component: Component, ctx: RunCtx, opts: {
|
|
5
|
+
stripeAccountId: string;
|
|
6
|
+
currency?: string;
|
|
7
|
+
amount: number;
|
|
8
|
+
minimumAmount?: number;
|
|
9
|
+
idempotencyKey?: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
_creationTime: number;
|
|
12
|
+
_id: import("convex/values").GenericId<"affiliatePayoutReleases">;
|
|
13
|
+
affiliateAccountId: string;
|
|
14
|
+
amount: number;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
currency: string;
|
|
17
|
+
minimumAmount: number;
|
|
18
|
+
operationKey: string;
|
|
19
|
+
state: "failed" | "pending" | "submitted" | "unknown";
|
|
20
|
+
stripePayoutId?: string | undefined;
|
|
21
|
+
} | null>;
|
|
22
|
+
export declare function completeAffiliatePayout(component: Component, ctx: RunCtx, operationKey: string, state: "unknown" | "submitted" | "failed", stripePayoutId?: string): Promise<any>;
|
|
23
|
+
export declare function reconcileAffiliatePayout(stripe: Stripe, release: Doc<"affiliatePayoutReleases">): Promise<string | null>;
|
|
24
|
+
//# sourceMappingURL=payoutGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payoutGate.d.ts","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/payoutGate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IACJ,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;;;;;;;;;;;UAcF;AACD,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,EACzC,cAAc,CAAC,EAAE,MAAM,gBAOxB;AACD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,GAAG,CAAC,yBAAyB,CAAC,0BAoBxC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { runMutationOrThrow } from "../helpers.js";
|
|
2
|
+
import { componentRef } from "../webhooks/helpers.js";
|
|
3
|
+
export async function reserveAffiliatePayout(component, ctx, opts) {
|
|
4
|
+
return runMutationOrThrow(ctx, componentRef(component, "affiliatePayouts/mutations/reservePayout"), {
|
|
5
|
+
affiliateAccountId: opts.stripeAccountId,
|
|
6
|
+
currency: opts.currency ?? "usd",
|
|
7
|
+
amount: opts.amount,
|
|
8
|
+
minimumAmount: opts.minimumAmount ?? 1,
|
|
9
|
+
operationKey: opts.idempotencyKey,
|
|
10
|
+
now: Date.now(),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export async function completeAffiliatePayout(component, ctx, operationKey, state, stripePayoutId) {
|
|
14
|
+
return runMutationOrThrow(ctx, componentRef(component, "affiliatePayouts/mutations/finishPayout"), { operationKey, state, stripePayoutId });
|
|
15
|
+
}
|
|
16
|
+
export async function reconcileAffiliatePayout(stripe, release) {
|
|
17
|
+
let count = 0;
|
|
18
|
+
for await (const receipt of stripe.payouts.list({ created: { gte: Math.floor(release.createdAt / 1000) }, limit: 100 }, { stripeAccount: release.affiliateAccountId })) {
|
|
19
|
+
if (++count > 1000)
|
|
20
|
+
throw new Error("Affiliate payout history requires operator reconciliation.");
|
|
21
|
+
if (receipt.metadata?.bsAffiliatePayout !== release._id)
|
|
22
|
+
continue;
|
|
23
|
+
if (receipt.currency !== release.currency ||
|
|
24
|
+
receipt.amount !== release.amount)
|
|
25
|
+
throw new Error("Affiliate payout receipt mismatch.");
|
|
26
|
+
return receipt.id;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=payoutGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payoutGate.js","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/payoutGate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAoB,EACpB,GAAW,EACX,IAMC;IAED,OAAO,kBAAkB,CACvB,GAAG,EACH,YAAY,CAAC,SAAS,EAAE,0CAA0C,CAAC,EACnE;QACE,kBAAkB,EAAE,IAAI,CAAC,eAAe;QACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;QAChC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,CAAC;QACtC,YAAY,EAAE,IAAI,CAAC,cAAc;QACjC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;KAChB,CACgD,CAAC;AACtD,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,SAAoB,EACpB,GAAW,EACX,YAAoB,EACpB,KAAyC,EACzC,cAAuB;IAEvB,OAAO,kBAAkB,CACvB,GAAG,EACH,YAAY,CAAC,SAAS,EAAE,yCAAyC,CAAC,EAClE,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,CACxC,CAAC;AACJ,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAc,EACd,OAAuC;IAEvC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAC7C,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EACtE,EAAE,aAAa,EAAE,OAAO,CAAC,kBAAkB,EAAE,CAC9C,EAAE,CAAC;QACF,IAAI,EAAE,KAAK,GAAG,IAAI;YAChB,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;QACJ,IAAI,OAAO,CAAC,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,GAAG;YAAE,SAAS;QAClE,IACE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ;YACrC,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAEjC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { Doc } from "../../component/_generated/dataModel.js";
|
|
3
|
+
export type RecoveryOperation = Doc<"affiliateRecoveryOperations">;
|
|
4
|
+
export type RecoveryObligation = Doc<"affiliateObligations">;
|
|
5
|
+
export declare function findReceipt(stripe: Stripe, op: RecoveryOperation, row: RecoveryObligation): Promise<string | null>;
|
|
6
|
+
export declare function verifyConnectPath(stripe: Stripe, row: RecoveryObligation, kind: RecoveryOperation["kind"]): Promise<{
|
|
7
|
+
supported: boolean;
|
|
8
|
+
reason: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function classifyRecoveryError(error: unknown): {
|
|
11
|
+
state: "unknown" | "insufficient_balance" | "unsupported" | "failed";
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,6BAA6B,CAAC,CAAC;AACnE,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAC7D,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,iBAAiB,EACrB,GAAG,EAAE,kBAAkB,0BAwCxB;AACD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC;;;GA4ChC;AACD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG;IACrD,KAAK,EAAE,SAAS,GAAG,sBAAsB,GAAG,aAAa,GAAG,QAAQ,CAAC;IACrE,KAAK,EAAE,MAAM,CAAC;CACf,CA2BA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export async function findReceipt(stripe, op, row) {
|
|
2
|
+
let count = 0;
|
|
3
|
+
if (op.kind === "recovery") {
|
|
4
|
+
if (!row.stripeTransferId)
|
|
5
|
+
return null;
|
|
6
|
+
for await (const receipt of stripe.transfers.listReversals(row.stripeTransferId, { limit: 100 })) {
|
|
7
|
+
if (++count > 1000)
|
|
8
|
+
throw new Error("Affiliate receipt history exceeds supported bound.");
|
|
9
|
+
if (receipt.metadata?.bsRecoveryOperation !== op._id)
|
|
10
|
+
continue;
|
|
11
|
+
if (receipt.amount !== op.amount)
|
|
12
|
+
throw new Error("Affiliate reversal receipt amount mismatch.");
|
|
13
|
+
return receipt.id;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
for await (const receipt of stripe.transfers.list({
|
|
18
|
+
transfer_group: `bs_restore_${row._id}`,
|
|
19
|
+
limit: 100,
|
|
20
|
+
})) {
|
|
21
|
+
if (++count > 1000)
|
|
22
|
+
throw new Error("Affiliate restoration history exceeds supported bound.");
|
|
23
|
+
if (receipt.metadata?.bsRecoveryOperation !== op._id)
|
|
24
|
+
continue;
|
|
25
|
+
const destination = typeof receipt.destination === "string"
|
|
26
|
+
? receipt.destination
|
|
27
|
+
: receipt.destination?.id;
|
|
28
|
+
if (receipt.amount !== op.amount ||
|
|
29
|
+
receipt.currency !== row.currency ||
|
|
30
|
+
destination !== row.affiliateAccountId)
|
|
31
|
+
throw new Error("Affiliate restoration receipt identity mismatch.");
|
|
32
|
+
return receipt.id;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
export async function verifyConnectPath(stripe, row, kind) {
|
|
38
|
+
const account = await stripe.v2.core.accounts.retrieve(row.affiliateAccountId, { include: ["configuration.recipient"] });
|
|
39
|
+
const capability = account.configuration?.recipient?.capabilities?.stripe_balance
|
|
40
|
+
?.stripe_transfers;
|
|
41
|
+
if (capability?.status !== "active")
|
|
42
|
+
return {
|
|
43
|
+
supported: false,
|
|
44
|
+
reason: "Connect recipient stripe_transfers capability is not active; operator review required.",
|
|
45
|
+
};
|
|
46
|
+
if (kind === "recovery") {
|
|
47
|
+
if (!row.stripeTransferId)
|
|
48
|
+
return {
|
|
49
|
+
supported: false,
|
|
50
|
+
reason: "No original Connect transfer; recover through future commissions.",
|
|
51
|
+
};
|
|
52
|
+
const original = await stripe.transfers.retrieve(row.stripeTransferId);
|
|
53
|
+
const destination = typeof original.destination === "string"
|
|
54
|
+
? original.destination
|
|
55
|
+
: original.destination?.id;
|
|
56
|
+
if (destination !== row.affiliateAccountId ||
|
|
57
|
+
original.currency !== row.currency ||
|
|
58
|
+
original.source_transaction !== row.sourceChargeId ||
|
|
59
|
+
original.amount !== (row.releasedCommission ?? row.originalCommission))
|
|
60
|
+
return {
|
|
61
|
+
supported: false,
|
|
62
|
+
reason: "Original Connect transfer identity does not match the commission; operator review required.",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
supported: true,
|
|
67
|
+
reason: "Original Connect path verified; reversal remains subject to available balance and provider restrictions.",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function classifyRecoveryError(error) {
|
|
71
|
+
const value = error;
|
|
72
|
+
const code = value.code ?? "";
|
|
73
|
+
const state = ["balance_insufficient", "insufficient_funds"].includes(code)
|
|
74
|
+
? "insufficient_balance"
|
|
75
|
+
: [
|
|
76
|
+
"account_invalid",
|
|
77
|
+
"transfers_not_allowed",
|
|
78
|
+
"resource_missing",
|
|
79
|
+
"platform_account_required",
|
|
80
|
+
].includes(code)
|
|
81
|
+
? "unsupported"
|
|
82
|
+
: [
|
|
83
|
+
"StripeConnectionError",
|
|
84
|
+
"StripeAPIError",
|
|
85
|
+
"StripeRateLimitError",
|
|
86
|
+
].includes(value.type ?? "") ||
|
|
87
|
+
code === "idempotency_key_in_use" ||
|
|
88
|
+
!value.type
|
|
89
|
+
? "unknown"
|
|
90
|
+
: "failed";
|
|
91
|
+
return {
|
|
92
|
+
state,
|
|
93
|
+
error: (value.message ?? "Affiliate provider outcome requires reconciliation.").slice(0, 500),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { RecoveryObligation } from "./provider.js";
|
|
3
|
+
/** Operator-selected provenance is verified against exact provider receipts. */
|
|
4
|
+
export declare function verifiedLegacyReview(stripe: Stripe, row: RecoveryObligation, opts: {
|
|
5
|
+
reversalIds: string[];
|
|
6
|
+
restorationTransferIds?: string[];
|
|
7
|
+
note: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
obligationId: import("convex/values").GenericId<"affiliateObligations">;
|
|
10
|
+
recoveredAmount: number;
|
|
11
|
+
restoredAmount: number;
|
|
12
|
+
receipts: {
|
|
13
|
+
id: string;
|
|
14
|
+
amount: number;
|
|
15
|
+
}[];
|
|
16
|
+
restorationIds: string[];
|
|
17
|
+
note: string;
|
|
18
|
+
exposure: {
|
|
19
|
+
kind: "dispute" | "refund";
|
|
20
|
+
chargeAmount: number;
|
|
21
|
+
principal: number;
|
|
22
|
+
finalized: boolean;
|
|
23
|
+
refundedPrincipal: number;
|
|
24
|
+
disputeId: string;
|
|
25
|
+
disputeState: "lost" | "open" | "won" | undefined;
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../src/client/affiliateRecovery/review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,gFAAgF;AAChF,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;;GAyGF"}
|