@volter/twin-stripe 2.0.37 → 3.0.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 (442) hide show
  1. package/README.md +82 -207
  2. package/dist/generated/pack-facts.json +94 -0
  3. package/dist/spec/SOURCE.md +14 -0
  4. package/dist/spec/fixtures3.json.gz +0 -0
  5. package/dist/spec/openapi.json.gz +0 -0
  6. package/dist/spec/patches.json +34 -0
  7. package/dist/spec/sources.json +378 -0
  8. package/dist/src/cli.js +12 -30
  9. package/dist/src/engine/after-payment.d.ts +9 -0
  10. package/dist/src/engine/after-payment.js +28 -0
  11. package/dist/src/engine/apps-secrets.d.ts +1 -0
  12. package/dist/src/engine/apps-secrets.js +3 -0
  13. package/dist/src/engine/balance.d.ts +5 -0
  14. package/dist/src/engine/balance.js +15 -0
  15. package/dist/src/engine/billing.d.ts +5 -0
  16. package/dist/src/engine/billing.js +6 -0
  17. package/dist/src/engine/charges.d.ts +6 -0
  18. package/dist/src/engine/charges.js +13 -0
  19. package/dist/src/engine/checkout.d.ts +24 -0
  20. package/dist/src/engine/checkout.js +30 -0
  21. package/dist/src/engine/common.d.ts +19 -0
  22. package/dist/src/engine/common.js +32 -0
  23. package/dist/src/{screens → engine}/connect-settings.d.ts +1 -7
  24. package/dist/src/engine/connect-settings.js +26 -0
  25. package/dist/src/engine/connect.d.ts +66 -0
  26. package/dist/src/engine/connect.js +189 -0
  27. package/dist/src/engine/coupons.d.ts +0 -0
  28. package/dist/src/engine/coupons.js +5 -0
  29. package/dist/src/engine/credit-notes.d.ts +1 -0
  30. package/dist/src/engine/credit-notes.js +5 -0
  31. package/dist/src/engine/customers.d.ts +12 -0
  32. package/dist/src/engine/customers.js +30 -0
  33. package/dist/src/engine/display.d.ts +14 -0
  34. package/dist/src/engine/display.js +16 -0
  35. package/dist/src/engine/disputes.d.ts +0 -0
  36. package/dist/src/engine/disputes.js +9 -0
  37. package/dist/src/engine/entitlements.d.ts +1 -0
  38. package/dist/src/engine/entitlements.js +4 -0
  39. package/dist/src/engine/ephemeral-keys.d.ts +0 -0
  40. package/dist/src/engine/ephemeral-keys.js +4 -0
  41. package/dist/src/engine/files.d.ts +18 -0
  42. package/dist/src/engine/files.js +33 -0
  43. package/dist/src/engine/front.d.ts +5 -0
  44. package/dist/src/engine/front.js +25 -0
  45. package/dist/src/engine/invoices.d.ts +24 -0
  46. package/dist/src/engine/invoices.js +57 -0
  47. package/dist/src/engine/issuing.d.ts +35 -0
  48. package/dist/src/engine/issuing.js +45 -0
  49. package/dist/src/engine/ledger.d.ts +9 -0
  50. package/dist/src/engine/ledger.js +9 -0
  51. package/dist/src/engine/oauth.d.ts +4 -0
  52. package/dist/src/engine/oauth.js +25 -0
  53. package/dist/src/engine/onboarding.d.ts +13 -0
  54. package/dist/src/engine/onboarding.js +42 -0
  55. package/dist/src/{stripe-params.d.ts → engine/params.d.ts} +20 -3
  56. package/dist/src/{stripe-params.js → engine/params.js} +7 -60
  57. package/dist/src/engine/payment-intents.d.ts +10 -0
  58. package/dist/src/engine/payment-intents.js +27 -0
  59. package/dist/src/engine/payment-links.d.ts +1 -0
  60. package/dist/src/engine/payment-links.js +5 -0
  61. package/dist/src/engine/payment-methods.d.ts +16 -0
  62. package/dist/src/engine/payment-methods.js +22 -0
  63. package/dist/src/engine/plans.d.ts +6 -0
  64. package/dist/src/engine/plans.js +26 -0
  65. package/dist/src/engine/platform.d.ts +39 -0
  66. package/dist/src/engine/platform.js +34 -0
  67. package/dist/src/engine/products.d.ts +0 -0
  68. package/dist/src/engine/products.js +4 -0
  69. package/dist/src/engine/radar.d.ts +2 -0
  70. package/dist/src/engine/radar.js +5 -0
  71. package/dist/src/engine/refunds.d.ts +1 -0
  72. package/dist/src/engine/refunds.js +28 -0
  73. package/dist/src/engine/renewals.d.ts +36 -0
  74. package/dist/src/engine/renewals.js +65 -0
  75. package/dist/src/engine/setup-intents.d.ts +1 -0
  76. package/dist/src/engine/setup-intents.js +4 -0
  77. package/dist/src/{stripe-twin.d.ts → engine/stripe.d.ts} +11 -39
  78. package/dist/src/{stripe-twin.js → engine/stripe.js} +25 -310
  79. package/dist/src/engine/subscription-schedules.d.ts +3 -0
  80. package/dist/src/engine/subscription-schedules.js +1 -0
  81. package/dist/src/engine/subscriptions.d.ts +12 -0
  82. package/dist/src/engine/subscriptions.js +31 -0
  83. package/dist/src/engine/tax.d.ts +7 -0
  84. package/dist/src/engine/tax.js +28 -0
  85. package/dist/src/engine/terminal.d.ts +4 -0
  86. package/dist/src/engine/terminal.js +20 -0
  87. package/dist/src/engine/test-clocks.d.ts +5 -0
  88. package/dist/src/engine/test-clocks.js +9 -0
  89. package/dist/src/engine/tokens.d.ts +7 -0
  90. package/dist/src/engine/tokens.js +20 -0
  91. package/dist/src/engine/transfers.d.ts +2 -0
  92. package/dist/src/engine/transfers.js +1 -0
  93. package/dist/src/engine/treasury.d.ts +23 -0
  94. package/dist/src/engine/treasury.js +45 -0
  95. package/dist/src/{stripe-version.js → engine/version.js} +4 -3
  96. package/dist/src/engine/webhook-endpoints.d.ts +1 -0
  97. package/dist/src/engine/webhook-endpoints.js +12 -0
  98. package/dist/src/engine/webhooks.d.ts +31 -0
  99. package/dist/src/engine/webhooks.js +68 -0
  100. package/dist/src/fetch.d.ts +2 -0
  101. package/dist/src/fetch.js +26 -0
  102. package/dist/src/generated/surface.gen.json +1 -1
  103. package/dist/src/generated/ui.gen.json +1 -1
  104. package/dist/src/index.d.ts +5 -14
  105. package/dist/src/index.js +8 -75
  106. package/dist/src/manifest.js +232 -758
  107. package/dist/src/screens/billing-portal.d.ts +3 -0
  108. package/dist/src/screens/{portal.js → billing-portal.js} +28 -54
  109. package/dist/src/screens/checkout.d.ts +2 -31
  110. package/dist/src/screens/checkout.js +32 -121
  111. package/dist/src/screens/connect-oauth.d.ts +3 -27
  112. package/dist/src/screens/connect-oauth.js +51 -132
  113. package/dist/src/screens/connect-onboarding.d.ts +3 -0
  114. package/dist/src/screens/{onboarding.js → connect-onboarding.js} +36 -102
  115. package/dist/src/screens/dashboard.d.ts +3 -0
  116. package/dist/src/screens/dashboard.js +117 -0
  117. package/dist/src/screens/shared.d.ts +27 -0
  118. package/dist/src/screens/shared.js +87 -0
  119. package/dist/src/screens/stripe-js.d.ts +3 -0
  120. package/dist/src/screens/stripe-js.js +135 -0
  121. package/dist/src/semantics/account_links.d.ts +2 -0
  122. package/dist/src/semantics/account_links.js +22 -0
  123. package/dist/src/semantics/accounts.d.ts +4 -0
  124. package/dist/src/semantics/accounts.js +38 -0
  125. package/dist/src/semantics/apps.d.ts +4 -0
  126. package/dist/src/semantics/{apps-secrets.js → apps.js} +14 -26
  127. package/dist/src/semantics/around.d.ts +2 -0
  128. package/dist/src/semantics/around.js +16 -0
  129. package/dist/src/semantics/balance.d.ts +2 -11
  130. package/dist/src/semantics/balance.js +3 -194
  131. package/dist/src/semantics/balance_transactions.d.ts +2 -0
  132. package/dist/src/semantics/balance_transactions.js +15 -0
  133. package/dist/src/semantics/billing_portal.d.ts +5 -0
  134. package/dist/src/semantics/billing_portal.js +42 -0
  135. package/dist/src/semantics/charges.d.ts +3 -28
  136. package/dist/src/semantics/charges.js +5 -202
  137. package/dist/src/semantics/checkout.d.ts +3 -20
  138. package/dist/src/semantics/checkout.js +8 -261
  139. package/dist/src/semantics/clock.d.ts +2 -0
  140. package/dist/src/semantics/clock.js +21 -0
  141. package/dist/src/semantics/coupons.d.ts +2 -6
  142. package/dist/src/semantics/coupons.js +2 -60
  143. package/dist/src/semantics/customers.d.ts +6 -6
  144. package/dist/src/semantics/customers.js +27 -387
  145. package/dist/src/semantics/disputes.d.ts +2 -2
  146. package/dist/src/semantics/disputes.js +3 -49
  147. package/dist/src/semantics/doors.d.ts +23 -0
  148. package/dist/src/semantics/doors.js +73 -0
  149. package/dist/src/semantics/entitlements.d.ts +2 -2
  150. package/dist/src/semantics/entitlements.js +5 -50
  151. package/dist/src/semantics/events.d.ts +12 -0
  152. package/dist/src/semantics/events.js +36 -0
  153. package/dist/src/semantics/index.d.ts +2 -0
  154. package/dist/src/semantics/index.js +107 -0
  155. package/dist/src/semantics/invoice_payments.d.ts +2 -0
  156. package/dist/src/semantics/invoice_payments.js +12 -0
  157. package/dist/src/semantics/invoiceitems.d.ts +3 -0
  158. package/dist/src/semantics/invoiceitems.js +59 -0
  159. package/dist/src/semantics/invoices.d.ts +16 -18
  160. package/dist/src/semantics/invoices.js +118 -444
  161. package/dist/src/semantics/payment_intents.d.ts +4 -0
  162. package/dist/src/semantics/payment_intents.js +79 -0
  163. package/dist/src/semantics/payment_methods.d.ts +5 -0
  164. package/dist/src/semantics/payment_methods.js +39 -0
  165. package/dist/src/semantics/payouts.d.ts +2 -0
  166. package/dist/src/semantics/payouts.js +21 -0
  167. package/dist/src/semantics/prices.d.ts +4 -0
  168. package/dist/src/semantics/prices.js +37 -0
  169. package/dist/src/semantics/products.d.ts +6 -2
  170. package/dist/src/semantics/products.js +15 -130
  171. package/dist/src/semantics/promotion_codes.d.ts +3 -0
  172. package/dist/src/semantics/promotion_codes.js +35 -0
  173. package/dist/src/semantics/radar.d.ts +2 -2
  174. package/dist/src/semantics/radar.js +5 -74
  175. package/dist/src/semantics/refunds.d.ts +3 -9
  176. package/dist/src/semantics/refunds.js +6 -192
  177. package/dist/src/semantics/setup_intents.d.ts +3 -0
  178. package/dist/src/semantics/{setup-intents.js → setup_intents.js} +10 -53
  179. package/dist/src/semantics/shared.d.ts +822 -47
  180. package/dist/src/semantics/shared.js +3044 -78
  181. package/dist/src/semantics/states.d.ts +531 -0
  182. package/dist/src/semantics/states.js +852 -0
  183. package/dist/src/semantics/subscription_items.d.ts +5 -0
  184. package/dist/src/semantics/subscription_items.js +55 -0
  185. package/dist/src/semantics/subscription_schedules.d.ts +4 -0
  186. package/dist/src/semantics/subscription_schedules.js +60 -0
  187. package/dist/src/semantics/subscriptions.d.ts +7 -11
  188. package/dist/src/semantics/subscriptions.js +44 -415
  189. package/dist/src/semantics/test_helpers.d.ts +6 -0
  190. package/dist/src/semantics/{test-clocks.js → test_helpers.js} +14 -40
  191. package/dist/src/semantics/transfers.d.ts +2 -2
  192. package/dist/src/semantics/transfers.js +6 -101
  193. package/dist/src/server.d.ts +7 -0
  194. package/dist/src/server.js +7 -0
  195. package/generated/pack-facts.json +94 -0
  196. package/package.json +10 -20
  197. package/spec/SOURCE.md +14 -0
  198. package/spec/fixtures3.json.gz +0 -0
  199. package/spec/openapi.json.gz +0 -0
  200. package/spec/patches.json +34 -0
  201. package/spec/sources.json +378 -0
  202. package/src/cli.ts +12 -26
  203. package/src/engine/after-payment.ts +31 -0
  204. package/src/engine/apps-secrets.ts +4 -0
  205. package/src/engine/balance.ts +33 -0
  206. package/src/engine/billing.ts +16 -0
  207. package/src/engine/charges.ts +19 -0
  208. package/src/engine/checkout.ts +40 -0
  209. package/src/engine/common.ts +47 -0
  210. package/src/engine/connect-settings.ts +23 -0
  211. package/src/engine/connect.ts +184 -0
  212. package/src/engine/coupons.ts +5 -0
  213. package/src/engine/credit-notes.ts +6 -0
  214. package/src/engine/customers.ts +35 -0
  215. package/src/engine/display.ts +19 -0
  216. package/src/engine/disputes.ts +9 -0
  217. package/src/engine/entitlements.ts +5 -0
  218. package/src/engine/ephemeral-keys.ts +3 -0
  219. package/src/engine/files.ts +41 -0
  220. package/src/engine/front.ts +31 -0
  221. package/src/engine/invoices.ts +71 -0
  222. package/src/engine/issuing.ts +61 -0
  223. package/src/engine/ledger.ts +50 -0
  224. package/src/engine/oauth.ts +19 -0
  225. package/src/engine/onboarding.ts +54 -0
  226. package/src/{stripe-params.ts → engine/params.ts} +8 -56
  227. package/src/engine/payment-intents.ts +33 -0
  228. package/src/engine/payment-links.ts +6 -0
  229. package/src/engine/payment-methods.ts +25 -0
  230. package/src/engine/plans.ts +42 -0
  231. package/src/engine/platform.ts +47 -0
  232. package/src/engine/products.ts +3 -0
  233. package/src/engine/radar.ts +6 -0
  234. package/src/engine/refunds.ts +29 -0
  235. package/src/engine/renewals.ts +73 -0
  236. package/src/engine/setup-intents.ts +5 -0
  237. package/src/{stripe-twin.ts → engine/stripe.ts} +28 -323
  238. package/src/engine/subscription-schedules.ts +10 -0
  239. package/src/engine/subscriptions.ts +40 -0
  240. package/src/engine/tax.ts +39 -0
  241. package/src/engine/terminal.ts +23 -0
  242. package/src/engine/test-clocks.ts +11 -0
  243. package/src/engine/tokens.ts +22 -0
  244. package/src/engine/transfers.ts +23 -0
  245. package/src/engine/treasury.ts +47 -0
  246. package/src/{stripe-version.ts → engine/version.ts} +4 -3
  247. package/src/engine/webhook-endpoints.ts +14 -0
  248. package/src/engine/webhooks.ts +72 -0
  249. package/src/fetch.ts +27 -0
  250. package/src/generated/surface.gen.json +1 -1
  251. package/src/generated/ui.gen.json +1 -1
  252. package/src/index.ts +8 -116
  253. package/src/manifest.ts +234 -786
  254. package/src/screens/{portal.tsx → billing-portal.tsx} +32 -37
  255. package/src/screens/checkout.tsx +33 -95
  256. package/src/screens/connect-oauth.tsx +64 -88
  257. package/src/screens/{onboarding.tsx → connect-onboarding.tsx} +38 -82
  258. package/src/screens/dashboard.tsx +198 -0
  259. package/src/screens/shared.tsx +84 -0
  260. package/src/screens/stripe-js.ts +170 -0
  261. package/src/semantics/account_links.ts +21 -0
  262. package/src/semantics/accounts.ts +43 -0
  263. package/src/semantics/{apps-secrets.ts → apps.ts} +17 -32
  264. package/src/semantics/around.ts +20 -0
  265. package/src/semantics/balance.ts +6 -207
  266. package/src/semantics/balance_transactions.ts +20 -0
  267. package/src/semantics/billing_portal.ts +51 -0
  268. package/src/semantics/charges.ts +12 -211
  269. package/src/semantics/checkout.ts +13 -268
  270. package/src/semantics/clock.ts +26 -0
  271. package/src/semantics/coupons.ts +5 -67
  272. package/src/semantics/customers.ts +29 -392
  273. package/src/semantics/disputes.ts +9 -59
  274. package/src/semantics/doors.ts +80 -0
  275. package/src/semantics/entitlements.ts +9 -56
  276. package/src/semantics/events.ts +44 -0
  277. package/src/semantics/index.ts +111 -0
  278. package/src/semantics/invoice_payments.ts +18 -0
  279. package/src/semantics/invoiceitems.ts +59 -0
  280. package/src/semantics/invoices.ts +109 -443
  281. package/src/semantics/payment_intents.ts +83 -0
  282. package/src/semantics/payment_methods.ts +43 -0
  283. package/src/semantics/payouts.ts +23 -0
  284. package/src/semantics/prices.ts +42 -0
  285. package/src/semantics/products.ts +18 -142
  286. package/src/semantics/promotion_codes.ts +39 -0
  287. package/src/semantics/radar.ts +8 -77
  288. package/src/semantics/refunds.ts +9 -212
  289. package/src/semantics/setup_intents.ts +45 -0
  290. package/src/semantics/shared.ts +3092 -106
  291. package/src/semantics/states.ts +883 -0
  292. package/src/semantics/subscription_items.ts +54 -0
  293. package/src/semantics/subscription_schedules.ts +66 -0
  294. package/src/semantics/subscriptions.ts +49 -432
  295. package/src/semantics/{test-clocks.ts → test_helpers.ts} +23 -47
  296. package/src/semantics/transfers.ts +11 -131
  297. package/src/server.ts +8 -0
  298. package/client/dashboard-api.ts +0 -286
  299. package/client/stripe-mirror.css +0 -277
  300. package/client/stripe-mirror.tsx +0 -1559
  301. package/dist/client/dashboard-api.d.ts +0 -107
  302. package/dist/client/dashboard-api.js +0 -238
  303. package/dist/client/dashboard-api.ts +0 -286
  304. package/dist/client/stripe-mirror.bundle.js +0 -236
  305. package/dist/client/stripe-mirror.css +0 -277
  306. package/dist/client/stripe-mirror.d.ts +0 -134
  307. package/dist/client/stripe-mirror.js +0 -859
  308. package/dist/client/stripe-mirror.tsx +0 -1559
  309. package/dist/src/screens/connect-settings.js +0 -103
  310. package/dist/src/screens/consent-skin.d.ts +0 -4
  311. package/dist/src/screens/consent-skin.js +0 -18
  312. package/dist/src/screens/financial-connections.d.ts +0 -5
  313. package/dist/src/screens/financial-connections.js +0 -90
  314. package/dist/src/screens/identity.d.ts +0 -5
  315. package/dist/src/screens/identity.js +0 -86
  316. package/dist/src/screens/onboarding.d.ts +0 -13
  317. package/dist/src/screens/portal.d.ts +0 -5
  318. package/dist/src/screens/public-details.d.ts +0 -5
  319. package/dist/src/screens/public-details.js +0 -90
  320. package/dist/src/semantics/after-payment.d.ts +0 -22
  321. package/dist/src/semantics/after-payment.js +0 -99
  322. package/dist/src/semantics/apps-secrets.d.ts +0 -2
  323. package/dist/src/semantics/billing.d.ts +0 -2
  324. package/dist/src/semantics/billing.js +0 -220
  325. package/dist/src/semantics/connect.d.ts +0 -5
  326. package/dist/src/semantics/connect.js +0 -493
  327. package/dist/src/semantics/credit-notes.d.ts +0 -2
  328. package/dist/src/semantics/credit-notes.js +0 -172
  329. package/dist/src/semantics/ephemeral-keys.d.ts +0 -2
  330. package/dist/src/semantics/ephemeral-keys.js +0 -34
  331. package/dist/src/semantics/files.d.ts +0 -2
  332. package/dist/src/semantics/files.js +0 -125
  333. package/dist/src/semantics/issuing.d.ts +0 -13
  334. package/dist/src/semantics/issuing.js +0 -575
  335. package/dist/src/semantics/ledger.d.ts +0 -59
  336. package/dist/src/semantics/ledger.js +0 -200
  337. package/dist/src/semantics/payment-intents.d.ts +0 -18
  338. package/dist/src/semantics/payment-intents.js +0 -452
  339. package/dist/src/semantics/payment-links.d.ts +0 -2
  340. package/dist/src/semantics/payment-links.js +0 -133
  341. package/dist/src/semantics/payment-methods.d.ts +0 -20
  342. package/dist/src/semantics/payment-methods.js +0 -140
  343. package/dist/src/semantics/plans.d.ts +0 -5
  344. package/dist/src/semantics/plans.js +0 -121
  345. package/dist/src/semantics/platform.d.ts +0 -9
  346. package/dist/src/semantics/platform.js +0 -206
  347. package/dist/src/semantics/renewals.d.ts +0 -47
  348. package/dist/src/semantics/renewals.js +0 -281
  349. package/dist/src/semantics/setup-intents.d.ts +0 -2
  350. package/dist/src/semantics/subscription-schedules.d.ts +0 -2
  351. package/dist/src/semantics/subscription-schedules.js +0 -119
  352. package/dist/src/semantics/tax.d.ts +0 -2
  353. package/dist/src/semantics/tax.js +0 -197
  354. package/dist/src/semantics/terminal.d.ts +0 -5
  355. package/dist/src/semantics/terminal.js +0 -182
  356. package/dist/src/semantics/test-clocks.d.ts +0 -6
  357. package/dist/src/semantics/tokens.d.ts +0 -4
  358. package/dist/src/semantics/tokens.js +0 -44
  359. package/dist/src/semantics/treasury.d.ts +0 -2
  360. package/dist/src/semantics/treasury.js +0 -377
  361. package/dist/src/semantics/webhook-endpoints.d.ts +0 -3
  362. package/dist/src/semantics/webhook-endpoints.js +0 -85
  363. package/dist/src/stripe-budget.d.ts +0 -55
  364. package/dist/src/stripe-budget.js +0 -155
  365. package/dist/src/stripe-capabilities.d.ts +0 -3
  366. package/dist/src/stripe-capabilities.js +0 -5836
  367. package/dist/src/stripe-conformance.d.ts +0 -43
  368. package/dist/src/stripe-conformance.js +0 -105
  369. package/dist/src/stripe-connector.d.ts +0 -161
  370. package/dist/src/stripe-connector.js +0 -414
  371. package/dist/src/stripe-emit.d.ts +0 -2
  372. package/dist/src/stripe-emit.js +0 -145
  373. package/dist/src/stripe-events.d.ts +0 -93
  374. package/dist/src/stripe-events.js +0 -392
  375. package/dist/src/stripe-js.d.ts +0 -4
  376. package/dist/src/stripe-js.js +0 -70
  377. package/dist/src/stripe-mirror-ui.d.ts +0 -15
  378. package/dist/src/stripe-mirror-ui.js +0 -87
  379. package/dist/src/stripe-perform-harness.d.ts +0 -9
  380. package/dist/src/stripe-perform-harness.js +0 -26
  381. package/dist/src/stripe-server.d.ts +0 -33
  382. package/dist/src/stripe-server.js +0 -429
  383. package/dist/src/stripe-shared.d.ts +0 -109
  384. package/dist/src/stripe-shared.js +0 -276
  385. package/dist/src/stripe-ui-conformance.d.ts +0 -5
  386. package/dist/src/stripe-ui-conformance.js +0 -79
  387. package/dist/src/stripe-ui-structure.d.ts +0 -3
  388. package/dist/src/stripe-ui-structure.js +0 -168
  389. package/dist/test-fixtures/stripe-known-deviations.json +0 -110
  390. package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +0 -14
  391. package/dist/test-fixtures/stripe-openapi-operations.json +0 -4717
  392. package/dist/test-fixtures/stripe-schemas.SOURCE.md +0 -35
  393. package/dist/test-fixtures/stripe-schemas.json +0 -3813
  394. package/src/screens/connect-settings.tsx +0 -121
  395. package/src/screens/consent-skin.ts +0 -20
  396. package/src/screens/financial-connections.tsx +0 -101
  397. package/src/screens/identity.tsx +0 -96
  398. package/src/screens/public-details.tsx +0 -105
  399. package/src/semantics/after-payment.ts +0 -118
  400. package/src/semantics/billing.ts +0 -216
  401. package/src/semantics/connect.ts +0 -487
  402. package/src/semantics/credit-notes.ts +0 -168
  403. package/src/semantics/ephemeral-keys.ts +0 -34
  404. package/src/semantics/files.ts +0 -143
  405. package/src/semantics/issuing.ts +0 -590
  406. package/src/semantics/ledger.ts +0 -253
  407. package/src/semantics/payment-intents.ts +0 -463
  408. package/src/semantics/payment-links.ts +0 -148
  409. package/src/semantics/payment-methods.ts +0 -145
  410. package/src/semantics/plans.ts +0 -131
  411. package/src/semantics/platform.ts +0 -220
  412. package/src/semantics/renewals.ts +0 -279
  413. package/src/semantics/setup-intents.ts +0 -87
  414. package/src/semantics/subscription-schedules.ts +0 -129
  415. package/src/semantics/tax.ts +0 -220
  416. package/src/semantics/terminal.ts +0 -195
  417. package/src/semantics/tokens.ts +0 -52
  418. package/src/semantics/treasury.ts +0 -383
  419. package/src/semantics/webhook-endpoints.ts +0 -87
  420. package/src/stripe-budget.ts +0 -181
  421. package/src/stripe-capabilities.ts +0 -5651
  422. package/src/stripe-conformance.ts +0 -124
  423. package/src/stripe-connector.ts +0 -462
  424. package/src/stripe-emit.ts +0 -157
  425. package/src/stripe-events.ts +0 -434
  426. package/src/stripe-js.ts +0 -70
  427. package/src/stripe-mirror-ui.ts +0 -90
  428. package/src/stripe-perform-harness.ts +0 -29
  429. package/src/stripe-server.ts +0 -393
  430. package/src/stripe-shared.ts +0 -297
  431. package/src/stripe-ui-conformance.ts +0 -88
  432. package/src/stripe-ui-structure.ts +0 -182
  433. package/test-fixtures/stripe-known-deviations.json +0 -110
  434. package/test-fixtures/stripe-openapi-operations.json +0 -4717
  435. package/test-fixtures/stripe-schemas.json +0 -3813
  436. /package/dist/src/{screens → engine}/industries.d.ts +0 -0
  437. /package/dist/src/{screens → engine}/industries.js +0 -0
  438. /package/dist/src/{semantics → engine}/test-cards.d.ts +0 -0
  439. /package/dist/src/{semantics → engine}/test-cards.js +0 -0
  440. /package/dist/src/{stripe-version.d.ts → engine/version.d.ts} +0 -0
  441. /package/src/{screens → engine}/industries.ts +0 -0
  442. /package/src/{semantics → engine}/test-cards.ts +0 -0
@@ -1,463 +0,0 @@
1
- // PaymentIntent semantics. The machine in ../manifest.ts says which moves of `status` are legal and
2
- // what Stripe answers otherwise; these handlers compute what each move does. List, retrieve and
3
- // update are the derived core's; search is Stripe's query language over the intents.
4
- import type { Semantics, SemanticsContext } from '@volter/world-core';
5
- import {
6
- asBool,
7
- cardError,
8
- chargeDefaults,
9
- declineFor,
10
- microdepositsNextAction,
11
- mintClientSecret,
12
- requiresAuthentication,
13
- requiresMicrodeposits,
14
- threeDsNextAction,
15
- validateMoney,
16
- verifyMicrodeposits,
17
- type StripeResponse,
18
- } from '../stripe-twin.ts';
19
- import { afterCharge, mintMandate } from './after-payment.ts';
20
- import { cancelAuthorization, captureAuthorization } from './charges.ts';
21
- import { settleCharge } from './ledger.ts';
22
- import { materializeTestMethod, methodFromData, microdepositsAsked, paymentMethodDetails } from './payment-methods.ts';
23
- import { at_, confirmOnly, created, fail, finder, search, type Row } from './shared.ts';
24
-
25
- const PI = 'payment_intent';
26
- /** Bookkeeping: the intents whose caller named the methods (payment_method_types, allowed_payment_method_types) on
27
- * create. The twin's default list (['card']) stands for Stripe's choice and restricts nothing, so only a named list
28
- * refuses a confirm with a method of another type. Kept apart from the intent: a `_` subject is never pushed
29
- * (world-core isTwinBookkeeping). */
30
- const TYPES_GIVEN = '_payment_intent_types_given';
31
- /** Whether the intent's caller named its methods: on create (TYPES_GIVEN), or by an update that set its
32
- * payment_method_types ("Alternatively, update the allowed payment_method_types for this PaymentIntent",
33
- * methodNotAllowed below), which the generic update writes as given, so the intent's history holds it. */
34
- const typesGiven = (ctx: SemanticsContext, id: string): boolean =>
35
- ctx.tree().some((r) => r.type === TYPES_GIVEN && r.id === id)
36
- || ctx.history(PI, id).some((e) => e.operation !== 'payment_intent.create' && Array.isArray(e.fields?.payment_method_types));
37
-
38
- const send = (ctx: SemanticsContext, r: StripeResponse): Response => ctx.reply(r.body, r.status);
39
-
40
- // ── THE MONEY MODEL, continued: a succeeded intent has a Charge ──
41
- //
42
- // Real Stripe always materializes a Charge when a PaymentIntent succeeds and points the intent's
43
- // `latest_charge` at it: the charge is what a refund lands on, so an intent that succeeds without
44
- // one cannot answer "refund this payment" at all. Idempotent against a repeated confirm: an intent
45
- // that already names a successful charge keeps it (a second one would double the money collected); a
46
- // declined attempt's failed charge is history, and the payment that succeeds makes its own. The id is
47
- // returned so the caller folds `latest_charge` into the same intent write (one write, one event). The
48
- // charge is written as the event Stripe sends for it, `charge.succeeded` ("Occurs whenever a charge is
49
- // successful", docs.stripe.com/api/events/types); Stripe has no `charge.created`.
50
- /** What every charge an intent makes carries of it: its transfer_group, which "identifies the resulting payment as part
51
- * of a group" (docs.stripe.com/api/payment_intents/object), its description and its metadata. */
52
- function intentCarries(pi: Row): Row {
53
- return {
54
- ...(typeof pi.transfer_group === 'string' ? { transfer_group: pi.transfer_group } : {}),
55
- ...(typeof pi.description === 'string' ? { description: pi.description } : {}),
56
- // "When a PaymentIntent creates a Charge, the metadata copies to the Charge in a one-time snapshot"
57
- // (docs.stripe.com/metadata, "Copy metadata to another object")
58
- ...(pi.metadata && typeof pi.metadata === 'object' ? { metadata: { ...(pi.metadata as Row) } } : {}),
59
- };
60
- }
61
-
62
- export async function mintCharge(ctx: SemanticsContext, piId: string, pi: Row, amount: number): Promise<string> {
63
- const already = ctx.get(PI, piId)?.latest_charge;
64
- if (typeof already === 'string' && ctx.get('charge', already)?.status === 'succeeded') return already;
65
- const chargeId = ctx.mint('charge');
66
- const bt = await settleCharge(ctx, chargeId, amount, String(pi.currency ?? 'usd'), typeof pi.payment_method === 'string' ? pi.payment_method : undefined);
67
- await created(ctx, 'charge', {
68
- id: chargeId, amount, currency: pi.currency ?? 'usd', payment_intent: piId,
69
- ...(typeof pi.customer === 'string' ? { customer: pi.customer } : {}),
70
- ...(typeof pi.payment_method === 'string' ? { payment_method: pi.payment_method } : {}),
71
- ...(typeof pi.invoice === 'string' ? { invoice: pi.invoice } : {}),
72
- ...intentCarries(pi),
73
- }, { ...chargeDefaults(chargeId, amount, true, ctx.occurredAt), balance_transaction: bt, payment_method_details: paymentMethodDetails(ctx, pi.payment_method) ?? null }, { operation: 'charge.succeeded' });
74
- await afterCharge(ctx, chargeId, { amount, currency: String(pi.currency ?? 'usd'), payment_intent: piId, application_fee_amount: pi.application_fee_amount, destination: (pi.transfer_data as Row | undefined)?.destination }, pi.payment_method);
75
- return chargeId;
76
- }
77
-
78
- /** A manual-capture intent's authorization: "Separate payment authorization and capture to create a charge now, but
79
- * capture funds later", and on capture "A partial capture automatically releases the remaining amount"
80
- * (docs.stripe.com/payments/place-a-hold-on-a-payment-method). The charge succeeded and is not captured, holding the
81
- * amount until capture_before, with nothing on the balance yet. Written as
82
- * charge.succeeded, as a charge made with capture=false is (charges.ts); its capture writes charge.captured
83
- * (captureAuthorization). Where the documentation stops and the twin decides: the events page names no event for an
84
- * authorization of its own, so the authorized charge sends charge.succeeded, its status being succeeded. */
85
- export async function authorizeCharge(ctx: SemanticsContext, piId: string, pi: Row, amount: number): Promise<string> {
86
- const chargeId = ctx.mint('charge');
87
- await created(ctx, 'charge', {
88
- id: chargeId, amount, currency: pi.currency ?? 'usd', payment_intent: piId,
89
- ...(typeof pi.customer === 'string' ? { customer: pi.customer } : {}),
90
- ...(typeof pi.payment_method === 'string' ? { payment_method: pi.payment_method } : {}),
91
- ...(typeof pi.invoice === 'string' ? { invoice: pi.invoice } : {}),
92
- ...intentCarries(pi),
93
- }, { ...chargeDefaults(chargeId, amount, false, ctx.occurredAt), balance_transaction: null, payment_method_details: paymentMethodDetails(ctx, pi.payment_method) ?? null }, { operation: 'charge.succeeded' });
94
- return chargeId;
95
- }
96
-
97
- /**
98
- * An intent an invoice collects through succeeded (the default_incomplete subscription flow: create
99
- * now, confirm client-side): its invoice is paid and, when it is a subscription's first, the
100
- * subscription leaves incomplete for active. Both are moves the invoice and subscription machines
101
- * declare under the confirming operation. No-op when the intent names no invoice, or the invoice is
102
- * already paid (a duplicate confirm) or void (voiding ends what it collects).
103
- */
104
- async function payInvoiceOf(ctx: SemanticsContext, pi: Row, operationId: string, actor?: 'vendor'): Promise<void> {
105
- const invoiceId = typeof pi.invoice === 'string' ? pi.invoice : undefined;
106
- const invoice = invoiceId ? ctx.get('invoice', invoiceId) : undefined;
107
- if (!invoiceId || !invoice || invoice.status === 'paid' || invoice.status === 'void') return;
108
- if (ctx.legal('invoice', 'status', operationId, invoice.status, 'paid', invoiceId, actor)) return;
109
- const amount = Number(invoice.total ?? invoice.amount_due) || 0;
110
- // `invoice.pay` is the event the standalone pay action sends: invoice.paid
111
- await ctx.write('invoice', invoiceId, {
112
- status: 'paid', paid: true, amount_paid: amount, amount_remaining: 0,
113
- status_transitions: { ...((invoice.status_transitions as object) ?? {}), paid_at: ctx.now() },
114
- }, 'invoice.pay');
115
- const subId = typeof invoice.subscription === 'string' ? invoice.subscription : undefined;
116
- const sub = subId ? ctx.get('subscription', subId) : undefined;
117
- if (subId && sub && sub.status === 'incomplete' && !ctx.legal('subscription', 'status', operationId, sub.status, 'active', subId, actor)) {
118
- await ctx.write('subscription', subId, { status: 'active' }, 'subscription.update');
119
- }
120
- }
121
-
122
- /** The intent the path names, and the machine's refusal when this operation may not move it. */
123
- function load(ctx: SemanticsContext, operationId: string): { pi: Record<string, unknown> } | { answer: Response } {
124
- const pi = ctx.id ? ctx.get(PI, ctx.id) : undefined;
125
- if (!pi) return { answer: ctx.notFound(PI, String(ctx.id)) };
126
- const refusal = ctx.legal(PI, 'status', operationId, pi.status);
127
- return refusal ? { answer: ctx.refuse(refusal) } : { pi };
128
- }
129
-
130
- const create: Semantics = async (ctx) => {
131
- const params = ctx.params;
132
- const bad = validateMoney(params);
133
- if (bad) return send(ctx, bad);
134
- // what describes a confirmation needs one: error_on_requires_action, mandate, mandate_data, off_session and return_url
135
- // are each, in the served spec, a parameter that "can only be used with `confirm=true`" (shared.ts confirmOnly)
136
- const unconfirmed = confirmOnly(ctx, ['error_on_requires_action', 'mandate', 'mandate_data', 'off_session', 'return_url'], asBool(params.confirm));
137
- if (unconfirmed) return unconfirmed;
138
- // automatic_payment_methods: when enabled, Stripe selects eligible methods and answers the
139
- // normalized { enabled, allow_redirects } object
140
- const apmIn = params.automatic_payment_methods;
141
- const apmEnabled = apmIn && typeof apmIn === 'object' ? asBool((apmIn as Record<string, unknown>).enabled) : false;
142
- const automatic_payment_methods = apmEnabled ? { enabled: true, allow_redirects: ((apmIn as Record<string, unknown>).allow_redirects as string) ?? 'always' } : null;
143
- // the methods it may be paid with: allowed_payment_method_types in the served version, payment_method_types for a
144
- // caller pinned to an earlier one (docs.stripe.com/api/payment_intents/create)
145
- const given = Array.isArray(params.allowed_payment_method_types) ? params.allowed_payment_method_types : params.payment_method_types;
146
- const payment_method_types = Array.isArray(given) ? given.map(String) : ['card'];
147
- // `confirm` is an instruction, not a field: `confirm=true` attempts to confirm the intent at once
148
- // (https://docs.stripe.com/api/payment_intents/create#create_payment_intent-confirm)
149
- const { automatic_payment_methods: _a, payment_method_types: _p, allowed_payment_method_types: _ap, id: provided, expand: _e, confirm: confirmNow, payment_method_data: data, mandate_data: _mandate, ...rest } = params;
150
- // a create's payment_method_data makes its method, as a confirm's does (payment-methods.ts methodFromData)
151
- const made = await methodFromData(ctx, data);
152
- if (made) rest.payment_method = made;
153
- // the id is minted first so the client secret can carry it: Stripe.js parses the id back out
154
- const id = typeof provided === 'string' && provided ? provided : ctx.mint(PI);
155
- // it waits for a payment method until it has one, then for its confirmation (manifest.ts)
156
- const hasMethod = typeof rest.payment_method === 'string' && rest.payment_method !== '';
157
- if (hasMethod) ctx.legal(PI, 'status', 'PostPaymentIntents', 'requires_payment_method', 'requires_confirmation', id);
158
- const body = await ctx.write(
159
- PI,
160
- id,
161
- {
162
- object: 'payment_intent',
163
- created: ctx.now(),
164
- status: hasMethod ? 'requires_confirmation' : 'requires_payment_method',
165
- client_secret: mintClientSecret(id),
166
- livemode: false,
167
- capture_method: 'automatic',
168
- amount_capturable: 0,
169
- amount_received: 0,
170
- next_action: null,
171
- automatic_payment_methods,
172
- payment_method_types,
173
- ...(Array.isArray(params.allowed_payment_method_types) ? { allowed_payment_method_types: payment_method_types } : {}),
174
- payment_method_options: (params.payment_method_options as object) ?? {},
175
- ...rest,
176
- },
177
- 'payment_intent.create',
178
- );
179
- if (Array.isArray(given)) await ctx.record(TYPES_GIVEN, { payment_intent: id }, id);
180
- if (asBool(confirmNow)) return confirmIntent(ctx, body, {});
181
- return ctx.reply(ctx.expand(PI, body));
182
- };
183
-
184
- const confirm: Semantics = async (ctx) => {
185
- const loaded = load(ctx, 'PostPaymentIntentsIntentConfirm');
186
- if ('answer' in loaded) return loaded.answer;
187
- const { expand: _e, payment_method_data: data, mandate_data: _mandate, ...params } = ctx.params;
188
- // a confirm's payment_method_data makes the method it pays with; neither it nor mandate_data is a field of the intent
189
- const made = await methodFromData(ctx, data);
190
- return confirmIntent(ctx, loaded.pi, made ? { ...params, payment_method: made } : params);
191
- };
192
-
193
- /** The type of the payment method an intent is paid with: its PaymentMethod's, or a documented test name's (pm_card_*,
194
- * tok_* a card; pm_usBankAccount_* a US bank account), else unknown. */
195
- function methodType(ctx: SemanticsContext, method: unknown): string | undefined {
196
- if (typeof method !== 'string' || !method) return undefined;
197
- const row = ctx.get('payment_method', method);
198
- if (row) return typeof row.type === 'string' ? row.type : undefined;
199
- if (/^pm_us_?bank_?account/i.test(method)) return 'us_bank_account';
200
- if (/^(pm_card_|tok_)/.test(method)) return 'card';
201
- return undefined;
202
- }
203
-
204
- /** A confirm with a payment method of a type the intent's caller did not allow, refused as Stripe refuses it: "The
205
- * PaymentMethod provided (card_present) is not allowed for this PaymentIntent. Please attach a PaymentMethod of one of
206
- * the following types: card. Alternatively, update the allowed payment_method_types for this PaymentIntent to include
207
- * "card_present"" (a real answer quoted in github.com/stripe/stripe-terminal-react-native/issues/745, an intent made
208
- * with payment_method_types ['card']). Only a list the caller gave restricts: without one Stripe offers the methods
209
- * the account enables, which the twin does not model. Where the evidence stops: that answer's code is not quoted; the
210
- * twin answers payment_intent_invalid_parameter ("One or more provided parameters wasn't allowed for the given
211
- * operation on the PaymentIntent", docs.stripe.com/error-codes), param payment_method. */
212
- function methodNotAllowed(ctx: SemanticsContext, existing: Row, params: Row): Response | undefined {
213
- const types = Array.isArray(params.payment_method_types) ? params.payment_method_types.map(String)
214
- : Array.isArray(params.allowed_payment_method_types) ? params.allowed_payment_method_types.map(String)
215
- : typesGiven(ctx, String(existing.id)) && Array.isArray(existing.payment_method_types) ? (existing.payment_method_types as unknown[]).map(String) : undefined;
216
- if (!types) return undefined;
217
- const type = methodType(ctx, params.payment_method ?? existing.payment_method);
218
- if (!type || types.includes(type)) return undefined;
219
- return ctx.refuse({ status: 400, code: 'payment_intent_invalid_parameter', param: 'payment_method', message: `The PaymentMethod provided (${type}) is not allowed for this PaymentIntent. Please attach a PaymentMethod of one of the following types: ${types.join(', ')}. Alternatively, update the allowed payment_method_types for this PaymentIntent to include "${type}"` });
220
- }
221
-
222
- /** Confirm an intent the confirm machine allows to move: the confirm action, and a create with `confirm=true`. */
223
- async function confirmIntent(ctx: SemanticsContext, existing: Record<string, unknown>, params: Record<string, unknown>): Promise<Response> {
224
- const id = String(existing.id);
225
- // a payment names a mandate only an active one authorizes: an inactive mandate "was rejected, revoked, or previously
226
- // used, and may not be used to initiate future payments" (docs.stripe.com/api/mandates/object), refused as
227
- // payment_intent_mandate_invalid, "The provided mandate is invalid and can't be used for the payment intent"
228
- // (docs.stripe.com/error-codes)
229
- const named = typeof params.mandate === 'string' ? params.mandate : typeof existing.mandate === 'string' ? existing.mandate : undefined;
230
- if (named && ctx.get('mandate', named)?.status !== 'active') return fail(ctx, 'The provided mandate is invalid and can\'t be used for the payment intent.', 400, 'payment_intent_mandate_invalid');
231
- const notAllowed = methodNotAllowed(ctx, existing, params);
232
- if (notAllowed) return notAllowed;
233
- // a declining test card leaves the intent needing a payment method, with the error on it and a 402
234
- const decline = declineFor(finder(ctx), params, existing);
235
- if (decline) {
236
- // Stripe records the attempt: a failed charge the intent's latest_charge names, the declined card on
237
- // last_payment_error (a test name made a real PaymentMethod), and no payment_method left on the intent
238
- // (docs.stripe.com/payments/paymentintents/lifecycle, docs.stripe.com/declines)
239
- const ref = params.payment_method ?? existing.payment_method;
240
- let pmId = typeof ref === 'string' ? ref : undefined;
241
- if (pmId && !ctx.get('payment_method', pmId) && pmId.startsWith('pm_card_')) pmId = String((await materializeTestMethod(ctx, pmId, typeof existing.customer === 'string' ? existing.customer : null)).id);
242
- const pm = pmId ? ctx.get('payment_method', pmId) : undefined;
243
- const amount = Number(params.amount ?? existing.amount) || 0;
244
- const chargeId = ctx.mint('charge');
245
- await created(ctx, 'charge', {
246
- id: chargeId, amount, currency: existing.currency ?? 'usd', payment_intent: id,
247
- ...(typeof existing.customer === 'string' ? { customer: existing.customer } : {}),
248
- ...(pmId ? { payment_method: pmId } : {}),
249
- ...intentCarries({ ...existing, ...params }),
250
- }, {
251
- ...chargeDefaults(chargeId, amount, false, ctx.occurredAt), status: 'failed', paid: false, captured: false, capture_before: null,
252
- failure_code: decline.code, failure_message: decline.message, balance_transaction: null,
253
- outcome: { type: 'issuer_declined', network_status: 'declined_by_network', reason: decline.decline_code ?? decline.code, risk_level: 'normal', seller_message: 'The bank did not return any further details with this decline.' },
254
- payment_method_details: pmId ? paymentMethodDetails(ctx, pmId) ?? null : null,
255
- }, { operation: 'charge.failed' }); // "Occurs whenever a failed charge attempt occurs." (docs.stripe.com/api/events/types)
256
- const { payment_method: _pm, ...rest } = params;
257
- const last_payment_error = { type: 'card_error', code: decline.code, ...(decline.decline_code ? { decline_code: decline.decline_code } : {}), message: decline.message, param: 'card', charge: chargeId, payment_method: pm ?? null };
258
- const failed = await ctx.write(PI, id, { ...rest, payment_method: null, latest_charge: chargeId, status: 'requires_payment_method', last_payment_error }, 'payment_intent.payment_failed');
259
- return send(ctx, cardError(decline, { payment_intent: failed, charge: chargeId }));
260
- }
261
- // a bank debit that needs micro-deposit verification waits in requires_action
262
- const method = typeof params.payment_method === 'string' ? params.payment_method : typeof existing.payment_method === 'string' ? existing.payment_method : undefined;
263
- // a bank transfer is paid from the customer's cash balance, or waits for the transfer (fundFromCashBalance)
264
- if (method && ctx.get('payment_method', method)?.type === 'customer_balance') return ctx.reply(ctx.expand(PI, await fundFromCashBalance(ctx, { ...existing, ...params }, params)));
265
- if (requiresMicrodeposits(params, existing) || microdepositsAsked(ctx, params, existing, method)) {
266
- return ctx.reply(ctx.expand(PI, await ctx.write(PI, id, { ...params, status: 'requires_action', next_action: microdepositsNextAction(), last_payment_error: null }, 'payment_intent.requires_action')));
267
- }
268
- // 3DS: the first confirm asks for authentication; a second one completes it
269
- if (existing.status !== 'requires_action' && requiresAuthentication(params, existing)) {
270
- return ctx.reply(ctx.expand(PI, await ctx.write(PI, id, { ...params, status: 'requires_action', next_action: threeDsNextAction(), last_payment_error: null }, 'payment_intent.requires_action')));
271
- }
272
- // manual capture authorizes and waits for /capture: the authorization is a charge, succeeded and not captured, the
273
- // intent's latest_charge
274
- if ((params.capture_method ?? existing.capture_method) === 'manual') {
275
- const amount = Number(existing.amount ?? params.amount ?? 0);
276
- const charge = await authorizeCharge(ctx, id, { ...existing, ...params }, amount);
277
- return ctx.reply(ctx.expand(PI, await ctx.write(PI, id, { ...params, status: 'requires_capture', amount_capturable: amount, amount_received: 0, next_action: null, last_payment_error: null, latest_charge: charge }, 'payment_intent.amount_capturable_updated')));
278
- }
279
- // a bank debit is submitted and settles later (beginBankDebit)
280
- if (isBankDebit(ctx, method)) return ctx.reply(ctx.expand(PI, await beginBankDebit(ctx, id, params)));
281
- const amount = Number(existing.amount ?? params.amount ?? 0);
282
- // a succeeded intent has a Charge, and latest_charge names it: what a refund of it lands on
283
- const charge = await mintCharge(ctx, id, { ...existing, ...params }, amount);
284
- const body = await ctx.write(PI, id, { ...params, status: 'succeeded', amount_received: amount, next_action: null, last_payment_error: null, latest_charge: charge }, 'payment_intent.confirm');
285
- await payInvoiceOf(ctx, body, 'PostPaymentIntentsIntentConfirm');
286
- return ctx.reply(ctx.expand(PI, body));
287
- }
288
-
289
- const capture: Semantics = async (ctx) => {
290
- const loaded = load(ctx, 'PostPaymentIntentsIntentCapture');
291
- if ('answer' in loaded) return loaded.answer;
292
- const pi = loaded.pi;
293
- const authorized = Number(pi.amount_capturable ?? pi.amount ?? 0);
294
- // amount_to_capture lowers the captured amount (a partial capture); it never raises it
295
- const asked = ctx.params.amount_to_capture !== undefined ? Math.max(0, Math.trunc(Number(ctx.params.amount_to_capture) || 0)) : authorized;
296
- const captured = Math.min(asked, authorized);
297
- // the authorization confirm made is captured; an intent authorized before it was kept is charged as before
298
- const auth = typeof pi.latest_charge === 'string' ? ctx.get('charge', pi.latest_charge) : undefined;
299
- const held = auth && auth.status === 'succeeded' && auth.captured === false ? auth : undefined;
300
- if (held) {
301
- const refused = ctx.legal('charge', 'captured', 'PostPaymentIntentsIntentCapture', 'false', 'true', String(held.id));
302
- if (refused) return ctx.refuse(refused);
303
- await captureAuthorization(ctx, held, captured, 'PostPaymentIntentsIntentCapture');
304
- await afterCharge(ctx, String(held.id), { amount: captured, currency: String(pi.currency ?? 'usd'), payment_intent: String(pi.id), application_fee_amount: pi.application_fee_amount, destination: (pi.transfer_data as Row | undefined)?.destination }, pi.payment_method);
305
- }
306
- const charge = held ? String(held.id) : await mintCharge(ctx, String(pi.id), pi, captured);
307
- const body = await ctx.write(PI, String(pi.id), { status: 'succeeded', amount_received: captured, amount_capturable: 0, latest_charge: charge }, 'payment_intent.succeeded');
308
- await payInvoiceOf(ctx, body, 'PostPaymentIntentsIntentCapture');
309
- return ctx.reply(ctx.expand(PI, body));
310
- };
311
-
312
- /** An increment that does not raise the authorization. */
313
- function incrementTooSmall(ctx: SemanticsContext): Response {
314
- return ctx.refuse({ status: 400, code: 'parameter_invalid_integer', message: 'The new amount must be greater than the current amount of the PaymentIntent.' });
315
- }
316
-
317
- const incrementAuthorization: Semantics = async (ctx) => {
318
- const loaded = load(ctx, 'PostPaymentIntentsIntentIncrementAuthorization');
319
- if ('answer' in loaded) return loaded.answer;
320
- const pi = loaded.pi;
321
- if (ctx.params.amount === undefined) return ctx.refuse({ status: 400, code: 'parameter_missing', message: 'Missing required param: amount.' });
322
- const amount = Math.trunc(Number(ctx.params.amount) || 0);
323
- if (!Number.isInteger(amount) || amount <= Number(pi.amount ?? 0)) return incrementTooSmall(ctx);
324
- // the authorization the charge holds grows with it: "If the incremental authorization fails ... no other fields on
325
- // the PaymentIntent or Charge update" (docs.stripe.com/api/payment_intents/increment_authorization), so on success the
326
- // charge's amount is the new authorized amount. Where the documentation stops and the twin decides: the charge's
327
- // update sends no event of its own (the events page names none for it).
328
- const held = typeof pi.latest_charge === 'string' ? ctx.get('charge', pi.latest_charge) : undefined;
329
- if (held && held.captured === false) await ctx.write('charge', String(held.id), { amount }, 'charge.authorization_incremented');
330
- return ctx.reply(ctx.expand(PI, await ctx.write(PI, String(pi.id), { amount, amount_capturable: amount }, 'payment_intent.amount_capturable_updated')));
331
- };
332
-
333
- const cancel: Semantics = async (ctx) => {
334
- const loaded = load(ctx, 'PostPaymentIntentsIntentCancel');
335
- if ('answer' in loaded) return loaded.answer;
336
- const cancellation_reason = typeof ctx.params.cancellation_reason === 'string' ? ctx.params.cancellation_reason : 'requested_by_customer';
337
- // "For PaymentIntents with a `status` of `requires_capture`, the remaining `amount_capturable` is automatically
338
- // refunded" (docs.stripe.com/api/payment_intents/cancel): the authorization is released (charges.ts)
339
- const held = loaded.pi.status === 'requires_capture' && typeof loaded.pi.latest_charge === 'string' ? ctx.get('charge', loaded.pi.latest_charge) : undefined;
340
- if (held && held.captured === false && held.refunded !== true) await cancelAuthorization(ctx, held);
341
- return ctx.reply(ctx.expand(PI, await ctx.write(PI, String(loaded.pi.id), { status: 'canceled', cancellation_reason, amount_capturable: 0 }, 'payment_intent.canceled')));
342
- };
343
-
344
- const verifyMicrodepositsHandler: Semantics = async (ctx) => {
345
- const pi = ctx.id ? ctx.get(PI, ctx.id) : undefined;
346
- if (!pi) return ctx.notFound(PI, String(ctx.id));
347
- // the deposit amounts are checked before the intent's state, as Stripe does
348
- const wrong = verifyMicrodeposits(ctx.params, pi);
349
- if (wrong) return send(ctx, wrong);
350
- const refusal = ctx.legal(PI, 'status', 'PostPaymentIntentsIntentVerifyMicrodeposits', pi.status);
351
- if (refusal) return ctx.refuse(refusal);
352
- const amount = Number(pi.amount) || 0;
353
- // a debit saved for reuse ("If you want to reuse the payment method in the future, provide the setup_future_usage
354
- // parameter with the value of off_session", the ACH page) is authorized for many payments (multi_use, "Represents
355
- // permission given for multiple payments"); else for this one (single_use, "a one-time permission given for a single
356
- // payment", docs.stripe.com/api/mandates/object)
357
- const reuse = pi.setup_future_usage === 'off_session' || pi.setup_future_usage === 'on_session';
358
- const mandate = reuse ? await mintMandate(ctx, pi.payment_method, 'multi_use') : await mintMandate(ctx, pi.payment_method, 'single_use', amount, String(pi.currency ?? 'usd'));
359
- // "When the bank account is successfully verified, Stripe returns the PaymentIntent object with a status of
360
- // `processing`" (docs.stripe.com/payments/ach-direct-debit/accept-a-payment?payment-ui=direct-api)
361
- return ctx.reply(ctx.expand(PI, await beginBankDebit(ctx, String(pi.id), { mandate })));
362
- };
363
-
364
- // ── a bank debit is submitted, and settles ──
365
- //
366
- // ACH Direct Debit "is a delayed notification payment method ... The PaymentIntent you create initially has a status
367
- // of `processing`. After the payment has succeeded, the PaymentIntent status is updated from `processing` to
368
- // `succeeded`"; verification by micro-deposits returns "a status of `processing`, and sends a payment_intent.processing
369
- // webhook event"; and, the twin being a test-mode account: "Test transactions settle instantly and are added to your
370
- // available test balance. This behavior differs from live mode, where transactions can take multiple days to settle"
371
- // (docs.stripe.com/payments/ach-direct-debit/accept-a-payment?payment-ui=direct-api). A confirm answers processing; the
372
- // debit succeeds at that same instant of the World's clock, caught up before the next request is answered (as
373
- // renewals and payouts are). The page's test accounts give each debit its outcome: pm_usBankAccount_success
374
- // (000123456789) "The payment succeeds"; pm_usBankAccount_processing (000000000009) "The payment stays in processing
375
- // indefinitely". Where the documentation stops and the twin decides: its Charge is made when it succeeds (the page
376
- // names none while it processes); the failing test accounts (closed, no account, insufficient funds, debit not
377
- // authorized, invalid currency, dispute, weekly limit, Radar block) are not modelled and succeed.
378
-
379
- /** Whether the method an intent is confirmed with is a bank debit: a us_bank_account PaymentMethod, or one of Stripe's
380
- * test bank accounts named as one (pm_usBankAccount_*, pm_us_bank_account). */
381
- export function isBankDebit(ctx: SemanticsContext, method: unknown): boolean {
382
- if (typeof method !== 'string') return false;
383
- return ctx.get('payment_method', method)?.type === 'us_bank_account' || /^pm_us_?bank_?account/i.test(method);
384
- }
385
-
386
- /** Whether a bank debit stays processing: the page's pm_usBankAccount_processing, or its account 000000000009. */
387
- function staysProcessing(ctx: SemanticsContext, method: unknown): boolean {
388
- if (method === 'pm_usBankAccount_processing') return true;
389
- const bank = typeof method === 'string' ? (ctx.get('payment_method', method)?.us_bank_account as Row | undefined) : undefined;
390
- return bank?.last4 === '0009';
391
- }
392
-
393
- /** A bank debit submitted: the intent processes, nothing received yet, sent as payment_intent.processing; it is due to
394
- * settle at this instant, unless its test account stays processing. */
395
- async function beginBankDebit(ctx: SemanticsContext, id: string, fields: Row): Promise<Row> {
396
- const method = fields.payment_method ?? ctx.get(PI, id)?.payment_method;
397
- return ctx.write(PI, id, { ...fields, status: 'processing', amount_received: 0, next_action: null, last_payment_error: null, _settles_at: staysProcessing(ctx, method) ? null : Number(ctx.now()) }, 'payment_intent.processing');
398
- }
399
-
400
- /** Time's settling of bank debits, caught up to the World's clock: each processing debit whose moment has come succeeds
401
- * then, its Charge made (naming the mandate it ran under) and the invoice it collects for paid. */
402
- export async function settleBankDebits(ctx: SemanticsContext): Promise<void> {
403
- const now = Number(ctx.now());
404
- const due = ctx.rowsRaw(PI).filter((p) => p.status === 'processing' && typeof p._settles_at === 'number' && p._settles_at <= now).sort((a, b) => Number(a._settles_at) - Number(b._settles_at));
405
- for (const pi of due) {
406
- const id = String(pi.id);
407
- const c = await at_(ctx)(Number(pi._settles_at));
408
- c.legal(PI, 'status', ctx.call.operation.id, 'processing', 'succeeded', id, 'vendor');
409
- const amount = Number(pi.amount) || 0;
410
- const charge = await mintCharge(c, id, pi, amount);
411
- // the charge names the mandate its bank debit ran under (payment_method_details.us_bank_account.mandate)
412
- const details = (c.get('charge', charge)?.payment_method_details ?? null) as Row | null;
413
- const kind = typeof details?.type === 'string' ? details.type : undefined;
414
- if (details && kind && typeof pi.mandate === 'string') await c.write('charge', charge, { payment_method_details: { ...details, [kind]: { ...((details[kind] as Row | undefined) ?? {}), mandate: pi.mandate } } }, 'charge.mandate');
415
- const body = await c.write(PI, id, { status: 'succeeded', amount_received: amount, latest_charge: charge, _settles_at: null }, 'payment_intent.succeeded');
416
- // a single-use mandate is spent by its payment: "previously used, and may not be used to initiate future payments"
417
- const used = typeof pi.mandate === 'string' ? c.get('mandate', pi.mandate) : undefined;
418
- if (used && used.type === 'single_use' && used.status === 'active') {
419
- c.legal('mandate', 'status', ctx.call.operation.id, 'active', 'inactive', String(used.id), 'vendor');
420
- await c.write('mandate', String(used.id), { status: 'inactive' }, 'mandate.updated');
421
- }
422
- await payInvoiceOf(c, body, ctx.call.operation.id, 'vendor');
423
- }
424
- }
425
-
426
- /** A customer_balance intent funded from the customer's cash balance: paid when the balance covers it ("If the customer
427
- * already has a balance high enough to cover the payment amount, the PaymentIntent immediately succeeds"), else
428
- * waiting for a transfer: "If the customer balance isn’t high enough to cover the request amount, the PaymentIntent
429
- * shows a requires_action status ... next_action ... display_bank_transfer_instructions" with the amount_remaining
430
- * (docs.stripe.com/payments/bank-transfers/accept-a-payment). Answers the intent as written. */
431
- async function fundFromCashBalance(ctx: SemanticsContext, pi: Row, fields: Row = {}): Promise<Row> {
432
- const customer = typeof pi.customer === 'string' ? pi.customer : '';
433
- const currency = String(pi.currency ?? 'usd');
434
- // what the customer's funded cash balance holds in this currency, from its ledger
435
- const available = customer ? ctx.rows('customer_cash_balance_transaction').filter((t) => t.customer === customer && String(t.currency) === currency).reduce((n, t) => n + (Number(t.net_amount) || 0), 0) : 0;
436
- const amount = Number(pi.amount) || 0;
437
- if (available >= amount) {
438
- // the balance pays the intent: its ledger records what was applied
439
- await created(ctx, 'customer_cash_balance_transaction', { customer }, { currency, type: 'applied_to_payment', net_amount: -amount, ending_balance: available - amount, applied_to_payment: { payment_intent: String(pi.id) }, livemode: false });
440
- const charge = await mintCharge(ctx, String(pi.id), { ...pi, ...fields }, amount);
441
- return ctx.write(PI, String(pi.id), { ...fields, status: 'succeeded', next_action: null, amount_received: amount, last_payment_error: null, latest_charge: charge }, 'payment_intent.succeeded');
442
- }
443
- // not enough yet: the intent waits for the rest, with the instructions to send it
444
- const next_action = { type: 'display_bank_transfer_instructions', display_bank_transfer_instructions: { amount_remaining: amount - available, currency, type: 'us_bank_transfer' } };
445
- return ctx.write(PI, String(pi.id), { ...fields, status: 'requires_action', next_action }, 'payment_intent.requires_action');
446
- }
447
-
448
- const applyCustomerBalance: Semantics = async (ctx) => {
449
- const loaded = load(ctx, 'PostPaymentIntentsIntentApplyCustomerBalance');
450
- if ('answer' in loaded) return loaded.answer;
451
- return ctx.reply(ctx.expand(PI, await fundFromCashBalance(ctx, loaded.pi)));
452
- };
453
-
454
- export const paymentIntents: Record<string, Semantics> = {
455
- PostPaymentIntents: create,
456
- GetPaymentIntentsSearch: async (ctx) => search(ctx, PI),
457
- PostPaymentIntentsIntentConfirm: confirm,
458
- PostPaymentIntentsIntentCapture: capture,
459
- PostPaymentIntentsIntentIncrementAuthorization: incrementAuthorization,
460
- PostPaymentIntentsIntentCancel: cancel,
461
- PostPaymentIntentsIntentVerifyMicrodeposits: verifyMicrodepositsHandler,
462
- PostPaymentIntentsIntentApplyCustomerBalance: applyCustomerBalance,
463
- };
@@ -1,148 +0,0 @@
1
- // Payment Link and Quote semantics. Both carry line items resolved from existing prices. A payment
2
- // link is a reusable hosted-checkout URL, active until switched off; a quote moves draft → open
3
- // (finalize) → accepted or canceled, the machine in ../manifest.ts. Link retrieve and update
4
- // (`active`, its machine), quote retrieve and the quote list are the derived core's.
5
- import type { Semantics, SemanticsContext } from '@volter/world-core';
6
- import { asBool, lineItemEntries, paginate, resolveLineItem, type CheckoutItem } from '../stripe-twin.ts';
7
- import { draftInvoiceFor } from './invoices.ts';
8
- import { at, created, fail, finder, list, newest, path, where, type Row } from './shared.ts';
9
-
10
- /** The request's line items resolved against their prices, or the refusal for a missing price. */
11
- function resolved(ctx: SemanticsContext): CheckoutItem[] | Response {
12
- const entries = lineItemEntries(ctx.params);
13
- const items: CheckoutItem[] = [];
14
- for (let i = 0; i < entries.length; i++) {
15
- const { item, priceMissing } = resolveLineItem(entries[i]!, i, 'usd', finder(ctx));
16
- if (priceMissing) return fail(ctx, `No such price: '${priceMissing}'`, 400, 'resource_missing');
17
- items.push(item);
18
- }
19
- return items;
20
- }
21
-
22
- /** A page of the line items a stored object carries. */
23
- function linePage(ctx: SemanticsContext, holder: Row): Response {
24
- const li = holder.line_items as { data?: Row[] } | undefined;
25
- const { page, hasMore } = paginate(Array.isArray(li?.data) ? li!.data! : [], ctx.params);
26
- return ctx.reply({ object: 'list', url: path(ctx), has_more: hasMore, data: page });
27
- }
28
-
29
- const PL = 'payment_link';
30
- const linkMissing = (ctx: SemanticsContext, id: string): Response => fail(ctx, `No such payment link: '${id}'`, 404, 'resource_missing');
31
-
32
- const createLink: Semantics = async (ctx) => {
33
- if (lineItemEntries(ctx.params).length === 0) return fail(ctx, 'Missing required param: line_items.', 400, 'parameter_missing');
34
- const items = resolved(ctx);
35
- if (items instanceof Response) return items;
36
- const id = ctx.mint(PL);
37
- const { line_items: _li, ...rest } = ctx.params;
38
- return ctx.reply(
39
- await created(ctx, PL, { ...rest, id }, {
40
- active: ctx.params.active !== undefined ? asBool(ctx.params.active) : true,
41
- url: `https://buy.twin.local/${id}`, livemode: false, metadata: {},
42
- // Stripe's defaults for what the link was not given (docs.stripe.com/api/payment-link/create)
43
- allow_promotion_codes: false, automatic_tax: { enabled: false, liability: null }, billing_address_collection: 'auto',
44
- custom_fields: [], custom_text: { after_submit: null, shipping_address: null, submit: null, terms_of_service_acceptance: null },
45
- customer_creation: 'if_required', payment_method_collection: 'always', phone_number_collection: { enabled: false },
46
- shipping_options: [], submit_type: 'auto', tax_id_collection: { enabled: false, required: 'never' },
47
- line_items: { object: 'list', data: items, has_more: false, url: `/v1/payment_links/${id}/line_items` },
48
- currency: items[0]?.currency ?? 'usd',
49
- after_completion: { type: 'redirect', redirect: typeof ctx.params.after_completion === 'object' ? ctx.params.after_completion : null },
50
- }),
51
- );
52
- };
53
-
54
- const listLinks: Semantics = async (ctx) => list(ctx, PL, where(ctx, newest(ctx, PL), { active: (p, v) => asBool(p.active) === asBool(v) }));
55
-
56
- const linkLines: Semantics = async (ctx) => {
57
- const p = ctx.get(PL, at(ctx, 'payment_link'));
58
- return p ? linePage(ctx, p) : linkMissing(ctx, at(ctx, 'payment_link'));
59
- };
60
-
61
- // ── quotes ──
62
-
63
- const quoteMissing = (ctx: SemanticsContext, id: string): Response => fail(ctx, `No such quote: '${id}'`, 404, 'resource_missing');
64
-
65
- const createQuote: Semantics = async (ctx) => {
66
- const customer = typeof ctx.params.customer === 'string' ? ctx.params.customer : '';
67
- if (!customer) return fail(ctx, 'Missing required param: customer.', 400, 'parameter_missing');
68
- if (!ctx.row('customer', customer, { withDeleted: true })) return fail(ctx, `No such customer: '${customer}'`, 400, 'resource_missing');
69
- const items = resolved(ctx);
70
- if (items instanceof Response) return items;
71
- const subtotal = items.reduce((s, it) => s + it.amount_subtotal, 0);
72
- const id = ctx.mint('quote');
73
- const { line_items: _li, ...rest } = ctx.params;
74
- return ctx.reply(
75
- await created(ctx, 'quote', { ...rest, id }, {
76
- status: 'draft', customer, currency: items[0]?.currency ?? 'usd', livemode: false, metadata: {},
77
- amount_subtotal: subtotal, amount_total: subtotal,
78
- // Stripe's defaults (docs.stripe.com/api/quotes/create); what is paid up front is the whole quote, and the twin bills in classic mode
79
- automatic_tax: { enabled: false, liability: null, provider: null, status: null }, collection_method: 'charge_automatically', discounts: [],
80
- invoice_settings: { days_until_due: null, issuer: { type: 'self' } },
81
- subscription_data: { billing_mode: { type: 'classic' }, description: null, effective_date: null, metadata: null, trial_period_days: null },
82
- computed: { recurring: null, upfront: { amount_subtotal: subtotal, amount_total: subtotal, total_details: { amount_discount: 0, amount_shipping: 0, amount_tax: 0 } } },
83
- line_items: { object: 'list', data: items, has_more: false, url: `/v1/quotes/${id}/line_items` },
84
- total_details: { amount_discount: 0, amount_shipping: 0, amount_tax: 0 },
85
- expires_at: Number(ctx.now()) + 30 * 24 * 3600,
86
- number: null, invoice: null, subscription: null,
87
- status_transitions: { accepted_at: null, canceled_at: null, finalized_at: null },
88
- }),
89
- );
90
- };
91
-
92
- const quoteLines: Semantics = async (ctx) => {
93
- const q = ctx.get('quote', at(ctx, 'quote'));
94
- return q ? linePage(ctx, q) : quoteMissing(ctx, at(ctx, 'quote'));
95
- };
96
-
97
- // Stripe streams the PDF; the twin answers a stable reference to it
98
- const pdf: Semantics = async (ctx) => {
99
- const id = at(ctx, 'quote');
100
- if (!ctx.get('quote', id)) return quoteMissing(ctx, id);
101
- return ctx.reply({ object: 'quote_pdf', url: `https://files.twin.local/quotes/${id}.pdf` });
102
- };
103
-
104
- function move(action: 'finalize' | 'accept' | 'cancel', operationId: string): Semantics {
105
- return async (ctx) => {
106
- const id = at(ctx, 'quote');
107
- const q = ctx.get('quote', id);
108
- if (!q) return quoteMissing(ctx, id);
109
- const refused = ctx.legal('quote', 'status', operationId, q.status, undefined, id);
110
- if (refused) return ctx.refuse(refused);
111
- const now = ctx.now();
112
- const stamp = action === 'finalize' ? { finalized_at: now } : action === 'accept' ? { accepted_at: now } : { canceled_at: now };
113
- // "Accepted quotes automatically generate an invoice, subscription, or subscription schedule"; "Quotes without
114
- // recurring prices: A draft invoice is created with auto_advance set to false" (docs.stripe.com/quotes/overview),
115
- // the quote its parent ("Details about the quote that generated this invoice", the served spec). Where the
116
- // documentation stops and the twin decides: a quote with recurring prices makes no subscription yet.
117
- let made: Row = {};
118
- if (action === 'accept') {
119
- const lines = ((q.line_items as Row | undefined)?.data as Row[] | undefined) ?? [];
120
- const recurring = lines.some((l) => l.price && typeof l.price === 'object' && (l.price as Row).recurring);
121
- if (!recurring && typeof q.customer === 'string') {
122
- const invoice = await draftInvoiceFor(ctx, q.customer, String(q.currency ?? 'usd'), { type: 'quote_details', quote_details: { quote: id }, subscription_details: null }, lines.map((l) => ({ price: l.price, quantity: Number(l.quantity) || 1, amount: Number(l.amount_subtotal ?? l.amount_total) || 0, description: l.description })));
123
- made = { invoice };
124
- }
125
- }
126
- return ctx.reply(
127
- await ctx.write('quote', id, {
128
- status: action === 'finalize' ? 'open' : action === 'accept' ? 'accepted' : 'canceled',
129
- status_transitions: { ...((q.status_transitions as Row) ?? {}), ...stamp },
130
- ...(action === 'finalize' ? { number: `QT-${id.toUpperCase()}` } : {}),
131
- ...made,
132
- }, `quote.${action}`),
133
- );
134
- };
135
- }
136
-
137
- export const paymentLinks: Record<string, Semantics> = {
138
- PostPaymentLinks: createLink,
139
- GetPaymentLinks: listLinks,
140
- GetPaymentLinksPaymentLinkLineItems: linkLines,
141
- PostQuotes: createQuote,
142
- GetQuotesQuoteLineItems: quoteLines,
143
- GetQuotesQuoteComputedUpfrontLineItems: quoteLines,
144
- GetQuotesQuotePdf: pdf,
145
- PostQuotesQuoteFinalize: move('finalize', 'PostQuotesQuoteFinalize'),
146
- PostQuotesQuoteAccept: move('accept', 'PostQuotesQuoteAccept'),
147
- PostQuotesQuoteCancel: move('cancel', 'PostQuotesQuoteCancel'),
148
- };