@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,370 @@
1
+ // @vitest-environment edge-runtime
2
+ import { convexTest } from "convex-test";
3
+ import { describe, expect, it } from "vitest";
4
+ import { api } from "./_generated/api.js";
5
+ import schema from "./schema.js";
6
+ import { proportionalCommission } from "./settlement/clawbacks.js";
7
+
8
+ const modules = import.meta.glob("./**/*.*s");
9
+ const setup = async (currency = "usd", commission = 1001) => {
10
+ const t = convexTest(schema, modules);
11
+ await t.mutation(api.settlement.mutations.reserveSettlementAllocation, {
12
+ sourceChargeId: "ch_1",
13
+ sourceInvoiceId: "in_1",
14
+ paymentIntentId: "pi_1",
15
+ gross: 10000,
16
+ currency,
17
+ processingFee: 0,
18
+ platformNet: 10000 - commission,
19
+ transfers: [
20
+ {
21
+ destinationAccountId: "acct_aff",
22
+ amount: commission,
23
+ role: "affiliate",
24
+ },
25
+ ],
26
+ });
27
+ await t.mutation(api.connect.mutations.upsertTransfer, {
28
+ stripeTransferId: "tr_1",
29
+ sourceChargeId: "ch_1",
30
+ destinationAccountId: "acct_aff",
31
+ currency,
32
+ amount: commission,
33
+ role: "affiliate",
34
+ status: "paid",
35
+ });
36
+ return t;
37
+ };
38
+ type Test = Awaited<ReturnType<typeof setup>>;
39
+ const claim = (
40
+ t: Test,
41
+ id: string,
42
+ principal: number,
43
+ kind: "refund" | "dispute" = "refund",
44
+ finalized = kind === "refund",
45
+ ) =>
46
+ t.mutation(api.connect.mutations.claimReversalSlices, {
47
+ sourceChargeId: "ch_1",
48
+ operationId: id,
49
+ mode: { kind: "fraction", chargeAmount: 10000, amountRefunded: principal },
50
+ commissionExposure: { kind, chargeAmount: 10000, principal, finalized },
51
+ });
52
+ const rows = (t: Test) =>
53
+ t.query(api.settlement.queries.listAffiliateObligations, {
54
+ affiliateAccountId: "acct_aff",
55
+ currency: "usd",
56
+ paginationOpts: { numItems: 10, cursor: null },
57
+ });
58
+
59
+ describe("original affiliate commission obligations", () => {
60
+ it("converges repeated, reordered, duplicate partial refunds on the original snapshot", async () => {
61
+ const t = await setup();
62
+ expect((await claim(t, "re_1", 2500)).slices).toEqual([
63
+ {
64
+ stripeTransferId: "tr_1",
65
+ from: 0,
66
+ to: 250,
67
+ confirmed: 0,
68
+ commissionTracked: true,
69
+ },
70
+ ]);
71
+ expect((await claim(t, "re_2", 7500)).slices[0]).toMatchObject({
72
+ from: 250,
73
+ to: 751,
74
+ });
75
+ expect((await claim(t, "re_old", 5000)).slices).toEqual([]);
76
+ expect((await claim(t, "re_1", 10000)).slices[0]).toMatchObject({
77
+ from: 0,
78
+ to: 250,
79
+ });
80
+ expect((await rows(t)).page[0]).toMatchObject({
81
+ originalCommission: 1001,
82
+ finalizedAmount: 751,
83
+ recoveredAmount: 0,
84
+ sourceInvoiceId: "in_1",
85
+ paymentIntentId: "pi_1",
86
+ provisionalAmount: 0,
87
+ });
88
+ expect((await claim(t, "re_full", 10000)).slices[0]).toMatchObject({
89
+ from: 751,
90
+ to: 1001,
91
+ });
92
+ });
93
+
94
+ it.each([false, true])(
95
+ "deduplicates refund/dispute overlap regardless of arrival order (refund first: %s)",
96
+ async (refundFirst) => {
97
+ const t = await setup();
98
+ if (refundFirst) {
99
+ await claim(t, "re_1", 5000);
100
+ await claim(t, "dp_1", 10000, "dispute");
101
+ } else {
102
+ await claim(t, "dp_1", 10000, "dispute");
103
+ await claim(t, "re_1", 5000);
104
+ }
105
+ expect((await rows(t)).page[0]).toMatchObject({
106
+ finalizedAmount: 501,
107
+ provisionalAmount: 500,
108
+ recoveredAmount: 0,
109
+ });
110
+ expect((await claim(t, "dp_1", 10000, "dispute", true)).replay).toBe(
111
+ true,
112
+ );
113
+ expect((await rows(t)).page[0]).toMatchObject({
114
+ finalizedAmount: 1001,
115
+ provisionalAmount: 0,
116
+ });
117
+ const ops = await t.run((ctx) =>
118
+ ctx.db.query("transferReversalOps").collect(),
119
+ );
120
+ expect(
121
+ ops.flatMap((o) => o.slices).reduce((n, s) => n + s.to - s.from, 0),
122
+ ).toBe(1001);
123
+ },
124
+ );
125
+
126
+ it("does not create an obligation for pending refunds or invalid money", async () => {
127
+ const t = await setup();
128
+ await expect(claim(t, "re_pending", 5000, "refund", false)).rejects.toThrow(
129
+ "Pending refunds",
130
+ );
131
+ await expect(claim(t, "re_bad", 1.5)).rejects.toThrow("integer units");
132
+ expect((await rows(t)).page).toEqual([]);
133
+ });
134
+
135
+ it("keeps simultaneous claims disjoint and reservations separate from recovery", async () => {
136
+ const t = await setup();
137
+ const results = await Promise.all([
138
+ claim(t, "re_1", 5000),
139
+ claim(t, "dp_1", 5000, "dispute"),
140
+ ]);
141
+ expect(
142
+ results.flatMap((r) => r.slices).reduce((n, s) => n + s.to - s.from, 0),
143
+ ).toBe(501);
144
+ expect((await rows(t)).page[0].recoveredAmount).toBe(0);
145
+ });
146
+
147
+ it("records exact receipts atomically, rejects mismatches, and survives duplicate confirmation", async () => {
148
+ const t = await setup();
149
+ await claim(t, "re_1", 5000);
150
+ const confirmation = {
151
+ stripeTransferId: "tr_1",
152
+ reversedAmount: 501,
153
+ receipt: { stripeReversalId: "trr_1", operationId: "re_1", amount: 501 },
154
+ };
155
+ await expect(
156
+ t.mutation(api.connect.mutations.recordTransferReversal, {
157
+ ...confirmation,
158
+ receipt: { ...confirmation.receipt, amount: 500 },
159
+ }),
160
+ ).rejects.toThrow("does not match");
161
+ expect((await rows(t)).page[0].recoveredAmount).toBe(0);
162
+ await t.mutation(
163
+ api.connect.mutations.recordTransferReversal,
164
+ confirmation,
165
+ );
166
+ await t.mutation(
167
+ api.connect.mutations.recordTransferReversal,
168
+ confirmation,
169
+ );
170
+ await t.mutation(api.connect.mutations.recordTransferReversal, {
171
+ stripeTransferId: "tr_1",
172
+ reversedAmount: 250,
173
+ });
174
+ expect((await rows(t)).page[0]).toMatchObject({
175
+ recoveredAmount: 501,
176
+ finalizedAmount: 501,
177
+ needsReconciliation: false,
178
+ });
179
+ expect(
180
+ await t.run((ctx) => ctx.db.query("affiliateReversalReceipts").collect()),
181
+ ).toHaveLength(1);
182
+ await expect(
183
+ t.mutation(api.connect.mutations.recordTransferReversal, {
184
+ stripeTransferId: "tr_1",
185
+ reversedAmount: 1002,
186
+ }),
187
+ ).rejects.toThrow("Invalid confirmed");
188
+ });
189
+
190
+ it("isolates recurring invoice payments and preserves fixed commission and currency units", async () => {
191
+ const t = await setup("jpy", 7);
192
+ await t.mutation(api.connect.mutations.upsertTransfer, {
193
+ stripeTransferId: "tr_2",
194
+ sourceChargeId: "ch_2",
195
+ sourceInvoiceId: "in_2",
196
+ destinationAccountId: "acct_aff",
197
+ currency: "jpy",
198
+ amount: 7,
199
+ role: "affiliate",
200
+ status: "paid",
201
+ });
202
+ await claim(t, "re_1", 5000);
203
+ await t.mutation(api.connect.mutations.claimReversalSlices, {
204
+ sourceChargeId: "ch_2",
205
+ operationId: "re_2",
206
+ mode: { kind: "full" },
207
+ commissionExposure: {
208
+ kind: "refund",
209
+ chargeAmount: 10000,
210
+ principal: 10000,
211
+ finalized: true,
212
+ },
213
+ });
214
+ const result = await t.query(
215
+ api.settlement.queries.listAffiliateObligations,
216
+ {
217
+ affiliateAccountId: "acct_aff",
218
+ currency: "jpy",
219
+ paginationOpts: { numItems: 10, cursor: null },
220
+ },
221
+ );
222
+ expect(
223
+ result.page.map((r) => [
224
+ r.sourceInvoiceId,
225
+ r.originalCommission,
226
+ r.finalizedAmount,
227
+ ]),
228
+ ).toEqual([
229
+ ["in_1", 7, 4],
230
+ ["in_2", 7, 7],
231
+ ]);
232
+ expect((await rows(t)).page).toEqual([]);
233
+ });
234
+
235
+ it("runs a resumable, replay-safe legacy migration and flags unknown provenance", async () => {
236
+ const t = await setup();
237
+ await t.mutation(api.connect.mutations.recordTransferReversal, {
238
+ stripeTransferId: "tr_1",
239
+ reversedAmount: 100,
240
+ });
241
+ const run = () =>
242
+ t.mutation(api.settlement.mutations.migrateAffiliateObligations, {
243
+ paginationOpts: { numItems: 10, cursor: null },
244
+ });
245
+ expect(await run()).toMatchObject({
246
+ migrated: 1,
247
+ skipped: 0,
248
+ isDone: true,
249
+ });
250
+ await run();
251
+ expect((await rows(t)).page).toHaveLength(1);
252
+ expect((await rows(t)).page[0]).toMatchObject({
253
+ originalCommission: 1001,
254
+ recoveredAmount: 100,
255
+ finalizedAmount: 0,
256
+ needsReconciliation: true,
257
+ });
258
+ expect(
259
+ await t.run((ctx) => ctx.db.query("affiliateReversalReceipts").collect()),
260
+ ).toEqual([]);
261
+ });
262
+
263
+ it("rejects reuse of operation/source and immutable commission identity", async () => {
264
+ const t = await setup();
265
+ await claim(t, "re_1", 5000);
266
+ await expect(
267
+ t.mutation(api.connect.mutations.claimReversalSlices, {
268
+ operationId: "re_1",
269
+ sourceChargeId: "ch_other",
270
+ mode: { kind: "full" },
271
+ }),
272
+ ).rejects.toThrow("source identity");
273
+ await t.run(async (ctx) => {
274
+ const transfer = (await ctx.db.query("transfers").first())!;
275
+ await ctx.db.patch("transfers", transfer._id, { amount: 2000 });
276
+ });
277
+ await expect(claim(t, "re_2", 10000)).rejects.toThrow(
278
+ "identity cannot change",
279
+ );
280
+ });
281
+ });
282
+
283
+ it("rounds proportional snapshots exactly even above floating-point multiplication precision", () => {
284
+ expect(proportionalCommission(7, 5000, 10000)).toBe(4);
285
+ expect(proportionalCommission(1001, 2500, 10000)).toBe(250);
286
+ expect(
287
+ proportionalCommission(
288
+ Number.MAX_SAFE_INTEGER,
289
+ Number.MAX_SAFE_INTEGER - 1,
290
+ Number.MAX_SAFE_INTEGER,
291
+ ),
292
+ ).toBe(Number.MAX_SAFE_INTEGER - 1);
293
+ expect(proportionalCommission(7, 20000, 10000)).toBe(7);
294
+ expect(() => proportionalCommission(7, 1, 0)).toThrow();
295
+ });
296
+
297
+ it("freezes webhook commission identity and keeps provider upserts monotonic", async () => {
298
+ const t = await setup();
299
+ await claim(t, "re_1", 5000);
300
+ const original = {
301
+ stripeTransferId: "tr_1",
302
+ sourceChargeId: "ch_1",
303
+ destinationAccountId: "acct_aff",
304
+ currency: "usd",
305
+ amount: 1001,
306
+ role: "affiliate" as const,
307
+ status: "paid" as const,
308
+ };
309
+ await expect(
310
+ t.mutation(api.connect.mutations.upsertTransfer, {
311
+ ...original,
312
+ amount: 2000,
313
+ }),
314
+ ).rejects.toThrow("identity cannot change");
315
+ await expect(
316
+ t.mutation(api.connect.mutations.upsertTransfer, {
317
+ ...original,
318
+ role: "store",
319
+ }),
320
+ ).rejects.toThrow("original affiliate");
321
+ await t.mutation(api.connect.mutations.upsertTransfer, {
322
+ ...original,
323
+ reversedAmount: 501,
324
+ });
325
+ await t.mutation(api.connect.mutations.upsertTransfer, original);
326
+ expect((await rows(t)).page[0].recoveredAmount).toBe(501);
327
+ });
328
+
329
+ it("sizes a partial dispute using gross charge principal, not the sum of transferred funds", async () => {
330
+ const t = await setup();
331
+ const result = await t.mutation(api.connect.mutations.claimReversalSlices, {
332
+ sourceChargeId: "ch_1",
333
+ operationId: "dp_1",
334
+ mode: { kind: "amount", amount: 5000 },
335
+ commissionExposure: {
336
+ kind: "dispute",
337
+ chargeAmount: 10000,
338
+ principal: 5000,
339
+ finalized: false,
340
+ },
341
+ });
342
+ expect(result.slices[0]).toMatchObject({ from: 0, to: 501 });
343
+ expect((await rows(t)).page[0]).toMatchObject({
344
+ finalizedAmount: 0,
345
+ provisionalAmount: 501,
346
+ });
347
+ });
348
+
349
+ it("fails a charge beyond the transfer bound before reserving any money", async () => {
350
+ const t = await setup();
351
+ await t.run(async (ctx) => {
352
+ for (let i = 0; i < 1000; i++)
353
+ await ctx.db.insert("transfers", {
354
+ stripeTransferId: `tr_extra_${i}`,
355
+ sourceChargeId: "ch_1",
356
+ destinationAccountId: "acct_store",
357
+ currency: "usd",
358
+ amount: 1,
359
+ role: "store",
360
+ status: "paid",
361
+ });
362
+ });
363
+ await expect(claim(t, "re_1", 10000)).rejects.toThrow(
364
+ "recovery limit exceeded",
365
+ );
366
+ expect((await rows(t)).page).toEqual([]);
367
+ expect(
368
+ await t.run((ctx) => ctx.db.query("transferReversalOps").collect()),
369
+ ).toEqual([]);
370
+ });
@@ -0,0 +1,83 @@
1
+ // @vitest-environment edge-runtime
2
+ import { convexTest } from "convex-test";
3
+ import { expect, it } from "vitest";
4
+ import type {
5
+ RegisteredQuery,
6
+ RegisteredMutation,
7
+ DefaultFunctionArgs,
8
+ } from "convex/server";
9
+ import type { MutationCtx } from "./_generated/server.js";
10
+ import schema from "./schema.js";
11
+ import {
12
+ recoverySnapshot,
13
+ pendingOperations,
14
+ } from "./affiliateRecovery/queries.js";
15
+ import { listAffiliateObligations } from "./settlement/queries.js";
16
+ import { migrateAffiliateObligations } from "./settlement/mutations.js";
17
+
18
+ function handler<Args extends DefaultFunctionArgs, Result>(
19
+ fn:
20
+ | RegisteredQuery<"public", Args, Result>
21
+ | RegisteredMutation<"public", Args, Result>,
22
+ ) {
23
+ return (
24
+ fn as unknown as { _handler: (ctx: MutationCtx, args: Args) => Result }
25
+ )._handler;
26
+ }
27
+
28
+ // convex-test supports native pagination even though deployed components do not.
29
+ // Make that runtime restriction explicit while exercising real indexed reads.
30
+ function componentDb<T extends object>(db: T): T {
31
+ function wrap<TValue extends object>(value: TValue): TValue {
32
+ return new Proxy(value, {
33
+ get(target, key) {
34
+ if (key === "paginate")
35
+ return () => {
36
+ throw new Error("paginate() is only supported in the app");
37
+ };
38
+ const member = Reflect.get(target, key);
39
+ if (typeof member !== "function") return member;
40
+ return (...args: unknown[]) => {
41
+ const result = member.apply(target, args);
42
+ return result &&
43
+ typeof result === "object" &&
44
+ !(result instanceof Promise)
45
+ ? wrap(result)
46
+ : result;
47
+ };
48
+ },
49
+ });
50
+ }
51
+ return wrap(db);
52
+ }
53
+ it("reads recovery pages and migrates inside the deployed component pagination boundary", async () => {
54
+ const t = convexTest(schema, import.meta.glob("./**/*.*s"));
55
+ await t.run(async (ctx) => {
56
+ const componentCtx = { ...ctx, db: componentDb(ctx.db) };
57
+ const paginationOpts = { numItems: 10, cursor: null };
58
+ expect(
59
+ await handler(listAffiliateObligations)(componentCtx, {
60
+ affiliateAccountId: "acct_a",
61
+ currency: "usd",
62
+ paginationOpts,
63
+ }),
64
+ ).toMatchObject({ page: [], isDone: true });
65
+ const snapshot = await handler(recoverySnapshot)(componentCtx, {
66
+ affiliateAccountId: "acct_a",
67
+ currency: "usd",
68
+ paginationOpts,
69
+ });
70
+ expect(snapshot.obligations).toMatchObject({ page: [], isDone: true });
71
+ expect(
72
+ await handler(pendingOperations)(componentCtx, {
73
+ state: "unknown",
74
+ paginationOpts,
75
+ }),
76
+ ).toMatchObject({ page: [], isDone: true });
77
+ expect(
78
+ await handler(migrateAffiliateObligations)(componentCtx, {
79
+ paginationOpts,
80
+ }),
81
+ ).toMatchObject({ migrated: 0, isDone: true });
82
+ });
83
+ });
@@ -0,0 +1,160 @@
1
+ // @vitest-environment edge-runtime
2
+ import { convexTest } from "convex-test";
3
+ import { expect, it, vi } from "vitest";
4
+ import type Stripe from "stripe";
5
+ import { api } from "./_generated/api.js";
6
+ import schema from "./schema.js";
7
+ import { changeBalance } from "./affiliateRecovery/accounting.js";
8
+ import { createPayout } from "../client/connect/payouts.js";
9
+ import { AffiliateRecoveryClient } from "../client/affiliateRecovery.js";
10
+ import type { Component, RunCtx } from "../client/helpers.js";
11
+ const modules = import.meta.glob("./**/*.*s");
12
+ const symbol = Symbol.for("toReferencePath");
13
+ function reference(path = "betterStripe"): Component {
14
+ return new Proxy(
15
+ {},
16
+ {
17
+ get: (_target, key) =>
18
+ key === symbol
19
+ ? path
20
+ : typeof key === "string"
21
+ ? reference(`${path}/${key}`)
22
+ : undefined,
23
+ },
24
+ ) as Component;
25
+ }
26
+ async function setup() {
27
+ const t = convexTest(schema, modules);
28
+ await t.run((ctx) => changeBalance(ctx, "acct_affiliate", "usd", {}));
29
+ const route = (ref: unknown) =>
30
+ (ref as Record<symbol, string>)[symbol]
31
+ .split("/")
32
+ .slice(1)
33
+ .reduce(
34
+ (node, key) => (node as Record<string, unknown>)[key],
35
+ api as unknown,
36
+ );
37
+ const mutation = t.mutation as unknown as (
38
+ ref: unknown,
39
+ args: unknown,
40
+ ) => Promise<unknown>;
41
+ const query = t.query as unknown as (
42
+ ref: unknown,
43
+ args: unknown,
44
+ ) => Promise<unknown>;
45
+ const ctx = {
46
+ runMutation: (ref: unknown, args: unknown) => mutation(route(ref), args),
47
+ runQuery: (ref: unknown, args: unknown) => query(route(ref), args),
48
+ } as unknown as RunCtx;
49
+ const payouts = {
50
+ list: vi.fn(() => ({ async *[Symbol.asyncIterator]() {} })),
51
+ create: vi.fn(async () => ({ id: "po_created" })),
52
+ };
53
+ const provider = { payouts } as unknown as Stripe;
54
+ const component = reference();
55
+ const opts = {
56
+ stripeAccountId: "acct_affiliate",
57
+ currency: "usd",
58
+ amount: 1000,
59
+ idempotencyKey: "direct-operation",
60
+ };
61
+ const balance = () =>
62
+ t.run((ctx) => ctx.db.query("affiliateRecoveryBalances").first());
63
+ return { t, ctx, provider, payouts, component, opts, balance };
64
+ }
65
+ it("rechecks debt recorded during direct payout receipt lookup before calling Stripe", async () => {
66
+ const f = await setup();
67
+ f.payouts.list.mockImplementation(() => ({
68
+ async *[Symbol.asyncIterator]() {
69
+ await f.t.run((ctx) =>
70
+ changeBalance(ctx, "acct_affiliate", "usd", {
71
+ outstandingDebt: 100,
72
+ }),
73
+ );
74
+ },
75
+ }));
76
+ await expect(
77
+ createPayout(f.provider, f.ctx, f.opts, f.component),
78
+ ).rejects.toThrow(/recovery/);
79
+ expect(f.payouts.create).not.toHaveBeenCalled();
80
+ expect(await f.balance()).toMatchObject({
81
+ outstandingDebt: 100,
82
+ activePayoutKey: f.opts.idempotencyKey,
83
+ pendingPayoutAmount: 1000,
84
+ });
85
+ });
86
+ it("keeps expired direct payout operations reserved without issuing a new payout", async () => {
87
+ const f = await setup();
88
+ await f.t.mutation(api.affiliatePayouts.mutations.reservePayout, {
89
+ affiliateAccountId: f.opts.stripeAccountId,
90
+ currency: "usd",
91
+ amount: 1000,
92
+ minimumAmount: 1,
93
+ operationKey: f.opts.idempotencyKey,
94
+ now: Date.now() - 24 * 60 * 60 * 1000,
95
+ });
96
+ await expect(
97
+ createPayout(f.provider, f.ctx, f.opts, f.component),
98
+ ).rejects.toThrow(/window expired/);
99
+ expect(f.payouts.create).not.toHaveBeenCalled();
100
+ expect(await f.balance()).toMatchObject({
101
+ activePayoutKey: f.opts.idempotencyKey,
102
+ });
103
+ });
104
+ it("submits an eligible direct payout once and returns the confirmed receipt on replay", async () => {
105
+ const f = await setup();
106
+ expect(await createPayout(f.provider, f.ctx, f.opts, f.component)).toEqual({
107
+ stripePayoutId: "po_created",
108
+ });
109
+ expect(await createPayout(f.provider, f.ctx, f.opts, f.component)).toEqual({
110
+ stripePayoutId: "po_created",
111
+ });
112
+ expect(f.payouts.create).toHaveBeenCalledTimes(1);
113
+ expect((await f.balance())?.activePayoutKey).toBeUndefined();
114
+ });
115
+ it("reconciles an expired payout receipt despite later debt without issuing another payout", async () => {
116
+ const f = await setup();
117
+ const release = await f.t.mutation(
118
+ api.affiliatePayouts.mutations.reservePayout,
119
+ {
120
+ affiliateAccountId: f.opts.stripeAccountId,
121
+ currency: "usd",
122
+ amount: 1000,
123
+ minimumAmount: 1,
124
+ operationKey: f.opts.idempotencyKey,
125
+ now: 1,
126
+ },
127
+ );
128
+ await f.t.run((ctx) =>
129
+ changeBalance(ctx, "acct_affiliate", "usd", { outstandingDebt: 100 }),
130
+ );
131
+ f.payouts.list.mockImplementation(
132
+ () =>
133
+ ({
134
+ async *[Symbol.asyncIterator]() {
135
+ yield {
136
+ id: "po_reconciled",
137
+ amount: 1000,
138
+ currency: "usd",
139
+ metadata: { bsAffiliatePayout: release!._id },
140
+ };
141
+ },
142
+ }) as never,
143
+ );
144
+ await new AffiliateRecoveryClient(
145
+ f.component,
146
+ () => f.provider,
147
+ ).reconcilePayouts(f.ctx, {
148
+ state: "pending",
149
+ paginationOpts: { numItems: 10, cursor: null },
150
+ });
151
+ expect(f.payouts.create).not.toHaveBeenCalled();
152
+ expect(await f.balance()).toMatchObject({
153
+ outstandingDebt: 100,
154
+ pendingPayoutAmount: 0,
155
+ });
156
+ expect((await f.balance())?.activePayoutKey).toBeUndefined();
157
+ expect(
158
+ await f.t.run((ctx) => ctx.db.query("affiliatePayoutReleases").first()),
159
+ ).toMatchObject({ state: "submitted", stripePayoutId: "po_reconciled" });
160
+ });