@volter/twin-stripe 2.0.38 → 3.0.1

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 (402) hide show
  1. package/README.md +112 -207
  2. package/dist/generated/pack-facts.json +94 -0
  3. package/dist/journeys/coverage.json +596 -0
  4. package/dist/journeys/customer-life.json +6108 -0
  5. package/dist/journeys/customer-life.outline.md +371 -0
  6. package/dist/journeys/decisions.json +677 -0
  7. package/dist/journeys/demand.json +706 -0
  8. package/dist/journeys/score.json +853 -0
  9. package/dist/journeys/unreachable.json +102 -0
  10. package/dist/journeys/vendor-examples.json +8010 -0
  11. package/dist/spec/SOURCE.md +18 -0
  12. package/dist/spec/doc-examples.json +445 -0
  13. package/dist/spec/fixtures3.json.gz +0 -0
  14. package/dist/spec/openapi.json.gz +0 -0
  15. package/dist/spec/patches.json +34 -0
  16. package/dist/spec/sources.json +306 -0
  17. package/dist/src/cli.d.ts +0 -1
  18. package/dist/src/cli.js +11 -31
  19. package/dist/src/engine/after-payment.d.ts +8 -0
  20. package/dist/src/engine/after-payment.js +27 -0
  21. package/dist/src/engine/apps-secrets.d.ts +1 -0
  22. package/dist/src/engine/apps-secrets.js +3 -0
  23. package/dist/src/engine/balance.d.ts +5 -0
  24. package/dist/src/engine/balance.js +15 -0
  25. package/dist/src/engine/checkout.d.ts +24 -0
  26. package/dist/src/engine/checkout.js +30 -0
  27. package/dist/src/engine/common.d.ts +19 -0
  28. package/dist/src/engine/common.js +34 -0
  29. package/dist/src/engine/connect.d.ts +45 -0
  30. package/dist/src/engine/connect.js +108 -0
  31. package/dist/src/engine/customers.d.ts +4 -0
  32. package/dist/src/engine/customers.js +8 -0
  33. package/dist/src/engine/display.d.ts +11 -0
  34. package/dist/src/engine/display.js +13 -0
  35. package/dist/src/engine/files.d.ts +11 -0
  36. package/dist/src/engine/files.js +23 -0
  37. package/dist/src/engine/invoices.d.ts +21 -0
  38. package/dist/src/engine/invoices.js +84 -0
  39. package/dist/src/engine/issuing.d.ts +33 -0
  40. package/dist/src/engine/issuing.js +54 -0
  41. package/dist/src/engine/ledger.d.ts +8 -0
  42. package/dist/src/engine/ledger.js +8 -0
  43. package/dist/src/engine/oauth.d.ts +4 -0
  44. package/dist/src/engine/oauth.js +24 -0
  45. package/dist/src/engine/onboarding.d.ts +13 -0
  46. package/dist/src/engine/onboarding.js +42 -0
  47. package/dist/src/{stripe-params.d.ts → engine/params.d.ts} +20 -3
  48. package/dist/src/{stripe-params.js → engine/params.js} +7 -60
  49. package/dist/src/engine/payment-intents.d.ts +10 -0
  50. package/dist/src/engine/payment-intents.js +27 -0
  51. package/dist/src/engine/payment-methods.d.ts +16 -0
  52. package/dist/src/engine/payment-methods.js +22 -0
  53. package/dist/src/engine/radar.d.ts +2 -0
  54. package/dist/src/engine/radar.js +5 -0
  55. package/dist/src/engine/refunds.d.ts +1 -0
  56. package/dist/src/engine/refunds.js +3 -0
  57. package/dist/src/engine/renewals.d.ts +36 -0
  58. package/dist/src/engine/renewals.js +65 -0
  59. package/dist/src/{stripe-twin.d.ts → engine/stripe.d.ts} +12 -45
  60. package/dist/src/{stripe-twin.js → engine/stripe.js} +53 -387
  61. package/dist/src/engine/subscriptions.d.ts +11 -0
  62. package/dist/src/engine/subscriptions.js +30 -0
  63. package/dist/src/engine/transfers.d.ts +2 -0
  64. package/dist/src/engine/transfers.js +1 -0
  65. package/dist/src/{stripe-version.d.ts → engine/version.d.ts} +0 -2
  66. package/dist/src/{stripe-version.js → engine/version.js} +21 -9
  67. package/dist/src/engine/webhook-endpoints.d.ts +1 -0
  68. package/dist/src/engine/webhook-endpoints.js +12 -0
  69. package/dist/src/engine/webhooks.d.ts +31 -0
  70. package/dist/src/engine/webhooks.js +68 -0
  71. package/dist/src/fetch.d.ts +2 -0
  72. package/dist/src/fetch.js +26 -0
  73. package/dist/src/generated/surface.gen.json +1 -1
  74. package/dist/src/generated/ui.gen.json +1 -1
  75. package/dist/src/index.d.ts +5 -14
  76. package/dist/src/index.js +10 -75
  77. package/dist/src/manifest.js +478 -876
  78. package/dist/src/screens/billing-portal.d.ts +3 -0
  79. package/dist/src/screens/{portal.js → billing-portal.js} +32 -58
  80. package/dist/src/screens/checkout-js.d.ts +2 -0
  81. package/dist/src/screens/checkout-js.js +84 -0
  82. package/dist/src/screens/checkout.d.ts +2 -31
  83. package/dist/src/screens/checkout.js +32 -121
  84. package/dist/src/screens/connect-onboarding.d.ts +3 -0
  85. package/dist/src/screens/{onboarding.js → connect-onboarding.js} +38 -104
  86. package/dist/src/screens/dashboard.d.ts +3 -0
  87. package/dist/src/screens/dashboard.js +116 -0
  88. package/dist/src/screens/shared.d.ts +27 -0
  89. package/dist/src/screens/shared.js +87 -0
  90. package/dist/src/screens/stripe-js.d.ts +3 -0
  91. package/dist/src/screens/stripe-js.js +95 -0
  92. package/dist/src/semantics/account.d.ts +2 -0
  93. package/dist/src/semantics/account.js +8 -0
  94. package/dist/src/semantics/account_links.d.ts +2 -0
  95. package/dist/src/semantics/account_links.js +22 -0
  96. package/dist/src/semantics/accounts.d.ts +4 -0
  97. package/dist/src/semantics/accounts.js +40 -0
  98. package/dist/src/semantics/apps.d.ts +4 -0
  99. package/dist/src/semantics/{apps-secrets.js → apps.js} +14 -26
  100. package/dist/src/semantics/around.d.ts +2 -0
  101. package/dist/src/semantics/around.js +13 -0
  102. package/dist/src/semantics/balance.d.ts +2 -11
  103. package/dist/src/semantics/balance.js +3 -194
  104. package/dist/src/semantics/balance_transactions.d.ts +2 -0
  105. package/dist/src/semantics/balance_transactions.js +15 -0
  106. package/dist/src/semantics/billing_portal.d.ts +4 -0
  107. package/dist/src/semantics/billing_portal.js +36 -0
  108. package/dist/src/semantics/charges.d.ts +3 -28
  109. package/dist/src/semantics/charges.js +5 -202
  110. package/dist/src/semantics/checkout.d.ts +3 -20
  111. package/dist/src/semantics/checkout.js +21 -271
  112. package/dist/src/semantics/clock.d.ts +2 -0
  113. package/dist/src/semantics/clock.js +15 -0
  114. package/dist/src/semantics/coupons.d.ts +2 -6
  115. package/dist/src/semantics/coupons.js +2 -60
  116. package/dist/src/semantics/customers.d.ts +5 -6
  117. package/dist/src/semantics/customers.js +47 -413
  118. package/dist/src/semantics/doors.d.ts +23 -0
  119. package/dist/src/semantics/doors.js +73 -0
  120. package/dist/src/semantics/entitlements.d.ts +0 -2
  121. package/dist/src/semantics/entitlements.js +4 -95
  122. package/dist/src/semantics/events.d.ts +11 -0
  123. package/dist/src/semantics/events.js +31 -0
  124. package/dist/src/semantics/index.d.ts +2 -0
  125. package/dist/src/semantics/index.js +85 -0
  126. package/dist/src/semantics/invoice_payments.d.ts +2 -0
  127. package/dist/src/semantics/invoice_payments.js +12 -0
  128. package/dist/src/semantics/invoices.d.ts +11 -18
  129. package/dist/src/semantics/invoices.js +121 -440
  130. package/dist/src/semantics/issuing.d.ts +6 -13
  131. package/dist/src/semantics/issuing.js +58 -556
  132. package/dist/src/semantics/payment_intents.d.ts +3 -0
  133. package/dist/src/semantics/payment_intents.js +71 -0
  134. package/dist/src/semantics/payment_methods.d.ts +3 -0
  135. package/dist/src/semantics/payment_methods.js +15 -0
  136. package/dist/src/semantics/payouts.d.ts +3 -0
  137. package/dist/src/semantics/payouts.js +28 -0
  138. package/dist/src/semantics/prices.d.ts +4 -0
  139. package/dist/src/semantics/prices.js +37 -0
  140. package/dist/src/semantics/products.d.ts +4 -2
  141. package/dist/src/semantics/products.js +12 -138
  142. package/dist/src/semantics/promotion_codes.d.ts +3 -0
  143. package/dist/src/semantics/promotion_codes.js +35 -0
  144. package/dist/src/semantics/radar.d.ts +2 -2
  145. package/dist/src/semantics/radar.js +5 -74
  146. package/dist/src/semantics/refunds.d.ts +2 -9
  147. package/dist/src/semantics/refunds.js +3 -193
  148. package/dist/src/semantics/shared.d.ts +558 -55
  149. package/dist/src/semantics/shared.js +2070 -114
  150. package/dist/src/semantics/states.d.ts +462 -0
  151. package/dist/src/semantics/states.js +634 -0
  152. package/dist/src/semantics/subscriptions.d.ts +6 -11
  153. package/dist/src/semantics/subscriptions.js +37 -530
  154. package/dist/src/semantics/test_helpers.d.ts +3 -0
  155. package/dist/src/semantics/test_helpers.js +125 -0
  156. package/dist/src/semantics/transfers.d.ts +2 -2
  157. package/dist/src/semantics/transfers.js +6 -101
  158. package/dist/src/semantics/webhook_endpoints.d.ts +2 -0
  159. package/dist/src/semantics/webhook_endpoints.js +26 -0
  160. package/dist/src/server.d.ts +7 -0
  161. package/dist/src/server.js +7 -0
  162. package/generated/pack-facts.json +94 -0
  163. package/journeys/coverage.json +596 -0
  164. package/journeys/customer-life.json +6108 -0
  165. package/journeys/customer-life.outline.md +371 -0
  166. package/journeys/decisions.json +677 -0
  167. package/journeys/demand.json +706 -0
  168. package/journeys/score.json +853 -0
  169. package/journeys/unreachable.json +102 -0
  170. package/journeys/vendor-examples.json +8010 -0
  171. package/package.json +11 -20
  172. package/spec/SOURCE.md +18 -0
  173. package/spec/doc-examples.json +445 -0
  174. package/spec/fixtures3.json.gz +0 -0
  175. package/spec/openapi.json.gz +0 -0
  176. package/spec/patches.json +34 -0
  177. package/spec/sources.json +306 -0
  178. package/src/cli.ts +11 -28
  179. package/src/engine/after-payment.ts +29 -0
  180. package/src/engine/apps-secrets.ts +4 -0
  181. package/src/engine/balance.ts +32 -0
  182. package/src/engine/checkout.ts +39 -0
  183. package/src/engine/common.ts +49 -0
  184. package/src/engine/connect.ts +106 -0
  185. package/src/engine/customers.ts +10 -0
  186. package/src/engine/display.ts +18 -0
  187. package/src/engine/files.ts +28 -0
  188. package/src/engine/invoices.ts +102 -0
  189. package/src/engine/issuing.ts +66 -0
  190. package/src/engine/ledger.ts +48 -0
  191. package/src/engine/oauth.ts +18 -0
  192. package/src/engine/onboarding.ts +53 -0
  193. package/src/{stripe-params.ts → engine/params.ts} +7 -56
  194. package/src/engine/payment-intents.ts +32 -0
  195. package/src/engine/payment-methods.ts +24 -0
  196. package/src/engine/radar.ts +6 -0
  197. package/src/engine/refunds.ts +3 -0
  198. package/src/engine/renewals.ts +72 -0
  199. package/src/{stripe-twin.ts → engine/stripe.ts} +57 -400
  200. package/src/engine/subscriptions.ts +37 -0
  201. package/src/engine/transfers.ts +22 -0
  202. package/src/{stripe-version.ts → engine/version.ts} +19 -10
  203. package/src/engine/webhook-endpoints.ts +14 -0
  204. package/src/engine/webhooks.ts +70 -0
  205. package/src/fetch.ts +27 -0
  206. package/src/generated/surface.gen.json +1 -1
  207. package/src/generated/ui.gen.json +1 -1
  208. package/src/index.ts +10 -116
  209. package/src/manifest.ts +480 -904
  210. package/src/screens/{portal.tsx → billing-portal.tsx} +35 -42
  211. package/src/screens/checkout-js.ts +80 -0
  212. package/src/screens/checkout.tsx +32 -96
  213. package/src/screens/{onboarding.tsx → connect-onboarding.tsx} +39 -85
  214. package/src/screens/dashboard.tsx +178 -0
  215. package/src/screens/shared.tsx +84 -0
  216. package/src/screens/stripe-js.ts +102 -0
  217. package/src/semantics/account.ts +10 -0
  218. package/src/semantics/account_links.ts +20 -0
  219. package/src/semantics/accounts.ts +44 -0
  220. package/src/semantics/{apps-secrets.ts → apps.ts} +17 -33
  221. package/src/semantics/around.ts +17 -0
  222. package/src/semantics/balance.ts +6 -208
  223. package/src/semantics/balance_transactions.ts +18 -0
  224. package/src/semantics/billing_portal.ts +45 -0
  225. package/src/semantics/charges.ts +8 -212
  226. package/src/semantics/checkout.ts +25 -276
  227. package/src/semantics/clock.ts +19 -0
  228. package/src/semantics/coupons.ts +5 -68
  229. package/src/semantics/customers.ts +48 -414
  230. package/src/semantics/doors.ts +79 -0
  231. package/src/semantics/entitlements.ts +3 -94
  232. package/src/semantics/events.ts +37 -0
  233. package/src/semantics/index.ts +89 -0
  234. package/src/semantics/invoice_payments.ts +16 -0
  235. package/src/semantics/invoices.ts +112 -441
  236. package/src/semantics/issuing.ts +57 -573
  237. package/src/semantics/payment_intents.ts +76 -0
  238. package/src/semantics/payment_methods.ts +20 -0
  239. package/src/semantics/payouts.ts +29 -0
  240. package/src/semantics/prices.ts +41 -0
  241. package/src/semantics/products.ts +12 -146
  242. package/src/semantics/promotion_codes.ts +38 -0
  243. package/src/semantics/radar.ts +8 -79
  244. package/src/semantics/refunds.ts +4 -216
  245. package/src/semantics/shared.ts +2089 -135
  246. package/src/semantics/states.ts +662 -0
  247. package/src/semantics/subscriptions.ts +39 -542
  248. package/src/semantics/test_helpers.ts +119 -0
  249. package/src/semantics/transfers.ts +10 -132
  250. package/src/semantics/webhook_endpoints.ts +27 -0
  251. package/src/server.ts +7 -0
  252. package/client/dashboard-api.ts +0 -286
  253. package/client/stripe-mirror.css +0 -277
  254. package/client/stripe-mirror.tsx +0 -1559
  255. package/dist/client/dashboard-api.d.ts +0 -107
  256. package/dist/client/dashboard-api.js +0 -238
  257. package/dist/client/dashboard-api.ts +0 -286
  258. package/dist/client/stripe-mirror.bundle.js +0 -236
  259. package/dist/client/stripe-mirror.css +0 -277
  260. package/dist/client/stripe-mirror.d.ts +0 -134
  261. package/dist/client/stripe-mirror.js +0 -859
  262. package/dist/client/stripe-mirror.tsx +0 -1559
  263. package/dist/src/screens/connect-oauth.d.ts +0 -27
  264. package/dist/src/screens/connect-oauth.js +0 -414
  265. package/dist/src/screens/connect-settings.d.ts +0 -22
  266. package/dist/src/screens/connect-settings.js +0 -103
  267. package/dist/src/screens/consent-skin.d.ts +0 -4
  268. package/dist/src/screens/consent-skin.js +0 -18
  269. package/dist/src/screens/financial-connections.d.ts +0 -5
  270. package/dist/src/screens/financial-connections.js +0 -90
  271. package/dist/src/screens/identity.d.ts +0 -5
  272. package/dist/src/screens/identity.js +0 -86
  273. package/dist/src/screens/onboarding.d.ts +0 -13
  274. package/dist/src/screens/portal.d.ts +0 -5
  275. package/dist/src/screens/public-details.d.ts +0 -5
  276. package/dist/src/screens/public-details.js +0 -90
  277. package/dist/src/semantics/after-payment.d.ts +0 -22
  278. package/dist/src/semantics/after-payment.js +0 -99
  279. package/dist/src/semantics/apps-secrets.d.ts +0 -2
  280. package/dist/src/semantics/billing.d.ts +0 -2
  281. package/dist/src/semantics/billing.js +0 -220
  282. package/dist/src/semantics/connect.d.ts +0 -5
  283. package/dist/src/semantics/connect.js +0 -493
  284. package/dist/src/semantics/credit-notes.d.ts +0 -2
  285. package/dist/src/semantics/credit-notes.js +0 -172
  286. package/dist/src/semantics/disputes.d.ts +0 -2
  287. package/dist/src/semantics/disputes.js +0 -51
  288. package/dist/src/semantics/ephemeral-keys.d.ts +0 -2
  289. package/dist/src/semantics/ephemeral-keys.js +0 -34
  290. package/dist/src/semantics/files.d.ts +0 -2
  291. package/dist/src/semantics/files.js +0 -125
  292. package/dist/src/semantics/ledger.d.ts +0 -59
  293. package/dist/src/semantics/ledger.js +0 -200
  294. package/dist/src/semantics/payment-intents.d.ts +0 -18
  295. package/dist/src/semantics/payment-intents.js +0 -452
  296. package/dist/src/semantics/payment-links.d.ts +0 -2
  297. package/dist/src/semantics/payment-links.js +0 -133
  298. package/dist/src/semantics/payment-methods.d.ts +0 -20
  299. package/dist/src/semantics/payment-methods.js +0 -140
  300. package/dist/src/semantics/plans.d.ts +0 -5
  301. package/dist/src/semantics/plans.js +0 -121
  302. package/dist/src/semantics/platform.d.ts +0 -9
  303. package/dist/src/semantics/platform.js +0 -206
  304. package/dist/src/semantics/renewals.d.ts +0 -47
  305. package/dist/src/semantics/renewals.js +0 -281
  306. package/dist/src/semantics/setup-intents.d.ts +0 -2
  307. package/dist/src/semantics/setup-intents.js +0 -84
  308. package/dist/src/semantics/subscription-schedules.d.ts +0 -2
  309. package/dist/src/semantics/subscription-schedules.js +0 -119
  310. package/dist/src/semantics/tax.d.ts +0 -2
  311. package/dist/src/semantics/tax.js +0 -197
  312. package/dist/src/semantics/terminal.d.ts +0 -5
  313. package/dist/src/semantics/terminal.js +0 -182
  314. package/dist/src/semantics/test-clocks.d.ts +0 -6
  315. package/dist/src/semantics/test-clocks.js +0 -73
  316. package/dist/src/semantics/tokens.d.ts +0 -4
  317. package/dist/src/semantics/tokens.js +0 -44
  318. package/dist/src/semantics/treasury.d.ts +0 -2
  319. package/dist/src/semantics/treasury.js +0 -377
  320. package/dist/src/semantics/webhook-endpoints.d.ts +0 -3
  321. package/dist/src/semantics/webhook-endpoints.js +0 -85
  322. package/dist/src/stripe-budget.d.ts +0 -55
  323. package/dist/src/stripe-budget.js +0 -155
  324. package/dist/src/stripe-capabilities.d.ts +0 -3
  325. package/dist/src/stripe-capabilities.js +0 -5836
  326. package/dist/src/stripe-conformance.d.ts +0 -43
  327. package/dist/src/stripe-conformance.js +0 -105
  328. package/dist/src/stripe-connector.d.ts +0 -161
  329. package/dist/src/stripe-connector.js +0 -414
  330. package/dist/src/stripe-emit.d.ts +0 -2
  331. package/dist/src/stripe-emit.js +0 -145
  332. package/dist/src/stripe-events.d.ts +0 -93
  333. package/dist/src/stripe-events.js +0 -392
  334. package/dist/src/stripe-js.d.ts +0 -4
  335. package/dist/src/stripe-js.js +0 -70
  336. package/dist/src/stripe-mirror-ui.d.ts +0 -15
  337. package/dist/src/stripe-mirror-ui.js +0 -87
  338. package/dist/src/stripe-perform-harness.d.ts +0 -9
  339. package/dist/src/stripe-perform-harness.js +0 -26
  340. package/dist/src/stripe-server.d.ts +0 -33
  341. package/dist/src/stripe-server.js +0 -429
  342. package/dist/src/stripe-shared.d.ts +0 -109
  343. package/dist/src/stripe-shared.js +0 -276
  344. package/dist/src/stripe-ui-conformance.d.ts +0 -5
  345. package/dist/src/stripe-ui-conformance.js +0 -79
  346. package/dist/src/stripe-ui-structure.d.ts +0 -3
  347. package/dist/src/stripe-ui-structure.js +0 -168
  348. package/dist/test-fixtures/stripe-known-deviations.json +0 -110
  349. package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +0 -14
  350. package/dist/test-fixtures/stripe-openapi-operations.json +0 -4717
  351. package/dist/test-fixtures/stripe-schemas.SOURCE.md +0 -35
  352. package/dist/test-fixtures/stripe-schemas.json +0 -3813
  353. package/src/screens/connect-oauth.tsx +0 -400
  354. package/src/screens/connect-settings.tsx +0 -121
  355. package/src/screens/consent-skin.ts +0 -20
  356. package/src/screens/financial-connections.tsx +0 -101
  357. package/src/screens/identity.tsx +0 -96
  358. package/src/screens/public-details.tsx +0 -105
  359. package/src/semantics/after-payment.ts +0 -118
  360. package/src/semantics/billing.ts +0 -216
  361. package/src/semantics/connect.ts +0 -487
  362. package/src/semantics/credit-notes.ts +0 -168
  363. package/src/semantics/disputes.ts +0 -62
  364. package/src/semantics/ephemeral-keys.ts +0 -34
  365. package/src/semantics/files.ts +0 -143
  366. package/src/semantics/ledger.ts +0 -253
  367. package/src/semantics/payment-intents.ts +0 -463
  368. package/src/semantics/payment-links.ts +0 -148
  369. package/src/semantics/payment-methods.ts +0 -145
  370. package/src/semantics/plans.ts +0 -131
  371. package/src/semantics/platform.ts +0 -220
  372. package/src/semantics/renewals.ts +0 -279
  373. package/src/semantics/setup-intents.ts +0 -87
  374. package/src/semantics/subscription-schedules.ts +0 -129
  375. package/src/semantics/tax.ts +0 -220
  376. package/src/semantics/terminal.ts +0 -195
  377. package/src/semantics/test-clocks.ts +0 -77
  378. package/src/semantics/tokens.ts +0 -52
  379. package/src/semantics/treasury.ts +0 -383
  380. package/src/semantics/webhook-endpoints.ts +0 -87
  381. package/src/stripe-budget.ts +0 -181
  382. package/src/stripe-capabilities.ts +0 -5651
  383. package/src/stripe-conformance.ts +0 -124
  384. package/src/stripe-connector.ts +0 -462
  385. package/src/stripe-emit.ts +0 -157
  386. package/src/stripe-events.ts +0 -434
  387. package/src/stripe-js.ts +0 -70
  388. package/src/stripe-mirror-ui.ts +0 -90
  389. package/src/stripe-perform-harness.ts +0 -29
  390. package/src/stripe-server.ts +0 -393
  391. package/src/stripe-shared.ts +0 -297
  392. package/src/stripe-ui-conformance.ts +0 -88
  393. package/src/stripe-ui-structure.ts +0 -182
  394. package/test-fixtures/stripe-known-deviations.json +0 -110
  395. package/test-fixtures/stripe-openapi-operations.json +0 -4717
  396. package/test-fixtures/stripe-schemas.json +0 -3813
  397. /package/dist/src/{screens → engine}/industries.d.ts +0 -0
  398. /package/dist/src/{screens → engine}/industries.js +0 -0
  399. /package/dist/src/{semantics → engine}/test-cards.d.ts +0 -0
  400. /package/dist/src/{semantics → engine}/test-cards.js +0 -0
  401. /package/src/{screens → engine}/industries.ts +0 -0
  402. /package/src/{semantics → engine}/test-cards.ts +0 -0
@@ -1,23 +1,11 @@
1
- // Stripe twin REQUEST HANDLER — the canonical Stripe API surface for the twin and
2
- // the QA stack's authoritative Stripe (it replaced the old hand-made in-process
3
- // mock). Contract: handleStripeTwinRequest({method, path, body, mode}) -> {status,
4
- // body}. The whole point: this is backed by the event/action-log kernel and its
5
- // response SHAPES are conformant to real Stripe (see stripe-conformance), instead
6
- // of being invented — so the SDK/API call shapes the app relies on actually match
7
- // Stripe. HTTP wrapper: stripe-server.ts → createStripeTwinServer.
8
- //
9
- // State lives in the action log (R18): writes are local actions, reads are the
10
- // projection. No real Stripe is ever called.
11
- import { applyTwinWrite } from '@volter/world-core';
12
- import { projectResources } from '@volter/world-core';
13
- import { createStripeTwinFetch } from "./stripe-server.js";
14
- import { emitStripeEvent, eventTypeFor } from "./stripe-events.js";
15
- import { SERVED_VERSION } from "./stripe-version.js";
16
- import { planOf } from "./semantics/plans.js";
17
- import { platformAccountDefault } from "./semantics/connect.js";
18
- import { OAUTH_CONNECTIONS } from "./stripe-shared.js";
19
- const SERVICE = 'stripe';
20
- // The Stripe API version a request that pins none is served in: the vendored spec's (stripe-version.ts renders
1
+ import { digest } from '@volter/world-core';
2
+ import { ownFields } from '@volter/world-core';
3
+ import { SERVED_VERSION } from "./version.js";
4
+ /** Stripe's error body: an invalid_request_error with the message, and the code when there is one. */
5
+ export function err(message, status = 404, code) {
6
+ return { status, body: { error: { type: 'invalid_request_error', message, ...(code ? { code } : {}) } } };
7
+ }
8
+ // The Stripe API version a request that pins none is served in: the vendored spec's (./version.ts renders
21
9
  // every answer in it). A request may override it via the `Stripe-Version` header (apiVersion);
22
10
  // the value is echoed onto every event the request produces, exactly like real Stripe,
23
11
  // whose stored Event.api_version reflects the version in force when the event was created.
@@ -28,61 +16,6 @@ const API_VERSION_RE = /^\d{4}-\d{2}-\d{2}(\.[a-z_]+)?$/;
28
16
  export function isValidApiVersion(v) {
29
17
  return API_VERSION_RE.test(v);
30
18
  }
31
- // resource type → id prefix (Stripe's conventions).
32
- const PREFIX = {
33
- charge: 'ch', customer: 'cus', payment_intent: 'pi', setup_intent: 'seti', payment_method: 'pm',
34
- subscription: 'sub', price: 'price', product: 'prod', invoice: 'in', invoiceitem: 'ii',
35
- refund: 're', verification_session: 'vs', ephemeral_key: 'ephkey', file_link: 'link',
36
- dispute: 'dp', payout: 'po', balance_transaction: 'txn', event: 'evt',
37
- checkout_session: 'cs', billing_portal_session: 'bps', billing_portal_configuration: 'bpc',
38
- account: 'acct', transfer: 'tr',
39
- tax_rate: 'txr', tax_calculation: 'taxcalc', tax_registration: 'taxreg',
40
- credit_note: 'cn', tax_id: 'txi', customer_balance_transaction: 'cbtxn',
41
- coupon: 'coupon', promotion_code: 'promo', payment_link: 'plink', quote: 'qt',
42
- webhook_endpoint: 'we',
43
- subscription_schedule: 'sub_sched', billing_meter: 'mtr', billing_meter_event: 'mtr_evt',
44
- usage_record: 'mbur', test_clock: 'clock',
45
- token: 'tok', mandate: 'mandate', application_fee: 'fee', fee_refund: 'fr',
46
- transfer_reversal: 'trr', account_link: 'acctlink', person: 'person',
47
- external_account: 'ba', file: 'file',
48
- subscription_item: 'si', tax_transaction: 'tax', radar_review: 'prv',
49
- radar_value_list: 'rsl', radar_value_list_item: 'rsli', radar_rule: 'rule',
50
- // Issuing family (vendor-faithful Stripe id prefixes).
51
- issuing_cardholder: 'ich', issuing_card: 'ic', issuing_authorization: 'iauth',
52
- issuing_transaction: 'ipi', issuing_dispute: 'idp',
53
- // Terminal family.
54
- terminal_location: 'tml', terminal_reader: 'tmr', terminal_configuration: 'tmc',
55
- // Top-ups, identity reports, reporting runs.
56
- topup: 'tu', verification_report: 'vr', report_run: 'frr',
57
- // Customer cash balance ledger + legacy sources.
58
- cash_balance_transaction: 'ccsbtxn', source: 'src',
59
- // Billing: prepaid credit grants + usage alerts.
60
- credit_grant: 'credgr', billing_alert: 'alert',
61
- // Entitlements: account-level features + per-customer active entitlements.
62
- entitlements_feature: 'feat', active_entitlement: 'ent',
63
- // Connect: embedded account sessions + apps secret store.
64
- account_session: 'accts', apps_secret: 'apmc',
65
- // Issuing: network tokens + card personalization designs.
66
- issuing_token: 'iss_tok', personalization_design: 'pd',
67
- // Treasury: financial accounts + money-movement flows + ledger.
68
- financial_account: 'fa',
69
- outbound_payment: 'obp', outbound_transfer: 'obt', inbound_transfer: 'ibt',
70
- received_credit: 'rc', received_debit: 'rd',
71
- treasury_transaction: 'trxn', treasury_transaction_entry: 'trxne',
72
- // Climate (carbon removal): orders. (products/suppliers are a fixed read-only catalog.)
73
- climate_order: 'climorder',
74
- // Financial Connections: sessions + linked accounts + account transactions.
75
- fc_session: 'fcsess', fc_account: 'fca', fc_transaction: 'fctxn',
76
- // Forwarding (PAN forwarding) requests + Crypto onramp sessions.
77
- forwarding_request: 'fwdr', onramp_session: 'cos',
78
- // Entitlements: a product↔feature grant.
79
- product_feature: 'prodft',
80
- // Tax settings singleton.
81
- tax_settings: 'taxset',
82
- };
83
- function err(message, status = 404, code) {
84
- return { status, body: { error: { type: status === 404 ? 'invalid_request_error' : 'invalid_request_error', message, ...(code ? { code } : {}) } } };
85
- }
86
19
  // Fidelity: real Stripe rejects a charge/payment_intent missing or with a non-positive
87
20
  // amount, or missing currency, before any state change.
88
21
  export function validateMoney(params) {
@@ -202,8 +135,6 @@ export function threeDsNextAction() {
202
135
  // a `descriptor_code` (the test value `SM11AA`). We model that exact pair so the verify
203
136
  // can FAIL with the wrong amounts. The well-known token that triggers this flow:
204
137
  const MICRODEPOSIT_TOKENS = new Set(['pm_usBankAccount_requiresVerification', 'pm_us_bank_account']);
205
- const MICRODEPOSIT_AMOUNTS = [32, 45];
206
- const MICRODEPOSIT_DESCRIPTOR = 'SM11AA';
207
138
  export function requiresMicrodeposits(...paramSets) {
208
139
  for (const params of paramSets) {
209
140
  const ref = resolveCardRef(params);
@@ -221,31 +152,6 @@ export function microdepositsNextAction() {
221
152
  },
222
153
  };
223
154
  }
224
- /** The two deposits' amounts as sent: the test values (docs.stripe.com/payments/ach-direct-debit/accept-a-payment). */
225
- function amountsMatch(params) {
226
- const got = params.amounts.map((a) => Math.trunc(Number(a) || 0));
227
- return got.length === 2 && got[0] === MICRODEPOSIT_AMOUNTS[0] && got[1] === MICRODEPOSIT_AMOUNTS[1];
228
- }
229
- /** The deposit's descriptor code as sent: the test value. */
230
- function descriptorMatches(params) {
231
- return String(params.descriptor_code) === MICRODEPOSIT_DESCRIPTOR;
232
- }
233
- /** Stripe's refusal of deposits that do not match what was sent. */
234
- function depositMismatch(byAmounts) {
235
- return byAmounts
236
- ? err('The amounts provided do not match the amounts that were sent to the bank account.', 400, 'payment_intent_microdeposit_verification_amounts_mismatch')
237
- : err('The descriptor code provided does not match the one that was sent to the bank account.', 400, 'payment_intent_microdeposit_verification_descriptor_code_mismatch');
238
- }
239
- // Validate the submitted micro-deposit verification params against the modeled test values.
240
- // Returns a vendor 400 on missing/both/wrong inputs, or null when the verification matches.
241
- export function verifyMicrodeposits(params, _existing) {
242
- const hasAmounts = Array.isArray(params.amounts);
243
- const hasDescriptor = typeof params.descriptor_code === 'string' && params.descriptor_code !== '';
244
- if (hasAmounts === hasDescriptor)
245
- return err('You must pass exactly one of `amounts` and `descriptor_code`.', 400, 'parameter_missing');
246
- const matches = hasAmounts ? amountsMatch(params) : descriptorMatches(params);
247
- return matches ? null : depositMismatch(hasAmounts);
248
- }
249
155
  // Build the real Stripe card-error envelope (HTTP 402). `param` is 'card' for card
250
156
  // errors; charge/payment_intent ids are attached when known so SDK error objects carry
251
157
  // them (Stripe does this for confirm/charge failures).
@@ -270,19 +176,6 @@ export function cardError(outcome, attach = {}) {
270
176
  export function nowUnix(occurredAt) {
271
177
  return Math.floor((occurredAt ? Date.parse(occurredAt) : 0) / 1000);
272
178
  }
273
- function rows(type, root) {
274
- return projectResources(SERVICE, root).filter((r) => r.type === type);
275
- }
276
- function nextId(type, root) {
277
- const prefix = PREFIX[type] ?? type;
278
- let max = 0;
279
- for (const r of rows(type, root)) {
280
- const m = new RegExp(`^${prefix}_twin_(\\d+)$`).exec(r.id);
281
- if (m)
282
- max = Math.max(max, Number(m[1]));
283
- }
284
- return `${prefix}_twin_${max + 1}`;
285
- }
286
179
  // Real Stripe embeds the resource's OWN id inside its client_secret (format
287
180
  // `<id>_secret_<random>`) — Stripe.js's confirmPayment/confirmSetup parse the id back
288
181
  // out of the client_secret (splitting on `_secret`) to build the same-origin confirm
@@ -290,25 +183,14 @@ function nextId(type, root) {
290
183
  // A secret that doesn't carry the real id breaks that round trip: a browser Stripe.js
291
184
  // call would try to confirm a resource whose id it invented from the string
292
185
  // (`'pi_twin_secret'` → `'pi_twin'`, which was never actually created). Every PI/SI a
293
- // caller might confirm CLIENT-SIDE (real Stripe.js — see the QA proxy's browserRouting
294
- // in packages/twin/stripe/src/index.ts) must mint a secret this way.
295
- export function mintClientSecret(id) {
296
- return `${id}_secret_twin`;
297
- }
298
- // Stripe resource view: inject `object` + `id`, drop kernel meta.
299
- //
300
- // The kernel reserves the field name `type` as its resource-type discriminator, so a
301
- // stored vendor field literally named `type` is dropped on projection (see actions.ts
302
- // META). Several Stripe objects (payout.type, balance_transaction.type, event.type,
303
- // dispute uses `reason` not `type`) DO have a vendor `type`, so the create path stashes
304
- // it under the reserved key `_stripe_type` (a `_`-prefixed twin-internal field that the
305
- // conformance gate exempts) and we restore it to `type` here. This keeps the vendor
306
- // field faithful without touching the kernel discriminator. (price.type stays a declared
307
- // deviation — its create path predates this and one_time/recurring is derivable.)
308
- // A few Stripe objects have a DOTTED `object` value that differs from the twin's
309
- // (identifier-safe) resource type — e.g. type 'checkout_session' emits object
310
- // 'checkout.session'. Map them on emit so the `object` field is vendor-faithful.
311
- // (stripe-conformance.ts emitted() mirrors this map.)
186
+ // caller might confirm CLIENT-SIDE (real Stripe.js) must mint a secret this way.
187
+ /** An intent's client secret: its id, `_secret_`, and a secret the World holds for it (ctx.secret, passed in), so no one
188
+ * derives it from the intent's id; Stripe.js confirms an intent with it and the publishable key. */
189
+ export function mintClientSecret(id, secret) {
190
+ return `${id}_secret_${secret.replace(/[^A-Za-z0-9]/g, '').slice(0, 24)}`;
191
+ }
192
+ // A few Stripe objects have a DOTTED `object` value that differs from the twin's (identifier-safe) resource type: type
193
+ // 'checkout_session' is object 'checkout.session'.
312
194
  export const OBJECT_NAME = {
313
195
  checkout_session: 'checkout.session',
314
196
  billing_portal_session: 'billing_portal.session',
@@ -369,18 +251,11 @@ export const OBJECT_NAME = {
369
251
  // Forwarding + Crypto onramp.
370
252
  forwarding_request: 'forwarding.request', onramp_session: 'crypto.onramp_session',
371
253
  };
372
- // Exported for the pack's own modules that must render the SAME vendor view of a stored
373
- // resource (stripe-emit.ts synthesizes `data.object` for emitted events from it) — one
374
- // projection, no drift. Not a public API for consumers.
254
+ /** A stored row as Stripe answers it: its `object`, its own fields (the vendor's `type` among them, which the tree's
255
+ * shadows: world-core ownFields) without the twin's `_` bookkeeping, and its id. */
375
256
  export function view(type, r) {
376
- // `_subscription_data` is twin-internal (a Checkout Session's create-only
377
- // subscription_data, held for the completion transition to copy onto the created
378
- // subscription) — real Stripe never returns it on the Session, so strip it here.
379
- const { type: _t, updatedAt: _u, _stripe_type, _subscription_data, ...rest } = r;
380
- const out = { object: OBJECT_NAME[type] ?? type, ...rest, id: r.id };
381
- if (_stripe_type !== undefined)
382
- out.type = _stripe_type;
383
- return out;
257
+ const own = Object.fromEntries(Object.entries(ownFields(r)).filter(([k]) => !k.startsWith('_') && k !== 'updatedAt'));
258
+ return { object: OBJECT_NAME[type] ?? type, ...own, id: r.id };
384
259
  }
385
260
  // Real Stripe list pagination. Honors limit (default 10, max 100, min 1),
386
261
  // starting_after (cursor: exclude up to & including that id), ending_before
@@ -414,90 +289,6 @@ export function paginate(items, params) {
414
289
  const page = items.slice(from, from + limit);
415
290
  return { page, hasMore: from + limit < items.length };
416
291
  }
417
- async function writeResource(type, id, fields, op, root, occurredAt, apiVersion) {
418
- const { resource } = await applyTwinWrite(SERVICE, { operation: op, subjectType: type, subjectId: id, fields, ...(occurredAt ? { occurredAt } : {}), actor: { kind: 'agent' } }, root);
419
- const out = view(type, resource);
420
- await afterStripeWrite(type, op, out, root, occurredAt, apiVersion);
421
- return out;
422
- }
423
- /** What follows every stored Stripe write: the webhook for the state change and the stored Event
424
- * the Events API lists. The derived pack's write hook (manifest.ts) is this same function. */
425
- export async function afterStripeWrite(type, op, out, root, occurredAt, apiVersion, stripeAccount) {
426
- // R17: fire the Stripe event/webhook for this state change (no-op if none registered). An event is a connected
427
- // account's when its resource lives in one (a request made as it, the Stripe-Account header) or is a connected
428
- // account itself (its account.updated): docs.stripe.com/connect/webhooks, "Connected accounts" scope.
429
- // A write with no header is still a connected account's when the row it wrote is kept on that account's books (a
430
- // payout or ledger entry's `_account`: an automatic payout time makes for it, semantics/balance.ts).
431
- const account = stripeAccount ?? (type === 'account' && typeof out.id === 'string' && out.id !== PLATFORM_ACCOUNT_ID ? out.id : undefined) ?? ownerOf(type, out.id, root);
432
- // a plan is stored as the recurring price it is; its own events carry the plan object (semantics/plans.ts)
433
- const object = type === 'price' && op.startsWith('plan.') ? planOf(out) : out;
434
- // events.full_types: the Stripe `event` envelope for this state change is also stored, so it appears in GET
435
- // /v1/events (independent of webhook registration), exactly like real Stripe's stored Events API. Skip when the op
436
- // has no mapped event type, and never for event/_idempotency writes themselves (no recursion / no meta-events). The
437
- // event's id is minted once and is the delivered webhook's id and the stored event's alike: one event, one id, which
438
- // a consumer deduplicates by and retrieves by.
439
- const eventType = type !== 'event' && type !== IDEMPOTENCY_TYPE ? eventTypeFor(op) : null;
440
- const id = eventType ? nextStripeEventId(root) : undefined;
441
- // stored before it is delivered: a consumer that looks its webhook's event up finds it
442
- if (eventType)
443
- await persistStripeEvent(eventType, object, root, occurredAt, apiVersion, account, id);
444
- // a connected account whose OAuth connection was revoked "can't be accessed by your platform" (docs.stripe.com/connect/
445
- // oauth-reference): its events (a payout time makes, a renewal) are its own and no longer reach the platform's Connect
446
- // endpoints, except the account.application.deauthorized that says so (screens/connect-oauth.tsx)
447
- if (account && eventType !== 'account.application.deauthorized' && rows(OAUTH_CONNECTIONS, root).some((c) => c.id === account && c.revoked === true))
448
- return;
449
- await emitStripeEvent(op, object, { occurredAt: occurredAt ?? '1970-01-01T00:00:00.000Z', endpoints: webhookTargets(root), ...(account ? { account } : {}), ...(id ? { id } : {}) });
450
- }
451
- /** Event numbers taken in this process, per root, before their events are stored: two writes whose events overlap
452
- * (a webhook handler calling back in, concurrent requests) never take the same number. */
453
- const takenEventNumbers = new Map();
454
- /** The id the World's next event takes: its place in the World's events, so it is deterministic and never repeats
455
- * within a World (a Balance has no id of its own, so nothing about the object can tell two events apart). The number
456
- * is taken synchronously — the count of stored events, or the last number taken, whichever is higher. */
457
- export function nextStripeEventId(root) {
458
- const key = root ?? '';
459
- const n = Math.max(rows('event', root).length, takenEventNumbers.get(key) ?? 0) + 1;
460
- takenEventNumbers.set(key, n);
461
- return `evt_twin_${n}`;
462
- }
463
- /** The connected account whose books a stored row is kept on (its `_account`), if any. */
464
- function ownerOf(type, id, root) {
465
- if (typeof id !== 'string')
466
- return undefined;
467
- const owner = rows(type, root).find((r) => r.id === id)?._account;
468
- return typeof owner === 'string' ? owner : undefined;
469
- }
470
- // Persist a Stripe `event` resource (object 'event') for a state change into the action
471
- // log so the Events API (GET /v1/events) lists/retrieves it. Deterministic id derived
472
- // from the per-root event count; data.object carries the resource snapshot. Written via
473
- // applyTwinWrite directly (NOT writeResource) to avoid re-emitting an event for the event.
474
- export async function persistStripeEvent(eventType, resource, root, occurredAt, apiVersion, account, id) {
475
- const eventId = id ?? nextStripeEventId(root);
476
- const fields = {
477
- object: 'event', _stripe_type: eventType, created: nowUnix(occurredAt), livemode: false,
478
- pending_webhooks: 0, api_version: apiVersion && isValidApiVersion(apiVersion) ? apiVersion : TWIN_API_VERSION,
479
- request: { id: null, idempotency_key: null },
480
- data: { object: resource },
481
- // a connected account's event names it, and is listed to requests made as it (semantics/webhook-endpoints.ts)
482
- ...(account ? { account } : {}),
483
- };
484
- await applyTwinWrite(SERVICE, { operation: 'event.record', subjectType: 'event', subjectId: eventId, fields, ...(occurredAt ? { occurredAt } : {}), actor: { kind: 'system' } }, root);
485
- }
486
- function getOne(type, id, root) {
487
- const r = rows(type, root).find((x) => x.id === id);
488
- return r ? view(type, r) : undefined;
489
- }
490
- // Stripe list order: newest first by `created` (or `date` for invoiceitem), ties
491
- // broken by id descending so the order is stable for cursor pagination.
492
- /** The World's live webhook endpoints, from its tree: delivery survives a restart and belongs to
493
- * this World alone. A disabled or deleted endpoint receives nothing, like the vendor. */
494
- function webhookTargets(root) {
495
- // `connect` is a create parameter the endpoint object does not answer, so it is read off the stored row
496
- return rows('webhook_endpoint', root)
497
- .map((r) => ({ w: view('webhook_endpoint', r), connect: asBool(r.connect) }))
498
- .filter(({ w }) => !w.deleted && w.status !== 'disabled' && typeof w.url === 'string')
499
- .map(({ w, connect }) => ({ url: String(w.url), ...(typeof w.secret === 'string' ? { secret: w.secret } : {}), ...(Array.isArray(w.enabled_events) ? { enabledEvents: w.enabled_events.map(String) } : {}), connect }));
500
- }
501
292
  // Stripe's `active=true|false` filters arrive as strings/booleans; normalize.
502
293
  export function asBool(v) {
503
294
  return v === true || v === 'true' || v === 1 || v === '1';
@@ -511,7 +302,7 @@ function parseSearchQuery(query) {
511
302
  const disjunction = / OR /i.test(trimmed) && !/ AND /i.test(trimmed);
512
303
  const parts = trimmed.split(disjunction ? / OR /i : / AND /i);
513
304
  const clauses = [];
514
- const re = /^\s*([A-Za-z0-9_]+(?:\[[^\]]+\])?)\s*(>=|<=|>|<|:)\s*(.+?)\s*$/;
305
+ const re = /^\s*([A-Za-z0-9_]+(?:\[[^\]]+\])?)\s*(>=|<=|>|<|:|~)\s*(.+?)\s*$/;
515
306
  for (const part of parts) {
516
307
  const m = re.exec(part);
517
308
  if (!m)
@@ -519,6 +310,8 @@ function parseSearchQuery(query) {
519
310
  let value = m[3].trim();
520
311
  if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))
521
312
  value = value.slice(1, -1);
313
+ if (m[2] === '~' && (!['email', 'name'].includes(m[1]) || value.length < 3))
314
+ return null;
522
315
  clauses.push({ field: m[1], op: m[2], value });
523
316
  }
524
317
  return { clauses, disjunction };
@@ -536,15 +329,13 @@ function searchFieldValue(item, field) {
536
329
  }
537
330
  /** A search's numeric comparison other than `>=` (docs.stripe.com/search#search-query-language). */
538
331
  function otherComparison(op, a, n) {
539
- switch (op) {
540
- case '>': return a > n;
541
- case '<': return a < n;
542
- case '<=': return a <= n;
543
- }
544
- return false;
332
+ return op === '>' ? a > n : op === '<' ? a < n : a <= n;
545
333
  }
546
334
  function evalSearchClause(item, c) {
547
335
  const actual = searchFieldValue(item, c.field);
336
+ // source: https://docs.stripe.com/search "Substring match operator (substrings must be a minimum of 3 characters)"
337
+ if (c.op === '~')
338
+ return actual != null && String(actual).toLowerCase().includes(c.value.toLowerCase());
548
339
  if (c.op === ':') {
549
340
  if (c.value === 'null')
550
341
  return actual === null || actual === undefined;
@@ -552,8 +343,8 @@ function evalSearchClause(item, c) {
552
343
  if (typeof actual === 'number' && /^-?\d+(\.\d+)?$/.test(c.value))
553
344
  return actual === Number(c.value);
554
345
  if (typeof actual === 'boolean')
555
- return String(actual) === c.value;
556
- return String(actual) === c.value;
346
+ return String(actual).toLowerCase() === c.value.toLowerCase();
347
+ return String(actual).toLowerCase() === c.value.toLowerCase();
557
348
  }
558
349
  const n = Number(c.value);
559
350
  const a = Number(actual);
@@ -611,24 +402,6 @@ export function buildDiscount(coupon, customer, at, on = {}) {
611
402
  invoice: null, invoice_item: null, promotion_code: null, checkout_session: null,
612
403
  };
613
404
  }
614
- // The (price id, quantity) pairs a subscription bills, normalized across the shapes the
615
- // twin stores items in (the create form array items:[{price,quantity}], or the canonical
616
- // items:{data:[{price}]} list). Used to compute upcoming-invoice lines.
617
- export function subscriptionItemPairs(sub) {
618
- const items = sub.items;
619
- const data = Array.isArray(items?.data) ? items.data : Array.isArray(sub.items) ? sub.items : [];
620
- const out = [];
621
- for (const it of data) {
622
- const p = it?.price;
623
- const id = typeof p === 'string' ? p : (p && typeof p === 'object' ? String(p.id ?? '') : '');
624
- if (id)
625
- out.push({ price: id, quantity: Math.max(1, Math.trunc(Number(it.quantity) || 1)) });
626
- }
627
- // a bare `price` field (the connector's flattened shape) counts as one item.
628
- if (out.length === 0 && typeof sub.price === 'string' && sub.price)
629
- out.push({ price: sub.price, quantity: 1 });
630
- return out;
631
- }
632
405
  // The raw item entries a subscription/checkout-derived-subscription create sent,
633
406
  // normalized to an ordered array. The form reader already turns the bracket form
634
407
  // items[0][price]=… into an array of objects (see lineItemEntries() above for the
@@ -686,18 +459,12 @@ export function addBillingInterval(atUnix, interval, count) {
686
459
  // response includes the actual items, mirroring what was requested.
687
460
  /** What a subscription item carries of its price: the Price itself and its plan view (a subscription item answers both,
688
461
  * docs.stripe.com/api/subscription_items/object), by the price's id or an already-resolved Price. */
462
+ // source: https://docs.stripe.com/api/plans/create "It replaces the Plans API and is backwards compatible to simplify your migration."
463
+ // source: spec:/components/schemas/plan "amount_decimal"
689
464
  export function subscriptionItemPrice(p, find) {
690
465
  const priceId = typeof p === 'string' ? p : (p && typeof p === 'object' ? String(p.id ?? '') : '');
691
466
  const price = priceId ? find('price', priceId) : undefined;
692
- const recurring = price?.recurring;
693
- const plan = price ? {
694
- id: price.id, object: 'plan', active: price.active ?? true, amount: price.unit_amount ?? null,
695
- amount_decimal: price.unit_amount_decimal ?? null, billing_scheme: price.billing_scheme ?? 'per_unit',
696
- currency: price.currency ?? 'usd', interval: recurring?.interval ?? null,
697
- interval_count: recurring?.interval_count ?? 1, livemode: false,
698
- metadata: price.metadata ?? {}, nickname: price.nickname ?? null,
699
- product: price.product ?? null, tiers_mode: price.tiers_mode ?? null, usage_type: 'licensed',
700
- } : null;
467
+ const plan = price ? planView(price) : null;
701
468
  return { price: price ?? (priceId || null), plan };
702
469
  }
703
470
  export function buildSubscriptionItemsList(entries, subId, at, find) {
@@ -748,40 +515,6 @@ export function resolveTrial(params, at) {
748
515
  }
749
516
  return undefined;
750
517
  }
751
- // Create a resource from form params, stamping object + id + created + the given
752
- // defaults. Only real Stripe field names are emitted (conformance-checked).
753
- // A vendor `type` field (a price's one_time or recurring, a payout's bank_account) always collides with the kernel's
754
- // row type, so it is stashed under `_stripe_type` and restored by view().
755
- export function stashVendorType(_type, fields) {
756
- if (!('type' in fields))
757
- return fields;
758
- const { type: vt, ...rest } = fields;
759
- return { ...rest, _stripe_type: vt };
760
- }
761
- async function create(type, params, defaults, req, opts = {}) {
762
- // honor a caller-provided id (seeding uses the catalog's real prod_*/price_* ids);
763
- // otherwise assign one. The id is identity, not a stored field.
764
- const { id: providedId, ...rest } = params;
765
- const id = typeof providedId === 'string' && providedId ? providedId : nextId(type, req.root);
766
- // Most Stripe objects stamp `created`; a few (invoiceitem) use a differently-named
767
- // timestamp instead (`date`) and have NO `created` field — honor that, don't fabricate.
768
- const fields = stashVendorType(type, { object: OBJECT_NAME[type] ?? type, [opts.timeField ?? 'created']: nowUnix(req.occurredAt), ...defaults, ...rest });
769
- return { status: 200, body: await writeResource(type, id, fields, `${type}.create`, req.root, req.occurredAt, req.apiVersion) };
770
- }
771
- // ── ISSUING helpers: spending controls, real-time-auth shapes ─────────────────────────
772
- // Authorization.card is the FULL Card object in every vendor egress (API responses and
773
- // webhook payloads) — it is not expandable (stripe@22.3.0 Issuing.Authorization.card:
774
- // Stripe.Issuing.Card). The twin stores the id internally and embeds at the boundary;
775
- // cardholder stays an id string (expandable, unexpanded default). Caught live: the
776
- // issuing-bridge reads event.data.object.card.id and refused every twin presentment.
777
- export function embedIssuingAuthorizationCard(body, root) {
778
- if (typeof body.card === 'string') {
779
- const cardRow = getOne('issuing_card', body.card, root);
780
- if (cardRow !== undefined)
781
- body.card = cardRow;
782
- }
783
- return body;
784
- }
785
518
  // The closed interval set for spending_controls[spending_limits][][interval]
786
519
  // (stripe@22.3.0 Issuing/Cards.d.ts SpendingLimit.Interval — a documented closed enum).
787
520
  const SPENDING_LIMIT_INTERVALS = new Set(['all_time', 'daily', 'monthly', 'per_authorization', 'weekly', 'yearly']);
@@ -809,12 +542,12 @@ function controlsRefused(what, i = 0) {
809
542
  // Vendor rules enforced: interval must be in the closed enum; a spending limit amount is a
810
543
  // positive integer; allowed_categories "Cannot be set with blocked_categories" (and the
811
544
  // merchant-country pair likewise) — both restrictions verbatim from the SDK's field docs.
812
- export function normalizeSpendingControls(raw, currency) {
545
+ export function normalizeSpendingControls(raw, currency, previous) {
813
546
  if (raw === undefined)
814
547
  return {};
815
548
  if (!raw || typeof raw !== 'object' || Array.isArray(raw))
816
549
  return controlsRefused('dictionary');
817
- const sc = raw;
550
+ const sc = { ...previous, ...raw };
818
551
  const strArr = (v) => (Array.isArray(v) ? v.map(String) : undefined);
819
552
  const allowedCats = strArr(sc.allowed_categories);
820
553
  const blockedCats = strArr(sc.blocked_categories);
@@ -841,6 +574,9 @@ export function normalizeSpendingControls(raw, currency) {
841
574
  const arrOrNull = (v) => (v === undefined ? undefined : v.length ? v : null);
842
575
  const controls = {
843
576
  ...emptySpendingControls(),
577
+ // source: spec:/paths/~1v1~1issuing~1cards~1{card}/post/requestBody/content/application~1x-www-form-urlencoded/schema/properties/spending_controls "Rules that control spending for this card."
578
+ // Keep every documented control, including allowed_card_presences and blocked_card_presences.
579
+ ...sc,
844
580
  spending_limits: limits,
845
581
  ...(limits.length ? { spending_limits_currency: typeof sc.spending_limits_currency === 'string' ? sc.spending_limits_currency : currency } : {}),
846
582
  };
@@ -948,6 +684,9 @@ function cardSubObject(params) {
948
684
  brand: 'visa', last4: number ? number.slice(-4) : '4242',
949
685
  exp_month: Number(c.exp_month) || 12, exp_year: Number(c.exp_year) || 2034,
950
686
  funding: 'credit', country: 'US',
687
+ // source: spec:/components/schemas/payment_method_card/properties/fingerprint/description "Uniquely identifies this particular card number."
688
+ // The synthetic fingerprint is stable for a number; Stripe's opaque fingerprint algorithm is not published.
689
+ fingerprint: digest('sha256', number || '4242424242424242').slice(0, 16),
951
690
  checks: { address_line1_check: null, address_postal_code_check: null, cvc_check: 'pass' },
952
691
  networks: { available: ['visa'], preferred: null },
953
692
  three_d_secure_usage: { supported: true }, wallet: null,
@@ -1121,18 +860,6 @@ export function lineItemEntries(params) {
1121
860
  const li = params.line_items;
1122
861
  return Array.isArray(li) ? li.filter((x) => x && typeof x === 'object') : [];
1123
862
  }
1124
- // ---- IDEMPOTENCY KEYS (vendor-faithful) ----
1125
- // Real Stripe honors the `Idempotency-Key` header on POST: the first request with a
1126
- // key executes and the (status + body) response is stored against the key; a replay
1127
- // with the SAME key returns the SAME stored response WITHOUT re-applying the write.
1128
- // We persist the stored response as an `_idempotency` resource in the action log
1129
- // (per-root, so a fork has its own keyspace). `_idempotency` is a twin-internal type
1130
- // (never served as a Stripe object), so it does not affect spec conformance.
1131
- //
1132
- // Note: real Stripe only persists idempotency for responses it considers "completed"
1133
- // (2xx and most 4xx, including card 402s) and scopes the key to the account; here we
1134
- // store every executed POST response (success and error alike), keyed per-root.
1135
- const IDEMPOTENCY_TYPE = '_idempotency';
1136
863
  // ── THE MONEY MODEL: a charge and the refunds taken out of it ──────────────────────────
1137
864
  //
1138
865
  // The rule this section exists to hold: A FACT RECORDED ON ONE MONEY OBJECT IS VISIBLE FROM
@@ -1154,79 +881,18 @@ export function chargeDefaults(chargeId, amount, captured, occurredAt) {
1154
881
  billing_details: { address: null, email: null, name: null, phone: null }, livemode: false,
1155
882
  };
1156
883
  }
1157
- /**
1158
- * Public entry: honors the Idempotency-Key (replay → stored response, no re-write)
1159
- * then delegates to the router. Only POSTs are idempotent (matches Stripe; GET/DELETE
1160
- * pass straight through). A read-only twin never stores (it cannot write).
1161
- */
1162
- /** Stripe's API as one call: the request goes through the pack's own dispatch (the derived dispatch over
1163
- * its semantics, its derived core and the hand-written routes below), so a caller that holds no
1164
- * HTTP server reaches exactly what an SDK does. `occurredAt` pins the World instant. */
1165
- export async function handleStripeTwinRequest(req) {
1166
- const twin = createStripeTwinFetch({ ...(req.root !== undefined ? { root: req.root } : {}), readOnly: req.readOnly ?? false, ...(req.occurredAt ? { clock: () => req.occurredAt } : {}) });
1167
- const method = req.method.toUpperCase();
1168
- const headers = { 'content-type': 'application/x-www-form-urlencoded' };
1169
- if (req.idempotencyKey)
1170
- headers['idempotency-key'] = req.idempotencyKey;
1171
- if (req.apiVersion !== undefined)
1172
- headers['stripe-version'] = req.apiVersion;
1173
- if (req.stripeAccount)
1174
- headers['stripe-account'] = req.stripeAccount;
1175
- // a GET carries its parameters in the query; a caller that handed them as a body keeps them
1176
- let path = req.path.startsWith('/') ? req.path : `/${req.path}`;
1177
- if ((method === 'GET' || method === 'HEAD') && req.body)
1178
- path += (path.includes('?') ? '&' : '?') + req.body;
1179
- const response = await twin(new Request(`http://stripe.test${path}`, { method, headers, ...(method !== 'GET' && method !== 'HEAD' && req.body ? { body: req.body } : {}) }));
1180
- const text = await response.text();
1181
- return { status: response.status, body: text ? JSON.parse(text) : null };
1182
- }
1183
- /**
1184
- * The twin's doors: what stands in for an act Stripe's API does not have, each under `/_twin/`. The
1185
- * platform's own account settings (its payout schedule) change on Stripe's Dashboard, which has no API:
1186
- * POST /_twin/account stands in for that page. Answers nothing for any other path.
1187
- */
1188
- export async function handleStripeDoor(req, params) {
1189
- const method = req.method.toUpperCase();
1190
- const path = (req.path.split('?')[0] ?? '/').replace(/\/+$/, '') || '/';
1191
- const seg = path.replace(/^\/+/, '').split('/');
1192
- if (seg[0] !== '_twin')
1193
- return undefined;
1194
- if (req.readOnly)
1195
- return err('twin is read-only; omit readOnly to accept writes', 405);
1196
- if (path === '/_twin/account' && method === 'POST') {
1197
- const ex = getOne('account', PLATFORM_ACCOUNT_ID, req.root);
1198
- const { type: _t, settings: rawSettings, business_profile: rawProfile, ...rest } = params;
1199
- // the platform's account is Stripe's default until the page first changes it (semantics/connect.ts, the same
1200
- // default GET /v1/account answers and the Public details page stores)
1201
- const fields = {
1202
- ...(ex ? {} : (({ id: _id, ...base }) => base)(platformAccountDefault())),
1203
- ...rest,
1204
- settings: accountSettings(rawSettings, ex?.settings),
1205
- // a hash is merged into what the account holds, as an update merges it (the name the Public details page saved stays)
1206
- ...(rawProfile && typeof rawProfile === 'object' ? { business_profile: { ...(ex?.business_profile ?? {}), ...rawProfile } } : {}),
1207
- };
1208
- return { status: 200, body: await writeResource('account', PLATFORM_ACCOUNT_ID, stashVendorType('account', fields), 'account.update', req.root, req.occurredAt) };
1209
- }
1210
- // Issuing network tokens are minted by the card network when a cardholder adds the card to a
1211
- // phone's wallet (docs.stripe.com/issuing/cards/digital-wallets): there is no API create, and the
1212
- // act happens in the phone's wallet app, not on a Stripe page. POST /_twin/issuing/cards/:card/wallets
1213
- // stands in for that act ({wallet_provider, network}); the API reads the token and moves its status.
1214
- // Where the documentation stops and the twin decides: the network approves at once (the token is active).
1215
- if (seg[0] === '_twin' && seg[1] === 'issuing' && seg[2] === 'cards' && seg[3] && seg[4] === 'wallets' && seg.length === 5 && method === 'POST') {
1216
- const card = decodeURIComponent(seg[3]);
1217
- const c = getOne('issuing_card', card, req.root);
1218
- if (!c)
1219
- return err(`No such card: '${card}'`, 400, 'resource_missing');
1220
- const wallet = typeof params.wallet_provider === 'string' && ['apple_pay', 'google_pay', 'samsung_pay'].includes(params.wallet_provider)
1221
- ? params.wallet_provider : 'apple_pay';
1222
- const network = typeof params.network === 'string' && ['visa', 'mastercard'].includes(params.network) ? params.network : 'visa';
1223
- const last4 = typeof c.last4 === 'string' ? c.last4 : '0000';
1224
- return create('issuing_token', { card, cardholder: typeof c.cardholder === 'string' ? c.cardholder : (c.cardholder?.id ?? null) }, {
1225
- status: 'active', wallet_provider: wallet, network, last4, livemode: false,
1226
- device_fingerprint: null, network_data: null, network_updated_at: nowUnix(req.occurredAt),
1227
- }, req);
1228
- }
1229
- return undefined;
1230
- }
1231
884
  // collection path segment → resource type, for the generic list of a collection Stripe does not
1232
885
  // list at that path (the twin's test clocks, which Stripe lists under /v1/test_helpers).
886
+ export function planView(price) {
887
+ const recurring = price.recurring;
888
+ return {
889
+ id: price.id, object: 'plan', active: price.active ?? true, amount: price.unit_amount ?? null,
890
+ amount_decimal: price.unit_amount_decimal ?? (price.unit_amount == null ? null : String(price.unit_amount)),
891
+ billing_scheme: price.billing_scheme ?? 'per_unit', created: price.created, currency: price.currency,
892
+ interval: recurring?.interval ?? null, interval_count: recurring?.interval_count ?? 1,
893
+ livemode: price.livemode ?? false, metadata: price.metadata ?? {}, nickname: price.nickname ?? null,
894
+ product: price.product ?? null, tiers_mode: price.tiers_mode ?? null,
895
+ meter: recurring?.meter ?? null, trial_period_days: recurring?.trial_period_days ?? null,
896
+ transform_usage: price.transform_quantity ?? null, usage_type: recurring?.usage_type ?? 'licensed',
897
+ };
898
+ }
@@ -0,0 +1,11 @@
1
+ import type { Row } from './common.js';
2
+ export declare const SUB = "subscription";
3
+ /** pause_collection pauses billing (the paused event fires) and clearing it resumes
4
+ * (docs.stripe.com/billing/subscriptions/pause-payment); answers the event, or nothing for a value it does not take. */
5
+ export declare function pauseCollection(pc: unknown, fields: Row): string | undefined;
6
+ /** The longest trial Stripe takes: `trial_end` "Can be at most two years from `billing_cycle_anchor`"
7
+ * (docs.stripe.com/api/subscriptions/update#update_subscription-trial_end); "The trial period must be 730 days (2 years)
8
+ * or less" (docs.stripe.com/billing/subscriptions/trials/free-trials). */
9
+ export declare const MAX_TRIAL: number;
10
+ /** A trial moved to end at `end`, or a new one to `end` on a subscription out of its trial (applyTrialEnd). */
11
+ export declare function movedTrial(sub: Row, fields: Row, end: number, now: number): undefined;
@@ -0,0 +1,30 @@
1
+ export const SUB = 'subscription';
2
+ // pause_collection pauses billing (the subscription stays active; the paused event fires) and
3
+ // clearing it resumes; cancel_at_period_end schedules or withdraws a cancel; a coupon replaces the
4
+ // discount and an empty one clears it
5
+ /** pause_collection pauses billing (the paused event fires) and clearing it resumes
6
+ * (docs.stripe.com/billing/subscriptions/pause-payment); answers the event, or nothing for a value it does not take. */
7
+ export function pauseCollection(pc, fields) {
8
+ const isClear = pc === '' || pc === null || (typeof pc === 'object' && pc !== null && Object.keys(pc).length === 0);
9
+ if (isClear) {
10
+ fields.pause_collection = null;
11
+ return 'customer.subscription.resumed';
12
+ }
13
+ if (!pc || typeof pc !== 'object')
14
+ return undefined;
15
+ const p = pc;
16
+ fields.pause_collection = { behavior: typeof p.behavior === 'string' ? p.behavior : 'void', resumes_at: p.resumes_at !== undefined ? Number(p.resumes_at) : null };
17
+ return 'customer.subscription.paused';
18
+ }
19
+ /** The longest trial Stripe takes: `trial_end` "Can be at most two years from `billing_cycle_anchor`"
20
+ * (docs.stripe.com/api/subscriptions/update#update_subscription-trial_end); "The trial period must be 730 days (2 years)
21
+ * or less" (docs.stripe.com/billing/subscriptions/trials/free-trials). */
22
+ export const MAX_TRIAL = 730 * 86400;
23
+ /** A trial moved to end at `end`, or a new one to `end` on a subscription out of its trial (applyTrialEnd). */
24
+ export function movedTrial(sub, fields, end, now) {
25
+ Object.assign(fields, {
26
+ status: 'trialing', trial_end: end, trial_start: sub.trial_start ?? now, billing_cycle_anchor: end,
27
+ current_period_end: end, ...(sub.status !== 'trialing' ? { current_period_start: now } : {}),
28
+ });
29
+ return undefined;
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { Row } from './common.js';
2
+ export declare const emptyList: (url: string) => Row;
@@ -0,0 +1 @@
1
+ export const emptyList = (url) => ({ object: 'list', data: [], has_more: false, total_count: 0, url });