@volter/twin-stripe 2.0.38 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/README.md +82 -207
  2. package/dist/generated/pack-facts.json +94 -0
  3. package/dist/spec/SOURCE.md +14 -0
  4. package/dist/spec/fixtures3.json.gz +0 -0
  5. package/dist/spec/openapi.json.gz +0 -0
  6. package/dist/spec/patches.json +34 -0
  7. package/dist/spec/sources.json +378 -0
  8. package/dist/src/cli.js +12 -30
  9. package/dist/src/engine/after-payment.d.ts +9 -0
  10. package/dist/src/engine/after-payment.js +28 -0
  11. package/dist/src/engine/apps-secrets.d.ts +1 -0
  12. package/dist/src/engine/apps-secrets.js +3 -0
  13. package/dist/src/engine/balance.d.ts +5 -0
  14. package/dist/src/engine/balance.js +15 -0
  15. package/dist/src/engine/billing.d.ts +5 -0
  16. package/dist/src/engine/billing.js +6 -0
  17. package/dist/src/engine/charges.d.ts +6 -0
  18. package/dist/src/engine/charges.js +13 -0
  19. package/dist/src/engine/checkout.d.ts +24 -0
  20. package/dist/src/engine/checkout.js +30 -0
  21. package/dist/src/engine/common.d.ts +19 -0
  22. package/dist/src/engine/common.js +32 -0
  23. package/dist/src/{screens → engine}/connect-settings.d.ts +1 -7
  24. package/dist/src/engine/connect-settings.js +26 -0
  25. package/dist/src/engine/connect.d.ts +66 -0
  26. package/dist/src/engine/connect.js +189 -0
  27. package/dist/src/engine/coupons.d.ts +0 -0
  28. package/dist/src/engine/coupons.js +5 -0
  29. package/dist/src/engine/credit-notes.d.ts +1 -0
  30. package/dist/src/engine/credit-notes.js +5 -0
  31. package/dist/src/engine/customers.d.ts +12 -0
  32. package/dist/src/engine/customers.js +30 -0
  33. package/dist/src/engine/display.d.ts +14 -0
  34. package/dist/src/engine/display.js +16 -0
  35. package/dist/src/engine/disputes.d.ts +0 -0
  36. package/dist/src/engine/disputes.js +9 -0
  37. package/dist/src/engine/entitlements.d.ts +1 -0
  38. package/dist/src/engine/entitlements.js +4 -0
  39. package/dist/src/engine/ephemeral-keys.d.ts +0 -0
  40. package/dist/src/engine/ephemeral-keys.js +4 -0
  41. package/dist/src/engine/files.d.ts +18 -0
  42. package/dist/src/engine/files.js +33 -0
  43. package/dist/src/engine/front.d.ts +5 -0
  44. package/dist/src/engine/front.js +25 -0
  45. package/dist/src/engine/invoices.d.ts +24 -0
  46. package/dist/src/engine/invoices.js +57 -0
  47. package/dist/src/engine/issuing.d.ts +35 -0
  48. package/dist/src/engine/issuing.js +45 -0
  49. package/dist/src/engine/ledger.d.ts +9 -0
  50. package/dist/src/engine/ledger.js +9 -0
  51. package/dist/src/engine/oauth.d.ts +4 -0
  52. package/dist/src/engine/oauth.js +25 -0
  53. package/dist/src/engine/onboarding.d.ts +13 -0
  54. package/dist/src/engine/onboarding.js +42 -0
  55. package/dist/src/{stripe-params.d.ts → engine/params.d.ts} +20 -3
  56. package/dist/src/{stripe-params.js → engine/params.js} +7 -60
  57. package/dist/src/engine/payment-intents.d.ts +10 -0
  58. package/dist/src/engine/payment-intents.js +27 -0
  59. package/dist/src/engine/payment-links.d.ts +1 -0
  60. package/dist/src/engine/payment-links.js +5 -0
  61. package/dist/src/engine/payment-methods.d.ts +16 -0
  62. package/dist/src/engine/payment-methods.js +22 -0
  63. package/dist/src/engine/plans.d.ts +6 -0
  64. package/dist/src/engine/plans.js +26 -0
  65. package/dist/src/engine/platform.d.ts +39 -0
  66. package/dist/src/engine/platform.js +34 -0
  67. package/dist/src/engine/products.d.ts +0 -0
  68. package/dist/src/engine/products.js +4 -0
  69. package/dist/src/engine/radar.d.ts +2 -0
  70. package/dist/src/engine/radar.js +5 -0
  71. package/dist/src/engine/refunds.d.ts +1 -0
  72. package/dist/src/engine/refunds.js +28 -0
  73. package/dist/src/engine/renewals.d.ts +36 -0
  74. package/dist/src/engine/renewals.js +65 -0
  75. package/dist/src/engine/setup-intents.d.ts +1 -0
  76. package/dist/src/engine/setup-intents.js +4 -0
  77. package/dist/src/{stripe-twin.d.ts → engine/stripe.d.ts} +11 -39
  78. package/dist/src/{stripe-twin.js → engine/stripe.js} +25 -310
  79. package/dist/src/engine/subscription-schedules.d.ts +3 -0
  80. package/dist/src/engine/subscription-schedules.js +1 -0
  81. package/dist/src/engine/subscriptions.d.ts +12 -0
  82. package/dist/src/engine/subscriptions.js +31 -0
  83. package/dist/src/engine/tax.d.ts +7 -0
  84. package/dist/src/engine/tax.js +28 -0
  85. package/dist/src/engine/terminal.d.ts +4 -0
  86. package/dist/src/engine/terminal.js +20 -0
  87. package/dist/src/engine/test-clocks.d.ts +5 -0
  88. package/dist/src/engine/test-clocks.js +9 -0
  89. package/dist/src/engine/tokens.d.ts +7 -0
  90. package/dist/src/engine/tokens.js +20 -0
  91. package/dist/src/engine/transfers.d.ts +2 -0
  92. package/dist/src/engine/transfers.js +1 -0
  93. package/dist/src/engine/treasury.d.ts +23 -0
  94. package/dist/src/engine/treasury.js +45 -0
  95. package/dist/src/{stripe-version.js → engine/version.js} +4 -3
  96. package/dist/src/engine/webhook-endpoints.d.ts +1 -0
  97. package/dist/src/engine/webhook-endpoints.js +12 -0
  98. package/dist/src/engine/webhooks.d.ts +31 -0
  99. package/dist/src/engine/webhooks.js +68 -0
  100. package/dist/src/fetch.d.ts +2 -0
  101. package/dist/src/fetch.js +26 -0
  102. package/dist/src/generated/surface.gen.json +1 -1
  103. package/dist/src/generated/ui.gen.json +1 -1
  104. package/dist/src/index.d.ts +5 -14
  105. package/dist/src/index.js +8 -75
  106. package/dist/src/manifest.js +232 -758
  107. package/dist/src/screens/billing-portal.d.ts +3 -0
  108. package/dist/src/screens/{portal.js → billing-portal.js} +28 -54
  109. package/dist/src/screens/checkout.d.ts +2 -31
  110. package/dist/src/screens/checkout.js +32 -121
  111. package/dist/src/screens/connect-oauth.d.ts +3 -27
  112. package/dist/src/screens/connect-oauth.js +51 -132
  113. package/dist/src/screens/connect-onboarding.d.ts +3 -0
  114. package/dist/src/screens/{onboarding.js → connect-onboarding.js} +36 -102
  115. package/dist/src/screens/dashboard.d.ts +3 -0
  116. package/dist/src/screens/dashboard.js +117 -0
  117. package/dist/src/screens/shared.d.ts +27 -0
  118. package/dist/src/screens/shared.js +87 -0
  119. package/dist/src/screens/stripe-js.d.ts +3 -0
  120. package/dist/src/screens/stripe-js.js +135 -0
  121. package/dist/src/semantics/account_links.d.ts +2 -0
  122. package/dist/src/semantics/account_links.js +22 -0
  123. package/dist/src/semantics/accounts.d.ts +4 -0
  124. package/dist/src/semantics/accounts.js +38 -0
  125. package/dist/src/semantics/apps.d.ts +4 -0
  126. package/dist/src/semantics/{apps-secrets.js → apps.js} +14 -26
  127. package/dist/src/semantics/around.d.ts +2 -0
  128. package/dist/src/semantics/around.js +16 -0
  129. package/dist/src/semantics/balance.d.ts +2 -11
  130. package/dist/src/semantics/balance.js +3 -194
  131. package/dist/src/semantics/balance_transactions.d.ts +2 -0
  132. package/dist/src/semantics/balance_transactions.js +15 -0
  133. package/dist/src/semantics/billing_portal.d.ts +5 -0
  134. package/dist/src/semantics/billing_portal.js +42 -0
  135. package/dist/src/semantics/charges.d.ts +3 -28
  136. package/dist/src/semantics/charges.js +5 -202
  137. package/dist/src/semantics/checkout.d.ts +3 -20
  138. package/dist/src/semantics/checkout.js +8 -261
  139. package/dist/src/semantics/clock.d.ts +2 -0
  140. package/dist/src/semantics/clock.js +21 -0
  141. package/dist/src/semantics/coupons.d.ts +2 -6
  142. package/dist/src/semantics/coupons.js +2 -60
  143. package/dist/src/semantics/customers.d.ts +6 -6
  144. package/dist/src/semantics/customers.js +27 -387
  145. package/dist/src/semantics/disputes.d.ts +2 -2
  146. package/dist/src/semantics/disputes.js +3 -49
  147. package/dist/src/semantics/doors.d.ts +23 -0
  148. package/dist/src/semantics/doors.js +73 -0
  149. package/dist/src/semantics/entitlements.d.ts +2 -2
  150. package/dist/src/semantics/entitlements.js +5 -50
  151. package/dist/src/semantics/events.d.ts +12 -0
  152. package/dist/src/semantics/events.js +36 -0
  153. package/dist/src/semantics/index.d.ts +2 -0
  154. package/dist/src/semantics/index.js +107 -0
  155. package/dist/src/semantics/invoice_payments.d.ts +2 -0
  156. package/dist/src/semantics/invoice_payments.js +12 -0
  157. package/dist/src/semantics/invoiceitems.d.ts +3 -0
  158. package/dist/src/semantics/invoiceitems.js +59 -0
  159. package/dist/src/semantics/invoices.d.ts +16 -18
  160. package/dist/src/semantics/invoices.js +118 -444
  161. package/dist/src/semantics/payment_intents.d.ts +4 -0
  162. package/dist/src/semantics/payment_intents.js +79 -0
  163. package/dist/src/semantics/payment_methods.d.ts +5 -0
  164. package/dist/src/semantics/payment_methods.js +39 -0
  165. package/dist/src/semantics/payouts.d.ts +2 -0
  166. package/dist/src/semantics/payouts.js +21 -0
  167. package/dist/src/semantics/prices.d.ts +4 -0
  168. package/dist/src/semantics/prices.js +37 -0
  169. package/dist/src/semantics/products.d.ts +6 -2
  170. package/dist/src/semantics/products.js +15 -130
  171. package/dist/src/semantics/promotion_codes.d.ts +3 -0
  172. package/dist/src/semantics/promotion_codes.js +35 -0
  173. package/dist/src/semantics/radar.d.ts +2 -2
  174. package/dist/src/semantics/radar.js +5 -74
  175. package/dist/src/semantics/refunds.d.ts +3 -9
  176. package/dist/src/semantics/refunds.js +6 -192
  177. package/dist/src/semantics/setup_intents.d.ts +3 -0
  178. package/dist/src/semantics/{setup-intents.js → setup_intents.js} +10 -53
  179. package/dist/src/semantics/shared.d.ts +822 -47
  180. package/dist/src/semantics/shared.js +3044 -78
  181. package/dist/src/semantics/states.d.ts +531 -0
  182. package/dist/src/semantics/states.js +852 -0
  183. package/dist/src/semantics/subscription_items.d.ts +5 -0
  184. package/dist/src/semantics/subscription_items.js +55 -0
  185. package/dist/src/semantics/subscription_schedules.d.ts +4 -0
  186. package/dist/src/semantics/subscription_schedules.js +60 -0
  187. package/dist/src/semantics/subscriptions.d.ts +7 -11
  188. package/dist/src/semantics/subscriptions.js +44 -415
  189. package/dist/src/semantics/test_helpers.d.ts +6 -0
  190. package/dist/src/semantics/{test-clocks.js → test_helpers.js} +14 -40
  191. package/dist/src/semantics/transfers.d.ts +2 -2
  192. package/dist/src/semantics/transfers.js +6 -101
  193. package/dist/src/server.d.ts +7 -0
  194. package/dist/src/server.js +7 -0
  195. package/generated/pack-facts.json +94 -0
  196. package/package.json +10 -20
  197. package/spec/SOURCE.md +14 -0
  198. package/spec/fixtures3.json.gz +0 -0
  199. package/spec/openapi.json.gz +0 -0
  200. package/spec/patches.json +34 -0
  201. package/spec/sources.json +378 -0
  202. package/src/cli.ts +12 -26
  203. package/src/engine/after-payment.ts +31 -0
  204. package/src/engine/apps-secrets.ts +4 -0
  205. package/src/engine/balance.ts +33 -0
  206. package/src/engine/billing.ts +16 -0
  207. package/src/engine/charges.ts +19 -0
  208. package/src/engine/checkout.ts +40 -0
  209. package/src/engine/common.ts +47 -0
  210. package/src/engine/connect-settings.ts +23 -0
  211. package/src/engine/connect.ts +184 -0
  212. package/src/engine/coupons.ts +5 -0
  213. package/src/engine/credit-notes.ts +6 -0
  214. package/src/engine/customers.ts +35 -0
  215. package/src/engine/display.ts +19 -0
  216. package/src/engine/disputes.ts +9 -0
  217. package/src/engine/entitlements.ts +5 -0
  218. package/src/engine/ephemeral-keys.ts +3 -0
  219. package/src/engine/files.ts +41 -0
  220. package/src/engine/front.ts +31 -0
  221. package/src/engine/invoices.ts +71 -0
  222. package/src/engine/issuing.ts +61 -0
  223. package/src/engine/ledger.ts +50 -0
  224. package/src/engine/oauth.ts +19 -0
  225. package/src/engine/onboarding.ts +54 -0
  226. package/src/{stripe-params.ts → engine/params.ts} +8 -56
  227. package/src/engine/payment-intents.ts +33 -0
  228. package/src/engine/payment-links.ts +6 -0
  229. package/src/engine/payment-methods.ts +25 -0
  230. package/src/engine/plans.ts +42 -0
  231. package/src/engine/platform.ts +47 -0
  232. package/src/engine/products.ts +3 -0
  233. package/src/engine/radar.ts +6 -0
  234. package/src/engine/refunds.ts +29 -0
  235. package/src/engine/renewals.ts +73 -0
  236. package/src/engine/setup-intents.ts +5 -0
  237. package/src/{stripe-twin.ts → engine/stripe.ts} +28 -323
  238. package/src/engine/subscription-schedules.ts +10 -0
  239. package/src/engine/subscriptions.ts +40 -0
  240. package/src/engine/tax.ts +39 -0
  241. package/src/engine/terminal.ts +23 -0
  242. package/src/engine/test-clocks.ts +11 -0
  243. package/src/engine/tokens.ts +22 -0
  244. package/src/engine/transfers.ts +23 -0
  245. package/src/engine/treasury.ts +47 -0
  246. package/src/{stripe-version.ts → engine/version.ts} +4 -3
  247. package/src/engine/webhook-endpoints.ts +14 -0
  248. package/src/engine/webhooks.ts +72 -0
  249. package/src/fetch.ts +27 -0
  250. package/src/generated/surface.gen.json +1 -1
  251. package/src/generated/ui.gen.json +1 -1
  252. package/src/index.ts +8 -116
  253. package/src/manifest.ts +234 -786
  254. package/src/screens/{portal.tsx → billing-portal.tsx} +32 -37
  255. package/src/screens/checkout.tsx +33 -95
  256. package/src/screens/connect-oauth.tsx +64 -88
  257. package/src/screens/{onboarding.tsx → connect-onboarding.tsx} +38 -82
  258. package/src/screens/dashboard.tsx +198 -0
  259. package/src/screens/shared.tsx +84 -0
  260. package/src/screens/stripe-js.ts +170 -0
  261. package/src/semantics/account_links.ts +21 -0
  262. package/src/semantics/accounts.ts +43 -0
  263. package/src/semantics/{apps-secrets.ts → apps.ts} +17 -32
  264. package/src/semantics/around.ts +20 -0
  265. package/src/semantics/balance.ts +6 -207
  266. package/src/semantics/balance_transactions.ts +20 -0
  267. package/src/semantics/billing_portal.ts +51 -0
  268. package/src/semantics/charges.ts +12 -211
  269. package/src/semantics/checkout.ts +13 -268
  270. package/src/semantics/clock.ts +26 -0
  271. package/src/semantics/coupons.ts +5 -67
  272. package/src/semantics/customers.ts +29 -392
  273. package/src/semantics/disputes.ts +9 -59
  274. package/src/semantics/doors.ts +80 -0
  275. package/src/semantics/entitlements.ts +9 -56
  276. package/src/semantics/events.ts +44 -0
  277. package/src/semantics/index.ts +111 -0
  278. package/src/semantics/invoice_payments.ts +18 -0
  279. package/src/semantics/invoiceitems.ts +59 -0
  280. package/src/semantics/invoices.ts +109 -443
  281. package/src/semantics/payment_intents.ts +83 -0
  282. package/src/semantics/payment_methods.ts +43 -0
  283. package/src/semantics/payouts.ts +23 -0
  284. package/src/semantics/prices.ts +42 -0
  285. package/src/semantics/products.ts +18 -142
  286. package/src/semantics/promotion_codes.ts +39 -0
  287. package/src/semantics/radar.ts +8 -77
  288. package/src/semantics/refunds.ts +9 -212
  289. package/src/semantics/setup_intents.ts +45 -0
  290. package/src/semantics/shared.ts +3092 -106
  291. package/src/semantics/states.ts +883 -0
  292. package/src/semantics/subscription_items.ts +54 -0
  293. package/src/semantics/subscription_schedules.ts +66 -0
  294. package/src/semantics/subscriptions.ts +49 -432
  295. package/src/semantics/{test-clocks.ts → test_helpers.ts} +23 -47
  296. package/src/semantics/transfers.ts +11 -131
  297. package/src/server.ts +8 -0
  298. package/client/dashboard-api.ts +0 -286
  299. package/client/stripe-mirror.css +0 -277
  300. package/client/stripe-mirror.tsx +0 -1559
  301. package/dist/client/dashboard-api.d.ts +0 -107
  302. package/dist/client/dashboard-api.js +0 -238
  303. package/dist/client/dashboard-api.ts +0 -286
  304. package/dist/client/stripe-mirror.bundle.js +0 -236
  305. package/dist/client/stripe-mirror.css +0 -277
  306. package/dist/client/stripe-mirror.d.ts +0 -134
  307. package/dist/client/stripe-mirror.js +0 -859
  308. package/dist/client/stripe-mirror.tsx +0 -1559
  309. package/dist/src/screens/connect-settings.js +0 -103
  310. package/dist/src/screens/consent-skin.d.ts +0 -4
  311. package/dist/src/screens/consent-skin.js +0 -18
  312. package/dist/src/screens/financial-connections.d.ts +0 -5
  313. package/dist/src/screens/financial-connections.js +0 -90
  314. package/dist/src/screens/identity.d.ts +0 -5
  315. package/dist/src/screens/identity.js +0 -86
  316. package/dist/src/screens/onboarding.d.ts +0 -13
  317. package/dist/src/screens/portal.d.ts +0 -5
  318. package/dist/src/screens/public-details.d.ts +0 -5
  319. package/dist/src/screens/public-details.js +0 -90
  320. package/dist/src/semantics/after-payment.d.ts +0 -22
  321. package/dist/src/semantics/after-payment.js +0 -99
  322. package/dist/src/semantics/apps-secrets.d.ts +0 -2
  323. package/dist/src/semantics/billing.d.ts +0 -2
  324. package/dist/src/semantics/billing.js +0 -220
  325. package/dist/src/semantics/connect.d.ts +0 -5
  326. package/dist/src/semantics/connect.js +0 -493
  327. package/dist/src/semantics/credit-notes.d.ts +0 -2
  328. package/dist/src/semantics/credit-notes.js +0 -172
  329. package/dist/src/semantics/ephemeral-keys.d.ts +0 -2
  330. package/dist/src/semantics/ephemeral-keys.js +0 -34
  331. package/dist/src/semantics/files.d.ts +0 -2
  332. package/dist/src/semantics/files.js +0 -125
  333. package/dist/src/semantics/issuing.d.ts +0 -13
  334. package/dist/src/semantics/issuing.js +0 -575
  335. package/dist/src/semantics/ledger.d.ts +0 -59
  336. package/dist/src/semantics/ledger.js +0 -200
  337. package/dist/src/semantics/payment-intents.d.ts +0 -18
  338. package/dist/src/semantics/payment-intents.js +0 -452
  339. package/dist/src/semantics/payment-links.d.ts +0 -2
  340. package/dist/src/semantics/payment-links.js +0 -133
  341. package/dist/src/semantics/payment-methods.d.ts +0 -20
  342. package/dist/src/semantics/payment-methods.js +0 -140
  343. package/dist/src/semantics/plans.d.ts +0 -5
  344. package/dist/src/semantics/plans.js +0 -121
  345. package/dist/src/semantics/platform.d.ts +0 -9
  346. package/dist/src/semantics/platform.js +0 -206
  347. package/dist/src/semantics/renewals.d.ts +0 -47
  348. package/dist/src/semantics/renewals.js +0 -281
  349. package/dist/src/semantics/setup-intents.d.ts +0 -2
  350. package/dist/src/semantics/subscription-schedules.d.ts +0 -2
  351. package/dist/src/semantics/subscription-schedules.js +0 -119
  352. package/dist/src/semantics/tax.d.ts +0 -2
  353. package/dist/src/semantics/tax.js +0 -197
  354. package/dist/src/semantics/terminal.d.ts +0 -5
  355. package/dist/src/semantics/terminal.js +0 -182
  356. package/dist/src/semantics/test-clocks.d.ts +0 -6
  357. package/dist/src/semantics/tokens.d.ts +0 -4
  358. package/dist/src/semantics/tokens.js +0 -44
  359. package/dist/src/semantics/treasury.d.ts +0 -2
  360. package/dist/src/semantics/treasury.js +0 -377
  361. package/dist/src/semantics/webhook-endpoints.d.ts +0 -3
  362. package/dist/src/semantics/webhook-endpoints.js +0 -85
  363. package/dist/src/stripe-budget.d.ts +0 -55
  364. package/dist/src/stripe-budget.js +0 -155
  365. package/dist/src/stripe-capabilities.d.ts +0 -3
  366. package/dist/src/stripe-capabilities.js +0 -5836
  367. package/dist/src/stripe-conformance.d.ts +0 -43
  368. package/dist/src/stripe-conformance.js +0 -105
  369. package/dist/src/stripe-connector.d.ts +0 -161
  370. package/dist/src/stripe-connector.js +0 -414
  371. package/dist/src/stripe-emit.d.ts +0 -2
  372. package/dist/src/stripe-emit.js +0 -145
  373. package/dist/src/stripe-events.d.ts +0 -93
  374. package/dist/src/stripe-events.js +0 -392
  375. package/dist/src/stripe-js.d.ts +0 -4
  376. package/dist/src/stripe-js.js +0 -70
  377. package/dist/src/stripe-mirror-ui.d.ts +0 -15
  378. package/dist/src/stripe-mirror-ui.js +0 -87
  379. package/dist/src/stripe-perform-harness.d.ts +0 -9
  380. package/dist/src/stripe-perform-harness.js +0 -26
  381. package/dist/src/stripe-server.d.ts +0 -33
  382. package/dist/src/stripe-server.js +0 -429
  383. package/dist/src/stripe-shared.d.ts +0 -109
  384. package/dist/src/stripe-shared.js +0 -276
  385. package/dist/src/stripe-ui-conformance.d.ts +0 -5
  386. package/dist/src/stripe-ui-conformance.js +0 -79
  387. package/dist/src/stripe-ui-structure.d.ts +0 -3
  388. package/dist/src/stripe-ui-structure.js +0 -168
  389. package/dist/test-fixtures/stripe-known-deviations.json +0 -110
  390. package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +0 -14
  391. package/dist/test-fixtures/stripe-openapi-operations.json +0 -4717
  392. package/dist/test-fixtures/stripe-schemas.SOURCE.md +0 -35
  393. package/dist/test-fixtures/stripe-schemas.json +0 -3813
  394. package/src/screens/connect-settings.tsx +0 -121
  395. package/src/screens/consent-skin.ts +0 -20
  396. package/src/screens/financial-connections.tsx +0 -101
  397. package/src/screens/identity.tsx +0 -96
  398. package/src/screens/public-details.tsx +0 -105
  399. package/src/semantics/after-payment.ts +0 -118
  400. package/src/semantics/billing.ts +0 -216
  401. package/src/semantics/connect.ts +0 -487
  402. package/src/semantics/credit-notes.ts +0 -168
  403. package/src/semantics/ephemeral-keys.ts +0 -34
  404. package/src/semantics/files.ts +0 -143
  405. package/src/semantics/issuing.ts +0 -590
  406. package/src/semantics/ledger.ts +0 -253
  407. package/src/semantics/payment-intents.ts +0 -463
  408. package/src/semantics/payment-links.ts +0 -148
  409. package/src/semantics/payment-methods.ts +0 -145
  410. package/src/semantics/plans.ts +0 -131
  411. package/src/semantics/platform.ts +0 -220
  412. package/src/semantics/renewals.ts +0 -279
  413. package/src/semantics/setup-intents.ts +0 -87
  414. package/src/semantics/subscription-schedules.ts +0 -129
  415. package/src/semantics/tax.ts +0 -220
  416. package/src/semantics/terminal.ts +0 -195
  417. package/src/semantics/tokens.ts +0 -52
  418. package/src/semantics/treasury.ts +0 -383
  419. package/src/semantics/webhook-endpoints.ts +0 -87
  420. package/src/stripe-budget.ts +0 -181
  421. package/src/stripe-capabilities.ts +0 -5651
  422. package/src/stripe-conformance.ts +0 -124
  423. package/src/stripe-connector.ts +0 -462
  424. package/src/stripe-emit.ts +0 -157
  425. package/src/stripe-events.ts +0 -434
  426. package/src/stripe-js.ts +0 -70
  427. package/src/stripe-mirror-ui.ts +0 -90
  428. package/src/stripe-perform-harness.ts +0 -29
  429. package/src/stripe-server.ts +0 -393
  430. package/src/stripe-shared.ts +0 -297
  431. package/src/stripe-ui-conformance.ts +0 -88
  432. package/src/stripe-ui-structure.ts +0 -182
  433. package/test-fixtures/stripe-known-deviations.json +0 -110
  434. package/test-fixtures/stripe-openapi-operations.json +0 -4717
  435. package/test-fixtures/stripe-schemas.json +0 -3813
  436. /package/dist/src/{screens → engine}/industries.d.ts +0 -0
  437. /package/dist/src/{screens → engine}/industries.js +0 -0
  438. /package/dist/src/{semantics → engine}/test-cards.d.ts +0 -0
  439. /package/dist/src/{semantics → engine}/test-cards.js +0 -0
  440. /package/dist/src/{stripe-version.d.ts → engine/version.d.ts} +0 -0
  441. /package/src/{screens → engine}/industries.ts +0 -0
  442. /package/src/{semantics → engine}/test-cards.ts +0 -0
@@ -1,157 +0,0 @@
1
- // The Stripe DELIVER verb — `world-stripe emit` (feature-sweep friction: hand-constructing
2
- // checkout.session.completed / invoice.paid envelopes + HMAC signing to poke an app's webhook
3
- // handler). This is the pack side of the kernel's emit seam (@volter/world-core emit.ts): the
4
- // catalog of emittable event types, endpoint registrations read FROM TWIN STATE AT REST
5
- // (webhook_endpoint rows minted by POST /v1/webhook_endpoints — the CLI runs in its own
6
- // process, so the in-memory delivery registry in stripe-events.ts does not apply), and
7
- // synthesis of the exact envelope real Stripe delivers, signed with the DESTINATION
8
- // endpoint's own whsec_twin_* so `stripe.webhooks.constructEvent(rawBody, header, secret)`
9
- // in the app's unmodified SDK verifies the delivery unchanged.
10
- //
11
- // DELIVER does not transition state: it snapshots the subject AS IT IS in the twin and fires
12
- // the named event about it. Completing a checkout session / paying an invoice is the twin
13
- // API's job; this verb answers "my app's webhook handler needs to SEE the event, now."
14
- import { projectResources, worldNow, type EmitEndpoint, type EmittableEvent, type SynthesizedDelivery, type TwinEmitter } from '@volter/world-core';
15
- import { OBJECT_NAME, PLATFORM_ACCOUNT_ID, TWIN_API_VERSION, view } from './stripe-twin.ts';
16
- import { STRIPE_WEBHOOK_FALLBACK_SECRET, generateTestHeaderString } from './stripe-events.ts';
17
- import { render } from './stripe-version.ts';
18
-
19
- const SERVICE = 'stripe';
20
-
21
- // event type → the twin resource type whose current state becomes `data.object`.
22
- // The catalog is every event type the twin's own write path can produce (eventTypeFor's
23
- // range) — the emit verb can re-fire anything the twin emits organically.
24
- const EMITTABLE: Record<string, string> = {
25
- // payment intents
26
- 'payment_intent.created': 'payment_intent',
27
- 'payment_intent.succeeded': 'payment_intent',
28
- 'payment_intent.processing': 'payment_intent',
29
- 'payment_intent.requires_action': 'payment_intent',
30
- 'payment_intent.amount_capturable_updated': 'payment_intent',
31
- 'payment_intent.payment_failed': 'payment_intent',
32
- 'payment_intent.canceled': 'payment_intent',
33
- // charges + disputes
34
- 'charge.succeeded': 'charge',
35
- 'charge.failed': 'charge',
36
- 'charge.captured': 'charge',
37
- 'charge.refunded': 'charge',
38
- 'charge.dispute.created': 'dispute',
39
- // customers
40
- 'customer.created': 'customer',
41
- 'customer.updated': 'customer',
42
- 'customer.deleted': 'customer',
43
- // checkout — the sweep's exact pain
44
- 'checkout.session.completed': 'checkout_session',
45
- // invoices — the sweep's exact pain
46
- 'invoice.created': 'invoice',
47
- 'invoice.finalized': 'invoice',
48
- 'invoice.paid': 'invoice',
49
- 'invoice.payment_failed': 'invoice',
50
- 'invoice.voided': 'invoice',
51
- 'invoice.marked_uncollectible': 'invoice',
52
- 'invoice.sent': 'invoice',
53
- // subscriptions
54
- 'customer.subscription.created': 'subscription',
55
- 'customer.subscription.updated': 'subscription',
56
- 'customer.subscription.deleted': 'subscription',
57
- 'customer.subscription.paused': 'subscription',
58
- 'customer.subscription.resumed': 'subscription',
59
- // payment methods
60
- 'payment_method.attached': 'payment_method',
61
- 'payment_method.detached': 'payment_method',
62
- // setup intents
63
- 'setup_intent.created': 'setup_intent',
64
- 'setup_intent.succeeded': 'setup_intent',
65
- // payouts, catalog
66
- 'payout.created': 'payout',
67
- 'payout.paid': 'payout',
68
- 'payout.canceled': 'payout',
69
- 'price.created': 'price',
70
- 'product.created': 'product',
71
- // issuing — the settlement consumer's diet (issuing_transaction.created) plus the
72
- // authorization/card/cardholder lifecycle. issuing_authorization.request is emittable
73
- // too: DELIVER re-fires the request event about an authorization AS IT IS in twin state
74
- // (the organic synchronous leg lives in the present-authorization flow; this verb lets
75
- // an app's handler see the event again without re-presenting).
76
- 'issuing_authorization.created': 'issuing_authorization',
77
- 'issuing_authorization.request': 'issuing_authorization',
78
- 'issuing_authorization.updated': 'issuing_authorization',
79
- 'issuing_card.created': 'issuing_card',
80
- 'issuing_card.updated': 'issuing_card',
81
- 'issuing_cardholder.created': 'issuing_cardholder',
82
- 'issuing_cardholder.updated': 'issuing_cardholder',
83
- 'issuing_transaction.created': 'issuing_transaction',
84
- };
85
-
86
- function rows(type: string, root?: string): Array<Record<string, unknown>> {
87
- return projectResources(SERVICE, root).filter((r) => r.type === type);
88
- }
89
-
90
- function liveEndpointRows(root?: string): Array<Record<string, unknown>> {
91
- return rows('webhook_endpoint', root).filter((w) => w.deleted !== true);
92
- }
93
-
94
- let emitSeq = 0;
95
-
96
- export const stripeEmitter: TwinEmitter = {
97
- vendor: 'stripe',
98
-
99
- events(): EmittableEvent[] {
100
- return Object.entries(EMITTABLE).map(([type, subjectType]) => ({ type, subjectType }));
101
- },
102
-
103
- endpoints(root?: string): EmitEndpoint[] {
104
- return liveEndpointRows(root).map((w) => ({
105
- id: String(w.id),
106
- url: String(w.url ?? ''),
107
- ...(Array.isArray(w.enabled_events) ? { enabledEvents: w.enabled_events.map(String) } : {}),
108
- }));
109
- },
110
-
111
- subjects(subjectType: string, root?: string): string[] {
112
- return rows(subjectType, root).filter((r) => r.deleted !== true).map((r) => String(r.id));
113
- },
114
-
115
- synthesize({ type, subjectId, endpoint, root, occurredAt }): SynthesizedDelivery {
116
- const subjectType = EMITTABLE[type];
117
- if (!subjectType) throw new Error(`emit: the stripe pack cannot synthesize "${type}".`);
118
- const row = rows(subjectType, root).find((r) => String(r.id) === subjectId && r.deleted !== true);
119
- if (!row) {
120
- const have = stripeEmitter.subjects(subjectType, root);
121
- throw new Error(
122
- `emit: no ${subjectType} "${subjectId}" in stripe twin state — cannot synthesize ${type}. ` +
123
- (have.length ? `${subjectType} ids in state: ${have.join(', ')}.` : `No ${subjectType} exists in this twin yet (object name: ${OBJECT_NAME[subjectType] ?? subjectType}) — create one through the vendor API first.`),
124
- );
125
- }
126
- // The endpoint's own signing secret, from its state row (real Stripe signs per-endpoint).
127
- const endpointRow = liveEndpointRows(root).find((w) => String(w.id) === endpoint.id || String(w.url) === endpoint.url);
128
- const secret = typeof endpointRow?.secret === 'string' && endpointRow.secret ? endpointRow.secret : STRIPE_WEBHOOK_FALLBACK_SECRET;
129
-
130
- const created = occurredAt ? Math.floor(Date.parse(occurredAt) / 1000) : Math.floor(Date.parse(worldNow()) / 1000);
131
- // a connected account's object is its event's, as the write path scopes it (stripe-twin.ts afterStripeWrite): the
132
- // account itself, or a row kept on its books (`_account`). "Each event for a connected account contains a top-level
133
- // `account` property that identifies the connected account" (docs.stripe.com/connect/webhooks).
134
- const account = typeof row._account === 'string' ? row._account : subjectType === 'account' && String(row.id) !== PLATFORM_ACCOUNT_ID ? String(row.id) : undefined;
135
- // Same envelope persistStripeEvent stores for GET /v1/events; `evt_twin_emit_*` marks it
136
- // operator-fired (never colliding with the write path's organic `evt_twin_<n>` ids).
137
- const event: Record<string, unknown> = {
138
- id: `evt_twin_emit_${created}_${++emitSeq}`,
139
- object: 'event',
140
- api_version: TWIN_API_VERSION,
141
- created,
142
- data: { object: view(subjectType, row) },
143
- livemode: false,
144
- pending_webhooks: 1,
145
- request: { id: null, idempotency_key: null },
146
- type,
147
- ...(account ? { account } : {}),
148
- };
149
- const payload = JSON.stringify(render(event, TWIN_API_VERSION));
150
- const header = generateTestHeaderString({ payload, secret, ...(occurredAt ? { timestamp: created } : {}) });
151
- return {
152
- payload,
153
- headers: { 'content-type': 'application/json', 'stripe-signature': header },
154
- event,
155
- };
156
- },
157
- };
@@ -1,434 +0,0 @@
1
- import { nodeBuiltin, worldNow, deliveryTraceHeaders } from '@volter/world-core';
2
- import { appDestination, appFetch } from '@volter/world-core/app-route';
3
- import { worldEgressRefusal } from '@volter/world-core/network-policy';
4
- import vendorEvents from './generated/events.gen.json' with { type: 'json' };
5
- import { render } from './stripe-version.ts';
6
- // Stripe event/webhook emission (scorecard R17). Real Stripe fires `event`
7
- // objects (and webhooks) on state changes — payment_intent.succeeded,
8
- // customer.subscription.created, invoice.paid, … — so an app's webhook handler
9
- // runs. A faithful twin must too. On a twin write we build the Stripe `event`
10
- // envelope and deliver it to registered endpoints (injected delivery: fake in
11
- // tests, HTTP POST live, failures swallowed like the real vendor).
12
- export type StripeEvent = {
13
- id: string;
14
- object: 'event';
15
- type: string;
16
- created: number;
17
- livemode: false;
18
- /** the connected account a Connect event is from */
19
- account?: string;
20
- data: { object: Record<string, unknown> };
21
- };
22
- // A deliverer MAY return the endpoint's raw HTTP response. Ordinary (asynchronous) event
23
- // emission ignores the return value; the SYNCHRONOUS real-time-authorization leg
24
- // (issuing_authorization.request) reads it to honor the endpoint's approve/decline. A
25
- // fire-and-forget deliverer returning void stays valid for the asynchronous path.
26
- export type StripeWebhookEndpointResponse = { status: number; body: string };
27
- export type StripeEventDelivery = (url: string, event: StripeEvent, secret?: string) => Promise<void | StripeWebhookEndpointResponse> | void | StripeWebhookEndpointResponse;
28
- /** A webhook endpoint a World holds in its tree: where to POST, what to sign with, what it subscribed to. */
29
- export type StripeWebhookTarget = { url: string; secret?: string; enabledEvents?: string[]; /** a Connect endpoint (`connect: true`): events from connected accounts */ connect?: boolean };
30
-
31
- // ── Webhook signature verification (Stripe's scheme) ────────────────────────────
32
- // Real Stripe signs each webhook delivery with an HMAC-SHA256 over `${timestamp}.${payload}`
33
- // keyed by the endpoint's signing secret, and sends it in the `Stripe-Signature` header as
34
- // `t=<unix>,v1=<hex-sig>`. The SDK's `stripe.webhooks.constructEvent(payload, header, secret)`
35
- // recomputes the signature and 400s on a mismatch / stale timestamp; tests build the header
36
- // with `stripe.webhooks.generateTestHeaderString(...)`. We reproduce BOTH, byte-for-byte, so a
37
- // real `stripe` SDK consumer verifies twin-delivered webhooks unchanged. Pure Node crypto —
38
- // no network, fully deterministic. `node:crypto` is required lazily (inside the functions that
39
- // use it) rather than as a top-level static import, so this module can be pulled into the
40
- // browser bundle of the UI mirror without Bun's browser target choking on `node:crypto` — the
41
- // signature helpers are server-only and never reached in the browser path.
42
- type NodeCrypto = typeof import('node:crypto');
43
- function nodeCrypto(): NodeCrypto {
44
- // eslint-disable-next-line @typescript-eslint/no-require-imports
45
- return nodeBuiltin('node:crypto') as NodeCrypto;
46
- }
47
-
48
- /** HMAC-SHA256(secret, `${timestamp}.${payload}`) as lowercase hex — Stripe's v1 signature. */
49
- export function computeStripeSignature(payload: string, secret: string, timestamp: number): string {
50
- return nodeCrypto().createHmac('sha256', secret).update(`${timestamp}.${payload}`, 'utf8').digest('hex');
51
- }
52
-
53
- /** Build a `Stripe-Signature` header value for `payload` (mirrors generateTestHeaderString). */
54
- export function generateTestHeaderString(opts: { payload: string; secret: string; timestamp?: number; scheme?: string }): string {
55
- // signed at the World's time, as its application reads the time (a moved World clock; the app keeps it too)
56
- const timestamp = opts.timestamp ?? Math.floor(Date.parse(worldNow()) / 1000);
57
- const scheme = opts.scheme ?? 'v1';
58
- const signature = computeStripeSignature(opts.payload, opts.secret, timestamp);
59
- return `t=${timestamp},${scheme}=${signature}`;
60
- }
61
-
62
- /** Parse a `Stripe-Signature` header into its timestamp + the list of v1 signatures. */
63
- function parseSignatureHeader(header: string): { timestamp: number; signatures: string[] } {
64
- let timestamp = -1;
65
- const signatures: string[] = [];
66
- for (const part of header.split(',')) {
67
- const eq = part.indexOf('=');
68
- if (eq === -1) continue;
69
- const key = part.slice(0, eq).trim();
70
- const value = part.slice(eq + 1).trim();
71
- if (key === 't') timestamp = Number(value);
72
- else if (key === 'v1') signatures.push(value);
73
- }
74
- return { timestamp, signatures };
75
- }
76
-
77
- export class StripeSignatureVerificationError extends Error {
78
- constructor(message: string) {
79
- super(message);
80
- this.name = 'StripeSignatureVerificationError';
81
- }
82
- }
83
-
84
- /**
85
- * Verify a webhook payload + signature header against the signing secret and return the
86
- * parsed event (mirrors `stripe.webhooks.constructEvent`). Throws a
87
- * StripeSignatureVerificationError on a malformed/missing header, a signature that does
88
- * not match, or (when `tolerance` is given) a timestamp outside the allowed window.
89
- */
90
- export function constructEvent(payload: string, header: string, secret: string, opts: { tolerance?: number; now?: number } = {}): StripeEvent {
91
- if (!header) throw new StripeSignatureVerificationError('No signatures found matching the expected signature for payload.');
92
- const { timestamp, signatures } = parseSignatureHeader(header);
93
- if (timestamp < 0 || signatures.length === 0) {
94
- throw new StripeSignatureVerificationError('Unable to extract timestamp and signatures from header');
95
- }
96
- const expected = computeStripeSignature(payload, secret, timestamp);
97
- const expectedBuf = Buffer.from(expected, 'utf8');
98
- const matched = signatures.some((s) => {
99
- const sBuf = Buffer.from(s, 'utf8');
100
- return sBuf.length === expectedBuf.length && nodeCrypto().timingSafeEqual(sBuf, expectedBuf);
101
- });
102
- if (!matched) {
103
- throw new StripeSignatureVerificationError('No signatures found matching the expected signature for payload.');
104
- }
105
- if (opts.tolerance !== undefined) {
106
- const now = opts.now ?? Math.floor(Date.parse(worldNow()) / 1000);
107
- if (now - timestamp > opts.tolerance) {
108
- throw new StripeSignatureVerificationError('Timestamp outside the tolerance zone');
109
- }
110
- }
111
- return JSON.parse(payload) as StripeEvent;
112
- }
113
-
114
- const registry: string[] = [];
115
- // URLs a HOST registered directly (registerStripeWebhook), with the secret to sign with and the
116
- // enabled_events to filter on. A World's own endpoints are not here: POST /v1/webhook_endpoints
117
- // writes the WebhookEndpoint to the tree, and every emit reads the tree (the caller passes them as
118
- // `endpoints`), so delivery survives a restart and belongs to one World. A URL registered here
119
- // without a secret is signed with the fallback below, so the header is still a real, verifiable
120
- // signature; one registered without a list receives every event, equivalent to ['*'].
121
- const secrets = new Map<string, string>();
122
- const subscriptions = new Map<string, string[]>();
123
- export const STRIPE_WEBHOOK_FALLBACK_SECRET = 'whsec_twin_default';
124
- export function registerStripeWebhook(url: string, secret?: string, enabledEvents?: string[]): void {
125
- if (!registry.includes(url)) registry.push(url);
126
- if (secret) secrets.set(url, secret);
127
- if (enabledEvents) subscriptions.set(url, enabledEvents.map(String));
128
- }
129
- export function unregisterStripeWebhook(url: string): void {
130
- const i = registry.indexOf(url);
131
- if (i !== -1) registry.splice(i, 1);
132
- secrets.delete(url);
133
- subscriptions.delete(url);
134
- }
135
- export function clearStripeWebhooks(): void {
136
- registry.length = 0;
137
- secrets.clear();
138
- subscriptions.clear();
139
- }
140
-
141
- // Does an enabled_events list subscribe an endpoint to `type`? Stripe semantics: '*'
142
- // matches everything, 'issuing_authorization.*' matches the family, else exact. Shared by
143
- // the organic fan-out below and the synchronous issuing leg's enrollment check.
144
- export function stripeEventMatches(enabledEvents: unknown, type: string): boolean {
145
- if (!Array.isArray(enabledEvents)) return false;
146
- return enabledEvents.some((p) => {
147
- const s = String(p);
148
- return s === '*' || (s.endsWith('.*') && type.startsWith(s.slice(0, -1))) || s === type;
149
- });
150
- }
151
- export function listStripeWebhooks(): string[] {
152
- return [...registry];
153
- }
154
-
155
- // twin write operation → Stripe event type (the ones an app's webhooks care about).
156
- // Returns null when an operation has no event (e.g. a plain create/retrieve we
157
- // don't model an event for).
158
- const VENDOR_EVENT_TYPES = new Set((vendorEvents as { types: string[] }).types);
159
-
160
- export function eventTypeFor(operation: string): string | null {
161
- // Several twin write operations ARE already the Stripe event type (we record the
162
- // op as `<resource>.<event>` so it maps 1:1): payment_intent.amount_capturable_updated,
163
- // payment_intent.canceled, payment_intent.succeeded. Those pass through directly.
164
- const PASSTHROUGH = new Set([
165
- 'payment_intent.amount_capturable_updated', 'payment_intent.canceled', 'payment_intent.succeeded',
166
- 'payment_intent.payment_failed', 'payment_intent.created', 'payment_intent.processing',
167
- 'payment_intent.requires_action',
168
- 'charge.succeeded', 'charge.captured', 'charge.refunded', 'charge.dispute.created',
169
- // A refund fires TWO real Stripe events: `refund.created` carrying the Refund, and
170
- // `charge.refunded` carrying the CHARGE with its new totals. The twin records the charge's
171
- // own write as `charge.refunded`, so the event a consumer receives is the charge — which is
172
- // the whole point of that event and the reason a reconciler subscribes to it.
173
- 'refund.created', 'refund.updated',
174
- 'customer.updated', 'customer.deleted', 'invoice.created', 'invoice.finalized',
175
- 'invoice.paid', 'invoice.payment_failed', 'invoice.voided', 'invoice.marked_uncollectible', 'invoice.sent',
176
- 'customer.subscription.updated', 'customer.subscription.paused', 'customer.subscription.resumed',
177
- 'payout.created', 'payout.paid', 'payout.canceled', 'price.created', 'product.created',
178
- 'setup_intent.succeeded', 'setup_intent.created',
179
- // The twin's checkout completion transition records the op AS the real event type:
180
- // real Stripe fires checkout.session.completed (data.object = the completed Session)
181
- // the moment a session reaches status=complete — the event most billing integrations
182
- // fulfill from, so it must pass through, not be swallowed.
183
- 'checkout.session.completed',
184
- // Issuing writes record the op AS the real event type (issuing_authorization.updated on
185
- // approve/decline/update, issuing_card.updated on card updates, …) — all real vendor
186
- // event types (stripe@22.3.0 WebhookEndpoint.EnabledEvent enum).
187
- 'issuing_authorization.updated', 'issuing_card.updated', 'issuing_cardholder.updated',
188
- 'issuing_transaction.updated',
189
- ]);
190
- if (PASSTHROUGH.has(operation)) return operation;
191
- switch (operation) {
192
- case 'customer.create': return 'customer.created';
193
- case 'payment_intent.create': return 'payment_intent.created';
194
- case 'payment_intent.confirm': return 'payment_intent.succeeded';
195
- case 'payment_method.attach': return 'payment_method.attached';
196
- case 'payment_method.detach': return 'payment_method.detached';
197
- case 'setup_intent.confirm': return 'setup_intent.succeeded';
198
- case 'subscription.create': return 'customer.subscription.created';
199
- case 'subscription.update': return 'customer.subscription.updated';
200
- case 'subscription.cancel': return 'customer.subscription.deleted';
201
- case 'invoice.finalize': return 'invoice.finalized';
202
- case 'invoice.pay': return 'invoice.paid';
203
- case 'invoice.void': return 'invoice.voided';
204
- case 'refund.create': return 'refund.created';
205
- case 'dispute.create': return 'charge.dispute.created';
206
- case 'payout.create': return 'payout.created';
207
- case 'product.create': return 'product.created';
208
- case 'price.create': return 'price.created';
209
- // Issuing: real Stripe fires issuing_authorization.created/.updated,
210
- // issuing_card.created/.updated, issuing_cardholder.created/.updated and
211
- // issuing_transaction.created (all present in the vendor SDK's
212
- // WebhookEndpoint.EnabledEvent enum, stripe@22.3.0 WebhookEndpoints.d.ts).
213
- // issuing_authorization.request is deliberately NOT here: it is not a
214
- // state-change event — the twin delivers it SYNCHRONOUSLY from the
215
- // present-authorization flow (requestAuthorizationDecision below).
216
- case 'issuing_authorization.create': return 'issuing_authorization.created';
217
- case 'issuing_card.create': return 'issuing_card.created';
218
- case 'issuing_cardholder.create': return 'issuing_cardholder.created';
219
- case 'issuing_transaction.create': return 'issuing_transaction.created';
220
- default: return vendorEventType(operation);
221
- }
222
- }
223
-
224
- /** Any other write records the event Stripe sends for it, when Stripe sends one: the operation itself, or its
225
- * resource's create/update/delete as `<resource>.created|updated|deleted` (the types Stripe's spec enumerates). */
226
- function vendorEventType(operation: string): string | null {
227
- if (VENDOR_EVENT_TYPES.has(operation)) return operation;
228
- const verb = /^(.+)\.(create|update|delete)$/.exec(operation);
229
- return verb && VENDOR_EVENT_TYPES.has(`${verb[1]}.${verb[2]}d`) ? `${verb[1]}.${verb[2]}d` : null;
230
- }
231
-
232
- /** An event's id, DERIVED from the event itself — the operation, the resource it carries and the instant —
233
- * never a process counter (protocol 2: two identical worlds deliver identically, and a replay of the same
234
- * write raises the same event id rather than one that drifts with process uptime). */
235
- function eventId(operation: string, resource: Record<string, unknown>, occurredAt: string, account?: string): string {
236
- let h = 0x811c9dc5;
237
- const seed = `${operation}|${String((resource as { id?: unknown }).id ?? '')}|${occurredAt}|${account ?? ''}`;
238
- for (let i = 0; i < seed.length; i += 1) { h ^= seed.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; }
239
- return `evt_twin_${h.toString(16).padStart(8, '0')}`;
240
- }
241
- // Live HTTP delivery signs exactly like real Stripe: HMAC-SHA256 over
242
- // `${timestamp}.${payload}` keyed by the ENDPOINT'S OWN signing secret (the
243
- // `whsec_twin_*` minted by POST /v1/webhook_endpoints and registered alongside the
244
- // URL), sent as `Stripe-Signature: t=...,v1=...`. The signed bytes are the exact
245
- // request body, so `stripe.webhooks.constructEvent(rawBody, header, secret)` in the
246
- // consumer's real SDK verifies twin deliveries unchanged (a hardcoded placeholder
247
- // header made every verifying consumer 400 — the delivery was unverifiable).
248
- // Delivery is AT-LEAST-ONCE, like the vendor: real Stripe re-attempts a webhook whose
249
- // delivery fails at the transport layer rather than dropping it, so a consumer that missed
250
- // one event because a socket blipped is a consumer real Stripe would have reached. A single
251
- // swallowed `fetch` rejection here is indistinguishable, downstream, from the consumer
252
- // having a bug — it cost a week of "the settlement webhook is racy" before anyone could see
253
- // that the event had simply never left the twin. Two cheap re-attempts cover a transient
254
- // local failure (ECONNRESET / socket exhaustion under a boot storm); a genuinely unreachable
255
- // endpoint refuses instantly, so the retries cost microseconds. A NON-2xx response is the
256
- // consumer answering, not a transport failure — it is not retried here.
257
- // A delivery that still fails after the re-attempts is DROPPED, and says so: silent loss is
258
- // the one outcome a twin must never have, because it makes every consumer look flaky.
259
- const DELIVERY_ATTEMPTS = 3;
260
- const httpDelivery: StripeEventDelivery = async (url, event, endpointSecret) => {
261
- // the event as its API version renders it (stripe-version.ts)
262
- const payload = JSON.stringify(render(event, (event as { api_version?: string }).api_version));
263
- const secret = endpointSecret ?? secrets.get(url) ?? STRIPE_WEBHOOK_FALLBACK_SECRET;
264
- // the World's egress rule: an endpoint it refuses is not delivered to, and says so; the application's own hostnames
265
- // reach it inside the World
266
- const refusal = appDestination(url) ? null : worldEgressRefusal(url);
267
- if (refusal !== null) console.error(`[twin:stripe] webhook delivery DROPPED — ${event.type} ${event.id} -> ${url}: ${refusal}`);
268
- return refusal !== null ? undefined : postEvent(url, event, payload, secret);
269
- };
270
-
271
- /** The HTTP POST of a signed event to an endpoint the World lets it reach, re-attempted on a transport failure. */
272
- async function postEvent(url: string, event: StripeEvent, payload: string, secret: string): Promise<void> {
273
- let last = '';
274
- for (let attempt = 1; attempt <= DELIVERY_ATTEMPTS; attempt += 1) {
275
- try {
276
- const header = generateTestHeaderString({ payload, secret });
277
- await appFetch(url, { method: 'POST', headers: { 'content-type': 'application/json', 'stripe-signature': header, ...deliveryTraceHeaders() }, body: payload });
278
- return;
279
- } catch (error) {
280
- last = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
281
- if (attempt < DELIVERY_ATTEMPTS) await new Promise((resolve) => setTimeout(resolve, 25 * attempt));
282
- }
283
- }
284
- console.error(`[twin:stripe] webhook delivery DROPPED after ${DELIVERY_ATTEMPTS} attempts — ${event.type} ${event.id} -> ${url}: ${last}`);
285
- }
286
-
287
- // Default deliverer override (a TEST SEAM). The twin's write path emits events without
288
- // threading a per-call deliverer, so to drive event delivery fully OFFLINE/deterministically
289
- // — no real sockets — a test installs a fake sink here. When unset, real HTTP POST is used
290
- // (prod behavior). This keeps D5 honest: verify() exercises the twin's own emission, in-process.
291
- // a slot, not module truth (protocol 2): WHERE a delivery goes, set once by the host that mounts the twin
292
- const defaultDelivery: { current: StripeEventDelivery | null } = { current: null };
293
- /** Install a default deliverer used by the write path when no per-call `deliver` is passed. */
294
- export function setStripeEventDelivery(deliver: StripeEventDelivery | null): void {
295
- defaultDelivery.current = deliver;
296
- }
297
-
298
- // ── Real-time authorization (issuing_authorization.request) — the SYNCHRONOUS leg ────────
299
- // Real Stripe delivers issuing_authorization.request to the ONE enrolled endpoint while the
300
- // card network holds the authorization open, and the endpoint must answer within Stripe's
301
- // documented 2-second window ("This request should be made within the timeout window of the
302
- // real-time authorization flow" — stripe@22.3.0 Issuing/Authorizations.d.ts; the current
303
- // method is to "respond directly to the webhook request to approve an authorization"). The
304
- // endpoint's HTTP response body carries the decision: a JSON object with a boolean
305
- // `approved` (and, for an amount-controllable request, an optional integer `amount` to hold
306
- // a different amount). Outcomes map onto the vendor's request_history.reason values
307
- // (stripe@22.3.0 RequestHistory.Reason):
308
- // webhook_approved / webhook_declined — the endpoint answered in time;
309
- // webhook_timeout — no response arrived inside the window (incl. unreachable endpoint);
310
- // webhook_error — "the direct webhook response is invalid (for example, parsing errors
311
- // or missing parameters)" (verbatim from the SDK's reason_message doc),
312
- // i.e. non-2xx status, non-JSON body, or a missing `approved`.
313
- // On timeout/error real Stripe DECLINES the authorization (fail-closed; the card's own
314
- // spending_controls remain the backstop) — the account-level "approve on timeout" opt-in is
315
- // not API-configurable and is not modeled.
316
- export const STRIPE_REALTIME_AUTH_TIMEOUT_MS = 2_000;
317
-
318
- export type StripeAuthRequestOutcome =
319
- | { kind: 'approved'; amount?: number }
320
- | { kind: 'declined' }
321
- | { kind: 'timeout'; message: string }
322
- | { kind: 'error'; message: string };
323
-
324
- // The live synchronous deliverer: POST the signed event and WAIT for the response within
325
- // the 2s window. Signs with the enrolled endpoint's own whsec (passed by the caller, read
326
- // from the endpoint's state row) — never a placeholder.
327
- async function httpAuthRequestDelivery(url: string, event: StripeEvent, secret: string): Promise<StripeWebhookEndpointResponse> {
328
- // the event as its API version renders it (stripe-version.ts)
329
- const payload = JSON.stringify(render(event, (event as { api_version?: string }).api_version));
330
- const header = generateTestHeaderString({ payload, secret });
331
- // the World's egress rule: a refused endpoint is an unreachable one (the caller's timeout fallback); the
332
- // application's own hostnames reach it inside the World
333
- const refusal = appDestination(url) ? null : worldEgressRefusal(url);
334
- return refusal !== null ? Promise.reject(new Error(refusal)) : postAuthRequest(url, payload, header);
335
- }
336
-
337
- /** The HTTP POST of a real-time authorization request, cut off at the window, and the endpoint's answer. */
338
- async function postAuthRequest(url: string, payload: string, header: string): Promise<StripeWebhookEndpointResponse> {
339
- const res = await appFetch(url, {
340
- method: 'POST',
341
- headers: { 'content-type': 'application/json', 'stripe-signature': header, ...deliveryTraceHeaders() },
342
- body: payload,
343
- signal: AbortSignal.timeout(STRIPE_REALTIME_AUTH_TIMEOUT_MS),
344
- });
345
- return { status: res.status, body: await res.text() };
346
- }
347
-
348
- /**
349
- * Deliver an issuing_authorization.request event to the enrolled endpoint and interpret
350
- * its synchronous response as the authorization decision. Honors the installed test
351
- * deliverer (setStripeEventDelivery) so verifies run fully offline: a fake returning a
352
- * {status, body} response drives the decision; a fake that throws a TimeoutError (the
353
- * exact error AbortSignal.timeout produces) exercises the timeout fallback.
354
- */
355
- export async function requestAuthorizationDecision(url: string, event: StripeEvent, secret: string): Promise<StripeAuthRequestOutcome> {
356
- let res: void | StripeWebhookEndpointResponse;
357
- try {
358
- res = defaultDelivery.current ? await defaultDelivery.current(url, event) : await httpAuthRequestDelivery(url, event, secret);
359
- } catch (e) {
360
- // no answer inside the window: cut off (TimeoutError, AbortError), or never reached (refused, DNS, reset), alike
361
- return (e instanceof Error && (e.name === 'TimeoutError' || e.name === 'AbortError')) ? TIMED_OUT : UNREACHED;
362
- }
363
- return readDecision(res);
364
- }
365
-
366
- const TIMED_OUT: StripeAuthRequestOutcome = { kind: 'timeout', message: 'Webhook endpoint did not respond within the real-time authorization window.' };
367
- const UNREACHED: StripeAuthRequestOutcome = { kind: 'timeout', message: 'Webhook endpoint was unreachable within the real-time authorization window.' };
368
-
369
- /** The endpoint's synchronous answer read as the decision: `approved` (and an optional `amount`), or an error for an
370
- * answer Stripe cannot read. */
371
- function readDecision(res: void | StripeWebhookEndpointResponse): StripeAuthRequestOutcome {
372
- if (!res || typeof res !== 'object') {
373
- return { kind: 'error', message: 'Webhook endpoint returned no response body.' };
374
- }
375
- if (res.status < 200 || res.status >= 300) {
376
- return { kind: 'error', message: `Webhook endpoint responded with HTTP status ${res.status}.` };
377
- }
378
- let parsed: unknown;
379
- try {
380
- parsed = JSON.parse(res.body);
381
- } catch {
382
- return { kind: 'error', message: 'Webhook response body was not valid JSON.' };
383
- }
384
- const approved = (parsed as { approved?: unknown } | null)?.approved;
385
- if (typeof approved !== 'boolean') {
386
- return { kind: 'error', message: 'Webhook response is missing the required `approved` parameter.' };
387
- }
388
- if (!approved) return { kind: 'declined' };
389
- const amount = (parsed as { amount?: unknown }).amount;
390
- return { kind: 'approved', ...(typeof amount === 'number' && Number.isInteger(amount) && amount > 0 ? { amount } : {}) };
391
- }
392
-
393
- /**
394
- * Emit a Stripe event for a twin write to registered endpoints. `resource` is the
395
- * Stripe object the event is about. `occurredAt` is caller-supplied (deterministic).
396
- * Returns the delivered events (for assertions); no-op when nothing is registered
397
- * or the operation has no mapped event type.
398
- *
399
- * Fan-out filters by each endpoint's registered enabled_events (stripeEventMatches — '*',
400
- * 'family.*', exact), like the vendor: a consumer enrolled for one event type receives
401
- * only that type. A URL registered without a list (test seam) receives everything.
402
- */
403
- export async function emitStripeEvent(
404
- operation: string,
405
- resource: Record<string, unknown>,
406
- opts: { occurredAt: string; deliver?: StripeEventDelivery; endpoints?: StripeWebhookTarget[]; account?: string; /** the id the stored event takes (afterStripeWrite) */ id?: string } = { occurredAt: '1970-01-01T00:00:00.000Z' },
407
- ): Promise<StripeEvent[]> {
408
- const type = eventTypeFor(operation);
409
- // the World's own endpoints (its tree is the record), then any URL a host registered directly
410
- const targets: StripeWebhookTarget[] = [...(opts.endpoints ?? [])];
411
- for (const url of registry) if (!targets.some((t) => t.url === url)) targets.push({ url, ...(secrets.has(url) ? { secret: secrets.get(url)! } : {}), ...(subscriptions.has(url) ? { enabledEvents: subscriptions.get(url)! } : {}) });
412
- if (!type || targets.length === 0) return [];
413
- const deliver = opts.deliver ?? defaultDelivery.current ?? httpDelivery;
414
- const event: StripeEvent = {
415
- id: opts.id ?? eventId(operation, resource, opts.occurredAt, opts.account),
416
- object: 'event',
417
- type,
418
- created: Math.floor(Date.parse(opts.occurredAt) / 1000) || 0,
419
- livemode: false,
420
- data: { object: resource },
421
- // "Each event for a connected account contains a top-level `account` property that identifies the connected account"
422
- ...(opts.account ? { account: opts.account } : {}),
423
- };
424
- const out: StripeEvent[] = [];
425
- for (const target of targets) {
426
- if (target.enabledEvents && !stripeEventMatches(target.enabledEvents, type)) continue;
427
- // an endpoint's scope (docs.stripe.com/connect/webhooks): `connect: true` receives the Connected accounts scope,
428
- // `connect: false` Your account; a URL registered without a scope (a host's seam) receives both
429
- if (target.connect !== undefined && target.connect !== Boolean(opts.account)) continue;
430
- await deliver(target.url, event, target.secret);
431
- out.push(event);
432
- }
433
- return out;
434
- }