@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
@@ -0,0 +1,247 @@
1
+ import type { Infer } from "convex/values";
2
+ import type { riskKind } from "./schema.js";
3
+ import type { Triggers } from "convex-helpers/server/triggers";
4
+ import type { DataModel, Doc } from "../_generated/dataModel.js";
5
+ import type { MutationCtx, QueryCtx } from "../_generated/server.js";
6
+ import { addSummaryAmounts as add } from "../earnings/arithmetic.js";
7
+ import {
8
+ transferFingerprint,
9
+ payoutClaimFingerprint,
10
+ } from "../payoutRelease/helpers.js";
11
+ export async function financialRevision(
12
+ ctx: QueryCtx | MutationCtx,
13
+ accountId: string,
14
+ ) {
15
+ const row = await ctx.db
16
+ .query("payoutFinancialRevisions")
17
+ .withIndex("by_account", (q) => q.eq("accountId", accountId))
18
+ .unique();
19
+ return { revision: row?.revision ?? 0, riskRevision: row?.riskRevision ?? 0 };
20
+ }
21
+ /** Stable fence: ordinary positive funding never writes this row. */
22
+ export async function riskRevision(
23
+ ctx: QueryCtx | MutationCtx,
24
+ accountId: string,
25
+ ) {
26
+ const risk = await ctx.db
27
+ .query("payoutRiskRevisions")
28
+ .withIndex("by_account", (q) => q.eq("accountId", accountId))
29
+ .unique();
30
+ if (risk) return risk.riskRevision;
31
+ return (await financialRevision(ctx, accountId)).riskRevision;
32
+ }
33
+ async function bump(
34
+ ctx: MutationCtx,
35
+ accountId: string,
36
+ risk: boolean,
37
+ kind: Infer<typeof riskKind>,
38
+ key: string,
39
+ removed = false,
40
+ ) {
41
+ const row = await ctx.db
42
+ .query("payoutFinancialRevisions")
43
+ .withIndex("by_account", (q) => q.eq("accountId", accountId))
44
+ .unique();
45
+ const fence = await ctx.db
46
+ .query("payoutRiskRevisions")
47
+ .withIndex("by_account", (q) => q.eq("accountId", accountId))
48
+ .unique();
49
+ if (row && fence && row.riskRevision !== fence.riskRevision)
50
+ throw new Error("Financial risk revisions disagree.");
51
+ const next = {
52
+ revision: add(row?.revision ?? 0, 1),
53
+ riskRevision: add(
54
+ fence?.riskRevision ?? row?.riskRevision ?? 0,
55
+ risk ? 1 : 0,
56
+ ),
57
+ };
58
+ if (row) await ctx.db.patch("payoutFinancialRevisions", row._id, next);
59
+ else await ctx.db.insert("payoutFinancialRevisions", { accountId, ...next });
60
+ if (!fence)
61
+ await ctx.db.insert("payoutRiskRevisions", {
62
+ accountId,
63
+ riskRevision: next.riskRevision,
64
+ });
65
+ else if (risk)
66
+ await ctx.db.patch("payoutRiskRevisions", fence._id, {
67
+ riskRevision: next.riskRevision,
68
+ });
69
+ if (risk)
70
+ await ctx.db.insert("payoutRiskEvents", {
71
+ accountId,
72
+ riskRevision: next.riskRevision,
73
+ kind,
74
+ key,
75
+ ...(removed ? { removed: true } : {}),
76
+ });
77
+ }
78
+ function positiveOnly(
79
+ old: Doc<"transfers"> | null,
80
+ next: Doc<"transfers"> | null,
81
+ ) {
82
+ if (!next) return false;
83
+ if (!old)
84
+ return (
85
+ next.amount >= 0 &&
86
+ (next.reversedAmount ?? 0) === 0 &&
87
+ (next.reversalClaimedAmount ?? 0) === 0 &&
88
+ next.status !== "reversed"
89
+ );
90
+ const identity = (t: Doc<"transfers">) =>
91
+ JSON.stringify([
92
+ t.stripeTransferId,
93
+ t.destinationAccountId,
94
+ t.sourceChargeId,
95
+ t.currency,
96
+ t.role,
97
+ t.reinstatement ?? false,
98
+ ]);
99
+ return (
100
+ identity(old) === identity(next) &&
101
+ next.amount >= old.amount &&
102
+ (next.reversedAmount ?? 0) <= (old.reversedAmount ?? 0) &&
103
+ (next.reversalClaimedAmount ?? 0) <= (old.reversalClaimedAmount ?? 0) &&
104
+ next.status !== "reversed" &&
105
+ !(old.status === "paid" && next.status !== "paid")
106
+ );
107
+ }
108
+ function payoutFingerprint(p: Doc<"payouts">) {
109
+ return JSON.stringify([
110
+ p.stripePayoutId,
111
+ p.accountId,
112
+ p.amount,
113
+ p.currency,
114
+ p.status,
115
+ p.arrivalDate,
116
+ p.method,
117
+ p.failureCode,
118
+ p.failureMessage,
119
+ ]);
120
+ }
121
+ /** Funding, payout claims and allocations commit with their revision counters. */
122
+ export function registerPayoutRevisions(triggers: Triggers<DataModel>) {
123
+ triggers.register("transfers", async (ctx, change) => {
124
+ if (
125
+ change.oldDoc &&
126
+ change.newDoc &&
127
+ transferFingerprint(change.oldDoc) === transferFingerprint(change.newDoc)
128
+ )
129
+ return;
130
+ const accounts = new Set([
131
+ change.oldDoc?.destinationAccountId,
132
+ change.newDoc?.destinationAccountId,
133
+ ]);
134
+ for (const account of accounts)
135
+ if (account)
136
+ await bump(
137
+ ctx,
138
+ account,
139
+ !positiveOnly(change.oldDoc, change.newDoc),
140
+ "transfer",
141
+ (change.newDoc ?? change.oldDoc)!.stripeTransferId,
142
+ change.newDoc === null,
143
+ );
144
+ });
145
+ triggers.register("payouts", async (ctx, change) => {
146
+ if (
147
+ change.oldDoc &&
148
+ change.newDoc &&
149
+ payoutFingerprint(change.oldDoc) === payoutFingerprint(change.newDoc)
150
+ )
151
+ return;
152
+ for (const account of new Set([
153
+ change.oldDoc?.accountId,
154
+ change.newDoc?.accountId,
155
+ ]))
156
+ if (account)
157
+ await bump(
158
+ ctx,
159
+ account,
160
+ true,
161
+ "provider_payout",
162
+ (change.newDoc ?? change.oldDoc)!.stripePayoutId,
163
+ change.newDoc === null,
164
+ );
165
+ });
166
+ triggers.register("payoutSourceAllocations", async (ctx, change) => {
167
+ const fp = (s: Doc<"payoutSourceAllocations">) =>
168
+ JSON.stringify([
169
+ s.accountId,
170
+ s.ownerKey,
171
+ s.stripeTransferId,
172
+ s.currency,
173
+ s.paidAmount,
174
+ s.reservedAmount,
175
+ ]);
176
+ if (
177
+ change.oldDoc &&
178
+ change.newDoc &&
179
+ fp(change.oldDoc) === fp(change.newDoc)
180
+ )
181
+ return;
182
+ for (const account of new Set([
183
+ change.oldDoc?.accountId,
184
+ change.newDoc?.accountId,
185
+ ]))
186
+ if (account)
187
+ await bump(
188
+ ctx,
189
+ account,
190
+ true,
191
+ "allocation",
192
+ (change.newDoc ?? change.oldDoc)!.stripeTransferId,
193
+ change.newDoc === null,
194
+ );
195
+ });
196
+ triggers.register("payoutReleaseClaims", async (ctx, change) => {
197
+ if (
198
+ change.oldDoc &&
199
+ change.newDoc &&
200
+ payoutClaimFingerprint(change.oldDoc) ===
201
+ payoutClaimFingerprint(change.newDoc)
202
+ )
203
+ return;
204
+ for (const account of new Set([
205
+ change.oldDoc?.accountId,
206
+ change.newDoc?.accountId,
207
+ ]))
208
+ if (account)
209
+ await bump(
210
+ ctx,
211
+ account,
212
+ true,
213
+ "claim",
214
+ (change.newDoc ?? change.oldDoc)!.operationKey,
215
+ change.newDoc === null,
216
+ );
217
+ });
218
+ triggers.register("payoutFeeBalances", async (ctx, change) => {
219
+ const fp = (s: Doc<"payoutFeeBalances">) =>
220
+ JSON.stringify([
221
+ s.accountId,
222
+ s.ownerKey,
223
+ s.currency,
224
+ s.amount,
225
+ s.revision,
226
+ ]);
227
+ if (
228
+ change.oldDoc &&
229
+ change.newDoc &&
230
+ fp(change.oldDoc) === fp(change.newDoc)
231
+ )
232
+ return;
233
+ for (const account of new Set([
234
+ change.oldDoc?.accountId,
235
+ change.newDoc?.accountId,
236
+ ]))
237
+ if (account)
238
+ await bump(
239
+ ctx,
240
+ account,
241
+ true,
242
+ "fee",
243
+ (change.newDoc ?? change.oldDoc)!.currency,
244
+ change.newDoc === null,
245
+ );
246
+ });
247
+ }
@@ -0,0 +1,125 @@
1
+ import { defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+ export const financialRevisionsTable = defineTable({
4
+ accountId: v.string(),
5
+ revision: v.number(),
6
+ riskRevision: v.number(),
7
+ }).index("by_account", ["accountId"]);
8
+ export const riskKind = v.union(
9
+ v.literal("transfer"),
10
+ v.literal("provider_payout"),
11
+ v.literal("allocation"),
12
+ v.literal("claim"),
13
+ v.literal("fee"),
14
+ );
15
+ export const riskEventsTable = defineTable({
16
+ accountId: v.string(),
17
+ riskRevision: v.number(),
18
+ kind: riskKind,
19
+ key: v.string(),
20
+ removed: v.optional(v.boolean()),
21
+ }).index("by_account_revision", ["accountId", "riskRevision"]);
22
+ export const collectionPhase = v.union(
23
+ v.literal("sources"),
24
+ v.literal("allocations"),
25
+ v.literal("fees"),
26
+ v.literal("claims"),
27
+ v.literal("repair"),
28
+ v.literal("check"),
29
+ v.literal("local_ready"),
30
+ v.literal("invalidated"),
31
+ v.literal("discarding"),
32
+ );
33
+ export const snapshotFields = {
34
+ readOnly: v.boolean(),
35
+ accountId: v.string(),
36
+ ownerKey: v.string(),
37
+ operationKey: v.string(),
38
+ expectedRevision: v.number(),
39
+ policyMode: v.optional(v.string()),
40
+ policyOperationKey: v.optional(v.string()),
41
+ phase: collectionPhase,
42
+ step: v.number(),
43
+ sourceFence: v.number(),
44
+ sourceCursor: v.optional(v.string()),
45
+ allocationCursor: v.optional(v.string()),
46
+ feeCursor: v.optional(v.string()),
47
+ claimCursor: v.optional(v.string()),
48
+ initialRevision: v.number(),
49
+ initialRiskRevision: v.number(),
50
+ riskRevision: v.number(),
51
+ observedRevision: v.number(),
52
+ sourceCount: v.number(),
53
+ allocationCount: v.number(),
54
+ claimCount: v.number(),
55
+ feeCount: v.number(),
56
+ deferredPositiveChanges: v.boolean(),
57
+ providerChanged: v.boolean(),
58
+ expiresAt: v.number(),
59
+ reason: v.optional(v.string()),
60
+ };
61
+ export const snapshotDoc = v.object({
62
+ ...snapshotFields,
63
+ _id: v.id("payoutSnapshots"),
64
+ _creationTime: v.number(),
65
+ });
66
+ export const snapshotsTable = defineTable(snapshotFields)
67
+ .index("by_operation", ["operationKey"])
68
+ .index("by_read_only", ["readOnly"])
69
+ .index("by_account_phase", ["accountId", "phase"]);
70
+ export const snapshotSourcesTable = defineTable({
71
+ snapshotId: v.id("payoutSnapshots"),
72
+ stripeTransferId: v.string(),
73
+ ledgerFingerprint: v.string(),
74
+ sourceCreatedAt: v.number(),
75
+ currency: v.string(),
76
+ amount: v.number(),
77
+ status: v.string(),
78
+ role: v.optional(v.string()),
79
+ sourceChargeId: v.optional(v.string()),
80
+ reversedAmount: v.number(),
81
+ reversalClaimedAmount: v.number(),
82
+ reinstatement: v.boolean(),
83
+ allocationPresent: v.boolean(),
84
+ allocationCurrency: v.optional(v.string()),
85
+ paidAmount: v.number(),
86
+ reservedAmount: v.number(),
87
+ expectedPaid: v.number(),
88
+ expectedReserved: v.number(),
89
+ needsCheck: v.boolean(),
90
+ })
91
+ .index("by_snapshot", ["snapshotId"])
92
+ .index("by_snapshot_transfer", ["snapshotId", "stripeTransferId"])
93
+ .index("by_snapshot_check", ["snapshotId", "needsCheck"]);
94
+ export const snapshotClaimsTable = defineTable({
95
+ snapshotId: v.id("payoutSnapshots"),
96
+ operationKey: v.string(),
97
+ fingerprint: v.string(),
98
+ currency: v.string(),
99
+ amount: v.number(),
100
+ status: v.string(),
101
+ stripePayoutId: v.optional(v.string()),
102
+ historical: v.optional(v.boolean()),
103
+ providerFee: v.optional(v.number()),
104
+ allocations: v.array(
105
+ v.object({
106
+ stripeTransferId: v.string(),
107
+ amount: v.number(),
108
+ role: v.optional(v.string()),
109
+ }),
110
+ ),
111
+ })
112
+ .index("by_snapshot", ["snapshotId"])
113
+ .index("by_snapshot_operation", ["snapshotId", "operationKey"]);
114
+ export const snapshotFeesTable = defineTable({
115
+ snapshotId: v.id("payoutSnapshots"),
116
+ currency: v.string(),
117
+ amount: v.number(),
118
+ revision: v.number(),
119
+ })
120
+ .index("by_snapshot", ["snapshotId"])
121
+ .index("by_snapshot_currency", ["snapshotId", "currency"]);
122
+ export const riskRevisionsTable = defineTable({
123
+ accountId: v.string(),
124
+ riskRevision: v.number(),
125
+ }).index("by_account", ["accountId"]);
@@ -0,0 +1,7 @@
1
+ import { v } from "convex/values";
2
+ export { validateIdentity } from "../payoutRelease/helpers.js";
3
+ export const identity = {
4
+ accountId: v.string(),
5
+ ownerKey: v.string(),
6
+ operationKey: v.string(),
7
+ };
@@ -0,0 +1,30 @@
1
+ import type { MutationCtx, QueryCtx } from "../_generated/server.js";
2
+ import { validateScope } from "../payoutPolicy/helpers.js";
3
+ export function validate(args: {
4
+ accountId: string;
5
+ ownerKey: string;
6
+ currency: string;
7
+ }) {
8
+ validateScope(args);
9
+ if (!/^[a-z]{3}$/.test(args.currency))
10
+ throw new Error("Invalid worker currency.");
11
+ }
12
+ export async function readJob(
13
+ ctx: MutationCtx | QueryCtx,
14
+ args: { accountId: string; ownerKey: string; currency: string },
15
+ ) {
16
+ validate(args);
17
+ const job = await ctx.db
18
+ .query("payoutWorkerJobs")
19
+ .withIndex("by_account_currency", (q) =>
20
+ q.eq("accountId", args.accountId).eq("currency", args.currency),
21
+ )
22
+ .unique();
23
+ if (job && job.ownerKey !== args.ownerKey)
24
+ throw new Error("Payout worker owner does not match.");
25
+ return job;
26
+ }
27
+ export function pageSize(n: number) {
28
+ if (!Number.isInteger(n) || n < 1 || n > 25)
29
+ throw new Error("Invalid worker page size.");
30
+ }
@@ -0,0 +1,181 @@
1
+ import { v } from "convex/values";
2
+ import { mutation } from "../_generated/server.js";
3
+ import { readPolicy } from "../payoutPolicy/helpers.js";
4
+ import { claimByKey, add } from "../payoutRelease/helpers.js";
5
+ import { balance as recoveryBalance } from "../affiliateRecovery/accounting.js";
6
+ import { readJob, validate } from "./helpers.js";
7
+ import { scope, jobDoc } from "./schema.js";
8
+ const held = v.object({
9
+ status: v.literal("held"),
10
+ reason: v.string(),
11
+ nextAttemptAt: v.optional(v.number()),
12
+ });
13
+ /** A worker lease serializes observations; only the payout claim reserves money. */
14
+ export const begin = mutation({
15
+ args: { ...scope, cadenceMs: v.number() },
16
+ returns: v.union(
17
+ held,
18
+ v.object({
19
+ status: v.literal("run"),
20
+ job: jobDoc,
21
+ reconcileOnly: v.boolean(),
22
+ }),
23
+ ),
24
+ handler: async (ctx, args) => {
25
+ validate(args);
26
+ if (
27
+ !Number.isSafeInteger(args.cadenceMs) ||
28
+ args.cadenceMs < 60000 ||
29
+ args.cadenceMs > 86400000
30
+ )
31
+ throw new Error("Worker cadence must be between one minute and one day.");
32
+ const policy = await readPolicy(ctx, args);
33
+ let job = await readJob(ctx, args);
34
+ const now = Date.now();
35
+ if (job?.leaseUntil && job.leaseUntil > now)
36
+ return {
37
+ status: "held" as const,
38
+ reason: "worker_busy",
39
+ nextAttemptAt: job.leaseUntil,
40
+ };
41
+ if (job && job.nextAttemptAt > now)
42
+ return {
43
+ status: "held" as const,
44
+ reason: "not_due",
45
+ nextAttemptAt: job.nextAttemptAt,
46
+ };
47
+ const claim = job?.operationKey
48
+ ? await claimByKey(ctx, { ...args, operationKey: job.operationKey })
49
+ : null;
50
+ if (claim && claim.currency !== args.currency)
51
+ throw new Error("Worker payout currency changed.");
52
+ const unresolved =
53
+ claim?.status === "reserved" || claim?.status === "submitted";
54
+ if (
55
+ !unresolved &&
56
+ (policy?.mode !== "custom" ||
57
+ policy.status !== "ready" ||
58
+ !policy.holdDeadline ||
59
+ policy.holdDeadline <= now ||
60
+ !policy.thresholds.some((t) => t.currency === args.currency))
61
+ ) {
62
+ if (job)
63
+ await ctx.db.patch("payoutWorkerJobs", job._id, {
64
+ operationKey: undefined,
65
+ hasOperation: false,
66
+ leaseUntil: undefined,
67
+ lastReason: "policy_inactive",
68
+ nextAttemptAt: now + args.cadenceMs,
69
+ });
70
+ return { status: "held" as const, reason: "policy_inactive" };
71
+ }
72
+ if (!job) {
73
+ const id = await ctx.db.insert("payoutWorkerJobs", {
74
+ accountId: args.accountId,
75
+ ownerKey: args.ownerKey,
76
+ currency: args.currency,
77
+ generation: 0,
78
+ sequence: 0,
79
+ cadenceMs: args.cadenceMs,
80
+ nextAttemptAt: now,
81
+ hasOperation: false,
82
+ });
83
+ job = (await ctx.db.get("payoutWorkerJobs", id))!;
84
+ }
85
+ if (!unresolved) {
86
+ const recovery = await recoveryBalance(
87
+ ctx,
88
+ args.accountId,
89
+ args.currency,
90
+ );
91
+ if (
92
+ recovery &&
93
+ (recovery.outstandingDebt > 0 ||
94
+ recovery.reservedRecovery > 0 ||
95
+ recovery.reservedRestoration > 0 ||
96
+ recovery.activePayoutKey)
97
+ ) {
98
+ const nextAttemptAt = now + args.cadenceMs;
99
+ await ctx.db.patch("payoutWorkerJobs", job._id, {
100
+ operationKey: undefined,
101
+ hasOperation: false,
102
+ leaseUntil: undefined,
103
+ lastReason: "affiliate_recovery_pending",
104
+ nextAttemptAt,
105
+ });
106
+ return {
107
+ status: "held" as const,
108
+ reason: "affiliate_recovery_pending",
109
+ nextAttemptAt,
110
+ };
111
+ }
112
+ }
113
+ // A lost response with no claim is safe to resume at the same operation ID.
114
+ // A retained attempted claim always reconciles, including after opt-out.
115
+ const retain = job.operationKey && (!claim || unresolved);
116
+ const sequence = retain ? job.sequence : add(job.sequence, 1);
117
+ const operationKey = retain
118
+ ? job.operationKey!
119
+ : `bs:auto:${job._id}:${sequence}`;
120
+ await ctx.db.patch("payoutWorkerJobs", job._id, {
121
+ generation: add(job.generation, 1),
122
+ sequence,
123
+ operationKey,
124
+ hasOperation: true,
125
+ cadenceMs: args.cadenceMs,
126
+ leaseUntil: now + 600000,
127
+ lastAttemptAt: now,
128
+ });
129
+ return {
130
+ status: "run" as const,
131
+ job: (await ctx.db.get("payoutWorkerJobs", job._id))!,
132
+ reconcileOnly: unresolved && claim.attemptedAt !== undefined,
133
+ };
134
+ },
135
+ });
136
+ export const finish = mutation({
137
+ args: {
138
+ ...scope,
139
+ generation: v.number(),
140
+ operationKey: v.string(),
141
+ reason: v.string(),
142
+ },
143
+ returns: jobDoc,
144
+ handler: async (ctx, args) => {
145
+ const job = await readJob(ctx, args);
146
+ if (
147
+ job?.generation === args.generation &&
148
+ job.completedGeneration === args.generation
149
+ ) {
150
+ if (
151
+ job.completedOperationKey !== args.operationKey ||
152
+ job.lastReason !== args.reason
153
+ )
154
+ throw new Error("Payout worker result cannot be changed.");
155
+ return job;
156
+ }
157
+ if (
158
+ !job ||
159
+ job.generation !== args.generation ||
160
+ job.operationKey !== args.operationKey
161
+ )
162
+ throw new Error("Payout worker generation changed.");
163
+ if (!args.reason || args.reason.length > 120 || /[\r\n]/.test(args.reason))
164
+ throw new Error("Invalid worker result reason.");
165
+ const claim = await claimByKey(ctx, args);
166
+ if (claim && claim.currency !== job.currency)
167
+ throw new Error("Worker payout currency changed.");
168
+ const retain =
169
+ claim?.status === "reserved" || claim?.status === "submitted";
170
+ await ctx.db.patch("payoutWorkerJobs", job._id, {
171
+ operationKey: retain ? job.operationKey : undefined,
172
+ hasOperation: retain,
173
+ leaseUntil: undefined,
174
+ nextAttemptAt: Date.now() + job.cadenceMs,
175
+ lastReason: args.reason,
176
+ completedGeneration: args.generation,
177
+ completedOperationKey: args.operationKey,
178
+ });
179
+ return (await ctx.db.get("payoutWorkerJobs", job._id))!;
180
+ },
181
+ });
@@ -0,0 +1,64 @@
1
+ import { paginationOptsValidator } from "convex/server";
2
+ import { v } from "convex/values";
3
+ import { query } from "../_generated/server.js";
4
+ import { policyDoc } from "../payoutPolicy/schema.js";
5
+ import { readJob, pageSize } from "./helpers.js";
6
+ import { jobDoc, scope } from "./schema.js";
7
+ export const get = query({
8
+ args: scope,
9
+ returns: v.union(jobDoc, v.null()),
10
+ handler: readJob,
11
+ });
12
+ export const policies = query({
13
+ args: { paginationOpts: paginationOptsValidator },
14
+ returns: v.object({
15
+ page: v.array(policyDoc),
16
+ continueCursor: v.string(),
17
+ isDone: v.boolean(),
18
+ }),
19
+ handler: async (ctx, args) => {
20
+ pageSize(args.paginationOpts.numItems);
21
+ const p = await ctx.db
22
+ .query("payoutPolicies")
23
+ .withIndex("by_mode_status", (q) =>
24
+ q.eq("mode", "custom").eq("status", "ready"),
25
+ )
26
+ .paginate(args.paginationOpts);
27
+ return { page: p.page, continueCursor: p.continueCursor, isDone: p.isDone };
28
+ },
29
+ });
30
+ export const changing = query({
31
+ args: { paginationOpts: paginationOptsValidator },
32
+ returns: v.object({
33
+ page: v.array(policyDoc),
34
+ continueCursor: v.string(),
35
+ isDone: v.boolean(),
36
+ }),
37
+ handler: async (ctx, args) => {
38
+ pageSize(args.paginationOpts.numItems);
39
+ const p = await ctx.db
40
+ .query("payoutPolicies")
41
+ .withIndex("by_status", (q) => q.eq("status", "changing"))
42
+ .paginate(args.paginationOpts);
43
+ return { page: p.page, continueCursor: p.continueCursor, isDone: p.isDone };
44
+ },
45
+ });
46
+ /** Includes attempted jobs after opt-out. It is not restricted to custom policies. */
47
+ export const recovering = query({
48
+ args: { paginationOpts: paginationOptsValidator },
49
+ returns: v.object({
50
+ page: v.array(jobDoc),
51
+ continueCursor: v.string(),
52
+ isDone: v.boolean(),
53
+ }),
54
+ handler: async (ctx, args) => {
55
+ pageSize(args.paginationOpts.numItems);
56
+ const p = await ctx.db
57
+ .query("payoutWorkerJobs")
58
+ .withIndex("by_operation_pending", (q) =>
59
+ q.eq("hasOperation", true).lte("nextAttemptAt", Date.now()),
60
+ )
61
+ .paginate(args.paginationOpts);
62
+ return { page: p.page, continueCursor: p.continueCursor, isDone: p.isDone };
63
+ },
64
+ });
@@ -0,0 +1,26 @@
1
+ import { defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+ import { scopeFields } from "../payoutPolicy/schema.js";
4
+ export const scope = { ...scopeFields, currency: v.string() };
5
+ export const fields = {
6
+ ...scope,
7
+ generation: v.number(),
8
+ sequence: v.number(),
9
+ cadenceMs: v.number(),
10
+ nextAttemptAt: v.number(),
11
+ leaseUntil: v.optional(v.number()),
12
+ operationKey: v.optional(v.string()),
13
+ hasOperation: v.boolean(),
14
+ lastReason: v.optional(v.string()),
15
+ lastAttemptAt: v.optional(v.number()),
16
+ completedGeneration: v.optional(v.number()),
17
+ completedOperationKey: v.optional(v.string()),
18
+ };
19
+ export const jobDoc = v.object({
20
+ ...fields,
21
+ _id: v.id("payoutWorkerJobs"),
22
+ _creationTime: v.number(),
23
+ });
24
+ export const jobsTable = defineTable(fields)
25
+ .index("by_account_currency", ["accountId", "currency"])
26
+ .index("by_operation_pending", ["hasOperation", "nextAttemptAt"]);
@@ -428,7 +428,7 @@ describe("core — clearAllTables", () => {
428
428
  accountId: "acct_clear",
429
429
  amount: 1000,
430
430
  currency: "usd",
431
- status: "pending",
431
+ status: "paid",
432
432
  });
433
433
 
434
434
  await t.mutation(api.connect.mutations.upsertRefund, {
@@ -471,7 +471,7 @@ describe("core — clearAllTables", () => {
471
471
  const result = await t.mutation(api.core.mutations.clearAllTables, {});
472
472
  const tableCount = Object.keys(schema.tables).length;
473
473
 
474
- expect(result.cleared).toBe(15);
474
+ expect(result.cleared).toBe(20);
475
475
  expect(result.tables).toHaveLength(tableCount);
476
476
 
477
477
  for (const tableName of Object.keys(schema.tables) as TableNames[]) {