@volter/twin-stripe 2.0.38 → 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,26 +1,34 @@
1
- // Customer semantics: the customer and what hangs off it (tax ids, the credit-balance ledger, the
2
- // cash balance, legacy sources, attached payment methods) and the top-level Source object. List,
3
- // update and delete of a customer, retrieve of a Source and the flat tax-id list are the derived core's.
4
- import type { Semantics, SemanticsContext } from '@volter/world-core';
5
- import { buildDiscount } from '../stripe-twin.ts';
6
- import { invoicePrefix } from './invoices.ts';
7
- import { mintCharge } from './payment-intents.ts';
8
- import { attachable, attachPaymentMethod } from './payment-methods.ts';
9
- import { at, created, deletedDiscount, expanded, fail, list, newest, redeemCoupon, search, where, type Row } from './shared.ts';
1
+ // Stripe's customers operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
2
+ // handler contract's context, computing with the engine (../engine/).
3
+ import type { HandlerContext } from '@volter/world-core';
4
+ import { deletedDiscount, type Row } from '../engine/common.ts';
5
+ import { newCustomer } from '../engine/customers.ts';
6
+ import { nowUnix } from '../engine/stripe.ts';
7
+ import { at, attachable, attachPaymentMethod, cashBalance, created, customerDiscount, customerMissing, customerSource, expanded, fail, list, makeTaxId, newest, redeemCoupon, search, sourceMissing, where } from './shared.ts';
10
8
 
11
- const customerMissing = (ctx: SemanticsContext, id: string): Response => fail(ctx, `No such customer: '${id}'`, 404, 'resource_missing');
9
+ // "If it’s for a deleted Customer, a subset of the customer’s information is returned, including a `deleted` property
10
+ // that’s set to true" (docs.stripe.com/api/customers/retrieve)
11
+ export async function GetCustomersCustomer(ctx: HandlerContext): Promise<Response> {
12
+ const id = at(ctx, 'customer');
13
+ const row = ctx.row('customer', id, { withDeleted: true });
14
+ if (!row) return customerMissing(ctx, id);
15
+ if (row.deleted === true) return ctx.reply({ id, object: 'customer', deleted: true });
16
+ return ctx.reply(expanded(ctx, 'customer', ctx.get('customer', id)!));
17
+ }
12
18
 
13
- /** The discount a customer's `coupon` makes, a parameter of API versions before the served one. */
14
- function customerDiscount(ctx: SemanticsContext, couponId: string): Row | Response {
15
- const coupon = ctx.get('coupon', couponId);
16
- return coupon ? buildDiscount(coupon, '', Number(ctx.now())) : fail(ctx, `No such coupon: '${couponId}'`, 400, 'resource_missing');
19
+ // the payment methods attached to a customer (customers.listPaymentMethods), optionally by `type`
20
+ export async function GetCustomersCustomerPaymentMethods(ctx: HandlerContext): Promise<Response> {
21
+ const customer = at(ctx, 'customer');
22
+ if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
23
+ const items = where(ctx, newest(ctx, 'payment_method').filter((m) => m.customer === customer), { type: (m, v) => m.type === v });
24
+ return list(ctx, 'payment_method', items);
17
25
  }
18
26
 
19
- /** What a new customer answers before any invoice: its invoice_prefix and a next_invoice_sequence of 1, a zero balance,
20
- * no metadata (docs.stripe.com/api/customers/object). */
21
- export const newCustomer = (id: string): Row => ({ invoice_prefix: invoicePrefix(id), next_invoice_sequence: 1, balance: 0, metadata: {}, invoice_settings: {} });
27
+ export async function GetCustomersSearch(ctx: HandlerContext): Promise<Response> {
28
+ return search(ctx, 'customer');
29
+ }
22
30
 
23
- const create: Semantics = async (ctx) => {
31
+ export async function PostCustomers(ctx: HandlerContext): Promise<Response> {
24
32
  // a `coupon` on create materializes the discount object on the customer (the coupon must exist)
25
33
  const couponId = typeof ctx.params.coupon === 'string' ? ctx.params.coupon : '';
26
34
  const discount = couponId ? customerDiscount(ctx, couponId) : null;
@@ -29,148 +37,18 @@ const create: Semantics = async (ctx) => {
29
37
  // (docs.stripe.com/api/customers/create#create_customer-payment_method)
30
38
  const pmId = typeof ctx.params.payment_method === 'string' ? ctx.params.payment_method : '';
31
39
  if (pmId && !attachable(ctx, pmId)) return fail(ctx, `No such PaymentMethod: '${pmId}'`, 400, 'resource_missing');
32
- const { coupon: _c, payment_method: _pm, ...rest } = ctx.params;
40
+ const given = ctx.params;
41
+ const { coupon: _c, payment_method: _pm, ...rest } = given;
33
42
  const id = ctx.mint('customer');
34
43
  const customer = await created(ctx, 'customer', { ...rest, id }, { livemode: false, discount: discount ? { ...discount, customer: id } : null, ...newCustomer(id) });
35
44
  if (discount) await redeemCoupon(ctx, couponId);
36
45
  if (pmId) await attachPaymentMethod(ctx, pmId, String(customer.id));
37
46
  return ctx.reply(customer);
38
- };
39
-
40
- const searchCustomers: Semantics = async (ctx) => search(ctx, 'customer');
41
-
42
- // "If it’s for a deleted Customer, a subset of the customer’s information is returned, including a `deleted` property
43
- // that’s set to true" (docs.stripe.com/api/customers/retrieve)
44
- const retrieve: Semantics = async (ctx) => {
45
- const id = at(ctx, 'customer');
46
- const row = ctx.row('customer', id, { withDeleted: true });
47
- if (!row) return customerMissing(ctx, id);
48
- if (row.deleted === true) return ctx.reply({ id, object: 'customer', deleted: true });
49
- return ctx.reply(expanded(ctx, 'customer', ctx.get('customer', id)!));
50
- };
51
-
52
- // the payment methods attached to a customer (customers.listPaymentMethods), optionally by `type`
53
- const paymentMethods: Semantics = async (ctx) => {
54
- const customer = at(ctx, 'customer');
55
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
56
- const items = where(ctx, newest(ctx, 'payment_method').filter((m) => m.customer === customer), { type: (m, v) => m.type === v });
57
- return list(ctx, 'payment_method', items);
58
- };
59
-
60
- const paymentMethod: Semantics = async (ctx) => {
61
- const customer = at(ctx, 'customer');
62
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
63
- const m = ctx.get('payment_method', at(ctx, 'payment_method'));
64
- return m && m.customer === customer ? ctx.reply(m) : fail(ctx, `No such PaymentMethod: '${at(ctx, 'payment_method')}'`, 404, 'resource_missing');
65
- };
66
-
67
- // removing the customer's discount answers the deleted-discount stub
68
- const deleteDiscount: Semantics = async (ctx) => {
69
- const id = at(ctx, 'customer');
70
- const customer = ctx.get('customer', id);
71
- if (!customer) return customerMissing(ctx, id);
72
- const discount = customer.discount as Row | null | undefined;
73
- if (!discount) return fail(ctx, `No active discount for customer: '${id}'`, 404, 'resource_missing');
74
- await ctx.write('customer', id, { discount: null }, 'customer.update');
75
- return ctx.reply(deletedDiscount(discount, ctx.now()));
76
- };
77
-
78
- // ── tax ids: a customer's, keyed under it; `type` and `value` are required ──
79
-
80
- // An EU VAT number is its country's prefix and that country's format (the European Commission's VIES formats,
81
- // ec.europa.eu/taxation_customs/vies); Stripe refuses one that does not fit as tax_id_invalid
82
- // (docs.stripe.com/error-codes#tax-id-invalid). The message's wording is the twin's.
83
- const EU_VAT: Record<string, RegExp> = {
84
- AT: /^U\d{8}$/, BE: /^[01]\d{9}$/, BG: /^\d{9,10}$/, CY: /^\d{8}[A-Z]$/, CZ: /^\d{8,10}$/, DE: /^\d{9}$/, DK: /^\d{8}$/,
85
- EE: /^\d{9}$/, EL: /^\d{9}$/, ES: /^[A-Z0-9]\d{7}[A-Z0-9]$/, FI: /^\d{8}$/, FR: /^[A-Z0-9]{2}\d{9}$/, HR: /^\d{11}$/,
86
- HU: /^\d{8}$/, IE: /^(\d{7}[A-W][A-I]?|\d[A-Z+*]\d{5}[A-W])$/, IT: /^\d{11}$/, LT: /^(\d{9}|\d{12})$/, LU: /^\d{8}$/,
87
- LV: /^\d{11}$/, MT: /^\d{8}$/, NL: /^\d{9}B\d{2}$/, PL: /^\d{10}$/, PT: /^\d{9}$/, RO: /^\d{2,10}$/, SE: /^\d{12}$/,
88
- SI: /^\d{8}$/, SK: /^\d{10}$/, XI: /^(\d{9}|\d{12}|GD\d{3}|HA\d{3})$/,
89
- };
90
- const euVatCountry = (value: string): string | undefined => {
91
- const v = value.replace(/[\s.-]/g, '').toUpperCase();
92
- const prefix = v.slice(0, 2);
93
- const format = EU_VAT[prefix];
94
- return format && format.test(v.slice(2)) ? (prefix === 'EL' ? 'GR' : prefix) : undefined;
95
- };
96
-
97
- /** The tax ID types Stripe checks against a government register: "Stripe automatically validates all Australian
98
- * Business Numbers (ABNs)", "all European Value-Added-Tax (EU VAT) numbers" (VIES) and "all UK Value-Added-Tax (GB VAT)
99
- * numbers" (HMRC); a verification's status "is pending, verified, unverified, or unavailable", and "When automatic
100
- * validation is unavailable, you must manually verify the tax ID" (docs.stripe.com/billing/customer/tax-ids). Any other
101
- * type answers unavailable; one of these waits pending. */
102
- const AUTO_VALIDATED = new Set(['au_abn', 'eu_vat', 'gb_vat']);
103
-
104
- /** A tax ID for its owner: the account itself, or one of its customers. */
105
- async function makeTaxId(ctx: SemanticsContext, customer: string | undefined): Promise<Response> {
106
- const { owner: _owner, ...params } = ctx.params;
107
- if (params.type === undefined || params.type === '') return fail(ctx, 'Missing required param: type.', 400, 'parameter_missing');
108
- if (params.value === undefined || params.value === '') return fail(ctx, 'Missing required param: value.', 400, 'parameter_missing');
109
- const vatCountry = params.type === 'eu_vat' ? euVatCountry(String(params.value)) : undefined;
110
- if (params.type === 'eu_vat' && !vatCountry) return ctx.refuse({ status: 400, code: 'tax_id_invalid', param: 'value', message: `Invalid value for eu_vat.` });
111
- return ctx.reply(
112
- await created(ctx, 'tax_id', { ...params, ...(customer ? { customer } : {}) }, {
113
- livemode: false,
114
- country: vatCountry ?? (typeof params.country === 'string' ? params.country : null),
115
- owner: customer ? { type: 'customer', customer } : { type: 'self' },
116
- verification: { status: AUTO_VALIDATED.has(String(params.type)) ? 'pending' : 'unavailable', verified_address: null, verified_name: null },
117
- }),
118
- );
119
47
  }
120
48
 
121
- const createTaxId: Semantics = async (ctx) => {
122
- const customer = at(ctx, 'customer');
123
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
124
- return makeTaxId(ctx, customer);
125
- };
126
-
127
- /** The owner a top-level tax-ID call names: the account itself unless `owner[type]=customer` names a customer
128
- * (docs.stripe.com/api/tax_ids/list: owner defaults to self). */
129
- function ownerOf(ctx: SemanticsContext): { customer?: string } | Response {
130
- const owner = (ctx.params.owner ?? {}) as Row;
131
- const type = String(owner.type ?? 'self');
132
- if (type === 'self') return {};
133
- if (type !== 'customer') return fail(ctx, `Invalid owner[type]: must be one of self or customer.`, 400, 'parameter_invalid_string_enum');
134
- const customer = typeof owner.customer === 'string' ? owner.customer : '';
135
- if (!customer) return fail(ctx, 'Missing required param: owner[customer].', 400, 'parameter_missing');
136
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
137
- return { customer };
138
- }
139
-
140
- const createOwnedTaxId: Semantics = async (ctx) => {
141
- const owner = ownerOf(ctx);
142
- return owner instanceof Response ? owner : makeTaxId(ctx, owner.customer);
143
- };
144
-
145
- const listOwnedTaxIds: Semantics = async (ctx) => {
146
- const owner = ownerOf(ctx);
147
- if (owner instanceof Response) return owner;
148
- return list(ctx, 'tax_id', newest(ctx, 'tax_id').filter((t) => (owner.customer ? t.customer === owner.customer : !t.customer)));
149
- };
150
-
151
- const taxIds: Semantics = async (ctx) => {
152
- const customer = at(ctx, 'customer');
153
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
154
- return list(ctx, 'tax_id', newest(ctx, 'tax_id').filter((t) => t.customer === customer));
155
- };
156
-
157
- const taxId: Semantics = async (ctx) => {
158
- const t = ctx.get('tax_id', at(ctx, 'id'));
159
- return t && t.customer === at(ctx, 'customer') ? ctx.reply(t) : fail(ctx, `No such tax_id: '${at(ctx, 'id')}'`, 404, 'resource_missing');
160
- };
161
-
162
- const deleteTaxId: Semantics = async (ctx) => {
163
- const id = at(ctx, 'id');
164
- const t = ctx.get('tax_id', id);
165
- if (!t || t.customer !== at(ctx, 'customer')) return fail(ctx, `No such tax_id: '${id}'`, 404, 'resource_missing');
166
- await ctx.write('tax_id', id, { deleted: true }, 'tax_id.delete');
167
- return ctx.reply({ id, object: 'tax_id', deleted: true });
168
- };
169
-
170
49
  // ── the credit-balance ledger: a negative amount is a credit; `ending_balance` is the running sum,
171
50
  // mirrored onto the customer's `balance` ──
172
-
173
- const createBalanceTransaction: Semantics = async (ctx) => {
51
+ export async function PostCustomersCustomerBalanceTransactions(ctx: HandlerContext): Promise<Response> {
174
52
  const customer = at(ctx, 'customer');
175
53
  if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
176
54
  const params = ctx.params;
@@ -187,246 +65,5 @@ const createBalanceTransaction: Semantics = async (ctx) => {
187
65
  // keyed by the new transaction so two adjustments at one instant are two writes, the last winning
188
66
  await ctx.write('customer', customer, { balance: ending }, `customer.balance_adjusted:${String(body.id)}`);
189
67
  return ctx.reply(body);
190
- };
191
-
192
- const balanceTransactions: Semantics = async (ctx) => {
193
- const customer = at(ctx, 'customer');
194
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
195
- return list(ctx, 'customer_balance_transaction', newest(ctx, 'customer_balance_transaction').filter((t) => t.customer === customer));
196
- };
197
-
198
- const balanceTransaction: Semantics = async (ctx) => {
199
- const t = ctx.get('customer_balance_transaction', at(ctx, 'transaction'));
200
- return t && t.customer === at(ctx, 'customer') ? ctx.reply(t) : fail(ctx, `No such balance transaction: '${at(ctx, 'transaction')}'`, 404, 'resource_missing');
201
- };
202
-
203
- // ── the cash balance: per-currency sums of the funding ledger, and the reconciliation setting ──
204
-
205
- function cashBalance(ctx: SemanticsContext, customer: string): Row {
206
- const byCurrency: Record<string, number> = {};
207
- for (const t of ctx.rows('customer_cash_balance_transaction')) {
208
- if (t.customer !== customer) continue;
209
- const cur = String(t.currency ?? 'usd');
210
- byCurrency[cur] = (byCurrency[cur] ?? 0) + (Number(t.net_amount) || 0);
211
- }
212
- const cust = ctx.row('customer', customer);
213
- const mode = typeof cust?._cash_balance_mode === 'string' ? cust._cash_balance_mode : 'automatic';
214
- return {
215
- object: 'cash_balance', customer,
216
- available: Object.keys(byCurrency).length ? byCurrency : null,
217
- settings: { reconciliation_mode: mode, using_merchant_default: mode === 'merchant_default' },
218
- livemode: false,
219
- };
220
- }
221
-
222
- const getCashBalance: Semantics = async (ctx) => {
223
- const customer = at(ctx, 'customer');
224
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
225
- return ctx.reply(cashBalance(ctx, customer));
226
- };
227
-
228
- const updateCashBalance: Semantics = async (ctx) => {
229
- const customer = at(ctx, 'customer');
230
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
231
- const settings = ctx.params.settings && typeof ctx.params.settings === 'object' ? (ctx.params.settings as Row) : {};
232
- const mode = typeof settings.reconciliation_mode === 'string' ? settings.reconciliation_mode : 'automatic';
233
- if (!['automatic', 'manual', 'merchant_default'].includes(mode)) return fail(ctx, 'Invalid settings[reconciliation_mode].', 400, 'parameter_invalid_string_enum');
234
- await ctx.write('customer', customer, { _cash_balance_mode: mode }, 'cash_balance.updated');
235
- return ctx.reply(cashBalance(ctx, customer));
236
- };
237
-
238
- const cashBalanceTransactions: Semantics = async (ctx) => {
239
- const customer = at(ctx, 'customer');
240
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
241
- return list(ctx, 'customer_cash_balance_transaction', newest(ctx, 'customer_cash_balance_transaction').filter((t) => t.customer === customer));
242
- };
243
-
244
- /** Automatic reconciliation (docs.stripe.com/payments/customer-balance/reconciliation): funds that arrive pay the
245
- * customer's open invoices that take bank transfers (payment_method_types customer_balance), the one whose number
246
- * the transfer's reference names first, then the oldest. Where the documentation stops and the twin decides: an
247
- * invoice is paid only when the balance covers all of what remains; a smaller balance waits for more funds. */
248
- async function reconcile(ctx: SemanticsContext, customer: string, currency: string, reference: string | null): Promise<void> {
249
- const takesTransfers = (inv: Row): boolean => ((inv.payment_settings as Row | undefined)?.payment_method_types as unknown[] | undefined)?.includes('customer_balance') === true;
250
- const open = ctx.rows('invoice').filter((inv) => inv.customer === customer && inv.status === 'open' && String(inv.currency ?? 'usd') === currency && takesTransfers(inv))
251
- .sort((a, b) => (reference && a.number === reference ? -1 : reference && b.number === reference ? 1 : Number(a.created) - Number(b.created)));
252
- for (const inv of open) {
253
- const available = Number(((cashBalance(ctx, customer).available as Row | null) ?? {})[currency] ?? 0);
254
- const amount = Number(inv.amount_remaining ?? inv.amount_due) || 0;
255
- if (amount <= 0 || available < amount) continue;
256
- const id = String(inv.id);
257
- if (ctx.legal('invoice', 'status', 'PostTestHelpersCustomersCustomerFundCashBalance', inv.status, 'paid', id)) continue;
258
- // the payment the balance makes: the invoice's own PaymentIntent (every invoice finalized with an amount due has one,
259
- // semantics/invoices.ts), paid from the cash balance, its charge, and the ledger's entry
260
- const piId = String(inv.payment_intent);
261
- 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: piId }, livemode: false });
262
- const charge = await mintCharge(ctx, piId, { currency, customer, invoice: id }, amount);
263
- await ctx.write('payment_intent', piId, { status: 'succeeded', amount_received: amount, latest_charge: charge }, 'payment_intent.succeeded');
264
- await ctx.write('invoice', id, {
265
- status: 'paid', paid: true, amount_paid: amount, amount_remaining: 0, payment_intent: piId, charge,
266
- status_transitions: { ...((inv.status_transitions as Row | undefined) ?? {}), paid_at: ctx.now() },
267
- }, 'invoice.paid');
268
- }
269
- }
270
-
271
- // test mode's stand-in for a bank transfer arriving (docs.stripe.com/payments/customer-balance/funding#test):
272
- // the customer's cash balance is funded by `amount`, and in automatic reconciliation the funds pay what waits on them
273
- const fundCashBalance: Semantics = async (ctx) => {
274
- const customer = at(ctx, 'customer');
275
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
276
- const amount = Math.trunc(Number(ctx.params.amount) || 0);
277
- if (amount <= 0) return fail(ctx, 'Missing required param: amount.', 400, 'parameter_missing');
278
- const currency = typeof ctx.params.currency === 'string' ? ctx.params.currency : '';
279
- if (!currency) return fail(ctx, 'Missing required param: currency.', 400, 'parameter_missing');
280
- const before = Number(((cashBalance(ctx, customer).available as Row | null) ?? {})[currency] ?? 0);
281
- const reference = typeof ctx.params.reference === 'string' ? ctx.params.reference : null;
282
- const funded = await created(ctx, 'customer_cash_balance_transaction', { customer }, {
283
- currency, type: 'funded', net_amount: amount, ending_balance: before + amount, livemode: false,
284
- funded: { bank_transfer: { reference, type: 'us_bank_transfer', us_bank_transfer: { network: 'ach', sender_name: null } } },
285
- });
286
- if ((cashBalance(ctx, customer).settings as Row).reconciliation_mode !== 'manual') await reconcile(ctx, customer, currency, reference);
287
- return ctx.reply(funded);
288
- };
289
-
290
- const cashBalanceTransaction: Semantics = async (ctx) => {
291
- const t = ctx.get('customer_cash_balance_transaction', at(ctx, 'transaction'));
292
- return t && t.customer === at(ctx, 'customer') ? ctx.reply(t) : fail(ctx, `No such cash balance transaction: '${at(ctx, 'transaction')}'`, 404, 'resource_missing');
293
- };
294
-
295
- // ── legacy sources: attaching a token mints a card with only the safe fields; the first becomes
296
- // the customer's default_source, and detaching the default promotes the next ──
297
-
298
- const attachSource: Semantics = async (ctx) => {
299
- const customer = at(ctx, 'customer');
300
- const c = ctx.get('customer', customer);
301
- if (!c) return customerMissing(ctx, customer);
302
- const src = typeof ctx.params.source === 'string' ? ctx.params.source : '';
303
- if (!src) return fail(ctx, 'Missing required param: source.', 400, 'parameter_missing');
304
- // a card token becomes the card it carries, the token spent: "you can only use tokens once" (the served spec's
305
- // token.used); any other source reference keeps the twin's stand-in card
306
- const token = src.startsWith('tok_') ? ctx.get('token', src) : undefined;
307
- if (token?.used === true) return fail(ctx, `You cannot use a Stripe token more than once: ${src}.`, 400, 'token_already_used');
308
- const tokenCard = token?.card && typeof token.card === 'object' ? (token.card as Row) : undefined;
309
- const last4 = String(4242 + ctx.rows('source').length + 1).slice(-4);
310
- const body = await created(ctx, 'source', { customer, ...(tokenCard ? { id: String(tokenCard.id) } : {}) }, tokenCard
311
- ? { ...tokenCard, customer, _source_token: src }
312
- : { object: 'card', last4, brand: 'Visa', exp_month: 12, exp_year: 2030, funding: 'credit', country: 'US', metadata: {}, _source_token: src });
313
- if (token) await ctx.write('token', src, { used: true }, 'token.used');
314
- const existing = ctx.rows('source').filter((s) => s.customer === customer);
315
- if (!c.default_source || existing.length === 1) await ctx.write('customer', customer, { default_source: body.id }, 'customer.updated');
316
- return ctx.reply(body);
317
- };
318
-
319
- const sources: Semantics = async (ctx) => {
320
- const customer = at(ctx, 'customer');
321
- if (!ctx.get('customer', customer)) return customerMissing(ctx, customer);
322
- const items = newest(ctx, 'source').filter((s) => s.customer === customer);
323
- return list(ctx, 'source', typeof ctx.params.object === 'string' ? items.filter((s) => s.object === ctx.params.object) : items);
324
- };
325
-
326
- /** The customer's source the path names, or undefined. */
327
- const customerSource = (ctx: SemanticsContext): Row | undefined => {
328
- const s = ctx.get('source', at(ctx, 'id'));
329
- return s && s.customer === at(ctx, 'customer') ? s : undefined;
330
- };
331
- const sourceMissing = (ctx: SemanticsContext): Response => fail(ctx, `No such source: '${at(ctx, 'id')}'`, 404, 'resource_missing');
332
-
333
- const source: Semantics = async (ctx) => {
334
- const s = customerSource(ctx);
335
- return s ? ctx.reply(s) : sourceMissing(ctx);
336
- };
337
-
338
- const updateSource: Semantics = async (ctx) => {
339
- if (!customerSource(ctx)) return sourceMissing(ctx);
340
- return ctx.reply(await ctx.write('source', at(ctx, 'id'), ctx.params, 'source.updated'));
341
- };
342
-
343
- const detachSource: Semantics = async (ctx) => {
344
- const customer = at(ctx, 'customer');
345
- const id = at(ctx, 'id');
346
- const c = ctx.get('customer', customer);
347
- const s = customerSource(ctx);
348
- if (!c || !s) return sourceMissing(ctx);
349
- await ctx.write('source', id, { deleted: true, customer: null }, 'source.detached');
350
- if (c.default_source === id) {
351
- const remaining = ctx.rows('source').filter((x) => x.customer === customer && x.id !== id);
352
- await ctx.write('customer', customer, { default_source: remaining[0]?.id ?? null }, 'customer.updated');
353
- }
354
- return ctx.reply({ id, object: s.object ?? 'card', deleted: true });
355
- };
356
-
357
- // ── the top-level Source: `type` is required; a card is chargeable at once, anything else pending ──
358
-
359
- const createSource: Semantics = async (ctx) => {
360
- const params = ctx.params;
361
- const srcType = typeof params.type === 'string' ? params.type : '';
362
- if (!srcType) return fail(ctx, 'Missing required param: type.', 400, 'parameter_missing');
363
- return ctx.reply(
364
- await created(ctx, 'source', { customer: null }, {
365
- object: 'source',
366
- status: srcType === 'card' ? 'chargeable' : 'pending',
367
- flow: srcType === 'card' ? 'none' : 'receiver',
368
- usage: (params.usage as string) ?? 'reusable',
369
- currency: typeof params.currency === 'string' ? params.currency : null,
370
- livemode: false,
371
- type: srcType,
372
- client_secret: `src_client_secret_twin_${ctx.rows('source').length + 1}`,
373
- owner: params.owner && typeof params.owner === 'object' ? params.owner : { name: null, email: null, address: null, phone: null, verified_name: null, verified_email: null, verified_address: null, verified_phone: null },
374
- metadata: params.metadata && typeof params.metadata === 'object' ? params.metadata : {},
375
- statement_descriptor: null,
376
- ...(srcType === 'ach_credit_transfer' ? achCreditTransfer(ctx) : {}),
377
- }),
378
- );
379
- };
380
-
381
- /** An ACH credit transfer source's account for the customer to wire to, and its receiver flow, as the sources create
382
- * page's example answers one (docs.stripe.com/api/sources/create): a test account number, "TEST BANK", routing
383
- * 110000000 and SWIFT TSTEZ122; the receiver's address "routing-account" with nothing yet charged, received or
384
- * returned, refund attributes by email and missing. Where the documentation stops and the twin decides: the account
385
- * number is the source's own. */
386
- function achCreditTransfer(ctx: SemanticsContext): Row {
387
- const account = `test_${String(ctx.rows('source').length + 1).padStart(12, '0')}`;
388
- return {
389
- ach_credit_transfer: { account_number: account, bank_name: 'TEST BANK', fingerprint: `twin_acct_${account}`, refund_account_holder_name: null, refund_account_holder_type: null, refund_routing_number: null, routing_number: '110000000', swift_code: 'TSTEZ122' },
390
- receiver: { address: `110000000-${account}`, amount_charged: 0, amount_received: 0, amount_returned: 0, refund_attributes_method: 'email', refund_attributes_status: 'missing' },
391
- };
392
68
  }
393
69
 
394
- // a Source update changes only its metadata and owner
395
- const updateTopSource: Semantics = async (ctx) => {
396
- const id = at(ctx, 'source');
397
- if (!ctx.get('source', id)) return fail(ctx, `No such source: '${id}'`, 404, 'resource_missing');
398
- const patch: Row = {};
399
- if (ctx.params.metadata !== undefined) patch.metadata = ctx.params.metadata;
400
- if (ctx.params.owner !== undefined) patch.owner = ctx.params.owner;
401
- return ctx.reply(await ctx.write('source', id, patch, 'source.updated'));
402
- };
403
-
404
- export const customers: Record<string, Semantics> = {
405
- PostCustomers: create,
406
- GetCustomersCustomer: retrieve,
407
- GetCustomersSearch: searchCustomers,
408
- GetCustomersCustomerPaymentMethods: paymentMethods,
409
- GetCustomersCustomerPaymentMethodsPaymentMethod: paymentMethod,
410
- DeleteCustomersCustomerDiscount: deleteDiscount,
411
- PostCustomersCustomerTaxIds: createTaxId,
412
- PostTaxIds: createOwnedTaxId,
413
- GetTaxIds: listOwnedTaxIds,
414
- GetCustomersCustomerTaxIds: taxIds,
415
- GetCustomersCustomerTaxIdsId: taxId,
416
- DeleteCustomersCustomerTaxIdsId: deleteTaxId,
417
- PostCustomersCustomerBalanceTransactions: createBalanceTransaction,
418
- GetCustomersCustomerBalanceTransactions: balanceTransactions,
419
- GetCustomersCustomerBalanceTransactionsTransaction: balanceTransaction,
420
- GetCustomersCustomerCashBalance: getCashBalance,
421
- PostCustomersCustomerCashBalance: updateCashBalance,
422
- GetCustomersCustomerCashBalanceTransactions: cashBalanceTransactions,
423
- GetCustomersCustomerCashBalanceTransactionsTransaction: cashBalanceTransaction,
424
- PostTestHelpersCustomersCustomerFundCashBalance: fundCashBalance,
425
- PostCustomersCustomerSources: attachSource,
426
- GetCustomersCustomerSources: sources,
427
- GetCustomersCustomerSourcesId: source,
428
- PostCustomersCustomerSourcesId: updateSource,
429
- DeleteCustomersCustomerSourcesId: detachSource,
430
- PostSources: createSource,
431
- PostSourcesSource: updateTopSource,
432
- };
@@ -1,62 +1,12 @@
1
- // Dispute semantics. A dispute is Stripe's to open, when a charge's cardholder disputes it: the test cards
2
- // that are disputed open one as their charge succeeds (./after-payment.ts). It is then listed, retrieved,
3
- // updated with evidence and closed. Closing concedes it (status → lost), the machine's move in
4
- // ../manifest.ts. Evidence is submitted with the update unless `submit` is false
5
- // (docs.stripe.com/api/disputes/update#update_dispute-submit), and the bank then decides it: in test mode, evidence whose
6
- // uncategorized_text is `winning_evidence` wins it and `losing_evidence` loses it (docs.stripe.com/testing#evidence);
7
- // a won dispute returns the disputed amount. Where the documentation stops and the twin decides: the bank decides at
8
- // once, and any other evidence leaves the dispute under review. List is the derived core's.
9
- import type { Semantics, SemanticsContext } from '@volter/world-core';
10
- import { asBool } from '../stripe-twin.ts';
11
- import { settleDisputeWon } from './ledger.ts';
12
- import { at, fail, type Row } from './shared.ts';
13
-
14
- const disputeMissing = (ctx: SemanticsContext, id: string): Response => fail(ctx, `No such dispute: '${id}'`, 404, 'resource_missing');
15
-
16
- const retrieve: Semantics = async (ctx) => {
1
+ // Stripe's disputes operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
2
+ // handler contract's context, computing with the engine (../engine/).
3
+ import type { HandlerContext } from '@volter/world-core';
4
+ import type { Row } from '../engine/common.ts';
5
+ import { asBool } from '../engine/stripe.ts';
6
+ import { at, disputesDisputeMissing, keepEvidence, settleDisputeWon } from './shared.ts';
7
+
8
+ export async function GetDisputesDispute(ctx: HandlerContext): Promise<Response> {
17
9
  const d = ctx.get('dispute', at(ctx, 'dispute'));
18
- return d ? ctx.reply(d) : disputeMissing(ctx, at(ctx, 'dispute'));
19
- };
20
-
21
- const close: Semantics = async (ctx) => {
22
- const id = at(ctx, 'dispute');
23
- const d = ctx.get('dispute', id);
24
- if (!d) return disputeMissing(ctx, id);
25
- const refused = ctx.legal('dispute', 'status', 'PostDisputesDisputeClose', d.status, undefined, id);
26
- if (refused) return ctx.refuse(refused);
27
- return ctx.reply(await ctx.write('dispute', id, { status: 'lost' }, 'dispute.close'));
28
- };
29
-
30
- /** Evidence staged without submitting it (submit=false), or on a dispute already under review or decided. */
31
- async function keepEvidence(ctx: SemanticsContext, id: string, fields: Row): Promise<Response> {
32
- return ctx.reply(await ctx.write('dispute', id, fields, 'dispute.update'));
10
+ return d ? ctx.reply(d) : disputesDisputeMissing(ctx, at(ctx, 'dispute'));
33
11
  }
34
12
 
35
- const update: Semantics = async (ctx) => {
36
- const id = at(ctx, 'dispute');
37
- const d = ctx.get('dispute', id);
38
- if (!d) return disputeMissing(ctx, id);
39
- const params = ctx.params;
40
- const given = params.evidence && typeof params.evidence === 'object' ? (params.evidence as Row) : {};
41
- const evidence = { ...((d.evidence as Row | undefined) ?? {}), ...given };
42
- const fields: Row = { evidence, ...(params.metadata && typeof params.metadata === 'object' ? { metadata: { ...((d.metadata as Row | undefined) ?? {}), ...(params.metadata as Row) } } : {}) };
43
- const submit = params.submit === undefined ? true : asBool(params.submit);
44
- const review = d.status === 'warning_needs_response' ? 'warning_under_review' : 'under_review';
45
- if (!submit || ctx.legal('dispute', 'status', 'PostDisputesDispute', d.status, review, id)) return keepEvidence(ctx, id, fields);
46
- const details = (d.evidence_details as Row | undefined) ?? {};
47
- let body = await ctx.write('dispute', id, { ...fields, status: review, evidence_details: { ...details, has_evidence: true, submission_count: (Number(details.submission_count) || 0) + 1 } }, 'charge.dispute.updated');
48
- const text = String(evidence.uncategorized_text ?? '');
49
- const decided = review === 'under_review' ? (text.includes('winning_evidence') ? 'won' : text.includes('losing_evidence') ? 'lost' : undefined) : undefined;
50
- if (decided && !ctx.legal('dispute', 'status', 'PostDisputesDispute', review, decided, id, 'vendor')) {
51
- const bt = decided === 'won' ? await settleDisputeWon(ctx, id, Number(d.amount) || 0, String(d.currency ?? 'usd')) : undefined;
52
- const kept = ((ctx.get('dispute', id)?.balance_transactions as unknown[] | undefined) ?? []);
53
- body = await ctx.write('dispute', id, { status: decided, ...(bt ? { balance_transactions: [...kept, ctx.get('balance_transaction', bt) ?? bt] } : {}) }, 'charge.dispute.closed');
54
- }
55
- return ctx.reply(body);
56
- };
57
-
58
- export const disputes: Record<string, Semantics> = {
59
- GetDisputesDispute: retrieve,
60
- PostDisputesDispute: update,
61
- PostDisputesDisputeClose: close,
62
- };
@@ -0,0 +1,80 @@
1
+ // The World's doors (the manifest's `doors`): what stands in for an act Stripe's API does not have, each under /_twin/.
2
+ import type { HandlerContext } from '@volter/world-core';
3
+ import type { Row } from '../engine/common.ts';
4
+ import { platformAccountDefault } from '../engine/connect.ts';
5
+ import { accountSettings, nowUnix, PLATFORM_ACCOUNT_ID } from '../engine/stripe.ts';
6
+ import { at, balanceBody, created, fail, payDuePayouts, settleDueEntries } from './shared.ts';
7
+
8
+ /** The platform's own account settings (its payout schedule, its public name) change on Stripe's Dashboard, which has no
9
+ * API: this stands in for that page. The account is Stripe's default until the page first changes it (the same default
10
+ * GET /v1/account answers); a hash is merged into what the account holds, as an update merges it. */
11
+ export async function account(ctx: HandlerContext): Promise<Response> {
12
+ const ex = ctx.get('account', PLATFORM_ACCOUNT_ID);
13
+ const given: Row = ctx.params;
14
+ const { type: _t, settings: rawSettings, business_profile: rawProfile, ...rest } = given;
15
+ const { id: _id, ...base } = platformAccountDefault();
16
+ const fields = {
17
+ ...(ex ? {} : base),
18
+ ...rest,
19
+ settings: accountSettings(rawSettings, ex?.settings as Row | undefined),
20
+ ...(rawProfile && typeof rawProfile === 'object' ? { business_profile: { ...((ex?.business_profile as Row | undefined) ?? {}), ...(rawProfile as Row) } } : {}),
21
+ };
22
+ return ctx.reply(await ctx.write('account', PLATFORM_ACCOUNT_ID, fields, 'account.update'));
23
+ }
24
+
25
+ /** Time's events, sent when a caller drives them (a runner's drainer): what time settled on each account is written —
26
+ * payouts whose arrival date came (pending → paid, sent as payout.paid) and funds that came due (pending → available),
27
+ * for which the account is sent one balance.available ("Occurs whenever your Stripe balance has been updated (e.g., when
28
+ * a charge is available to be paid out)", docs.stripe.com/api/events/types) carrying its Balance, when an entry that
29
+ * settled adds to it ("This event is not fired for negative transactions", the same page); a connected account's
30
+ * events go to Connect endpoints. A read already sees these moves; the drain is what makes Stripe's events about them
31
+ * arrive. Answers what it sent. */
32
+ export async function drain(ctx: HandlerContext): Promise<Response> {
33
+ const accounts = [undefined, ...ctx.rows('account').map((a) => String(a.id)).filter((id) => id !== PLATFORM_ACCOUNT_ID)];
34
+ const delivered: Array<{ type: string; id?: string; account?: string }> = [];
35
+ for (const account of accounts) {
36
+ for (const id of await payDuePayouts(ctx, account)) delivered.push({ type: 'payout.paid', id, ...(account ? { account } : {}) });
37
+ if ((await settleDueEntries(ctx, account)).some((t) => Number(t.net) > 0)) {
38
+ // the notice the event is sent for: the account's Balance, which has no id of its own (./events.ts renders it)
39
+ await ctx.write('_balance_notice', `${account ?? PLATFORM_ACCOUNT_ID}:${ctx.occurredAt}`, { account: account ?? null, balance: balanceBody(ctx, account) }, 'balance.available');
40
+ delivered.push({ type: 'balance.available', ...(account ? { account } : {}) });
41
+ }
42
+ }
43
+ return Response.json({ delivered });
44
+ }
45
+
46
+ /** POST /_twin/app-credentials: the platform's API keys as the dashboard's API keys page shows them, a secret key and a
47
+ * publishable key (`sk_test_…`, `pk_test_…`), held by their SHA-256, and the signing secrets its webhook endpoints
48
+ * are made with. The runtime issues them to a World's applications at every boot (the descriptor's credentialDoor), so
49
+ * the answer is the same each time: the account's one pair of keys, made the first time. Where the documentation
50
+ * stops: the keys' length and alphabet after the prefix. */
51
+ // source: https://docs.stripe.com/keys "Publishable keys"
52
+ export async function appCredentials(ctx: HandlerContext): Promise<Response> {
53
+ const secretKey = `sk_test_51${(await ctx.secret('stripe-platform-key:app')).replace(/[^A-Za-z0-9]/g, '').slice(0, 96)}`;
54
+ const publishableKey = `pk_test_51${(await ctx.secret('stripe-publishable-key:app')).replace(/[^A-Za-z0-9]/g, '').slice(0, 96)}`;
55
+ if (!ctx.rowsRaw('_platform_key').some((k) => k.id === 'secret:app' && k._sha256 === ctx.crypto.sha256(secretKey))) {
56
+ await ctx.record('_platform_key', { kind: 'secret', _sha256: ctx.crypto.sha256(secretKey), created: ctx.occurredAt, deleted: false }, 'secret:app');
57
+ await ctx.record('_platform_key', { kind: 'publishable', _sha256: ctx.crypto.sha256(publishableKey), created: ctx.occurredAt, deleted: false }, 'publishable:app');
58
+ }
59
+ // the signing secrets the application verifies its webhooks with, its account's and its Connect endpoint's: an
60
+ // endpoint made for it (the webhookEndpoints door) is signed with the one of its kind, so the two agree
61
+ return Response.json({ secret_key: secretKey, publishable_key: publishableKey, webhook_secret: await appEndpointSecret(ctx, false), connect_webhook_secret: await appEndpointSecret(ctx, true) }, { status: 201 });
62
+ }
63
+
64
+ /** The signing secret the World's application is issued for its account's endpoints, or for its Connect endpoint. */
65
+ const appEndpointSecret = async (ctx: HandlerContext, connect: boolean): Promise<string> =>
66
+ `whsec_${(await ctx.secret(connect ? 'stripe-webhook-secret:connect' : 'stripe-webhook-secret:app')).replace(/[^A-Za-z0-9]/g, '').slice(0, 32)}`;
67
+
68
+ /** POST /_twin/webhook-endpoints {url, events, connect?}: an endpoint added on the Dashboard's Webhooks page, the events
69
+ * it takes, signed with the secret the World's application is issued (./doors.ts `appCredentials`): the twin's
70
+ * decision, since Stripe mints an endpoint's secret and lets no one choose one. */
71
+ // source: https://docs.stripe.com/webhooks "register a webhook endpoint, Stripe pushes real-time data to it when"
72
+ export async function webhookEndpoints(ctx: HandlerContext): Promise<Response> {
73
+ const b = (ctx.body && typeof ctx.body === 'object' ? ctx.body : {}) as Record<string, unknown>;
74
+ if (typeof b.url !== 'string' || !/^https?:\/\//.test(b.url) || !Array.isArray(b.events) || !b.events.length) return Response.json({ error: 'a url and its events are required' }, { status: 400 });
75
+ const connect = b.connect === true;
76
+ const id = ctx.mint('webhook_endpoint');
77
+ const secret = await appEndpointSecret(ctx, connect);
78
+ await ctx.write('webhook_endpoint', id, { id, object: 'webhook_endpoint', url: b.url, enabled_events: b.events.map(String), status: 'enabled', connect, livemode: false, metadata: {}, api_version: null, application: null, description: null, secret, created: Math.floor(Date.parse(ctx.occurredAt) / 1000) }, 'webhook_endpoint.create');
79
+ return Response.json({ id, secret }, { status: 201 });
80
+ }