@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,443 +1,6 @@
1
- import { afterStripeWrite, embedIssuingAuthorizationCard, isValidApiVersion, view } from "./stripe-twin.js";
2
- const unexpected = (verb, allowed) => ({
3
- status: 400,
4
- code: 'payment_intent_unexpected_state',
5
- message: `This PaymentIntent could not be ${verb} because it has a status of {from}. ${allowed}`,
6
- });
7
- const OPEN = ['requires_payment_method', 'requires_confirmation', 'requires_action', 'requires_capture', 'processing'];
8
- /** A move no API call makes: Stripe's own (`vendor`), the clock's (`time`), or the person on a
9
- * Stripe-hosted page (`external`: Checkout, the billing portal, a hosted invoice or 3DS page). */
10
- const by = (actor, from, to, source) => ({ actor, from, to, source });
11
- /** A bank account's `status` as a payout or payment destination. Stripe validates its routing number,
12
- * verifies it (micro-deposits, or instantly) and marks it errored when a payout or debit to it fails
13
- * (the page each machine cites); no call the twin serves does any of that (the customer-source verify is
14
- * a gap), so no move of it is the twin's. */
15
- const bankAccountStatus = (_doc) => ({ initial: 'new', transitions: [] });
16
- /** A flag an update sets either way. */
17
- const flag = (operation, source) => ['true', 'false'].map((to) => ({ operation, from: '*', to, source }));
18
- /** A PaymentIntent's `status`: which operation may move it from where, and what Stripe answers otherwise. */
19
- const paymentIntentStatus = {
20
- // "After you create the PaymentIntent, its status is `requires_payment_method` until you attach a payment method"
21
- // (docs.stripe.com/payments/paymentintents/lifecycle)
22
- initial: 'requires_payment_method',
23
- transitions: [
24
- // created with its payment method, it "enters the `requires_confirmation` status and is ready to confirm" (the same page)
25
- { operation: 'PostPaymentIntents', from: ['requires_payment_method'], to: 'requires_confirmation', source: 'https://docs.stripe.com/payments/paymentintents/lifecycle' },
26
- // confirm lands on one of its outcomes, decided by the payment method and the capture method
27
- ...['requires_payment_method', 'requires_action', 'requires_capture', 'succeeded'].map((to) => ({
28
- operation: 'PostPaymentIntentsIntentConfirm',
29
- from: ['requires_payment_method', 'requires_confirmation', 'requires_action'],
30
- to,
31
- refusal: { status: 400, code: 'payment_intent_unexpected_state', message: 'You cannot confirm this PaymentIntent because it has a status of {from}.' },
32
- source: 'https://docs.stripe.com/api/payment_intents/confirm',
33
- })),
34
- // a bank debit confirmed (created with `confirm`, confirmed, or verified by micro-deposits) is submitted and
35
- // processes: "The PaymentIntent you create initially has a status of `processing`"; "When the bank account is
36
- // successfully verified, Stripe returns the PaymentIntent object with a status of `processing`"
37
- ...['PostPaymentIntents', 'PostPaymentIntentsIntentConfirm'].map((operation) => ({
38
- operation, from: ['requires_payment_method', 'requires_confirmation', 'requires_action'], to: 'processing',
39
- source: 'https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment?payment-ui=direct-api',
40
- })),
41
- // and succeeds: "After the payment has succeeded, the PaymentIntent status is updated from `processing`
42
- // to `succeeded`" (semantics/payment-intents.ts settleBankDebits)
43
- by('vendor', ['processing'], 'succeeded', 'https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment?payment-ui=direct-api'),
44
- // creating one with `confirm` confirms it at once, landing where a confirm lands
45
- ...['requires_payment_method', 'requires_action', 'requires_capture', 'succeeded'].map((to) => ({
46
- operation: 'PostPaymentIntents',
47
- from: ['requires_payment_method', 'requires_confirmation'],
48
- to,
49
- source: 'spec:PostPaymentIntents "Set to `true` to attempt to [confirm this PaymentIntent](https://docs.stripe.com/api/payment_intents/confirm) immediately."',
50
- })),
51
- {
52
- operation: 'PostPaymentIntentsIntentCapture',
53
- from: ['requires_capture'],
54
- to: 'succeeded',
55
- refusal: unexpected('captured', 'Only a PaymentIntent with one of the following statuses may be captured: requires_capture.'),
56
- source: 'https://docs.stripe.com/api/payment_intents/capture',
57
- },
58
- {
59
- operation: 'PostPaymentIntentsIntentIncrementAuthorization',
60
- from: ['requires_capture'],
61
- to: 'requires_capture',
62
- refusal: unexpected('incremented', 'Only a PaymentIntent with a status of requires_capture may be incremented.'),
63
- source: 'https://docs.stripe.com/api/payment_intents/increment_authorization',
64
- },
65
- {
66
- operation: 'PostPaymentIntentsIntentCancel',
67
- from: OPEN,
68
- to: 'canceled',
69
- refusal: unexpected('canceled', 'Only a PaymentIntent with one of the following statuses may be canceled: requires_payment_method, requires_capture, requires_confirmation, requires_action, processing.'),
70
- source: 'https://docs.stripe.com/api/payment_intents/cancel',
71
- },
72
- {
73
- operation: 'PostPaymentIntentsIntentVerifyMicrodeposits',
74
- from: ['requires_action'],
75
- to: 'processing',
76
- refusal: { status: 400, code: 'payment_intent_unexpected_state', message: 'This PaymentIntent could not be verified because it has a status of {from}.' },
77
- source: 'https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment?payment-ui=direct-api',
78
- },
79
- {
80
- // paying the invoice an intent collects for settles the intent
81
- operation: 'PostInvoicesInvoicePay',
82
- from: OPEN,
83
- to: 'succeeded',
84
- source: 'https://docs.stripe.com/api/invoices/pay',
85
- },
86
- ...['succeeded', 'requires_action'].map((to) => ({
87
- operation: 'PostPaymentIntentsIntentApplyCustomerBalance',
88
- from: OPEN,
89
- to,
90
- refusal: { status: 400, code: 'payment_intent_unexpected_state', message: 'This PaymentIntent could not be updated because it has a status of {from}.' },
91
- source: 'https://docs.stripe.com/api/payment_intents/apply_customer_balance',
92
- })),
93
- // a card presented to the Terminal reader processing an intent pays it (held, under manual capture)
94
- ...['succeeded', 'requires_capture'].map((to) => ({
95
- operation: 'PostTestHelpersTerminalReadersReaderPresentPaymentMethod', from: ['requires_payment_method', 'requires_confirmation'], to,
96
- source: 'https://docs.stripe.com/api/terminal/readers/present_payment_method',
97
- })),
98
- // the customer pays, in the billing portal, the invoice an intent collects for after its charge was declined
99
- by('external', ['requires_payment_method'], 'succeeded', 'https://docs.stripe.com/customer-management'),
100
- // Stripe also fails a bank debit back to needing a payment method, completes 3-D Secure on the bank's page and
101
- // cancels a hold left uncaptured; the twin's bank debits succeed unless their test account stays processing, it has no 3-D Secure page, and it keeps
102
- // holds until they are captured or canceled
103
- ],
104
- };
105
- /** A charge's `captured`: an authorization (capture=false) is captured once. */
106
- const chargeCaptured = {
107
- initial: true,
108
- transitions: [
109
- {
110
- operation: 'PostChargesChargeCapture',
111
- from: ['false'],
112
- to: 'true',
113
- refusal: { status: 400, code: 'charge_already_captured', message: 'Charge ch_xxx has already been captured.' },
114
- source: 'https://docs.stripe.com/api/charges/capture',
115
- },
116
- // a manual-capture intent's capture captures the charge its confirm authorized
117
- { operation: 'PostPaymentIntentsIntentCapture', from: ['false'], to: 'true', source: 'https://docs.stripe.com/api/payment_intents/capture' },
118
- ],
119
- };
120
- /** A charge's `refunded`: true once refunds take its whole amount, false again when a refund that made it so is
121
- * canceled. A capture or a cancel never makes it so: "`refunded` will no longer be `true` for payment cancellation
122
- * flows", and a partial capture makes no Refund (docs.stripe.com/changelog/basil/2025-03-31/remove-refund-from-partial-
123
- * capture-and-payment-cancellation-flow). */
124
- const chargeRefunded = {
125
- initial: false,
126
- transitions: [
127
- ...['PostRefunds', 'PostChargesChargeRefunds', 'PostCreditNotes'].map((operation) => ({ operation, from: ['false'], to: 'true', source: 'https://docs.stripe.com/api/charges/object#charge_object-refunded' })),
128
- { operation: 'PostRefundsRefundCancel', from: ['true'], to: 'false', source: 'https://docs.stripe.com/api/refunds/cancel' },
129
- // (an authorization left uncaptured is canceled after seven days, "Uncaptured PaymentIntents are cancelled a set
130
- // number of days (7 by default) after their creation", docs.stripe.com/api/payment_intents/capture; the twin keeps it)
131
- ],
132
- };
133
- /** A charge's `status`: the twin settles a charge when it is made (a declined one is never
134
- * stored); an asynchronous one settles later, on the network's word. */
135
- const chargeStatus = {
136
- initial: 'succeeded',
137
- transitions: [
138
- // (an asynchronous charge is pending at Stripe until it settles or fails; the twin's settle when made)
139
- ],
140
- };
141
- /** A charge's `paid`: true once it succeeded or was authorized; a pending one becomes paid when it settles. */
142
- const chargePaid = {
143
- initial: true,
144
- // (a charge is paid when it settles; the twin's settle when made)
145
- transitions: [],
146
- };
147
- /** A refund's `status`: only one waiting on the customer (requires_action) may be canceled; the
148
- * rest is the network's. */
149
- const refundStatus = {
150
- initial: 'succeeded',
151
- transitions: [
152
- {
153
- operation: 'PostRefundsRefundCancel',
154
- from: ['requires_action'],
155
- to: 'canceled',
156
- refusal: { status: 400, code: 'refund_status_invalid', message: 'Refund {id} cannot be canceled because its status is {from}.' },
157
- source: 'https://docs.stripe.com/api/refunds/cancel',
158
- },
159
- // a card refund the available balance does not cover is held pending, and made once the balance covers it: "Stripe
160
- // holds the refund as pending for card transactions ... until your Stripe balance becomes sufficient"
161
- // (docs.stripe.com/refunds; semantics/refunds.ts)
162
- // (a paid invoice's credit note makes its refund as a refund call does: "Refunds: create a new refund (using
163
- // refund_amount)", docs.stripe.com/api/credit_notes/create)
164
- ...['PostRefunds', 'PostChargesChargeRefunds', 'PostCreditNotes'].map((operation) => ({ operation, from: ['succeeded'], to: 'pending', source: 'https://docs.stripe.com/refunds' })),
165
- by('vendor', ['pending'], 'succeeded', 'https://docs.stripe.com/refunds'),
166
- // a bank-transfer payment's refund waits in requires_action for the customer's bank details, and is made so
167
- // (semantics/refunds.ts); (Stripe also fails some refunds; the twin's do not fail)
168
- ],
169
- };
170
- const SETUP_OPEN = ['requires_payment_method', 'requires_confirmation', 'requires_action'];
171
- const setupUnexpected = (verb) => ({ status: 400, code: 'setup_intent_unexpected_state', message: `You cannot ${verb} this SetupIntent because it has a status of {from}.` });
172
- /** A SetupIntent's `status`: confirm settles it (or leaves a bank account awaiting micro-deposits),
173
- * cancel ends an open one, and verification completes the micro-deposit wait. */
174
- const setupIntentStatus = {
175
- // "After you create the SetupIntent, it has a status of `requires_payment_method` until you attach a payment method"
176
- // (docs.stripe.com/payments/paymentintents/lifecycle)
177
- initial: 'requires_payment_method',
178
- transitions: [
179
- // created with its payment method, it "enters the `requires_confirmation` status and is ready to confirm" (the same page)
180
- { operation: 'PostSetupIntents', from: ['requires_payment_method'], to: 'requires_confirmation', source: 'https://docs.stripe.com/payments/paymentintents/lifecycle' },
181
- ...['requires_action', 'succeeded'].map((to) => ({
182
- operation: 'PostSetupIntentsIntentConfirm',
183
- from: SETUP_OPEN,
184
- to,
185
- refusal: setupUnexpected('confirm'),
186
- source: 'https://docs.stripe.com/api/setup_intents/confirm',
187
- })),
188
- {
189
- operation: 'PostSetupIntentsIntentCancel',
190
- from: SETUP_OPEN,
191
- to: 'canceled',
192
- refusal: setupUnexpected('cancel'),
193
- source: 'https://docs.stripe.com/api/setup_intents/cancel',
194
- },
195
- {
196
- operation: 'PostSetupIntentsIntentVerifyMicrodeposits',
197
- from: ['requires_action'],
198
- to: 'succeeded',
199
- refusal: { status: 400, code: 'setup_intent_unexpected_state', message: 'This SetupIntent could not be verified because it has a status of {from}.' },
200
- source: 'https://docs.stripe.com/api/setup_intents/verify_microdeposits',
201
- },
202
- // (Stripe completes 3-D Secure on the bank's page and settles an asynchronous method from processing; the
203
- // twin has no such page and settles every method at once)
204
- ],
205
- };
206
- const invoiceRefusal = (message) => ({ status: 400, code: 'invoice_not_editable', message });
207
- /** An invoice's `status`: a draft is finalized (or sent, which finalizes it) into open, an open one
208
- * is paid, written off or voided. Sending leaves any other status where it is. */
209
- const invoiceStatus = {
210
- initial: 'draft',
211
- transitions: [
212
- {
213
- operation: 'PostInvoicesInvoiceFinalize',
214
- from: ['draft'],
215
- to: 'open',
216
- refusal: invoiceRefusal('This invoice cannot be finalized because it has status {from}.'),
217
- source: 'https://docs.stripe.com/api/invoices/finalize',
218
- },
219
- // docs.stripe.com/invoicing/integration: "To finalize a draft invoice, use the Dashboard, send it to the customer, or pay it."
220
- { operation: 'PostInvoicesInvoiceSend', from: ['draft'], to: 'open', source: 'https://docs.stripe.com/invoicing/integration' },
221
- { operation: 'PostInvoicesInvoiceSend', from: ['open', 'paid', 'uncollectible', 'void'], source: 'https://docs.stripe.com/api/invoices/send' },
222
- {
223
- operation: 'PostInvoicesInvoicePay',
224
- from: ['open', 'uncollectible'],
225
- to: 'paid',
226
- refusal: invoiceRefusal('This invoice cannot be paid because it has status {from}.'),
227
- refusals: { paid: { status: 400, code: 'invoice_payment_intent_requires_action', message: 'This invoice has already been paid.' } },
228
- source: 'https://docs.stripe.com/api/invoices/pay',
229
- },
230
- {
231
- operation: 'PostInvoicesInvoiceMarkUncollectible',
232
- from: ['open'],
233
- to: 'uncollectible',
234
- refusal: invoiceRefusal('Only open invoices can be marked uncollectible (this one is {from}).'),
235
- source: 'https://docs.stripe.com/api/invoices/mark_uncollectible',
236
- },
237
- {
238
- // only a finalized invoice is voided; a draft is deleted
239
- operation: 'PostInvoicesInvoiceVoid',
240
- from: ['open', 'uncollectible'],
241
- to: 'void',
242
- refusal: invoiceRefusal('This invoice cannot be voided because it has status {from}.'),
243
- source: 'https://docs.stripe.com/api/invoices/void',
244
- },
245
- // confirming or capturing the PaymentIntent an invoice collects through pays the invoice
246
- ...['PostPaymentIntentsIntentConfirm', 'PostPaymentIntentsIntentCapture'].map((operation) => ({
247
- operation, from: ['draft', 'open', 'uncollectible'], to: 'paid', source: 'https://docs.stripe.com/billing/subscriptions/build-subscriptions',
248
- })),
249
- // as does its bank debit succeeding once submitted (semantics/payment-intents.ts settleBankDebits)
250
- by('vendor', ['draft', 'open', 'uncollectible'], 'paid', 'https://docs.stripe.com/billing/subscriptions/build-subscriptions'),
251
- // a bank transfer that arrives in the customer's cash balance pays an open invoice that takes transfers
252
- // (automatic reconciliation; in test mode the funding helper stands in for the transfer)
253
- { operation: 'PostTestHelpersCustomersCustomerFundCashBalance', from: ['open'], to: 'paid', source: 'https://docs.stripe.com/payments/customer-balance/reconciliation' },
254
- // a subscription's first invoice is paid on the Checkout page that started it
255
- by('external', ['draft'], 'paid', 'https://docs.stripe.com/payments/checkout/how-checkout-works'),
256
- // a subscription's renewal, drafted at its period's end, is finalized about an hour later and charged: paid, or
257
- // open when the charge is declined (semantics/renewals.ts)
258
- by('time', ['draft'], 'paid', 'https://docs.stripe.com/invoicing/integration/workflow-transitions#finalized'),
259
- by('time', ['draft'], 'open', 'https://docs.stripe.com/billing/subscriptions/overview#payment-status'),
260
- // an update that ends a subscription's trial invoices its new period and attempts payment at once: paid, or open
261
- // when the charge is declined or not attempted (semantics/subscriptions.ts applyTrialEnd)
262
- { operation: 'PostSubscriptionsSubscriptionExposedId', from: ['draft'], to: 'paid', source: 'https://docs.stripe.com/billing/subscriptions/upgrade-downgrade#immediate-payment' },
263
- // a trialing subscription's first invoice, for nothing, is paid as it is made ("An immediate invoice is still
264
- // created, but the amount is 0", docs.stripe.com/billing/subscriptions/trials/free-trials)
265
- { operation: 'PostSubscriptions', from: ['draft'], to: 'paid', source: 'https://docs.stripe.com/billing/subscriptions/trials/free-trials' },
266
- { operation: 'PostSubscriptionsSubscriptionExposedId', from: ['draft'], to: 'open', source: 'https://docs.stripe.com/billing/subscriptions/upgrade-downgrade#immediate-payment' },
267
- // the customer pays an open invoice in the billing portal
268
- by('external', ['open'], 'paid', 'https://docs.stripe.com/customer-management'),
269
- // (Stripe also finalizes any other auto-advancing draft after an hour, retries an open invoice on its schedule
270
- // and, retries exhausted, may write it off, and takes payment on the hosted invoice page; the twin's other
271
- // invoices move only by the calls above)
272
- ],
273
- };
274
- /** A subscription's `status` as the twin's own operations move it; canceling ends any subscription. */
275
- const subscriptionStatus = {
276
- initial: 'active',
277
- transitions: [
278
- { operation: 'DeleteSubscriptionsSubscriptionExposedId', from: '*', to: 'canceled', source: 'https://docs.stripe.com/api/subscriptions/cancel' },
279
- // (a test clock's advance moves no subscription itself: when the clock reaches its target, a trial that ended there
280
- // ends by time's move below, on the next request; semantics/test-clocks.ts finishClockAdvances)
281
- // "Deleting the simulation also deletes the test customers associated with the clock and cancels their
282
- // subscriptions" (docs.stripe.com/billing/testing/test-clocks/api-advanced-usage)
283
- { operation: 'DeleteTestHelpersTestClocksTestClock', from: '*', to: 'canceled', source: 'https://docs.stripe.com/billing/testing/test-clocks/api-advanced-usage' },
284
- // "Cancels a subscription schedule and its associated subscription immediately" (docs.stripe.com/api/subscription_schedules/cancel)
285
- { operation: 'PostSubscriptionSchedulesScheduleCancel', from: ['active', 'trialing', 'past_due', 'incomplete'], to: 'canceled', source: 'https://docs.stripe.com/api/subscription_schedules/cancel' },
286
- // confirming or capturing the first invoice's PaymentIntent, or paying the invoice itself, pays it and
287
- // activates the subscription
288
- ...['PostPaymentIntentsIntentConfirm', 'PostPaymentIntentsIntentCapture'].map((operation) => ({
289
- operation, from: ['incomplete'], to: 'active', source: 'https://docs.stripe.com/billing/subscriptions/build-subscriptions',
290
- })),
291
- // or when its bank debit succeeds once submitted (semantics/payment-intents.ts settleBankDebits)
292
- by('vendor', ['incomplete'], 'active', 'https://docs.stripe.com/billing/subscriptions/build-subscriptions'),
293
- // paying the invoice a subscription waits on (its first, or a renewal that failed) makes it active
294
- { operation: 'PostInvoicesInvoicePay', from: ['incomplete', 'past_due'], to: 'active', source: 'https://docs.stripe.com/billing/subscriptions/overview#payment-status' },
295
- // an update's `trial_end`: `now` ends a trial at once and charges the new period, active when paid and past_due when
296
- // not; a future timestamp starts a trial on a subscription out of one (semantics/subscriptions.ts applyTrialEnd)
297
- { operation: 'PostSubscriptionsSubscriptionExposedId', from: ['trialing'], to: 'active', source: 'https://docs.stripe.com/api/subscriptions/update#update_subscription-trial_end' },
298
- { operation: 'PostSubscriptionsSubscriptionExposedId', from: ['trialing'], to: 'past_due', source: 'https://docs.stripe.com/billing/subscriptions/upgrade-downgrade#immediate-payment' },
299
- {
300
- operation: 'PostSubscriptionsSubscriptionExposedId', from: ['active', 'past_due'], to: 'trialing',
301
- // (Where the documentation stops and the twin decides: the wording of the refusal for a subscription neither
302
- // active nor past_due; a canceled one answers what Stripe answers any update of a canceled subscription.)
303
- refusal: { status: 400, message: 'A trial can only be added to an active or past_due subscription; this one is {from}.' },
304
- refusals: { canceled: { status: 400, message: 'A canceled subscription can only update its cancellation_details and metadata.' } },
305
- source: 'https://docs.stripe.com/billing/subscriptions/trials/free-trials#adding-a-new-trial-to-a-subscription-previously-in-a-trial',
306
- },
307
- // a trial ends on its own clock (Stripe pauses one that ends with no payment method to bill; the twin's
308
- // trials end active), and a renewal whose charge is declined leaves the subscription past_due
309
- // (semantics/renewals.ts)
310
- by('time', ['trialing'], 'active', 'https://docs.stripe.com/billing/subscriptions/trials'),
311
- by('time', ['active'], 'past_due', 'https://docs.stripe.com/billing/subscriptions/overview#payment-status'),
312
- // one set to cancel at its period's end is canceled when the period ends
313
- by('time', ['active', 'trialing', 'past_due'], 'canceled', 'https://docs.stripe.com/billing/subscriptions/cancel#cancel-at-end-of-cycle'),
314
- // the customer pays the invoice it waits on in the billing portal
315
- by('external', ['past_due'], 'active', 'https://docs.stripe.com/customer-management'),
316
- // (Stripe also expires an incomplete subscription after 23 hours, and retries a failed renewal on its schedule,
317
- // the settings ending the retries unpaid or canceled; the twin makes no retry)
318
- // the customer cancels in the billing portal
319
- by('external', ['active', 'trialing', 'past_due', 'unpaid', 'paused'], 'canceled', 'spec:/components/schemas/portal_subscription_cancel "Whether to cancel subscriptions immediately or at the end of the billing period."'),
320
- ],
321
- };
322
- const notOpen = (verb) => ({ status: 400, code: 'checkout_session_not_open', message: `You may only ${verb} a Checkout Session that is currently in the \`open\` state.` });
323
- /** A Checkout Session's `status`: an open session expires (asked to, or 24 hours on), or completes
324
- * when the customer pays on the hosted page. Completion is the external actor's move, the one the
325
- * hosted Checkout page (src/screens/checkout.tsx) performs. */
326
- const checkoutStatus = {
327
- initial: 'open',
328
- transitions: [
329
- { operation: 'PostCheckoutSessionsSessionExpire', from: ['open'], to: 'expired', refusal: notOpen('expire'), source: 'https://docs.stripe.com/api/checkout/sessions/expire' },
330
- { actor: 'external', from: ['open'], to: 'complete', refusal: notOpen('complete'), source: 'https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-status' },
331
- // (Stripe expires an open session at expires_at; the twin keeps it open until asked)
332
- ],
333
- };
334
- /** A Checkout Session's `payment_status`: the customer completing a payment or subscription session
335
- * pays it; a delayed payment method settles later. */
336
- const checkoutPaymentStatus = {
337
- initial: 'unpaid',
338
- transitions: [
339
- { actor: 'external', from: '*', to: 'paid', source: 'https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-payment_status' },
340
- // (a delayed method settles a session later at Stripe; the twin's settle on the page)
341
- ],
342
- };
343
- /** A dispute's `status`: closing concedes it; the bank decides the rest, and an unanswered one is
344
- * lost when its evidence is due. */
345
- const disputeStatus = {
346
- initial: 'warning_needs_response',
347
- transitions: [
348
- { operation: 'PostDisputesDisputeClose', from: '*', to: 'lost', source: 'https://docs.stripe.com/api/disputes/close' },
349
- // submitting evidence puts a dispute (or an inquiry) under review; in test mode the evidence decides it
350
- { operation: 'PostDisputesDispute', from: ['needs_response'], to: 'under_review', source: 'https://docs.stripe.com/api/disputes/update#update_dispute-submit' },
351
- { operation: 'PostDisputesDispute', from: ['warning_needs_response'], to: 'warning_under_review', source: 'https://docs.stripe.com/api/disputes/update#update_dispute-submit' },
352
- by('vendor', ['under_review'], 'won', 'https://docs.stripe.com/testing#evidence'),
353
- by('vendor', ['under_review'], 'lost', 'https://docs.stripe.com/testing#evidence'),
354
- // (the bank decides a dispute under review, escalates an inquiry and loses one left unanswered; the twin
355
- // decides none)
356
- // Stripe also closes an inquiry the bank drops (warning_closed); no inquiry the twin holds is dropped
357
- ],
358
- };
359
- /** A payout's `status`: cancel ends it; reversing cancels it and pays a reversal, never twice. */
360
- const payoutStatus = {
361
- initial: 'pending',
362
- transitions: [
363
- // only a pending payout can be canceled (docs.stripe.com/api/payouts/cancel); reversing a paid one makes a new
364
- // payout and leaves the original paid (semantics/balance.ts)
365
- { operation: 'PostPayoutsPayoutCancel', from: ['pending'], to: 'canceled', source: 'https://docs.stripe.com/api/payouts/cancel' },
366
- // it arrives in the bank on its arrival date
367
- by('time', ['pending'], 'paid', 'https://docs.stripe.com/payouts#payout-statuses'),
368
- // (a bank may fail a payout at Stripe; the twin's arrive)
369
- ],
370
- };
371
- /** A credit note's `status`: issued, then voided once. */
372
- const creditNoteStatus = {
373
- initial: 'issued',
374
- transitions: [
375
- {
376
- operation: 'PostCreditNotesIdVoid',
377
- from: ['issued'],
378
- to: 'void',
379
- refusal: { status: 400, code: 'credit_note_already_voided', message: 'This credit note has already been voided.' },
380
- source: 'https://docs.stripe.com/api/credit_notes/void',
381
- },
382
- ],
383
- };
384
- const scheduleEnded = (verb) => ({ status: 400, code: 'subscription_schedule_status_invalid', message: `This subscription schedule has status {from} and cannot be ${verb}.` });
385
- /** A subscription schedule's `status`: canceling or releasing ends it; an ended one refuses both. */
386
- const scheduleStatus = {
387
- initial: 'active',
388
- transitions: [
389
- // "A subscription schedule can only be canceled if its status is not_started or active"; "A schedule can only be
390
- // released if its status is not_started or active" (the cancel and release pages)
391
- { operation: 'PostSubscriptionSchedulesScheduleCancel', from: ['active', 'not_started'], to: 'canceled', refusal: scheduleEnded('canceled'), source: 'https://docs.stripe.com/api/subscription_schedules/cancel' },
392
- { operation: 'PostSubscriptionSchedulesScheduleRelease', from: ['active', 'not_started'], to: 'released', refusal: scheduleEnded('released'), source: 'https://docs.stripe.com/api/subscription_schedules/release' },
393
- // its phases run on the clock: it starts at its start date and ends with its last phase
394
- // (a schedule whose end_behavior is cancel completes at its end at Stripe; the twin's release)
395
- // (Stripe starts a schedule at its start date and ends it with its last phase; the twin's start at once
396
- // and end when canceled or released)
397
- ],
398
- };
399
- const quoteRefusal = (verb) => ({ status: 400, code: 'quote_invalid_status', message: `This quote cannot be ${verb} because it has status {from}.` });
400
- /** A quote's `status`: a draft is finalized into open, an open one accepted, and either canceled. */
401
- const quoteStatus = {
402
- initial: 'draft',
403
- transitions: [
404
- { operation: 'PostQuotesQuoteFinalize', from: ['draft'], to: 'open', refusal: quoteRefusal('finalized'), source: 'https://docs.stripe.com/api/quotes/finalize' },
405
- { operation: 'PostQuotesQuoteAccept', from: ['open'], to: 'accepted', refusal: quoteRefusal('accepted'), source: 'https://docs.stripe.com/api/quotes/accept' },
406
- { operation: 'PostQuotesQuoteCancel', from: ['draft', 'open'], to: 'canceled', refusal: quoteRefusal('canceled'), source: 'https://docs.stripe.com/api/quotes/cancel' },
407
- // an open quote not accepted by its expires_at is canceled
408
- // (Stripe cancels an open quote at its expires_at; the twin keeps it open)
409
- ],
410
- };
411
- const setBy = (operation, values, source) => values.map((to) => ({ operation, from: '*', to, source }));
412
- /** An Issuing authorization's `status`: approve and decline close a pending one, once; a capture
413
- * closes it, or leaves it pending when told not to close. */
414
- const issuingAuthorizationStatus = {
415
- initial: 'pending',
416
- transitions: [
417
- // approving keeps it pending (approved) until the merchant captures; declining closes it
418
- { operation: 'PostIssuingAuthorizationsAuthorizationApprove', from: ['pending'], refusal: { status: 400, code: 'authorization_already_finalized', message: 'This authorization has already been finalized (status {from}).' }, source: 'https://docs.stripe.com/issuing/purchases/authorizations' },
419
- { operation: 'PostIssuingAuthorizationsAuthorizationDecline', from: ['pending'], to: 'closed', refusal: { status: 400, code: 'authorization_already_finalized', message: 'This authorization has already been finalized (status {from}).' }, source: 'https://docs.stripe.com/api/issuing/authorizations/decline' },
420
- { operation: 'PostTestHelpersIssuingAuthorizationsAuthorizationCapture', from: ['pending'], to: 'closed', source: 'https://docs.stripe.com/api/issuing/authorizations/test_mode_capture' },
421
- { operation: 'PostTestHelpersIssuingAuthorizationsAuthorizationCapture', from: ['pending'], source: 'https://docs.stripe.com/api/issuing/authorizations/test_mode_capture' },
422
- // a real-time request no one answered is decided when its window ends: "If Stripe doesn't receive your approve or
423
- // decline response within 2 seconds, the Authorization is automatically approved or declined based on your timeout
424
- // settings" (semantics/issuing.ts lapseRealtimeRequests)
425
- by('time', ['pending'], 'closed', 'https://docs.stripe.com/issuing/controls/real-time-authorizations'),
426
- // (Stripe also closes, reverses and expires one as the merchant and the clock act; in test mode the
427
- // capture helper above closes it, and the twin serves no other)
428
- ],
429
- };
430
- /** A Treasury flow's `status` as the twin's own operations move it: only a processing flow cancels. */
431
- const flowCancel = (operation, code, name, network) => ({
432
- initial: 'processing',
433
- transitions: [
434
- { operation, from: ['processing'], to: 'canceled', refusal: { status: 400, code, message: `This ${name} cannot be canceled because it is no longer processing.` }, source: 'https://docs.stripe.com/api/treasury' },
435
- ...network,
436
- ],
437
- });
438
- /** An outbound flow on the network: Stripe posts or fails it and a posted one may come back returned (in
439
- * test mode by test helpers the twin does not serve), so none of those moves is the twin's. */
440
- const outbound = (_doc) => [];
1
+ import { data, values } from "./semantics/events.js";
2
+ import { STRIPE_EVENTS } from "./semantics/shared.js";
3
+ import { states } from "./semantics/states.js";
441
4
  export const manifest = {
442
5
  vendor: 'stripe',
443
6
  service: 'stripe',
@@ -445,6 +8,90 @@ export const manifest = {
445
8
  ids: { template: '{prefix}_twin_{n}', acceptProvided: true },
446
9
  // on declared resources, but never served by the twin: Stripe's unknown-URL answer
447
10
  unmodeled: [
11
+ // served by the core on a declared resource, but no application's demand (../journeys/demand.json) names them: the gap
12
+ 'DeleteWebhookEndpointsWebhookEndpoint',
13
+ 'GetAccountsAccountPersons',
14
+ 'GetApplicationFees',
15
+ 'GetApplicationFeesId',
16
+ 'GetBillingAlertsId',
17
+ 'GetBillingCreditGrants',
18
+ 'GetBillingCreditGrantsId',
19
+ 'GetBillingPortalConfigurationsConfiguration',
20
+ 'GetClimateOrders',
21
+ 'GetClimateOrdersOrder',
22
+ 'GetCoupons',
23
+ 'GetCreditNotes',
24
+ 'GetEntitlementsFeaturesId',
25
+ 'GetFiles',
26
+ 'GetFilesFile',
27
+ 'GetFinancialConnectionsAccountsAccount',
28
+ 'GetForwardingRequests',
29
+ 'GetForwardingRequestsId',
30
+ 'GetIdentityVerificationReports',
31
+ 'GetIdentityVerificationReportsReport',
32
+ 'GetIdentityVerificationSessionsSession',
33
+ 'GetIssuingCardholders',
34
+ 'GetIssuingCardholdersCardholder',
35
+ 'GetIssuingCardsCard',
36
+ 'GetIssuingDisputes',
37
+ 'GetIssuingDisputesDispute',
38
+ 'GetIssuingPersonalizationDesignsPersonalizationDesign',
39
+ 'GetIssuingTokens',
40
+ 'GetIssuingTokensToken',
41
+ 'GetIssuingTransactions',
42
+ 'GetIssuingTransactionsTransaction',
43
+ 'GetMandatesMandate',
44
+ 'GetPaymentLinksPaymentLink',
45
+ 'GetQuotes',
46
+ 'GetQuotesQuote',
47
+ 'GetRadarValueListItemsItem',
48
+ 'GetRadarValueLists',
49
+ 'GetRadarValueListsValueList',
50
+ 'GetReportingReportRuns',
51
+ 'GetReportingReportRunsReportRun',
52
+ 'GetReviews',
53
+ 'GetReviewsReview',
54
+ 'GetSetupIntentsIntent',
55
+ 'GetSourcesSource',
56
+ 'GetSubscriptionSchedules',
57
+ 'GetTaxCalculationsCalculation',
58
+ 'GetTaxRatesTaxRate',
59
+ 'GetTaxRegistrations',
60
+ 'GetTaxRegistrationsId',
61
+ 'GetTaxTransactionsTransaction',
62
+ 'GetTerminalConfigurations',
63
+ 'GetTerminalConfigurationsConfiguration',
64
+ 'GetTerminalLocations',
65
+ 'GetTerminalLocationsLocation',
66
+ 'GetTerminalReaders',
67
+ 'GetTerminalReadersReader',
68
+ 'GetTestHelpersTestClocks',
69
+ 'GetTokensToken',
70
+ 'GetTopups',
71
+ 'GetTopupsTopup',
72
+ 'GetTransfersTransfer',
73
+ 'GetTreasuryFinancialAccounts',
74
+ 'GetTreasuryFinancialAccountsFinancialAccount',
75
+ 'GetTreasuryInboundTransfers',
76
+ 'GetTreasuryInboundTransfersId',
77
+ 'GetTreasuryOutboundPayments',
78
+ 'GetTreasuryOutboundPaymentsId',
79
+ 'GetTreasuryOutboundTransfers',
80
+ 'GetTreasuryOutboundTransfersOutboundTransfer',
81
+ 'GetTreasuryReceivedCredits',
82
+ 'GetTreasuryReceivedCreditsId',
83
+ 'GetTreasuryReceivedDebits',
84
+ 'GetTreasuryReceivedDebitsId',
85
+ 'GetTreasuryTransactionEntriesId',
86
+ 'GetTreasuryTransactionsId',
87
+ 'GetWebhookEndpoints',
88
+ 'GetWebhookEndpointsWebhookEndpoint',
89
+ 'PostCouponsCoupon',
90
+ 'PostPaymentIntentsIntent',
91
+ 'PostPaymentLinksPaymentLink',
92
+ 'PostRefundsRefund',
93
+ 'PostSetupIntentsIntent',
94
+ 'PostTransfersTransfer',
448
95
  'DeleteAccountsAccountBankAccountsId',
449
96
  'DeleteAccountsAccountPeoplePerson',
450
97
  'DeleteCustomersCustomerSubscriptionsSubscriptionExposedId',
@@ -516,6 +163,64 @@ export const manifest = {
516
163
  message: 'Keys for idempotent requests can only be used with the same parameters they were originally used with. Provide a new idempotency key, or use the original parameters that were used with this key.',
517
164
  },
518
165
  },
166
+ // the pack as the World registers it (world-core packRegistry)
167
+ // the fail-closed ceiling on live calls against Stripe (world-core rateBudget.ts, architecture D8)
168
+ rateBudget: {
169
+ windowMs: 60_000,
170
+ ceiling: 120,
171
+ defaultWeight: 1,
172
+ maxRetryAfterSeconds: 300,
173
+ rules: [
174
+ { match: '^POST /v1/payouts$', weight: 5 },
175
+ { match: '^(POST|DELETE) ', weight: 2 },
176
+ ],
177
+ reason: 'Stripe documents scalar limits (docs.stripe.com/rate-limits, read 2026-07-26): 100 requests/second in live mode, 25/second in ' +
178
+ 'sandbox, 25/second for an individual endpoint unless noted, 15 payout creates/second; over it, 429 with a Stripe-Rate-Limited-Reason ' +
179
+ 'header. 120 weighted units / 60s is 2 requests/second, 2% of live mode and 8% of the tightest general limit. POST/DELETE cost 2, a ' +
180
+ 'judgement about blast radius (a write creates a charge, refund or receipt e-mail and cannot be taken back), not a published ratio; ' +
181
+ "POST /v1/payouts costs 5 because payout creation is documented at 15/second. Stripe's own window is a second, so an intra-second " +
182
+ 'burst reaches its limiter first; the 429 cooldown is the backstop for that shape.',
183
+ },
184
+ descriptor: {
185
+ protocol: '3',
186
+ transport: 'rest',
187
+ archetype: 'crud',
188
+ bin: 'world-stripe',
189
+ resources: ['charge', 'customer', 'payment_intent', 'setup_intent', 'payment_method', 'subscription', 'price', 'product', 'invoice', 'invoiceitem', 'refund', 'event'],
190
+ specSource: "Stripe's OpenAPI document (spec/, provenance in spec/SOURCE.md)",
191
+ description: 'Stripe REST twin — spec-correct shapes, signed events, and the hosted pages an application sends people through.',
192
+ // Stripe.js calls the same-origin '/v1/…' and loads from api.stripe.com — the dev proxy forwards '/v1/' to the twin
193
+ // and strips the absolute host so calls come back same-origin
194
+ browserRouting: { apiPathPrefix: '/v1/', loaderHost: 'https://api.stripe.com' },
195
+ // the official server SDK and its Python bindings, and the STRIPE_* credential stems (NEXT_PRIVATE_STRIPE_* too);
196
+ // `stripeconnect` is deliberately not claimed: no Connect-only credential stem is modelled
197
+ adoption: { pypi: ['stripe'], sdks: ['stripe'], envStems: ['STRIPE', 'NEXTPRIVATESTRIPE'] },
198
+ // the API and the screens' hosts, each claimed for the paths its screens serve: an application that sends a person
199
+ // to one of their urls lands in the World; any other path on a claimed host is refused in a World rather than sent
200
+ // to the real Stripe
201
+ // a World's applications hold the platform's keys the dashboard shows (the credential door), never fixtures: the API
202
+ // refuses a key Stripe did not issue
203
+ credentialDoor: { path: '/_twin/app-credentials', body: {}, fill: { STRIPE_SECRET_KEY: 'secret_key', STRIPE_API_KEY: 'secret_key', STRIPE_PRIVATE_KEY: 'secret_key', BILLING_STRIPE_API_KEY: 'secret_key', STRIPE_PUBLISHABLE_KEY: 'publishable_key', NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: 'publishable_key', NEXT_PUBLIC_STRIPE_PUBLIC_KEY: 'publishable_key', NEXT_PUBLIC_STRIPE_KEY: 'publishable_key', STRIPE_WEBHOOK_SECRET: 'webhook_secret', STRIPE_WEBHOOK_SIGNING_SECRET: 'webhook_secret', STRIPE_ENDPOINT_SECRET: 'webhook_secret', STRIPE_WEBHOOK_SECRET_KEY: 'webhook_secret', NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: 'webhook_secret', BILLING_STRIPE_WEBHOOK_SECRET: 'webhook_secret', STRIPE_CONNECT_WEBHOOK_SECRET: 'connect_webhook_secret', STRIPE_WEBHOOK_SECRET_CONNECT: 'connect_webhook_secret', STRIPE_CONNECT_WEBHOOK_SIGNING_SECRET: 'connect_webhook_secret' } },
204
+ hosts: [
205
+ { host: 'api.stripe.com' }, { host: 'checkout.stripe.com', pathPattern: '^/c/pay/' }, { host: 'billing.stripe.com', pathPattern: '^/p/session/' },
206
+ { host: 'connect.stripe.com', pathPattern: '^/setup/|^/oauth/(authorize|token|deauthorize)/?$' },
207
+ { host: 'dashboard.stripe.com', pathPattern: '^/settings/public/?$|^/(test/)?settings/connect(/onboarding-options/oauth)?/?$' },
208
+ { host: 'verify.stripe.com', pathPattern: '^/start/' },
209
+ { host: 'js.stripe.com', pathPattern: '^/v3/financial-connections/|^/v3/?$|^/(v3|[a-z]+)/stripe\\.js$' },
210
+ ],
211
+ },
212
+ // what GET /twin says this twin is
213
+ discovery: {
214
+ twinOf: 'the Stripe REST API, its hosted pages (Checkout, the customer portal, Connect onboarding and OAuth, Identity, Financial Connections) and Stripe.js',
215
+ stores: 'customers, payment objects, checkout sessions, products and prices, subscriptions and invoices, Connect accounts, Issuing, Treasury, and webhook endpoints (signed events)',
216
+ },
217
+ // the World's doors (./semantics/doors.ts): what stands in for an act Stripe's API does not have
218
+ doors: [
219
+ { id: 'account', method: 'POST', path: '/_twin/account', note: "the platform's own account settings, as the Dashboard changes them" },
220
+ { id: 'appCredentials', method: 'POST', path: '/_twin/app-credentials', note: 'the platform\'s API keys (secret and publishable), as the runtime issues them' },
221
+ { id: 'webhookEndpoints', method: 'POST', path: '/_twin/webhook-endpoints', note: '{ url, events, connect? }: an endpoint added on the Dashboard\'s Webhooks page, signed with the secret the World\'s application holds' },
222
+ { id: 'drain', method: 'POST', path: '/_twin/drain', note: "time's events, sent when a runner drives them: payouts paid and funds made available" },
223
+ ],
519
224
  // The screens applications and people reach (docs/contributing/architecture.md, "Screens"). Demand is the
520
225
  // ladder's 85 applications: the code that sends a customer to the page.
521
226
  screens: [
@@ -531,63 +236,51 @@ export const manifest = {
531
236
  source: 'https://docs.stripe.com/customer-management',
532
237
  },
533
238
  {
534
- id: 'connect-onboarding', kind: 'flow', host: 'connect.stripe.com', path: '/setup/{account_link} → /setup/s/{session}', status: 'done',
239
+ // an Account Link's url (/setup/{account_link}) opens the onboarding session the page is (/setup/s/{session})
240
+ id: 'connect-onboarding', kind: 'flow', host: 'connect.stripe.com', path: '/setup', status: 'done',
535
241
  demand: '3 of 85 applications onboard connected accounts with Account Links', controls: ['Agree and submit', '← Return to platform'],
536
242
  source: 'https://docs.stripe.com/connect/hosted-onboarding',
537
243
  },
538
244
  {
539
- id: 'connect-oauth', kind: 'flow', host: 'connect.stripe.com', path: '/oauth/authorize', status: 'done',
245
+ // the authorize page, and the token and deauthorize endpoints the platform's server calls (not in Stripe's spec)
246
+ id: 'connect-oauth', kind: 'flow', host: 'connect.stripe.com', path: '/oauth', status: 'done',
540
247
  demand: "Cal.com's Stripe app connects each organizer's Standard account through Connect OAuth", controls: ['Skip this form', 'Deny access'],
541
248
  source: 'https://docs.stripe.com/connect/oauth-reference',
542
249
  },
543
250
  {
544
- id: 'identity-verification', kind: 'flow', host: 'verify.stripe.com', path: '/start/{verification_session}', status: 'done',
545
- demand: 'a verification report is written only when a person completes this page', controls: ['Verify', 'Fail verification'],
546
- source: 'https://docs.stripe.com/identity/how-sessions-work',
547
- },
548
- {
549
- id: 'financial-connections', kind: 'flow', host: 'js.stripe.com', path: '/v3/financial-connections/{client_secret}', status: 'done',
550
- demand: 'a linked bank account exists only once its holder links it here', controls: ['Link accounts', 'Cancel'],
551
- source: 'https://docs.stripe.com/js/financial_connections/collect_financial_connections_accounts',
251
+ // Stripe.js, the client library an application's page loads: /v3/, /v3/stripe.js and /<release train>/stripe.js
252
+ id: 'stripe-js', kind: 'content', host: 'js.stripe.com', path: '/', status: 'done',
253
+ demand: "Dub's upgrade button loads Stripe.js and calls redirectToCheckout",
254
+ source: 'https://docs.stripe.com/js',
552
255
  },
553
256
  {
554
- id: 'dashboard', kind: 'workspace', host: 'dashboard.stripe.com', path: '/test/{section}; /settings/public; /settings/connect/onboarding-options/oauth', status: 'done',
555
- demand: 'testers and agents inspect payments, customers and subscriptions; an operator sets the business name Checkout shows (Public details) and a Connect platform its OAuth client_id and redirect URIs',
257
+ id: 'dashboard', kind: 'workspace', host: 'dashboard.stripe.com', path: '/', status: 'done',
258
+ demand: 'an operator sets the business name Checkout shows (Public details: /settings/public) and a Connect platform its OAuth client_id and redirect URIs (/settings/connect/onboarding-options/oauth)',
556
259
  controls: ['Business name', 'Save', 'Enable OAuth for Standard accounts', 'Redirect URIs'], source: 'https://docs.stripe.com/dashboard/basics',
557
260
  },
558
261
  ],
559
262
  resources: {
560
263
  'billing.meter': {
561
- storedAs: 'billing_meter', idPrefix: 'mtr', notFound: "No such meter: '{id}'",
562
- notState: ['event_time_window'], filters: ['status'],
563
- state: { status: { initial: 'active', transitions: [
564
- { operation: 'PostBillingMetersIdDeactivate', from: '*', to: 'inactive', source: 'https://docs.stripe.com/api/billing/meter/deactivate' },
565
- { operation: 'PostBillingMetersIdReactivate', from: '*', to: 'active', source: 'https://docs.stripe.com/api/billing/meter/reactivate' },
566
- ] } },
264
+ storedAs: 'billing_meter', idPrefix: 'mtr', notFound: "No such meter: '{id}'", ...states["billing.meter"], filters: ['status'],
567
265
  },
568
266
  'billing.meter_event': { storedAs: 'billing_meter_event', idPrefix: 'mtr_evt' },
569
- 'billing.credit_grant': { storedAs: 'credit_grant', idPrefix: 'credgr', notState: ['category'], filters: ['customer'], notFound: "No such credit grant: '{id}'" },
267
+ 'billing.credit_grant': { storedAs: 'credit_grant', idPrefix: 'credgr', ...states["billing.credit_grant"], filters: ['customer'], notFound: "No such credit grant: '{id}'" },
570
268
  'billing.alert': {
571
- storedAs: 'billing_alert', idPrefix: 'alert', notFound: "No such alert: '{id}'",
572
- state: { status: { initial: 'active', transitions: [
573
- { operation: 'PostBillingAlertsIdActivate', from: '*', to: 'active', source: 'https://docs.stripe.com/api/billing/alert/activate' },
574
- { operation: 'PostBillingAlertsIdDeactivate', from: '*', to: 'inactive', source: 'https://docs.stripe.com/api/billing/alert/deactivate' },
575
- { operation: 'PostBillingAlertsIdArchive', from: '*', to: 'archived', source: 'https://docs.stripe.com/api/billing/alert/archive' },
576
- ] } },
269
+ storedAs: 'billing_alert', idPrefix: 'alert', notFound: "No such alert: '{id}'", ...states["billing.alert"],
577
270
  },
578
271
  payment_intent: {
579
- idPrefix: 'pi',
580
- state: { status: paymentIntentStatus },
581
- notState: ['cancellation_reason', 'capture_method', 'confirmation_method', 'setup_future_usage'],
272
+ idPrefix: 'pi', ...states["payment_intent"],
582
273
  embeds: { customer: 'customer', latest_charge: 'charge', payment_method: 'payment_method', invoice: 'invoice' },
583
274
  filters: ['customer'],
584
275
  },
585
276
  // an update stores what it is given, nested objects whole, as the twin always has (Stripe merges
586
277
  // metadata keys; `update: 'replace'` keeps the twin's answer)
587
- customer: { idPrefix: 'cus', update: 'replace', notState: ['tax_exempt'], filters: ['email'] },
588
- tax_id: { idPrefix: 'txi', notState: ['type'], filters: ['customer'] },
589
- customer_balance_transaction: { idPrefix: 'cbtxn', notState: ['type'] },
590
- customer_cash_balance_transaction: { storedAs: 'cash_balance_transaction', idPrefix: 'ccsbtxn', notState: ['type'] },
278
+ // next_invoice_sequence, "The suffix of the customer's next invoice number" (docs.stripe.com/api/customers/object): one
279
+ // past its numbered invoices (a finalized invoice is never deleted)
280
+ customer: { idPrefix: 'cus', update: 'replace', ...states["customer"], filters: ['email'], counts: { next_invoice_sequence: { of: 'invoice', by: 'customer', present: 'number', plus: 1 } } },
281
+ tax_id: { idPrefix: 'txi', ...states["tax_id"], filters: ['customer'] },
282
+ customer_balance_transaction: { idPrefix: 'cbtxn', ...states["customer_balance_transaction"], },
283
+ customer_cash_balance_transaction: { storedAs: 'cash_balance_transaction', idPrefix: 'ccsbtxn', ...states["customer_cash_balance_transaction"], },
591
284
  // a card is chargeable at once; any other source waits for its owner to authorize it through its
592
285
  // flow, and is used up (single-use) or lapses
593
286
  // what a customer's sources list answers (a card, a bank account, a Source, an account) as one
@@ -595,69 +288,46 @@ export const manifest = {
595
288
  // attributes. A customer's bank account verifies as a payout account does.
596
289
  payment_source: {
597
290
  storedAs: 'source',
598
- idPrefix: 'src',
599
- state: { status: bankAccountStatus('https://docs.stripe.com/api/customer_bank_accounts/object#customer_bank_account_object-status') },
600
- notState: ['allow_redisplay', 'business_type', 'regulated_status', 'type'],
291
+ idPrefix: 'src', ...states["payment_source"],
601
292
  },
602
293
  source: {
603
- idPrefix: 'src',
604
- state: {
605
- status: {
606
- initial: 'chargeable',
607
- transitions: [
608
- // (Stripe makes a redirect source chargeable when the customer authorizes it on the bank's page;
609
- // the twin has no such page, so a non-card source stays pending)
610
- // Stripe also fails a source the customer abandons, consumes a single-use one once charged and
611
- // cancels one left unused; the twin's sources stay chargeable
612
- ],
613
- },
614
- },
615
- notState: ['allow_redisplay', 'type'],
294
+ idPrefix: 'src', ...states["source"],
616
295
  },
296
+ // Connect OAuth's authorization codes: the row's id minted, the code's value drawn from the World's secret
297
+ _oauth_code: { idPrefix: '', ids: '{n}' },
617
298
  charge: {
618
- idPrefix: 'ch',
619
- state: { status: chargeStatus, captured: chargeCaptured, paid: chargePaid, refunded: chargeRefunded },
299
+ idPrefix: 'ch', ...states["charge"],
620
300
  embeds: { customer: 'customer', payment_intent: 'payment_intent', invoice: 'invoice', balance_transaction: 'balance_transaction' },
621
301
  filters: ['customer', 'payment_intent'],
622
302
  },
623
303
  refund: {
624
304
  idPrefix: 're',
625
- update: 'replace',
626
- state: { status: refundStatus },
627
- notState: ['pending_reason', 'reason'],
305
+ update: 'replace', ...states["refund"],
628
306
  embeds: { charge: 'charge', payment_intent: 'payment_intent', balance_transaction: 'balance_transaction' },
629
307
  filters: ['charge', 'payment_intent'],
630
308
  },
631
309
  setup_intent: {
632
310
  idPrefix: 'seti',
633
- update: 'replace',
634
- state: { status: setupIntentStatus },
635
- notState: ['cancellation_reason'],
311
+ update: 'replace', ...states["setup_intent"],
636
312
  embeds: { customer: 'customer', payment_method: 'payment_method' },
637
313
  filters: ['customer', 'payment_method'],
638
314
  },
639
- payment_method: { idPrefix: 'pm', notState: ['allow_redisplay', 'type'], embeds: { customer: 'customer' }, filters: ['customer', 'type'] },
315
+ payment_method: { idPrefix: 'pm', ...states["payment_method"], embeds: { customer: 'customer' }, filters: ['customer', 'type'] },
640
316
  subscription: {
641
- idPrefix: 'sub',
642
- state: { status: subscriptionStatus },
643
- notState: ['collection_method'],
317
+ idPrefix: 'sub', ...states["subscription"],
644
318
  embeds: { customer: 'customer', latest_invoice: 'invoice', default_payment_method: 'payment_method', schedule: 'subscription_schedule' },
645
319
  filters: ['customer'],
646
320
  },
647
321
  subscription_item: { idPrefix: 'si' },
648
322
  invoice: {
649
- idPrefix: 'in',
650
- state: { status: invoiceStatus },
651
- notState: ['billing_reason', 'collection_method', 'customer_tax_exempt'],
323
+ idPrefix: 'in', ...states["invoice"],
652
324
  embeds: { customer: 'customer', subscription: 'subscription', charge: 'charge', payment_intent: 'payment_intent' },
653
325
  filters: ['customer', 'status', 'subscription'],
654
326
  },
655
327
  invoiceitem: { idPrefix: 'ii', embeds: { customer: 'customer', price: 'price' }, filters: ['customer', 'invoice'], order: { field: 'date', direction: 'desc' } },
656
328
  'checkout.session': {
657
329
  storedAs: 'checkout_session',
658
- idPrefix: 'cs',
659
- state: { status: checkoutStatus, payment_status: checkoutPaymentStatus },
660
- notState: ['billing_address_collection', 'customer_creation', 'locale', 'mode', 'origin_context', 'payment_method_collection', 'redirect_on_completion', 'submit_type', 'ui_mode'],
330
+ idPrefix: 'cs', ...states["checkout.session"],
661
331
  filters: ['customer', 'payment_intent', 'subscription', 'status'],
662
332
  // the objects a session links to, as its x-expandableFields name them
663
333
  embeds: { customer: 'customer', invoice: 'invoice', payment_intent: 'payment_intent', payment_link: 'payment_link', setup_intent: 'setup_intent', subscription: 'subscription' },
@@ -665,120 +335,61 @@ export const manifest = {
665
335
  ephemeral_key: { idPrefix: 'ephkey' },
666
336
  // a mandate's status is the network's to move; nothing here moves it
667
337
  mandate: {
668
- idPrefix: 'mandate',
669
- state: {
670
- status: {
671
- initial: 'active',
672
- transitions: [
673
- // a single-use mandate is spent by its payment: inactive "The mandate was rejected, revoked, or previously
674
- // used, and may not be used to initiate future payments" (semantics/payment-intents.ts settleBankDebits)
675
- by('vendor', ['active'], 'inactive', 'https://docs.stripe.com/api/mandates/object'),
676
- // (Stripe also activates a pending mandate and deactivates one revoked; the twin's are active when made)
677
- ],
678
- },
679
- },
680
- notState: ['type'],
338
+ idPrefix: 'mandate', ...states["mandate"],
681
339
  },
682
- dispute: { idPrefix: 'dp', update: 'replace', state: { status: disputeStatus }, notState: ['is_charge_refundable'], embeds: { charge: 'charge', payment_intent: 'payment_intent' }, filters: ['charge', 'payment_intent'] },
683
- payout: { idPrefix: 'po', state: { status: payoutStatus }, notState: ['reconciliation_status', 'type'], embeds: { balance_transaction: 'balance_transaction', destination: 'account' } },
340
+ dispute: { idPrefix: 'dp', update: 'replace', ...states["dispute"], embeds: { charge: 'charge', payment_intent: 'payment_intent' }, filters: ['charge', 'payment_intent'] },
341
+ payout: { idPrefix: 'po', ...states["payout"], embeds: { balance_transaction: 'balance_transaction', destination: 'account' } },
684
342
  // the twin's ledger is seeded available; Stripe holds funds pending until their available_on
685
343
  balance_transaction: {
686
- idPrefix: 'txn',
687
- state: { status: { initial: 'available', vendorInitial: 'pending', transitions: [by('time', ['pending'], 'available', 'https://docs.stripe.com/api/balance_transactions/object#balance_transaction_object-status')] } },
688
- notState: ['balance_type', 'type'],
344
+ idPrefix: 'txn', ...states["balance_transaction"],
689
345
  filters: ['type', 'currency'],
690
346
  },
691
347
  // a coupon lapses at its redeem_by: "Date after which the coupon can no longer be redeemed", and `valid` is
692
348
  // "Taking account of the above properties, whether this coupon can still be applied to a customer" (the served
693
349
  // spec's coupon); the twin keeps no redemption count, so max_redemptions never lapses one
694
350
  coupon: {
695
- idPrefix: 'coupon', update: 'replace', notState: ['duration'],
696
- state: { valid: { initial: true, transitions: [by('time', ['true'], 'false', 'spec:/components/schemas/coupon/properties/redeem_by "Date after which the coupon can no longer be redeemed."')] } },
351
+ idPrefix: 'coupon', update: 'replace', ...states["coupon"],
697
352
  },
698
353
  // switched on and off by an update (Stripe also lapses one at expires_at or when fully redeemed; the
699
354
  // twin keeps no redemption count or expiry)
700
355
  promotion_code: {
701
356
  idPrefix: 'promo',
702
357
  update: 'replace',
703
- notFound: "No such promotion code: '{id}'",
704
- state: {
705
- active: {
706
- initial: true,
707
- transitions: [
708
- ...flag('PostPromotionCodesPromotionCode', 'https://docs.stripe.com/api/promotion_codes/update'),
709
- ],
710
- },
711
- },
358
+ notFound: "No such promotion code: '{id}'", ...states["promotion_code"],
712
359
  },
713
- credit_note: { idPrefix: 'cn', state: { status: creditNoteStatus }, notState: ['reason', 'type'], embeds: { customer: 'customer', invoice: 'invoice' }, filters: ['invoice', 'customer'] },
360
+ credit_note: { idPrefix: 'cn', ...states["credit_note"], embeds: { customer: 'customer', invoice: 'invoice' }, filters: ['invoice', 'customer'] },
714
361
  subscription_schedule: {
715
362
  idPrefix: 'sub_sched',
716
- update: 'replace',
717
- state: { status: scheduleStatus },
718
- notState: ['end_behavior'],
363
+ update: 'replace', ...states["subscription_schedule"],
719
364
  embeds: { customer: 'customer', subscription: 'subscription' },
720
365
  filters: ['customer', 'subscription'],
721
366
  },
722
367
  'entitlements.feature': {
723
- storedAs: 'entitlements_feature', idPrefix: 'feat', notFound: "No such feature: '{id}'",
724
- state: { active: { initial: true, transitions: flag('PostEntitlementsFeaturesId', 'spec:PostEntitlementsFeaturesId "Update a feature’s metadata or permanently deactivate it."') } },
368
+ storedAs: 'entitlements_feature', idPrefix: 'feat', notFound: "No such feature: '{id}'", ...states["entitlements.feature"],
725
369
  },
726
- // an advance answers advancing and the clock is ready once it has reached the time (semantics/test-clocks.ts)
370
+ // an advance answers advancing and the clock is ready once it has reached the time (./engine/test-clocks.ts)
727
371
  'test_helpers.test_clock': {
728
372
  storedAs: 'test_clock',
729
373
  idPrefix: 'clock',
730
- notFound: "No such test clock: '{id}'",
731
- state: {
732
- status: {
733
- initial: 'ready',
734
- transitions: [
735
- // "advancing: The clock has started to advance but hasn't reached the specified time"; "ready: The clock has
736
- // completed advancing to the specified time" (docs.stripe.com/billing/testing/test-clocks/api-advanced-usage)
737
- { operation: 'PostTestHelpersTestClocksTestClockAdvance', from: ['ready'], to: 'advancing', refusal: { status: 400, code: 'test_clock_not_ready', message: 'This test clock is still advancing.' }, source: 'https://docs.stripe.com/api/test_clocks/advance' },
738
- by('vendor', ['advancing'], 'ready', 'https://docs.stripe.com/billing/testing/test-clocks/api-advanced-usage'),
739
- // (Stripe may also end an advance in internal_failure; the twin's advances complete)
740
- ],
741
- },
742
- },
374
+ notFound: "No such test clock: '{id}'", ...states["test_helpers.test_clock"],
743
375
  },
744
376
  event: { idPrefix: 'evt', filters: ['type'] },
745
377
  // disabled and re-enabled by an update's `disabled` (Stripe also disables one that keeps failing; the
746
378
  // twin counts no failures)
747
379
  webhook_endpoint: {
748
380
  idPrefix: 'we',
749
- notFound: "No such webhook endpoint: '{id}'",
750
- state: {
751
- status: {
752
- initial: 'enabled',
753
- transitions: [
754
- ...['enabled', 'disabled'].map((to) => ({ operation: 'PostWebhookEndpointsWebhookEndpoint', from: '*', to, source: 'https://docs.stripe.com/api/webhook_endpoints/update#update_webhook_endpoint-disabled' })),
755
- ],
756
- },
757
- },
381
+ notFound: "No such webhook endpoint: '{id}'", ...states["webhook_endpoint"],
758
382
  },
759
- account: { idPrefix: 'acct', notState: ['business_type', 'type'] },
760
- person: { idPrefix: 'person', notState: ['political_exposure'], parent: { param: 'account', field: 'account', resource: 'account' } },
383
+ account: { idPrefix: 'acct', ...states["account"], },
384
+ person: { idPrefix: 'person', ...states["person"], parent: { param: 'account', field: 'account', resource: 'account' } },
761
385
  // a capability lives in its account's capability map; requesting one leaves it pending, withdrawing it inactive
762
386
  capability: {
763
- idPrefix: 'cap',
764
- state: {
765
- status: {
766
- initial: 'unrequested',
767
- transitions: [
768
- ...['pending', 'inactive'].map((to) => ({ operation: 'PostAccountsAccountCapabilitiesCapability', from: '*', to, source: 'https://docs.stripe.com/api/capabilities/update' })),
769
- // Stripe reviews the account's requirements
770
- by('vendor', ['pending', 'inactive'], 'active', 'https://docs.stripe.com/connect/account-capabilities#capability-status'),
771
- by('vendor', ['active', 'pending'], 'inactive', 'https://docs.stripe.com/connect/account-capabilities#capability-status'),
772
- ],
773
- },
774
- },
387
+ idPrefix: 'cap', ...states["capability"],
775
388
  },
776
389
  // allow_redisplay and regulated_status belong to a card destination: consent and the issuer's
777
390
  // regulation, set when it is added, not a lifecycle
778
391
  external_account: {
779
- idPrefix: 'ba',
780
- state: { status: bankAccountStatus('https://docs.stripe.com/api/external_account_bank_accounts/object#account_bank_account_object-status') },
781
- notState: ['allow_redisplay', 'regulated_status'],
392
+ idPrefix: 'ba', ...states["external_account"],
782
393
  },
783
394
  account_link: { idPrefix: 'acctlink' },
784
395
  account_session: { idPrefix: 'accts' },
@@ -786,285 +397,148 @@ export const manifest = {
786
397
  transfer_reversal: { idPrefix: 'trr' },
787
398
  // refunded once fee refunds take its whole amount
788
399
  application_fee: {
789
- idPrefix: 'fee', notFound: "No such application fee: '{id}'", filters: ['charge'],
790
- state: { refunded: { initial: false, transitions: [
791
- { operation: 'PostApplicationFeesIdRefunds', from: ['false'], to: 'true', source: 'https://docs.stripe.com/api/fee_refunds/create' },
792
- // a refund of its charge with refund_application_fee refunds it in proportion
793
- ...['PostRefunds', 'PostChargesChargeRefunds'].map((operation) => ({ operation, from: ['false'], to: 'true', source: 'https://docs.stripe.com/api/refunds/create#create_refund-refund_application_fee' })),
794
- ] } },
400
+ idPrefix: 'fee', notFound: "No such application fee: '{id}'", filters: ['charge'], ...states["application_fee"],
795
401
  },
796
402
  fee_refund: { idPrefix: 'fr' },
797
403
  'apps.secret': { storedAs: 'apps_secret', idPrefix: 'apmc' },
798
404
  token: { idPrefix: 'tok' },
799
- file: { idPrefix: 'file', notState: ['purpose'], filters: ['purpose'] },
405
+ file: { idPrefix: 'file', ...states["file"], filters: ['purpose'] },
800
406
  file_link: { idPrefix: 'link' },
801
407
  // the user submits on Stripe's hosted verification page and Stripe checks it (the twin's own helper
802
408
  // outside Stripe's surface, POST …/verify, stands in for both at once)
803
409
  'identity.verification_session': {
804
410
  storedAs: 'verification_session',
805
411
  idPrefix: 'vs',
806
- notFound: "No such VerificationSession: '{id}'",
807
- state: {
808
- status: {
809
- initial: 'requires_input',
810
- transitions: [
811
- by('external', ['requires_input'], 'processing', 'https://docs.stripe.com/identity/how-sessions-work'),
812
- by('vendor', ['processing'], 'verified', 'https://docs.stripe.com/identity/how-sessions-work'),
813
- by('vendor', ['processing'], 'requires_input', 'https://docs.stripe.com/identity/how-sessions-work'),
814
- ],
815
- },
816
- },
817
- notState: ['type'],
412
+ notFound: "No such VerificationSession: '{id}'", ...states["identity.verification_session"],
818
413
  },
819
- 'identity.verification_report': { storedAs: 'verification_report', idPrefix: 'vr', notFound: "No such verification report: '{id}'", notState: ['type'], filters: ['verification_session', 'type'] },
820
- 'billing_portal.session': { storedAs: 'billing_portal_session', idPrefix: 'bps', notState: ['locale'] },
414
+ 'identity.verification_report': { storedAs: 'verification_report', idPrefix: 'vr', notFound: "No such verification report: '{id}'", ...states["identity.verification_report"], filters: ['verification_session', 'type'] },
415
+ 'billing_portal.session': { storedAs: 'billing_portal_session', idPrefix: 'bps', ...states["billing_portal.session"], },
821
416
  // is_default is Stripe's: the configuration the dashboard made first
822
417
  'billing_portal.configuration': {
823
- storedAs: 'billing_portal_configuration', idPrefix: 'bpc', notFound: "No such configuration: '{id}'",
824
- state: { active: { initial: true, transitions: flag('PostBillingPortalConfigurationsConfiguration', 'https://docs.stripe.com/api/customer_portal/configurations/update') } },
825
- notState: ['is_default'],
418
+ storedAs: 'billing_portal_configuration', idPrefix: 'bpc', notFound: "No such configuration: '{id}'", ...states["billing_portal.configuration"],
826
419
  },
827
420
  // `state` is a US state code, not a lifecycle
828
421
  tax_rate: {
829
- idPrefix: 'txr', notFound: "No such tax rate: '{id}'",
830
- state: { active: { initial: true, transitions: flag('PostTaxRatesTaxRate', 'https://docs.stripe.com/api/tax_rates/update') } },
831
- notState: ['jurisdiction_level', 'rate_type', 'state', 'tax_type'],
422
+ idPrefix: 'txr', notFound: "No such tax rate: '{id}'", ...states["tax_rate"],
832
423
  },
833
424
  // its status is derived on every read from whether a head office is set; nothing stores it
834
- 'tax.settings': { storedAs: 'tax_settings', idPrefix: 'taxset', notState: ['status'] },
425
+ 'tax.settings': { storedAs: 'tax_settings', idPrefix: 'taxset', ...states["tax.settings"], },
835
426
  'tax.calculation': { storedAs: 'tax_calculation', idPrefix: 'taxcalc', notFound: "No such tax calculation: '{id}'" },
836
427
  // a registration is active from creation here; its dates move it on the clock
837
428
  'tax.registration': {
838
- storedAs: 'tax_registration', idPrefix: 'taxreg', notFound: "No such tax registration: '{id}'", filters: ['status'],
839
- state: {
840
- // Stripe schedules a registration with a future active_from and expires one at expires_at; the twin's
841
- // are active from the start and never expire
842
- status: { initial: 'active', transitions: [] },
843
- },
429
+ storedAs: 'tax_registration', idPrefix: 'taxreg', notFound: "No such tax registration: '{id}'", filters: ['status'], ...states["tax.registration"],
844
430
  },
845
- 'tax.transaction': { storedAs: 'tax_transaction', idPrefix: 'tax', notFound: "No such tax transaction: '{id}'", notState: ['type'] },
431
+ 'tax.transaction': { storedAs: 'tax_transaction', idPrefix: 'tax', notFound: "No such tax transaction: '{id}'", ...states["tax.transaction"], },
846
432
  payment_link: {
847
433
  idPrefix: 'plink',
848
- update: 'replace', notFound: "No such payment link: '{id}'",
849
- state: { active: { initial: true, transitions: flag('PostPaymentLinksPaymentLink', 'https://docs.stripe.com/api/payment_links/payment_links/update') } },
850
- notState: ['billing_address_collection', 'customer_creation', 'payment_method_collection', 'submit_type'],
434
+ update: 'replace', notFound: "No such payment link: '{id}'", ...states["payment_link"],
851
435
  },
852
- quote: { idPrefix: 'qt', state: { status: quoteStatus }, notState: ['collection_method'], filters: ['customer', 'status'] },
436
+ quote: { idPrefix: 'qt', ...states["quote"], filters: ['customer', 'status'] },
853
437
  // a review is open until it is closed; approving closes it, once
854
438
  review: {
855
439
  storedAs: 'radar_review',
856
- idPrefix: 'prv',
857
- state: {
858
- open: {
859
- initial: true,
860
- transitions: [
861
- // (Stripe also closes one when the payment is refunded or disputed; the twin's close only on approval)
862
- { operation: 'PostReviewsReviewApprove', from: ['true'], to: 'false', refusal: { status: 400, code: 'review_already_closed', message: 'This review has already been closed.' }, source: 'https://docs.stripe.com/api/radar/reviews/approve' },
863
- ],
864
- },
865
- },
866
- notState: ['closed_reason', 'opened_reason'],
440
+ idPrefix: 'prv', ...states["review"],
867
441
  },
868
- 'radar.value_list': { storedAs: 'radar_value_list', idPrefix: 'rsl', notFound: "No such radar value list: '{id}'", notState: ['item_type'], filters: ['alias'] },
442
+ 'radar.value_list': { storedAs: 'radar_value_list', idPrefix: 'rsl', notFound: "No such radar value list: '{id}'", ...states["radar.value_list"], filters: ['alias'] },
869
443
  'radar.value_list_item': { storedAs: 'radar_value_list_item', idPrefix: 'rsli', notFound: "No such radar value list item: '{id}'" },
870
444
  'issuing.cardholder': {
871
445
  storedAs: 'issuing_cardholder',
872
446
  idPrefix: 'ich',
873
- notFound: "No such cardholder: '{id}'",
874
- state: { status: { initial: 'active', transitions: setBy('PostIssuingCardholdersCardholder', ['active', 'inactive', 'blocked'], 'https://docs.stripe.com/api/issuing/cardholders/update') } },
875
- notState: ['type'],
447
+ notFound: "No such cardholder: '{id}'", ...states["issuing.cardholder"],
876
448
  filters: ['status', 'type', 'email'],
877
449
  },
878
450
  'issuing.card': {
879
451
  storedAs: 'issuing_card',
880
452
  idPrefix: 'ic',
881
- notFound: "No such card: '{id}'",
882
- state: { status: { initial: 'active', transitions: setBy('PostIssuingCardsCard', ['active', 'inactive', 'canceled'], 'https://docs.stripe.com/api/issuing/cards/update') } },
883
- notState: ['cancellation_reason', 'replacement_reason', 'type'],
453
+ notFound: "No such card: '{id}'", ...states["issuing.card"],
884
454
  filters: ['cardholder', 'status', 'type'],
885
455
  },
886
- 'issuing.authorization': { storedAs: 'issuing_authorization', idPrefix: 'iauth', state: { status: issuingAuthorizationStatus }, notState: ['authorization_method', 'card_presence'] },
887
- 'issuing.transaction': { storedAs: 'issuing_transaction', idPrefix: 'ipi', notFound: "No such transaction: '{id}'", notState: ['type', 'wallet'], filters: ['card', 'cardholder'] },
456
+ 'issuing.authorization': { storedAs: 'issuing_authorization', idPrefix: 'iauth', ...states["issuing.authorization"], },
457
+ 'issuing.transaction': { storedAs: 'issuing_transaction', idPrefix: 'ipi', notFound: "No such transaction: '{id}'", ...states["issuing.transaction"], filters: ['card', 'cardholder'] },
888
458
  'issuing.dispute': {
889
459
  storedAs: 'issuing_dispute',
890
460
  idPrefix: 'idp',
891
- notFound: "No such dispute: '{id}'",
892
- state: {
893
- status: {
894
- initial: 'unsubmitted',
895
- transitions: [
896
- { operation: 'PostIssuingDisputesDisputeSubmit', from: ['unsubmitted'], to: 'submitted', refusal: { status: 400, code: 'dispute_unsubmitted_required', message: 'This dispute has already been submitted (status {from}).' }, source: 'spec:PostIssuingDisputesDisputeSubmit "Submits an Issuing <code>Dispute</code> to the card network."' },
897
- // (Stripe decides a submitted dispute won or lost and expires one never submitted; the twin decides none)
898
- ],
899
- },
900
- },
901
- notState: ['loss_reason'],
461
+ notFound: "No such dispute: '{id}'", ...states["issuing.dispute"],
902
462
  filters: ['status', 'transaction'],
903
463
  },
904
464
  'issuing.token': {
905
465
  storedAs: 'issuing_token',
906
466
  idPrefix: 'iss_tok',
907
- notFound: "No such issuing token: '{id}'",
908
- state: { status: { initial: 'active', transitions: [...setBy('PostIssuingTokensToken', ['active', 'deleted', 'suspended'], 'https://docs.stripe.com/api/issuing/tokens/update')] } },
909
- notState: ['network', 'wallet_provider'],
467
+ notFound: "No such issuing token: '{id}'", ...states["issuing.token"],
910
468
  filters: ['card', 'status'],
911
469
  },
912
470
  // a design waits in review (inactive) until the review test helpers decide it
913
471
  'issuing.personalization_design': {
914
472
  storedAs: 'personalization_design',
915
473
  idPrefix: 'pd',
916
- notFound: "No such personalization design: '{id}'",
917
- state: {
918
- status: {
919
- // a new design waits on Stripe's review (docs.stripe.com/issuing/cards/physical/personalization-design)
920
- initial: 'review',
921
- transitions: [
922
- ...setBy('PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignActivate', ['active'], 'spec:PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignActivate "to <code>active</code>"'),
923
- ...setBy('PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignDeactivate', ['inactive'], 'spec:PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignDeactivate "to <code>inactive</code>"'),
924
- ...setBy('PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignReject', ['rejected'], 'spec:PostTestHelpersIssuingPersonalizationDesignsPersonalizationDesignReject "to <code>rejected</code>"'),
925
- // (outside test mode Stripe's review decides it; the twin is test mode, where these helpers do)
926
- ],
927
- },
928
- },
474
+ notFound: "No such personalization design: '{id}'", ...states["issuing.personalization_design"],
929
475
  },
930
476
  'terminal.location': { storedAs: 'terminal_location', idPrefix: 'tml', notFound: "No such location: '{id}'" },
931
477
  // a simulated reader is online from registration; a real one drops off and comes back
932
478
  'terminal.reader': {
933
- storedAs: 'terminal_reader', idPrefix: 'tmr', notFound: "No such reader: '{id}'", notState: ['device_type'], filters: ['location', 'status'],
934
- // (a real reader goes offline and back; the twin's simulated readers stay online)
935
- state: { status: { initial: 'online', transitions: [] } },
479
+ storedAs: 'terminal_reader', idPrefix: 'tmr', notFound: "No such reader: '{id}'", ...states["terminal.reader"], filters: ['location', 'status'],
936
480
  },
937
- 'terminal.configuration': { storedAs: 'terminal_configuration', idPrefix: 'tmc', notFound: "No such configuration: '{id}'", notState: ['is_account_default'] },
481
+ 'terminal.configuration': { storedAs: 'terminal_configuration', idPrefix: 'tmc', notFound: "No such configuration: '{id}'", ...states["terminal.configuration"], },
938
482
  topup: {
939
- idPrefix: 'tu',
940
- state: {
941
- status: {
942
- // a top-up starts pending (the create page's example answers "status": "pending") and its funds arrive days
943
- // later: "USA (USD) ACH Debit Transfer 5 days" (docs.stripe.com/connect/top-ups; semantics/terminal.ts)
944
- initial: 'pending',
945
- transitions: [
946
- { operation: 'PostTopupsTopupCancel', from: ['pending'], to: 'canceled', refusal: { status: 400, code: 'topup_unexpected_state', message: 'This top-up cannot be canceled because it has a status of {from}.' }, source: 'https://docs.stripe.com/api/topups/cancel' },
947
- // to the Issuing balance alike: "Your top-ups can take up to 5 business days to become available"
948
- // (docs.stripe.com/issuing/funding/balance)
949
- by('vendor', ['pending'], 'succeeded', 'https://docs.stripe.com/connect/top-ups'),
950
- // (Stripe also fails a top-up or reverses a settled one; the twin's top-ups do neither)
951
- ],
952
- },
953
- },
954
- notState: ['initiated_by'],
483
+ idPrefix: 'tu', ...states["topup"],
955
484
  filters: ['status'],
956
485
  },
957
486
  // closing an account (POST …/close) is not modeled here; is_default is Stripe's
958
- 'treasury.financial_account': { storedAs: 'financial_account', idPrefix: 'fa', notFound: "No such financial account: '{id}'", state: { status: { initial: 'open', transitions: [] } }, notState: ['is_default'], filters: ['status'] },
487
+ 'treasury.financial_account': { storedAs: 'financial_account', idPrefix: 'fa', notFound: "No such financial account: '{id}'", ...states["treasury.financial_account"], filters: ['status'] },
959
488
  'treasury.outbound_payment': {
960
489
  storedAs: 'outbound_payment',
961
490
  idPrefix: 'obp',
962
- notFound: "No such outbound payment: '{id}'",
963
- state: { status: flowCancel('PostTreasuryOutboundPaymentsIdCancel', 'outbound_payment_not_cancelable', 'OutboundPayment', outbound('https://docs.stripe.com/treasury/moving-money/financial-accounts/out-of/outbound-payments')) },
491
+ notFound: "No such outbound payment: '{id}'", ...states["treasury.outbound_payment"],
964
492
  filters: ['financial_account', 'status'],
965
493
  },
966
494
  'treasury.outbound_transfer': {
967
495
  storedAs: 'outbound_transfer',
968
496
  idPrefix: 'obt',
969
- notFound: "No such outbound transfer: '{id}'",
970
- state: { status: flowCancel('PostTreasuryOutboundTransfersOutboundTransferCancel', 'outbound_transfer_not_cancelable', 'OutboundTransfer', outbound('https://docs.stripe.com/treasury/moving-money/financial-accounts/out-of/outbound-transfers')) },
497
+ notFound: "No such outbound transfer: '{id}'", ...states["treasury.outbound_transfer"],
971
498
  filters: ['financial_account', 'status'],
972
499
  },
973
500
  'treasury.inbound_transfer': {
974
501
  storedAs: 'inbound_transfer',
975
502
  idPrefix: 'ibt',
976
- notFound: "No such inbound transfer: '{id}'",
977
- // "An InboundTransfer is processing if it is created and pending. The status changes to succeeded once the funds have
978
- // been "confirmed""; in test mode the succeed helper confirms them (docs.stripe.com/api/treasury/inbound_transfers)
979
- state: { status: flowCancel('PostTreasuryInboundTransfersInboundTransferCancel', 'inbound_transfer_not_cancelable', 'InboundTransfer', [
980
- { operation: 'PostTestHelpersTreasuryInboundTransfersIdSucceed', from: ['processing'], to: 'succeeded', refusal: { status: 400, code: 'inbound_transfer_not_processing', message: 'This InboundTransfer cannot succeed because it is no longer processing.' }, source: 'https://docs.stripe.com/api/treasury/inbound_transfers' },
981
- ]) },
503
+ notFound: "No such inbound transfer: '{id}'", ...states["treasury.inbound_transfer"],
982
504
  filters: ['financial_account', 'status'],
983
505
  },
984
506
  // received credits and debits are materialized by the network (test helpers the twin does not serve)
985
- 'treasury.received_credit': { storedAs: 'received_credit', idPrefix: 'rc', notFound: "No such received credit: '{id}'", state: { status: { initial: 'succeeded', transitions: [] } }, notState: ['failure_code', 'network'], filters: ['financial_account', 'status'] },
986
- 'treasury.received_debit': { storedAs: 'received_debit', idPrefix: 'rd', notFound: "No such received debit: '{id}'", state: { status: { initial: 'succeeded', transitions: [] } }, notState: ['failure_code', 'network'], filters: ['financial_account', 'status'] },
507
+ 'treasury.received_credit': { storedAs: 'received_credit', idPrefix: 'rc', notFound: "No such received credit: '{id}'", ...states["treasury.received_credit"], filters: ['financial_account', 'status'] },
508
+ 'treasury.received_debit': { storedAs: 'received_debit', idPrefix: 'rd', notFound: "No such received debit: '{id}'", ...states["treasury.received_debit"], filters: ['financial_account', 'status'] },
987
509
  // a ledger transaction is posted or left open by the flow that writes it; the network settles an open one
988
510
  'treasury.transaction': {
989
- storedAs: 'treasury_transaction', idPrefix: 'trxn', notFound: "No such treasury transaction: '{id}'", notState: ['flow_type'],
990
- // void: "The Transaction never impacted the balance. For example, a Transaction would enter this state if an
991
- // OutboundPayment was initiated but then canceled" (docs.stripe.com/api/treasury/transactions/object); canceling an
992
- // outbound flow voids its open transaction (semantics/treasury.ts). (Stripe also posts an open transaction as its
993
- // flow settles; the twin's outbound flows stay processing)
994
- state: { status: { initial: 'open', transitions: [
995
- ...['PostTreasuryOutboundPaymentsIdCancel', 'PostTreasuryOutboundTransfersOutboundTransferCancel', 'PostTreasuryInboundTransfersInboundTransferCancel'].map((operation) => ({ operation, from: ['open'], to: 'void', source: 'https://docs.stripe.com/api/treasury/transactions/object' })),
996
- // an inbound transfer's transaction posts when it succeeds ("a transaction is created and posted")
997
- { operation: 'PostTestHelpersTreasuryInboundTransfersIdSucceed', from: ['open'], to: 'posted', source: 'https://docs.stripe.com/api/treasury/inbound_transfers' },
998
- ] } },
511
+ storedAs: 'treasury_transaction', idPrefix: 'trxn', notFound: "No such treasury transaction: '{id}'", ...states["treasury.transaction"],
999
512
  },
1000
- 'treasury.transaction_entry': { storedAs: 'treasury_transaction_entry', idPrefix: 'trxne', notFound: "No such transaction entry: '{id}'", notState: ['flow_type', 'type'] },
513
+ 'treasury.transaction_entry': { storedAs: 'treasury_transaction_entry', idPrefix: 'trxne', notFound: "No such transaction entry: '{id}'", ...states["treasury.transaction_entry"], },
1001
514
  'climate.order': {
1002
515
  storedAs: 'climate_order',
1003
516
  idPrefix: 'climorder',
1004
- notFound: "No such order: '{id}'",
1005
- state: {
1006
- status: {
1007
- // an order is created confirmed: the create page's example answers "status": "confirmed" with confirmed_at set
1008
- // (docs.stripe.com/api/climate/order/create)
1009
- initial: 'confirmed',
1010
- transitions: [
1011
- { operation: 'PostClimateOrdersOrderCancel', from: ['awaiting_funds', 'confirmed', 'open'], to: 'canceled', refusal: { status: 400, code: 'climate_order_not_cancelable', message: 'This order can no longer be canceled because it is {from}.' }, source: 'https://docs.stripe.com/api/climate/order/cancel' },
1012
- // funded, confirmed by the supplier, then delivered
1013
- // (Stripe funds, confirms and delivers an order over months; the twin's orders stay where they are made)
1014
- ],
1015
- },
1016
- },
1017
- notState: ['cancellation_reason'],
517
+ notFound: "No such order: '{id}'", ...states["climate.order"],
1018
518
  },
1019
519
  'financial_connections.session': { storedAs: 'fc_session', idPrefix: 'fcsess' },
1020
520
  'financial_connections.account': {
1021
521
  storedAs: 'fc_account',
1022
522
  idPrefix: 'fca',
1023
- notFound: "No such account: '{id}'",
1024
- state: {
1025
- status: {
1026
- initial: 'active',
1027
- transitions: [
1028
- { operation: 'PostFinancialConnectionsAccountsAccountDisconnect', from: '*', to: 'disconnected', source: 'https://docs.stripe.com/api/financial_connections/accounts/disconnect' },
1029
- // (Stripe marks an account inactive when the bank link breaks, and active again; the twin's links hold)
1030
- ],
1031
- },
1032
- },
1033
- notState: ['category', 'subcategory'],
523
+ notFound: "No such account: '{id}'", ...states["financial_connections.account"],
1034
524
  },
1035
525
  // a linked account's transactions arrive posted here; the bank settles a pending one
1036
526
  'financial_connections.transaction': {
1037
- storedAs: 'fc_transaction', idPrefix: 'fctxn',
1038
- // (a bank's pending transaction posts or is voided at Stripe; the twin's arrive posted)
1039
- state: { status: { initial: 'posted', transitions: [] } },
527
+ storedAs: 'fc_transaction', idPrefix: 'fctxn', ...states["financial_connections.transaction"],
1040
528
  },
1041
529
  'forwarding.request': { storedAs: 'forwarding_request', idPrefix: 'fwdr', notFound: "No such forwarding request: '{id}'" },
1042
530
  // the twin runs a report at once; Stripe runs it in the background
1043
531
  'reporting.report_run': {
1044
- storedAs: 'report_run', idPrefix: 'frr', notFound: "No such report run: '{id}'",
1045
- // "When first created, the object appears with status="pending""; "When the run completes, Stripe updates the
1046
- // object, and it has a status of succeeded" (docs.stripe.com/reports/api; semantics/platform.ts finishReportRuns).
1047
- // (Stripe may also fail a run; the twin's runs succeed)
1048
- state: { status: { initial: 'pending', transitions: [by('vendor', ['pending'], 'succeeded', 'https://docs.stripe.com/reports/api')] } },
532
+ storedAs: 'report_run', idPrefix: 'frr', notFound: "No such report run: '{id}'", ...states["reporting.report_run"],
1049
533
  },
1050
534
  // archived (active=false) and restored by an update
1051
- product: { idPrefix: 'prod', update: 'replace', state: { active: { initial: true, transitions: flag('PostProductsId', 'https://docs.stripe.com/api/products/update#update_product-active') } } },
535
+ product: { idPrefix: 'prod', update: 'replace', ...states["product"], },
1052
536
  product_feature: { idPrefix: 'prodft' },
1053
537
  price: {
1054
538
  idPrefix: 'price',
1055
- update: 'replace', notState: ['billing_scheme', 'tax_behavior', 'tiers_mode', 'type'], embeds: { product: 'product' },
1056
- // deleting a plan leaves its price for its subscribers, inactive: "Deleting plans means new subscribers can’t be added.
1057
- // Existing subscribers aren’t affected." (docs.stripe.com/api/plans/delete; semantics/plans.ts)
1058
- state: { active: { initial: true, transitions: [...flag('PostPricesPrice', 'https://docs.stripe.com/api/prices/update#update_price-active'), { operation: 'DeletePlansPlan', from: ['true'], to: 'false', source: 'https://docs.stripe.com/api/plans/delete' }] } },
539
+ update: 'replace', ...states["price"], embeds: { product: 'product' },
1059
540
  },
1060
541
  },
1061
- view: (storedType, row) => view(storedType, row),
1062
- onWrite: async ({ operation, storedType, body, root, occurredAt, request }) => {
1063
- const version = request.headers.get('stripe-version') ?? undefined;
1064
- // an Issuing authorization carries its full card in every egress, webhooks included; the write
1065
- // hook is where the root is known, and the body it embeds into is the one the handler answers
1066
- if (storedType === 'issuing_authorization')
1067
- embedIssuingAuthorizationCard(body, root);
1068
- await afterStripeWrite(storedType, operation, body, root, occurredAt, version && isValidApiVersion(version) ? version : undefined, request.headers.get('stripe-account') ?? undefined);
1069
- },
542
+ // the webhooks Stripe's writes send (./semantics/shared.ts), each event's data and account rendered by ./semantics/events.ts
543
+ events: { ...STRIPE_EVENTS, render: data, values },
1070
544
  };