@fantastic.dev/better-stripe 0.23.2 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/README.md +46 -5
  3. package/dist/client/affiliateRecovery/disputes.d.ts +18 -0
  4. package/dist/client/affiliateRecovery/disputes.d.ts.map +1 -0
  5. package/dist/client/affiliateRecovery/disputes.js +33 -0
  6. package/dist/client/affiliateRecovery/disputes.js.map +1 -0
  7. package/dist/client/affiliateRecovery/engine.d.ts +29 -0
  8. package/dist/client/affiliateRecovery/engine.d.ts.map +1 -0
  9. package/dist/client/affiliateRecovery/engine.js +116 -0
  10. package/dist/client/affiliateRecovery/engine.js.map +1 -0
  11. package/dist/client/affiliateRecovery/payoutGate.d.ts +24 -0
  12. package/dist/client/affiliateRecovery/payoutGate.d.ts.map +1 -0
  13. package/dist/client/affiliateRecovery/payoutGate.js +30 -0
  14. package/dist/client/affiliateRecovery/payoutGate.js.map +1 -0
  15. package/dist/client/affiliateRecovery/provider.d.ts +14 -0
  16. package/dist/client/affiliateRecovery/provider.d.ts.map +1 -0
  17. package/dist/client/affiliateRecovery/provider.js +96 -0
  18. package/dist/client/affiliateRecovery/provider.js.map +1 -0
  19. package/dist/client/affiliateRecovery/review.d.ts +28 -0
  20. package/dist/client/affiliateRecovery/review.d.ts.map +1 -0
  21. package/dist/client/affiliateRecovery/review.js +86 -0
  22. package/dist/client/affiliateRecovery/review.js.map +1 -0
  23. package/dist/client/affiliateRecovery/validators.d.ts +402 -0
  24. package/dist/client/affiliateRecovery/validators.d.ts.map +1 -0
  25. package/dist/client/affiliateRecovery/validators.js +27 -0
  26. package/dist/client/affiliateRecovery/validators.js.map +1 -0
  27. package/dist/client/affiliateRecovery.d.ts +131 -0
  28. package/dist/client/affiliateRecovery.d.ts.map +1 -0
  29. package/dist/client/affiliateRecovery.js +69 -0
  30. package/dist/client/affiliateRecovery.js.map +1 -0
  31. package/dist/client/billing/checkout.d.ts +3 -43
  32. package/dist/client/billing/checkout.d.ts.map +1 -1
  33. package/dist/client/billing/checkout.js +5 -260
  34. package/dist/client/billing/checkout.js.map +1 -1
  35. package/dist/client/billing/checkoutRequest.d.ts +129 -0
  36. package/dist/client/billing/checkoutRequest.d.ts.map +1 -0
  37. package/dist/client/billing/checkoutRequest.js +263 -0
  38. package/dist/client/billing/checkoutRequest.js.map +1 -0
  39. package/dist/client/billing/preparedCheckout.d.ts +22 -0
  40. package/dist/client/billing/preparedCheckout.d.ts.map +1 -0
  41. package/dist/client/billing/preparedCheckout.js +86 -0
  42. package/dist/client/billing/preparedCheckout.js.map +1 -0
  43. package/dist/client/checkoutRequests.d.ts +80 -0
  44. package/dist/client/checkoutRequests.d.ts.map +1 -0
  45. package/dist/client/checkoutRequests.js +28 -0
  46. package/dist/client/checkoutRequests.js.map +1 -0
  47. package/dist/client/connect/commissionReceipts.d.ts +4 -0
  48. package/dist/client/connect/commissionReceipts.d.ts.map +1 -0
  49. package/dist/client/connect/commissionReceipts.js +17 -0
  50. package/dist/client/connect/commissionReceipts.js.map +1 -0
  51. package/dist/client/connect/payoutAdoptionTestHelpers.d.ts +958 -2
  52. package/dist/client/connect/payoutAdoptionTestHelpers.d.ts.map +1 -1
  53. package/dist/client/connect/payoutCollection.d.ts +21 -0
  54. package/dist/client/connect/payoutCollection.d.ts.map +1 -0
  55. package/dist/client/connect/payoutCollection.js +47 -0
  56. package/dist/client/connect/payoutCollection.js.map +1 -0
  57. package/dist/client/connect/payoutExecutionTestHelpers.d.ts +1805 -0
  58. package/dist/client/connect/payoutExecutionTestHelpers.d.ts.map +1 -0
  59. package/dist/client/connect/payoutExecutionTestHelpers.js +194 -0
  60. package/dist/client/connect/payoutExecutionTestHelpers.js.map +1 -0
  61. package/dist/client/connect/payoutSyncTestHelpers.d.ts +479 -1
  62. package/dist/client/connect/payoutSyncTestHelpers.d.ts.map +1 -1
  63. package/dist/client/connect/payoutWorker.d.ts +48 -0
  64. package/dist/client/connect/payoutWorker.d.ts.map +1 -0
  65. package/dist/client/connect/payoutWorker.js +78 -0
  66. package/dist/client/connect/payoutWorker.js.map +1 -0
  67. package/dist/client/connect/payouts.d.ts +3 -1
  68. package/dist/client/connect/payouts.d.ts.map +1 -1
  69. package/dist/client/connect/payouts.js +45 -12
  70. package/dist/client/connect/payouts.js.map +1 -1
  71. package/dist/client/connect/refundPrincipal.d.ts +4 -0
  72. package/dist/client/connect/refundPrincipal.d.ts.map +1 -0
  73. package/dist/client/connect/refundPrincipal.js +21 -0
  74. package/dist/client/connect/refundPrincipal.js.map +1 -0
  75. package/dist/client/connect/reversalExecution.d.ts +47 -0
  76. package/dist/client/connect/reversalExecution.d.ts.map +1 -0
  77. package/dist/client/connect/reversalExecution.js +84 -0
  78. package/dist/client/connect/reversalExecution.js.map +1 -0
  79. package/dist/client/connect/transfers.d.ts +8 -4
  80. package/dist/client/connect/transfers.d.ts.map +1 -1
  81. package/dist/client/connect/transfers.js +52 -57
  82. package/dist/client/connect/transfers.js.map +1 -1
  83. package/dist/client/index.d.ts +7 -11
  84. package/dist/client/index.d.ts.map +1 -1
  85. package/dist/client/index.js +10 -10
  86. package/dist/client/index.js.map +1 -1
  87. package/dist/client/payoutPolicy.d.ts +67 -0
  88. package/dist/client/payoutPolicy.d.ts.map +1 -1
  89. package/dist/client/payoutPolicy.js +10 -0
  90. package/dist/client/payoutPolicy.js.map +1 -1
  91. package/dist/client/readContracts.d.ts +11 -1
  92. package/dist/client/readContracts.d.ts.map +1 -1
  93. package/dist/client/readContracts.js +10 -0
  94. package/dist/client/readContracts.js.map +1 -1
  95. package/dist/client/settlement/execution.d.ts.map +1 -1
  96. package/dist/client/settlement/execution.js +30 -3
  97. package/dist/client/settlement/execution.js.map +1 -1
  98. package/dist/client/settlement/reconciliation.d.ts.map +1 -1
  99. package/dist/client/settlement/reconciliation.js +16 -2
  100. package/dist/client/settlement/reconciliation.js.map +1 -1
  101. package/dist/client/webhooks/disputeRecovery.d.ts +4 -0
  102. package/dist/client/webhooks/disputeRecovery.d.ts.map +1 -0
  103. package/dist/client/webhooks/disputeRecovery.js +40 -0
  104. package/dist/client/webhooks/disputeRecovery.js.map +1 -0
  105. package/dist/client/webhooks/processors.d.ts.map +1 -1
  106. package/dist/client/webhooks/processors.js +14 -29
  107. package/dist/client/webhooks/processors.js.map +1 -1
  108. package/dist/component/_generated/component.d.ts +784 -0
  109. package/dist/component/_generated/component.d.ts.map +1 -1
  110. package/dist/component/affiliateEarnings/mutations.d.ts +22 -0
  111. package/dist/component/affiliateEarnings/mutations.d.ts.map +1 -0
  112. package/dist/component/affiliateEarnings/mutations.js +160 -0
  113. package/dist/component/affiliateEarnings/mutations.js.map +1 -0
  114. package/dist/component/affiliatePayouts/mutations.d.ts +25 -0
  115. package/dist/component/affiliatePayouts/mutations.d.ts.map +1 -0
  116. package/dist/component/affiliatePayouts/mutations.js +104 -0
  117. package/dist/component/affiliatePayouts/mutations.js.map +1 -0
  118. package/dist/component/affiliatePayouts/queries.d.ts +23 -0
  119. package/dist/component/affiliatePayouts/queries.d.ts.map +1 -0
  120. package/dist/component/affiliatePayouts/queries.js +23 -0
  121. package/dist/component/affiliatePayouts/queries.js.map +1 -0
  122. package/dist/component/affiliatePayouts/recipientGuard.d.ts +12 -0
  123. package/dist/component/affiliatePayouts/recipientGuard.d.ts.map +1 -0
  124. package/dist/component/affiliatePayouts/recipientGuard.js +26 -0
  125. package/dist/component/affiliatePayouts/recipientGuard.js.map +1 -0
  126. package/dist/component/affiliateRecovery/accounting.d.ts +28 -0
  127. package/dist/component/affiliateRecovery/accounting.d.ts.map +1 -0
  128. package/dist/component/affiliateRecovery/accounting.js +71 -0
  129. package/dist/component/affiliateRecovery/accounting.js.map +1 -0
  130. package/dist/component/affiliateRecovery/mutations.d.ts +65 -0
  131. package/dist/component/affiliateRecovery/mutations.d.ts.map +1 -0
  132. package/dist/component/affiliateRecovery/mutations.js +250 -0
  133. package/dist/component/affiliateRecovery/mutations.js.map +1 -0
  134. package/dist/component/affiliateRecovery/queries.d.ts +305 -0
  135. package/dist/component/affiliateRecovery/queries.d.ts.map +1 -0
  136. package/dist/component/affiliateRecovery/queries.js +113 -0
  137. package/dist/component/affiliateRecovery/queries.js.map +1 -0
  138. package/dist/component/affiliateRecovery/schema.d.ts +171 -0
  139. package/dist/component/affiliateRecovery/schema.d.ts.map +1 -0
  140. package/dist/component/affiliateRecovery/schema.js +71 -0
  141. package/dist/component/affiliateRecovery/schema.js.map +1 -0
  142. package/dist/component/connect/mutations.d.ts +36 -23
  143. package/dist/component/connect/mutations.d.ts.map +1 -1
  144. package/dist/component/connect/mutations.js +7 -124
  145. package/dist/component/connect/mutations.js.map +1 -1
  146. package/dist/component/connect/queries.d.ts +10 -0
  147. package/dist/component/connect/queries.d.ts.map +1 -1
  148. package/dist/component/connect/queries.js +3 -40
  149. package/dist/component/connect/queries.js.map +1 -1
  150. package/dist/component/connect/reversalClaims.d.ts +169 -0
  151. package/dist/component/connect/reversalClaims.d.ts.map +1 -0
  152. package/dist/component/connect/reversalClaims.js +176 -0
  153. package/dist/component/connect/reversalClaims.js.map +1 -0
  154. package/dist/component/connect/reversalDiagnostics.d.ts +59 -0
  155. package/dist/component/connect/reversalDiagnostics.d.ts.map +1 -0
  156. package/dist/component/connect/reversalDiagnostics.js +50 -0
  157. package/dist/component/connect/reversalDiagnostics.js.map +1 -0
  158. package/dist/component/connect/reversalReceipts.d.ts +27 -0
  159. package/dist/component/connect/reversalReceipts.d.ts.map +1 -0
  160. package/dist/component/connect/reversalReceipts.js +85 -0
  161. package/dist/component/connect/reversalReceipts.js.map +1 -0
  162. package/dist/component/connect/schema.d.ts +31 -1
  163. package/dist/component/connect/schema.d.ts.map +1 -1
  164. package/dist/component/connect/schema.js +3 -1
  165. package/dist/component/connect/schema.js.map +1 -1
  166. package/dist/component/connect/validators.d.ts +49 -1
  167. package/dist/component/connect/validators.d.ts.map +1 -1
  168. package/dist/component/connect/validators.js +2 -0
  169. package/dist/component/connect/validators.js.map +1 -1
  170. package/dist/component/core/mutations.d.ts.map +1 -1
  171. package/dist/component/core/mutations.js +2 -0
  172. package/dist/component/core/mutations.js.map +1 -1
  173. package/dist/component/core/resetSafety.d.ts +4 -0
  174. package/dist/component/core/resetSafety.d.ts.map +1 -0
  175. package/dist/component/core/resetSafety.js +54 -0
  176. package/dist/component/core/resetSafety.js.map +1 -0
  177. package/dist/component/earnings/triggers.d.ts +828 -2
  178. package/dist/component/earnings/triggers.d.ts.map +1 -1
  179. package/dist/component/earnings/triggers.js +2 -0
  180. package/dist/component/earnings/triggers.js.map +1 -1
  181. package/dist/component/payoutPolicy/schema.d.ts +2 -0
  182. package/dist/component/payoutPolicy/schema.d.ts.map +1 -1
  183. package/dist/component/payoutPolicy/schema.js +3 -1
  184. package/dist/component/payoutPolicy/schema.js.map +1 -1
  185. package/dist/component/payoutRelease/mutations.d.ts.map +1 -1
  186. package/dist/component/payoutRelease/mutations.js +7 -1
  187. package/dist/component/payoutRelease/mutations.js.map +1 -1
  188. package/dist/component/payoutRelease/queries.d.ts.map +1 -1
  189. package/dist/component/payoutRelease/queries.js +3 -1
  190. package/dist/component/payoutRelease/queries.js.map +1 -1
  191. package/dist/component/payoutRelease/schema.d.ts +1 -0
  192. package/dist/component/payoutRelease/schema.d.ts.map +1 -1
  193. package/dist/component/payoutRelease/schema.js +1 -0
  194. package/dist/component/payoutRelease/schema.js.map +1 -1
  195. package/dist/component/payoutSnapshots/localState.d.ts +89 -0
  196. package/dist/component/payoutSnapshots/localState.d.ts.map +1 -0
  197. package/dist/component/payoutSnapshots/localState.js +186 -0
  198. package/dist/component/payoutSnapshots/localState.js.map +1 -0
  199. package/dist/component/payoutSnapshots/mutations.d.ts +108 -0
  200. package/dist/component/payoutSnapshots/mutations.d.ts.map +1 -0
  201. package/dist/component/payoutSnapshots/mutations.js +263 -0
  202. package/dist/component/payoutSnapshots/mutations.js.map +1 -0
  203. package/dist/component/payoutSnapshots/revisions.d.ts +12 -0
  204. package/dist/component/payoutSnapshots/revisions.d.ts.map +1 -0
  205. package/dist/component/payoutSnapshots/revisions.js +173 -0
  206. package/dist/component/payoutSnapshots/revisions.js.map +1 -0
  207. package/dist/component/payoutSnapshots/schema.d.ts +285 -0
  208. package/dist/component/payoutSnapshots/schema.d.ts.map +1 -0
  209. package/dist/component/payoutSnapshots/schema.js +108 -0
  210. package/dist/component/payoutSnapshots/schema.js.map +1 -0
  211. package/dist/component/payoutSnapshots/scope.d.ts +7 -0
  212. package/dist/component/payoutSnapshots/scope.d.ts.map +1 -0
  213. package/dist/component/payoutSnapshots/scope.js +8 -0
  214. package/dist/component/payoutSnapshots/scope.js.map +1 -0
  215. package/dist/component/payoutWorker/helpers.d.ts +30 -0
  216. package/dist/component/payoutWorker/helpers.d.ts.map +1 -0
  217. package/dist/component/payoutWorker/helpers.js +21 -0
  218. package/dist/component/payoutWorker/helpers.js.map +1 -0
  219. package/dist/component/payoutWorker/mutations.d.ts +68 -0
  220. package/dist/component/payoutWorker/mutations.d.ts.map +1 -0
  221. package/dist/component/payoutWorker/mutations.js +161 -0
  222. package/dist/component/payoutWorker/mutations.js.map +1 -0
  223. package/dist/component/payoutWorker/queries.d.ts +123 -0
  224. package/dist/component/payoutWorker/queries.d.ts.map +1 -0
  225. package/dist/component/payoutWorker/queries.js +61 -0
  226. package/dist/component/payoutWorker/queries.js.map +1 -0
  227. package/dist/component/payoutWorker/schema.d.ts +91 -0
  228. package/dist/component/payoutWorker/schema.d.ts.map +1 -0
  229. package/dist/component/payoutWorker/schema.js +27 -0
  230. package/dist/component/payoutWorker/schema.js.map +1 -0
  231. package/dist/component/schema.d.ts +479 -1
  232. package/dist/component/schema.js +19 -0
  233. package/dist/component/schema.js.map +1 -1
  234. package/dist/component/settlement/clawbackSchema.d.ts +161 -0
  235. package/dist/component/settlement/clawbackSchema.d.ts.map +1 -0
  236. package/dist/component/settlement/clawbackSchema.js +73 -0
  237. package/dist/component/settlement/clawbackSchema.js.map +1 -0
  238. package/dist/component/settlement/clawbacks.d.ts +55 -0
  239. package/dist/component/settlement/clawbacks.d.ts.map +1 -0
  240. package/dist/component/settlement/clawbacks.js +146 -0
  241. package/dist/component/settlement/clawbacks.js.map +1 -0
  242. package/dist/component/settlement/mutations.d.ts +16 -0
  243. package/dist/component/settlement/mutations.d.ts.map +1 -1
  244. package/dist/component/settlement/mutations.js +48 -0
  245. package/dist/component/settlement/mutations.js.map +1 -1
  246. package/dist/component/settlement/queries.d.ts +53 -0
  247. package/dist/component/settlement/queries.d.ts.map +1 -1
  248. package/dist/component/settlement/queries.js +26 -0
  249. package/dist/component/settlement/queries.js.map +1 -1
  250. package/dist/react/workflows/AffiliateRecovery.d.ts +46 -0
  251. package/dist/react/workflows/AffiliateRecovery.d.ts.map +1 -0
  252. package/dist/react/workflows/AffiliateRecovery.js +84 -0
  253. package/dist/react/workflows/AffiliateRecovery.js.map +1 -0
  254. package/dist/react/workflows/index.d.ts +2 -0
  255. package/dist/react/workflows/index.d.ts.map +1 -1
  256. package/dist/react/workflows/index.js +1 -0
  257. package/dist/react/workflows/index.js.map +1 -1
  258. package/dist/testing/affiliate-recovery.d.ts +10 -0
  259. package/dist/testing/affiliate-recovery.d.ts.map +1 -0
  260. package/dist/testing/affiliate-recovery.js +126 -0
  261. package/dist/testing/affiliate-recovery.js.map +1 -0
  262. package/dist/testing/index.d.ts +2 -0
  263. package/dist/testing/index.d.ts.map +1 -1
  264. package/dist/testing/index.js +1 -0
  265. package/dist/testing/index.js.map +1 -1
  266. package/docs/affiliate-clawbacks.md +306 -0
  267. package/docs/read-contracts.md +48 -0
  268. package/docs/settlement.md +145 -1
  269. package/package.json +2 -1
  270. package/src/client/affiliateRecovery/disputes.ts +62 -0
  271. package/src/client/affiliateRecovery/engine.ts +196 -0
  272. package/src/client/affiliateRecovery/payoutGate.ts +65 -0
  273. package/src/client/affiliateRecovery/provider.ts +127 -0
  274. package/src/client/affiliateRecovery/review.ts +117 -0
  275. package/src/client/affiliateRecovery/validators.ts +38 -0
  276. package/src/client/affiliateRecovery.ts +173 -0
  277. package/src/client/billing/checkout.ts +11 -359
  278. package/src/client/billing/checkoutRequest.ts +362 -0
  279. package/src/client/billing/prepared-checkout.test.ts +430 -0
  280. package/src/client/billing/preparedCheckout.ts +143 -0
  281. package/src/client/checkoutRequests.ts +40 -0
  282. package/src/client/connect/commissionReceipts.test.ts +157 -0
  283. package/src/client/connect/commissionReceipts.ts +23 -0
  284. package/src/client/connect/payoutCollection.test.ts +74 -0
  285. package/src/client/connect/payoutCollection.ts +82 -0
  286. package/src/client/connect/payoutExecution.test.ts +1 -211
  287. package/src/client/connect/payoutExecutionTestHelpers.ts +213 -0
  288. package/src/client/connect/payoutWorker.test.ts +145 -0
  289. package/src/client/connect/payoutWorker.ts +144 -0
  290. package/src/client/connect/payouts.ts +75 -15
  291. package/src/client/connect/refundPrincipal.test.ts +46 -0
  292. package/src/client/connect/refundPrincipal.ts +26 -0
  293. package/src/client/connect/reversalExecution.ts +130 -0
  294. package/src/client/connect/transfers.test.ts +3 -3
  295. package/src/client/connect/transfers.ts +93 -92
  296. package/src/client/index.ts +16 -28
  297. package/src/client/payoutPolicy.ts +26 -1
  298. package/src/client/readContracts.ts +23 -0
  299. package/src/client/settlement/execution.test.ts +5 -0
  300. package/src/client/settlement/execution.ts +50 -3
  301. package/src/client/settlement/reconciliation.ts +20 -2
  302. package/src/client/webhooks/disputeRecovery.ts +74 -0
  303. package/src/client/webhooks/processors.test.ts +36 -53
  304. package/src/client/webhooks/processors.ts +18 -31
  305. package/src/component/_generated/api.ts +40 -0
  306. package/src/component/_generated/component.ts +975 -1
  307. package/src/component/affiliate-clawbacks.test.ts +370 -0
  308. package/src/component/affiliate-component-pagination.test.ts +83 -0
  309. package/src/component/affiliate-direct-payout.test.ts +160 -0
  310. package/src/component/affiliate-recovery-lifecycle.test.ts +587 -0
  311. package/src/component/affiliateEarnings/mutations.ts +196 -0
  312. package/src/component/affiliatePayouts/mutations.ts +121 -0
  313. package/src/component/affiliatePayouts/queries.ts +28 -0
  314. package/src/component/affiliatePayouts/recipientGuard.ts +42 -0
  315. package/src/component/affiliateRecovery/accounting.ts +111 -0
  316. package/src/component/affiliateRecovery/mutations.ts +275 -0
  317. package/src/component/affiliateRecovery/queries.ts +138 -0
  318. package/src/component/affiliateRecovery/schema.ts +86 -0
  319. package/src/component/connect/mutations.ts +9 -160
  320. package/src/component/connect/queries.ts +4 -47
  321. package/src/component/connect/reversalClaims.ts +232 -0
  322. package/src/component/connect/reversalDiagnostics.ts +61 -0
  323. package/src/component/connect/reversalReceipts.ts +147 -0
  324. package/src/component/connect/schema.ts +3 -3
  325. package/src/component/connect/validators.ts +2 -0
  326. package/src/component/core/mutations.ts +2 -0
  327. package/src/component/core/resetSafety.ts +67 -0
  328. package/src/component/earnings/triggers.ts +2 -0
  329. package/src/component/payout-recovery-integration.test.ts +172 -0
  330. package/src/component/payout-reset-safety.test.ts +148 -0
  331. package/src/component/payout-revisions.test.ts +125 -0
  332. package/src/component/payout-snapshots.test.ts +395 -0
  333. package/src/component/payout-worker.test.ts +165 -0
  334. package/src/component/payoutPolicy/schema.ts +3 -1
  335. package/src/component/payoutRelease/mutations.ts +10 -1
  336. package/src/component/payoutRelease/queries.ts +3 -1
  337. package/src/component/payoutRelease/schema.ts +1 -0
  338. package/src/component/payoutSnapshots/localState.ts +238 -0
  339. package/src/component/payoutSnapshots/mutations.ts +275 -0
  340. package/src/component/payoutSnapshots/revisions.ts +247 -0
  341. package/src/component/payoutSnapshots/schema.ts +125 -0
  342. package/src/component/payoutSnapshots/scope.ts +7 -0
  343. package/src/component/payoutWorker/helpers.ts +30 -0
  344. package/src/component/payoutWorker/mutations.ts +181 -0
  345. package/src/component/payoutWorker/queries.ts +64 -0
  346. package/src/component/payoutWorker/schema.ts +26 -0
  347. package/src/component/public.test.ts +2 -2
  348. package/src/component/schema.ts +36 -0
  349. package/src/component/settlement/clawbackSchema.ts +78 -0
  350. package/src/component/settlement/clawbacks.ts +233 -0
  351. package/src/component/settlement/mutations.ts +54 -0
  352. package/src/component/settlement/queries.ts +35 -0
  353. package/src/component/summary-arithmetic.test.ts +9 -4
  354. package/src/react/workflows/AffiliateRecovery.test.tsx +106 -0
  355. package/src/react/workflows/AffiliateRecovery.tsx +273 -0
  356. package/src/react/workflows/index.ts +9 -0
  357. package/src/testing/affiliate-recovery.ts +137 -0
  358. package/src/testing/index.ts +2 -0
@@ -1,3 +1,4 @@
1
+ import { listWedgedReversalClaimsDefinition } from "./reversalDiagnostics.js";
1
2
  import {
2
3
  getAccountEarnings as getAccountEarningsLegacy,
3
4
  getAccountPayouts as getAccountPayoutsLegacy,
@@ -24,7 +25,6 @@ import {
24
25
  refundStatusValidator,
25
26
  transferDocValidator,
26
27
  transferReversalOpDocValidator,
27
- wedgedReversalClaimValidator,
28
28
  } from "./validators";
29
29
 
30
30
  // PAYMENT QUERIES
@@ -394,52 +394,9 @@ export const getReversalOp = query({
394
394
  * per-query document-read limit (16,384) — beyond which it throws (loud) rather
395
395
  * than silently truncating, matching the BTS-64 aggregate convention.
396
396
  */
397
- export const listWedgedReversalClaims = query({
398
- args: {},
399
- returns: v.array(wedgedReversalClaimValidator),
400
- handler: async (ctx) => {
401
- // Collect the wedged legs first.
402
- const wedged: Doc<"transfers">[] = [];
403
- for await (const tr of ctx.db.query("transfers")) {
404
- if ((tr.reversalClaimedAmount ?? 0) > (tr.reversedAmount ?? 0)) {
405
- wedged.push(tr);
406
- }
407
- }
408
- if (wedged.length === 0) return [];
409
-
410
- const wedgedIds = new Set(wedged.map((t) => t.stripeTransferId));
411
-
412
- // Index the ops that hold an unexecuted slice on a wedged leg. A slice is
413
- // unexecuted when its `to` exceeds the leg's confirmed `reversedAmount`.
414
- const confirmedById = new Map(
415
- wedged.map((t) => [t.stripeTransferId, t.reversedAmount ?? 0]),
416
- );
417
- const blockersByLeg = new Map<
418
- string,
419
- { operationId: string; from: number; to: number }[]
420
- >();
421
- for await (const op of ctx.db.query("transferReversalOps")) {
422
- for (const s of op.slices) {
423
- if (!wedgedIds.has(s.stripeTransferId)) continue;
424
- if (s.to <= (confirmedById.get(s.stripeTransferId) ?? 0)) continue;
425
- const list = blockersByLeg.get(s.stripeTransferId) ?? [];
426
- list.push({ operationId: op.operationId, from: s.from, to: s.to });
427
- blockersByLeg.set(s.stripeTransferId, list);
428
- }
429
- }
430
-
431
- return wedged.map((t) => ({
432
- stripeTransferId: t.stripeTransferId,
433
- sourceChargeId: t.sourceChargeId,
434
- amount: t.amount,
435
- reversedAmount: t.reversedAmount ?? 0,
436
- reversalClaimedAmount: t.reversalClaimedAmount ?? 0,
437
- blockingOps: (blockersByLeg.get(t.stripeTransferId) ?? []).sort(
438
- (a, b) => a.from - b.from,
439
- ),
440
- }));
441
- },
442
- });
397
+ export const listWedgedReversalClaims = query(
398
+ listWedgedReversalClaimsDefinition,
399
+ );
443
400
 
444
401
  // EARNINGS AGGREGATES (BTS-64)
445
402
  //
@@ -0,0 +1,232 @@
1
+ import { v } from "convex/values";
2
+ import type { MutationCtx } from "../_generated/server.js";
3
+ import { computeReversalSlices } from "../lib/reversals.js";
4
+ import {
5
+ applyCommissionExposure,
6
+ applyObligationExposure,
7
+ } from "../settlement/clawbacks.js";
8
+ import { commissionExposureValidator } from "../settlement/clawbackSchema.js";
9
+ import type { Infer } from "convex/values";
10
+ /** How `claimReversalSlices` sizes each leg's slice. */
11
+ const reversalModeValidator = v.union(
12
+ v.object({ kind: v.literal("full") }),
13
+ v.object({ kind: v.literal("percent"), percent: v.number() }),
14
+ v.object({ kind: v.literal("amount"), amount: v.number() }),
15
+ v.object({
16
+ kind: v.literal("fraction"),
17
+ chargeAmount: v.number(),
18
+ amountRefunded: v.number(),
19
+ }),
20
+ );
21
+
22
+ /**
23
+ * Atomically claim reversal slices for one operation (a dispute or refund
24
+ * clawback) BEFORE any money moves (BTS-63). Runs in a single transaction, so
25
+ * concurrent operations serialize here instead of racing between the ledger
26
+ * read and the Stripe call:
27
+ *
28
+ * - First claim for an `operationId`: computes slices against each leg's
29
+ * claim frontier (`max(reversedAmount, reversalClaimedAmount)`), advances
30
+ * the frontier, and records the slices — atomically. A concurrent distinct
31
+ * operation claiming the same target gets an EMPTY claim, not a duplicate.
32
+ * - Any later call with the same `operationId` (webhook redelivery, retry
33
+ * after a partial failure) replays the recorded slices verbatim, so the
34
+ * caller re-sends byte-identical Stripe params under the same idempotency
35
+ * keys no matter how the ledger moved in between.
36
+ *
37
+ * Each returned slice carries the leg's current CONFIRMED `reversedAmount` so
38
+ * the executor can skip slices whose money already moved and was recorded.
39
+ */
40
+ export const claimReversalSlicesDefinition = {
41
+ args: {
42
+ operationId: v.string(),
43
+ sourceChargeId: v.string(),
44
+ mode: reversalModeValidator,
45
+ commissionExposure: v.optional(commissionExposureValidator),
46
+ },
47
+ returns: v.object({
48
+ replay: v.boolean(),
49
+ slices: v.array(
50
+ v.object({
51
+ stripeTransferId: v.string(),
52
+ from: v.number(),
53
+ to: v.number(),
54
+ confirmed: v.number(),
55
+ commissionTracked: v.optional(v.boolean()),
56
+ }),
57
+ ),
58
+ }),
59
+ handler: async (
60
+ ctx: MutationCtx,
61
+ args: {
62
+ operationId: string;
63
+ sourceChargeId: string;
64
+ mode: Infer<typeof reversalModeValidator>;
65
+ commissionExposure?: Infer<typeof commissionExposureValidator>;
66
+ },
67
+ ) => {
68
+ const confirmedFor = async (stripeTransferId: string) => {
69
+ const row = await ctx.db
70
+ .query("transfers")
71
+ .withIndex("by_stripe_transfer_id", (q) =>
72
+ q.eq("stripeTransferId", stripeTransferId),
73
+ )
74
+ .first();
75
+ return row?.reversedAmount ?? 0;
76
+ };
77
+
78
+ const isAffiliate = async (stripeTransferId: string) => {
79
+ const row = await ctx.db
80
+ .query("transfers")
81
+ .withIndex("by_stripe_transfer_id", (q) =>
82
+ q.eq("stripeTransferId", stripeTransferId),
83
+ )
84
+ .unique();
85
+ return row?.role === "affiliate";
86
+ };
87
+ // Fail atomically rather than commit a partial recovery beyond the bound.
88
+ const chargeRows = await ctx.db
89
+ .query("transfers")
90
+ .withIndex("by_source_charge_id", (q) =>
91
+ q.eq("sourceChargeId", args.sourceChargeId),
92
+ )
93
+ .take(1001);
94
+ if (chargeRows.length > 1000)
95
+ throw new Error("Charge transfer recovery limit exceeded.");
96
+ const rows = chargeRows.filter((t) => !t.reinstatement);
97
+
98
+ const existingOp = await ctx.db
99
+ .query("transferReversalOps")
100
+ .withIndex("by_operation_id", (q) =>
101
+ q.eq("operationId", args.operationId),
102
+ )
103
+ .first();
104
+ const exposure =
105
+ existingOp?.commissionExposure && args.commissionExposure
106
+ ? {
107
+ ...existingOp.commissionExposure,
108
+ finalized:
109
+ existingOp.commissionExposure.finalized ||
110
+ args.commissionExposure.finalized,
111
+ disputeState:
112
+ args.commissionExposure.disputeState ??
113
+ existingOp.commissionExposure.disputeState,
114
+ refundedPrincipal: args.commissionExposure.refundedPrincipal,
115
+ }
116
+ : existingOp
117
+ ? undefined
118
+ : args.commissionExposure;
119
+ const targets = new Map<string, number>();
120
+ if (exposure) {
121
+ for (const transfer of rows)
122
+ if (transfer.role === "affiliate")
123
+ targets.set(
124
+ transfer.stripeTransferId,
125
+ await applyCommissionExposure(ctx, transfer, exposure),
126
+ );
127
+ const untransferred = await ctx.db
128
+ .query("affiliateObligations")
129
+ .withIndex("by_charge", (q) =>
130
+ q.eq("sourceChargeId", args.sourceChargeId),
131
+ )
132
+ .take(1001);
133
+ if (untransferred.length > 1000)
134
+ throw new Error("Affiliate obligation limit exceeded.");
135
+ for (const row of untransferred)
136
+ if (!row.stripeTransferId)
137
+ await applyObligationExposure(ctx, row, exposure);
138
+ }
139
+ if (existingOp) {
140
+ if (existingOp.sourceChargeId !== args.sourceChargeId)
141
+ throw new Error("Reversal operation source identity cannot change.");
142
+ if (existingOp.commissionExposure && args.commissionExposure) {
143
+ if (
144
+ existingOp.commissionExposure.kind !== args.commissionExposure.kind ||
145
+ existingOp.commissionExposure.chargeAmount !==
146
+ args.commissionExposure.chargeAmount
147
+ )
148
+ throw new Error("Commission operation identity cannot change.");
149
+ if (exposure)
150
+ await ctx.db.patch("transferReversalOps", existingOp._id, {
151
+ commissionExposure: exposure,
152
+ });
153
+ }
154
+ const slices = [];
155
+ for (const s of existingOp.slices) {
156
+ slices.push({
157
+ ...s,
158
+ confirmed: await confirmedFor(s.stripeTransferId),
159
+ ...(existingOp.commissionExposure
160
+ ? { commissionTracked: await isAffiliate(s.stripeTransferId) }
161
+ : {}),
162
+ });
163
+ }
164
+ return { replay: true, slices };
165
+ }
166
+
167
+ const byId = new Map(rows.map((r) => [r.stripeTransferId, r]));
168
+ let slices = computeReversalSlices(
169
+ rows.map((t) => ({
170
+ stripeTransferId: t.stripeTransferId,
171
+ amount: t.amount,
172
+ frontier: Math.max(t.reversedAmount ?? 0, t.reversalClaimedAmount ?? 0),
173
+ })),
174
+ args.mode,
175
+ );
176
+
177
+ if (args.commissionExposure) {
178
+ // Keep the shipped seller/other paths; affiliates converge on original
179
+ // commission exposure rather than a second incremental dispute debit.
180
+ const affiliates = rows.filter((t) => t.role === "affiliate");
181
+ slices = slices.filter(
182
+ (s) => byId.get(s.stripeTransferId)!.role !== "affiliate",
183
+ );
184
+ for (const transfer of affiliates) {
185
+ const target = Math.min(
186
+ transfer.amount,
187
+ targets.get(transfer.stripeTransferId) ?? 0,
188
+ );
189
+ const from = Math.max(
190
+ transfer.reversedAmount ?? 0,
191
+ transfer.reversalClaimedAmount ?? 0,
192
+ );
193
+ if (target > from)
194
+ slices.push({
195
+ stripeTransferId: transfer.stripeTransferId,
196
+ from,
197
+ to: target,
198
+ });
199
+ }
200
+ }
201
+
202
+ for (const s of slices) {
203
+ await ctx.db.patch("transfers", byId.get(s.stripeTransferId)!._id, {
204
+ reversalClaimedAmount: s.to,
205
+ });
206
+ }
207
+ // Record the operation even when it claims nothing, so a redelivery is a
208
+ // deterministic no-op instead of a recomputation against newer state.
209
+ await ctx.db.insert("transferReversalOps", {
210
+ operationId: args.operationId,
211
+ sourceChargeId: args.sourceChargeId,
212
+ slices,
213
+ ...(args.commissionExposure
214
+ ? { commissionExposure: args.commissionExposure }
215
+ : {}),
216
+ });
217
+
218
+ return {
219
+ replay: false,
220
+ slices: slices.map((s) => ({
221
+ ...s,
222
+ confirmed: byId.get(s.stripeTransferId)!.reversedAmount ?? 0,
223
+ ...(args.commissionExposure
224
+ ? {
225
+ commissionTracked:
226
+ byId.get(s.stripeTransferId)!.role === "affiliate",
227
+ }
228
+ : {}),
229
+ })),
230
+ };
231
+ },
232
+ };
@@ -0,0 +1,61 @@
1
+ import { v } from "convex/values";
2
+ import type { QueryCtx } from "../_generated/server.js";
3
+ import type { Doc } from "../_generated/dataModel.js";
4
+ import { wedgedReversalClaimValidator } from "./validators.js";
5
+
6
+ export const listWedgedReversalClaimsDefinition = {
7
+ args: {},
8
+ returns: v.array(wedgedReversalClaimValidator),
9
+ handler: async (ctx: QueryCtx) => {
10
+ // Collect the wedged legs first.
11
+ const wedged: Doc<"transfers">[] = [];
12
+ for await (const tr of ctx.db.query("transfers")) {
13
+ const tracked =
14
+ tr.role === "affiliate"
15
+ ? await ctx.db
16
+ .query("affiliateObligations")
17
+ .withIndex("by_transfer", (q) =>
18
+ q.eq("stripeTransferId", tr.stripeTransferId),
19
+ )
20
+ .unique()
21
+ : null;
22
+ if (tracked?.accounted) continue;
23
+ if ((tr.reversalClaimedAmount ?? 0) > (tr.reversedAmount ?? 0)) {
24
+ wedged.push(tr);
25
+ }
26
+ }
27
+ if (wedged.length === 0) return [];
28
+
29
+ const wedgedIds = new Set(wedged.map((t) => t.stripeTransferId));
30
+
31
+ // Index the ops that hold an unexecuted slice on a wedged leg. A slice is
32
+ // unexecuted when its `to` exceeds the leg's confirmed `reversedAmount`.
33
+ const confirmedById = new Map(
34
+ wedged.map((t) => [t.stripeTransferId, t.reversedAmount ?? 0]),
35
+ );
36
+ const blockersByLeg = new Map<
37
+ string,
38
+ { operationId: string; from: number; to: number }[]
39
+ >();
40
+ for await (const op of ctx.db.query("transferReversalOps")) {
41
+ for (const s of op.slices) {
42
+ if (!wedgedIds.has(s.stripeTransferId)) continue;
43
+ if (s.to <= (confirmedById.get(s.stripeTransferId) ?? 0)) continue;
44
+ const list = blockersByLeg.get(s.stripeTransferId) ?? [];
45
+ list.push({ operationId: op.operationId, from: s.from, to: s.to });
46
+ blockersByLeg.set(s.stripeTransferId, list);
47
+ }
48
+ }
49
+
50
+ return wedged.map((t) => ({
51
+ stripeTransferId: t.stripeTransferId,
52
+ sourceChargeId: t.sourceChargeId,
53
+ amount: t.amount,
54
+ reversedAmount: t.reversedAmount ?? 0,
55
+ reversalClaimedAmount: t.reversalClaimedAmount ?? 0,
56
+ blockingOps: (blockersByLeg.get(t.stripeTransferId) ?? []).sort(
57
+ (a, b) => a.from - b.from,
58
+ ),
59
+ }));
60
+ },
61
+ };
@@ -0,0 +1,147 @@
1
+ import { updateObligation } from "../affiliateRecovery/accounting.js";
2
+ import { v } from "convex/values";
3
+ import type { Doc } from "../_generated/dataModel.js";
4
+ import type { MutationCtx } from "../_generated/server.js";
5
+ import { deriveReversalState } from "../lib/reversals.js";
6
+
7
+ async function recordCommissionReceipt(
8
+ ctx: MutationCtx,
9
+ transfer: Doc<"transfers">,
10
+ reversedAmount: number,
11
+ receipt: { stripeReversalId: string; operationId: string; amount: number },
12
+ ) {
13
+ const op = await ctx.db
14
+ .query("transferReversalOps")
15
+ .withIndex("by_operation_id", (q) =>
16
+ q.eq("operationId", receipt.operationId),
17
+ )
18
+ .unique();
19
+ const slice = op?.slices.find(
20
+ (s) => s.stripeTransferId === transfer.stripeTransferId,
21
+ );
22
+ if (
23
+ transfer.role !== "affiliate" ||
24
+ !op?.commissionExposure ||
25
+ !slice ||
26
+ receipt.amount !== slice.to - slice.from ||
27
+ reversedAmount !== slice.to ||
28
+ !receipt.stripeReversalId
29
+ )
30
+ throw new Error(
31
+ "Commission reversal receipt does not match its reserved operation.",
32
+ );
33
+ const priorOperationReceipt = await ctx.db
34
+ .query("affiliateReversalReceipts")
35
+ .withIndex("by_transfer_operation", (q) =>
36
+ q
37
+ .eq("stripeTransferId", transfer.stripeTransferId)
38
+ .eq("operationId", receipt.operationId),
39
+ )
40
+ .unique();
41
+ if (
42
+ priorOperationReceipt &&
43
+ priorOperationReceipt.stripeReversalId !== receipt.stripeReversalId
44
+ )
45
+ throw new Error(
46
+ "Commission operation already has a different reversal receipt.",
47
+ );
48
+ const existing = await ctx.db
49
+ .query("affiliateReversalReceipts")
50
+ .withIndex("by_receipt", (q) =>
51
+ q.eq("stripeReversalId", receipt.stripeReversalId),
52
+ )
53
+ .unique();
54
+ if (existing) {
55
+ if (
56
+ existing.stripeTransferId !== transfer.stripeTransferId ||
57
+ existing.operationId !== receipt.operationId ||
58
+ existing.amount !== receipt.amount
59
+ )
60
+ throw new Error("Commission reversal receipt identity cannot change.");
61
+ } else {
62
+ await ctx.db.insert("affiliateReversalReceipts", {
63
+ ...receipt,
64
+ stripeTransferId: transfer.stripeTransferId,
65
+ });
66
+ }
67
+ }
68
+
69
+ export const recordTransferReversalDefinition = {
70
+ args: {
71
+ stripeTransferId: v.string(),
72
+ reversedAmount: v.number(),
73
+ receipt: v.optional(
74
+ v.object({
75
+ stripeReversalId: v.string(),
76
+ operationId: v.string(),
77
+ amount: v.number(),
78
+ }),
79
+ ),
80
+ },
81
+ returns: v.null(),
82
+ handler: async (
83
+ ctx: MutationCtx,
84
+ args: {
85
+ stripeTransferId: string;
86
+ reversedAmount: number;
87
+ receipt?: {
88
+ stripeReversalId: string;
89
+ operationId: string;
90
+ amount: number;
91
+ };
92
+ },
93
+ ) => {
94
+ const transfer = await ctx.db
95
+ .query("transfers")
96
+ .withIndex("by_stripe_transfer_id", (q) =>
97
+ q.eq("stripeTransferId", args.stripeTransferId),
98
+ )
99
+ .first();
100
+
101
+ if (!transfer) {
102
+ throw new Error(
103
+ `Cannot record reversal: no transfer found for ${args.stripeTransferId}`,
104
+ );
105
+ }
106
+
107
+ // `reversedAmount` is a cumulative total (matches Stripe's
108
+ // `Transfer.amount_reversed`). Keep it monotonic so a stale/out-of-order
109
+ // event can't shrink it or undo a full reversal.
110
+ const reversedAmount = Math.max(
111
+ transfer.reversedAmount ?? 0,
112
+ args.reversedAmount,
113
+ );
114
+ const derived = deriveReversalState(
115
+ transfer.amount,
116
+ reversedAmount,
117
+ transfer.status,
118
+ );
119
+
120
+ if (
121
+ !Number.isSafeInteger(reversedAmount) ||
122
+ reversedAmount < 0 ||
123
+ reversedAmount > transfer.amount
124
+ )
125
+ throw new Error("Invalid confirmed transfer reversal amount.");
126
+ if (args.receipt)
127
+ await recordCommissionReceipt(
128
+ ctx,
129
+ transfer,
130
+ args.reversedAmount,
131
+ args.receipt,
132
+ );
133
+ await ctx.db.patch("transfers", transfer._id, derived);
134
+ const obligation = await ctx.db
135
+ .query("affiliateObligations")
136
+ .withIndex("by_transfer", (q) =>
137
+ q.eq("stripeTransferId", args.stripeTransferId),
138
+ )
139
+ .unique();
140
+ if (obligation)
141
+ await updateObligation(ctx, obligation, {
142
+ recoveredAmount: reversedAmount,
143
+ });
144
+
145
+ return null;
146
+ },
147
+ };
@@ -90,9 +90,9 @@ export const transfersTable = defineTable({
90
90
  * delivery replays byte-identical Stripe requests instead of recomputing
91
91
  * amounts from mutable ledger state.
92
92
  */
93
- export const transferReversalOpsTable = defineTable(
94
- transferReversalOpFields,
95
- ).index("by_operation_id", ["operationId"]);
93
+ export const transferReversalOpsTable = defineTable(transferReversalOpFields)
94
+ .index("by_operation_id", ["operationId"])
95
+ .index("by_source_charge", ["sourceChargeId"]);
96
96
 
97
97
  /**
98
98
  * Disputes (chargebacks). Same indexing strategy as refunds.
@@ -1,3 +1,4 @@
1
+ import { commissionExposureValidator } from "../settlement/clawbackSchema.js";
1
2
  import { type Infer, v } from "convex/values";
2
3
 
3
4
  import { feeRoutingFields, splitRecipientRoleValidator } from "../lib/fees";
@@ -305,6 +306,7 @@ export const transferReversalOpFields = {
305
306
  operationId: v.string(),
306
307
  sourceChargeId: v.string(),
307
308
  slices: v.array(reversalSliceValidator),
309
+ commissionExposure: v.optional(commissionExposureValidator),
308
310
  };
309
311
 
310
312
  /** Full `transferReversalOps` document, including system fields. */
@@ -1,3 +1,4 @@
1
+ import { assertPayoutResetSafe } from "./resetSafety.js";
1
2
  import { v } from "convex/values";
2
3
 
3
4
  import type { TableNames } from "../_generated/dataModel";
@@ -155,6 +156,7 @@ export const clearAllTables = mutation({
155
156
  tables: v.array(v.string()),
156
157
  }),
157
158
  handler: async (ctx) => {
159
+ await assertPayoutResetSafe(ctx);
158
160
  // Derive table names from the schema definition so the clear list can never
159
161
  // drift out of sync when new tables are added.
160
162
  const tables = Object.keys(schema.tables) as TableNames[];
@@ -0,0 +1,67 @@
1
+ import type { MutationCtx } from "../_generated/server.js";
2
+ /** A database reset cannot erase the only restoration or unresolved-payout record. */
3
+ export async function assertPayoutResetSafe(ctx: MutationCtx) {
4
+ const [bound, custom, changing, reserved, submitted, pending, transit] =
5
+ await Promise.all([
6
+ ctx.db
7
+ .query("payoutSnapshots")
8
+ .withIndex("by_read_only", (q) => q.eq("readOnly", false))
9
+ .first(),
10
+ ctx.db
11
+ .query("payoutPolicies")
12
+ .withIndex("by_mode_status", (q) => q.eq("mode", "custom"))
13
+ .first(),
14
+ ctx.db
15
+ .query("payoutPolicies")
16
+ .withIndex("by_mode_status", (q) =>
17
+ q.eq("mode", "stripe_managed").eq("status", "changing"),
18
+ )
19
+ .first(),
20
+ ctx.db
21
+ .query("payoutReleaseClaims")
22
+ .withIndex("by_status", (q) => q.eq("status", "reserved"))
23
+ .first(),
24
+ ctx.db
25
+ .query("payoutReleaseClaims")
26
+ .withIndex("by_status", (q) => q.eq("status", "submitted"))
27
+ .first(),
28
+ ctx.db
29
+ .query("payouts")
30
+ .withIndex("by_status", (q) => q.eq("status", "pending"))
31
+ .first(),
32
+ ctx.db
33
+ .query("payouts")
34
+ .withIndex("by_status", (q) => q.eq("status", "in_transit"))
35
+ .first(),
36
+ ]);
37
+ if (bound)
38
+ throw new Error(
39
+ "A financially bound payout collection must be resolved before reset.",
40
+ );
41
+ if (custom || changing)
42
+ throw new Error(
43
+ "Restore and verify payout schedules before reset; restoration records cannot be erased.",
44
+ );
45
+ if (reserved || submitted)
46
+ throw new Error(
47
+ "Resolve payout claims before reset; unresolved reservations cannot be erased.",
48
+ );
49
+ if (pending || transit)
50
+ throw new Error("Verify in-flight provider payouts before reset.");
51
+ const affiliateOperations = await Promise.all(
52
+ (["pending", "unknown"] as const).flatMap((state) => [
53
+ ctx.db
54
+ .query("affiliatePayoutReleases")
55
+ .withIndex("by_state", (q) => q.eq("state", state))
56
+ .first(),
57
+ ctx.db
58
+ .query("affiliateRecoveryOperations")
59
+ .withIndex("by_state", (q) => q.eq("state", state))
60
+ .first(),
61
+ ]),
62
+ );
63
+ if (affiliateOperations.some(Boolean))
64
+ throw new Error(
65
+ "Resolve pending or unknown affiliate operations before reset.",
66
+ );
67
+ }
@@ -1,3 +1,4 @@
1
+ import { registerPayoutRevisions } from "../payoutSnapshots/revisions.js";
1
2
  import { Triggers } from "convex-helpers/server/triggers";
2
3
  import {
3
4
  customCtx,
@@ -93,4 +94,5 @@ triggers.register("payouts", async (ctx, change) => {
93
94
  await ctx.innerDb.patch("payouts", change.id, { totalsRecorded: true });
94
95
  }
95
96
  });
97
+ registerPayoutRevisions(triggers);
96
98
  export const mutation = customMutation(rawMutation, customCtx(triggers.wrapDB));