@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,44 +1,16 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // STRIPE CONNECT HOSTED ONBOARDING — a hosted flow (docs/contributing/architecture.md, "Screens"): a platform
3
- // creates an Account Link for a connected account and sends the account's owner to its `url`. The link is a
4
- // single-use entry point: visiting it takes the owner to their onboarding session, and visiting it again, or after it
5
- // expires, sends them to refresh_url, where the platform makes a new one (docs.stripe.com/connect/hosted-onboarding).
6
- // At the session's own address they give Stripe what it requires, and Stripe sends them to return_url. Submitting is
7
- // the owner's move, not an API call. Authored from @volter/world-ui's payment piece; nothing of Stripe's page is copied.
8
- //
9
- // The page asks for exactly what Stripe requires before a US Express account with card_payments and transfers can
10
- // take card payments (docs.stripe.com/connect/required-verification-information; the set is the one Stripe's
11
- // requirements endpoint answers, docs.stripe.com/_endpoint/get-requirements-for-setups with apiVersion=v1,
12
- // platformCountry=US, accountCountry=US, dashboardType=express, tosType=full, capabilities card_payments and transfers):
13
- // thirty requirements for a company (the business profile, the statement descriptor, the company with its tax id,
14
- // phone and address, its owners, its representative with their title and address, the agreement and the payout bank
15
- // account) and eighteen for an individual. A submit that leaves any out is refused with what is missing. The account's
16
- // requirements.currently_due lists what is still missing, and charges and payouts are enabled, and its requested
17
- // capabilities become active (Stripe's move on the capability machine), only once none is. The agreement is the
18
- // page's own checkbox, recorded as tos_acceptance with the request's address and the time.
19
- //
20
- // Where the documentation stops and the twin decides: only these two US sets are modelled; an account elsewhere, or
21
- // of another business type, is told its requirements are not modelled. Test mode verifies at once whatever is
22
- // submitted (docs.stripe.com/connect/testing lists the values that pass: date of birth 1901-01-01, SSN last four
23
- // 0000, tax id 000000000, routing 110000000, account 000123456789); the industries offered are Stripe's supported
24
- // merchant category codes as its list gives them (./industries.ts), and an industry the page does not offer leaves
25
- // business_profile.mcc due; the states are the fifty and the District of Columbia; a request with no forwarded address
26
- // records 127.0.0.1. An update link (account_update) shows the same page.
27
- import { semanticsContext } from '@volter/world-core';
28
2
  import { CONSENT_CSS, flowPage, Payment, PAYMENT_CSS } from '@volter/world-ui';
29
- import surface from '../generated/surface.gen.json' with { type: 'json' };
30
- import { manifest } from "../manifest.js";
3
+ import { nowUnix } from "../engine/stripe.js";
4
+ import { formOf, notFound, seeOther, STRIPE_CONSENT_SKIN } from "./shared.js";
5
+ import { INDUSTRIES as MCCS } from "../engine/industries.js";
6
+ import { currentlyDue, kindOf, OFFERED } from "../engine/onboarding.js";
31
7
  import { created, syncExternals } from "../semantics/shared.js";
32
- import { formOf, seeOther, STRIPE_CONSENT_SKIN } from "./consent-skin.js";
33
- import { INDUSTRIES as MCCS } from "./industries.js";
34
8
  const LINK = 'account_link';
35
- const OPERATION = surface.operations.find((o) => o.id === 'PostAccountsAccount');
36
9
  const gone = () => flowPage({ title: 'Stripe', status: 404, css: [CONSENT_CSS, STRIPE_CONSENT_SKIN], body: _jsx("main", { className: "consent", children: _jsx("h1", { className: "consent-heading", children: "This link is invalid." }) }) });
37
10
  /** A link that can still be used: not used and not past its expiry. */
38
- const usable = (ctx, link) => link.used !== true && Number(link.expires_at) > Number(ctx.now());
11
+ const usable = (ctx, link) => link.used !== true && Number(link.expires_at) > nowUnix(ctx.occurredAt);
39
12
  // the field asks, with no industry chosen until the person chooses one (an unanswered field leaves the MCC due)
40
13
  const INDUSTRIES = [{ value: '', label: 'Select your industry' }, ...MCCS.map(([value, label]) => ({ value, label }))];
41
- const OFFERED = new Set(MCCS.map(([mcc]) => mcc));
42
14
  // the fifty states and the District of Columbia, by their USPS codes (the address's `state`, ISO 3166-2 without its
43
15
  // country, docs.stripe.com/api/accounts/object)
44
16
  const STATES = [
@@ -51,43 +23,6 @@ const STATES = [
51
23
  ['SC', 'South Carolina'], ['SD', 'South Dakota'], ['TN', 'Tennessee'], ['TX', 'Texas'], ['UT', 'Utah'], ['VT', 'Vermont'], ['VA', 'Virginia'],
52
24
  ['WA', 'Washington'], ['WV', 'West Virginia'], ['WI', 'Wisconsin'], ['WY', 'Wyoming'],
53
25
  ].map(([value, label]) => ({ value, label }));
54
- const has = (v, k) => (v[k] ?? '').trim() !== '';
55
- const dobPart = (v, i) => /^\d{4}-\d{2}-\d{2}$/.test(v.dob ?? '') && Number((v.dob ?? '').split('-')[i]) > 0;
56
- /** Each requirement Stripe names, the form's answer that meets it, and which of the two sets it belongs to. */
57
- const REQUIREMENTS = [
58
- ['business_profile.mcc', (v) => OFFERED.has((v.mcc ?? '').trim()), ['company', 'individual']],
59
- ['business_profile.url', (v) => has(v, 'url'), ['company', 'individual']],
60
- ['settings.payments.statement_descriptor', (v) => has(v, 'statement_descriptor'), ['company', 'individual']],
61
- ['company.name', (v) => has(v, 'company_name'), ['company']],
62
- ['company.tax_id', (v) => has(v, 'company_tax_id'), ['company']],
63
- ['company.phone', (v) => has(v, 'company_phone'), ['company']],
64
- ['company.address.line1', (v) => has(v, 'company_line1'), ['company']],
65
- ['company.address.city', (v) => has(v, 'company_city'), ['company']],
66
- ['company.address.state', (v) => has(v, 'company_state'), ['company']],
67
- ['company.address.postal_code', (v) => has(v, 'company_postal_code'), ['company']],
68
- ['company.owners_provided', (v) => v.owners_provided === 'provided', ['company']],
69
- ['owners.first_name', (v) => has(v, 'owner_first_name'), ['company']],
70
- ['owners.last_name', (v) => has(v, 'owner_last_name'), ['company']],
71
- ['owners.email', (v) => has(v, 'owner_email'), ['company']],
72
- ...['first_name', 'last_name', 'email', 'phone'].flatMap((f) => [[`representative.${f}`, (v) => has(v, f), ['company']], [`individual.${f}`, (v) => has(v, f), ['individual']]]),
73
- ...['day', 'month', 'year'].flatMap((f, i) => [[`representative.dob.${f}`, (v) => dobPart(v, 2 - i), ['company']], [`individual.dob.${f}`, (v) => dobPart(v, 2 - i), ['individual']]]),
74
- ...['line1', 'city', 'state', 'postal_code'].flatMap((f) => [[`representative.address.${f}`, (v) => has(v, f), ['company']], [`individual.address.${f}`, (v) => has(v, f), ['individual']]]),
75
- ['representative.relationship.title', (v) => has(v, 'title'), ['company']],
76
- ['representative.ssn_last_4', (v) => /^\d{4}$/.test(v.ssn_last_4 ?? ''), ['company']],
77
- ['individual.ssn_last_4', (v) => /^\d{4}$/.test(v.ssn_last_4 ?? ''), ['individual']],
78
- ['tos_acceptance.date', (v) => v.tos === 'accepted', ['company', 'individual']],
79
- ['tos_acceptance.ip', (v) => v.tos === 'accepted', ['company', 'individual']],
80
- ['external_account', (v) => has(v, 'routing_number') && has(v, 'account_number'), ['company', 'individual']],
81
- ];
82
- /** The set an account onboards against: the two US sets the twin models, or none. */
83
- export function kindOf(account) {
84
- const type = account.business_type ?? 'individual';
85
- return (account.country ?? 'US') === 'US' && (type === 'company' || type === 'individual') ? type : undefined;
86
- }
87
- /** What an account of this kind still owes, given the form's answers (none: all of it). */
88
- export function currentlyDue(kind, v = {}) {
89
- return REQUIREMENTS.filter(([, met, kinds]) => kinds.includes(kind) && !met(v)).map(([req]) => req);
90
- }
91
26
  const field = (id, label, v, extra = {}) => ({ id, label, value: v[id] ?? '', ...extra });
92
27
  const addressFields = (prefix, v) => [
93
28
  field(`${prefix}line1`, 'Address', v, { autoComplete: 'address-line1' }), field(`${prefix}city`, 'City', v),
@@ -145,7 +80,7 @@ async function submit(ctx, link, kind, v, ip) {
145
80
  return;
146
81
  const caps = { ...(account.capabilities ?? {}) };
147
82
  for (const [cap, status] of Object.entries(caps)) {
148
- if (status !== 'active' && !ctx.legal('capability', 'status', OPERATION.id, String(status), 'active', cap, 'vendor'))
83
+ if (status !== 'active')
149
84
  caps[cap] = 'active';
150
85
  }
151
86
  const due = currentlyDue(kind, v);
@@ -155,6 +90,7 @@ async function submit(ctx, link, kind, v, ip) {
155
90
  const person = { first_name: v.first_name, last_name: v.last_name, email: v.email, phone: v.phone, dob: { day, month, year }, address: addressOf(''), ssn_last_4_provided: true, id_number_provided: false };
156
91
  const company = kind === 'company';
157
92
  const settings = account.settings ?? {};
93
+ const individual = company ? null : await created(ctx, 'person', { ...person, account: id }, { relationship: { representative: true }, metadata: {} });
158
94
  await ctx.write('account', id, {
159
95
  details_submitted: true, charges_enabled: due.length === 0, payouts_enabled: due.length === 0, capabilities: caps, requirements,
160
96
  business_type: kind,
@@ -162,8 +98,8 @@ async function submit(ctx, link, kind, v, ip) {
162
98
  settings: { ...settings, payments: { ...(settings.payments ?? {}), statement_descriptor: v.statement_descriptor } },
163
99
  ...(company
164
100
  ? { company: { name: v.company_name, phone: v.company_phone, address: addressOf('company_'), tax_id_provided: true, owners_provided: true, directors_provided: false, executives_provided: false } }
165
- : { individual: { ...person, id: `person_${id}` } }),
166
- tos_acceptance: { date: Number(ctx.now()), ip, user_agent: null },
101
+ : { individual: individual.id }),
102
+ tos_acceptance: { date: nowUnix(ctx.occurredAt), ip, user_agent: null },
167
103
  }, 'account.updated');
168
104
  if (company) {
169
105
  await created(ctx, 'person', { account: id }, { ...person, relationship: { representative: true, executive: true, director: false, owner: false, percent_ownership: null, title: v.title }, metadata: {} });
@@ -191,35 +127,33 @@ async function visit(ctx, id) {
191
127
  return seeOther(`${SESSIONS}${session}`);
192
128
  }
193
129
  /** connect.stripe.com's onboarding pages, or undefined for any other request. */
194
- export function stripeOnboardingFlow(scope) {
195
- return async (request) => {
196
- const path = new URL(request.url).pathname;
197
- const entry = /^\/setup\/(acctlink_[A-Za-z0-9_]+)\/?$/.exec(path);
198
- const at = /^\/setup\/s\/(onbs_[A-Za-z0-9_]+)\/?$/.exec(path);
199
- if (entry && request.method === 'GET')
200
- return visit(await semanticsContext(manifest, request, OPERATION, scope), entry[1]);
201
- if (!at || (request.method !== 'GET' && request.method !== 'POST'))
202
- return undefined;
203
- const sid = at[1];
204
- const form = request.method === 'POST' ? await formOf(request.clone()) : {};
205
- const ctx = await semanticsContext(manifest, request, OPERATION, scope);
206
- // the session the visited link opened, until it is submitted
207
- const stored = ctx.rowsRaw(LINK).find((l) => l._session === sid && l._submitted !== true);
208
- const link = stored ? ctx.get(LINK, String(stored.id)) : undefined;
209
- if (!stored || !link)
210
- return gone();
211
- const sessionUrl = `${SESSIONS}${sid}`;
212
- const account = ctx.get('account', String(link.account)) ?? {};
213
- const kind = kindOf(account);
214
- if (!kind)
215
- return unmodelled(account);
216
- if (request.method === 'GET')
217
- return page(ctx, sessionUrl, link, kind);
218
- const missing = currentlyDue(kind, form);
219
- if (missing.length)
220
- return incomplete(ctx, sessionUrl, link, kind, form, missing);
221
- await ctx.write(LINK, String(stored.id), { _submitted: true }, 'account_link.submitted');
222
- await submit(ctx, link, kind, form, request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || '127.0.0.1');
223
- return seeOther(String(link.return_url));
224
- };
130
+ export async function screen(ctx) {
131
+ const request = ctx.call.request;
132
+ const path = new URL(request.url).pathname;
133
+ const entry = /^\/setup\/(acctlink_[A-Za-z0-9_]+)\/?$/.exec(path);
134
+ const at = /^\/setup\/s\/(onbs_[A-Za-z0-9_]+)\/?$/.exec(path);
135
+ if (entry && request.method === 'GET')
136
+ return visit(ctx, entry[1]);
137
+ if (!at || (request.method !== 'GET' && request.method !== 'POST'))
138
+ return notFound();
139
+ const sid = at[1];
140
+ const form = request.method === 'POST' ? await formOf(request.clone()) : {};
141
+ // the session the visited link opened, until it is submitted
142
+ const stored = ctx.rowsRaw(LINK).find((l) => l._session === sid && l._submitted !== true);
143
+ const link = stored ? ctx.get(LINK, String(stored.id)) : undefined;
144
+ if (!stored || !link)
145
+ return gone();
146
+ const sessionUrl = `${SESSIONS}${sid}`;
147
+ const account = ctx.get('account', String(link.account)) ?? {};
148
+ const kind = kindOf(account);
149
+ if (!kind)
150
+ return unmodelled(account);
151
+ if (request.method === 'GET')
152
+ return page(ctx, sessionUrl, link, kind);
153
+ const missing = currentlyDue(kind, form);
154
+ if (missing.length)
155
+ return incomplete(ctx, sessionUrl, link, kind, form, missing);
156
+ await ctx.write(LINK, String(stored.id), { _submitted: true }, 'account_link.submitted');
157
+ await submit(ctx, link, kind, form, request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || '127.0.0.1');
158
+ return seeOther(String(link.return_url));
225
159
  }
@@ -0,0 +1,3 @@
1
+ import type { HandlerContext } from '@volter/world-core';
2
+ /** Dashboard settings, Radar list creation and test Issuing funding; other pages answer Stripe's 404. */
3
+ export declare function screen(ctx: HandlerContext): Promise<Response>;
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { flowPage } from '@volter/world-ui';
3
+ import { VL } from "../engine/radar.js";
4
+ import { nowUnix } from "../engine/stripe.js";
5
+ import { platformAccountDefault } from "../engine/connect.js";
6
+ import { publicBusinessName } from "../engine/display.js";
7
+ import { PLATFORM_ACCOUNT_ID } from "../engine/stripe.js";
8
+ import { formOf, notFound } from "./shared.js";
9
+ import { created } from "../semantics/shared.js";
10
+ const redirect = (path) => new Response(null, { status: 303, headers: { location: path } });
11
+ const invalid = (message) => flowPage({ css: [], title: 'Stripe', status: 400, body: _jsx("main", { children: _jsx("p", { role: "alert", children: message }) }) });
12
+ const PUBLIC_DETAILS_PATH = '/settings/public';
13
+ const MAX_NAME = 5000;
14
+ const DETAILS_CSS = `
15
+ * { box-sizing: border-box; }
16
+ body { margin: 0; background: #f6f8fa; color: #1a1f36; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Ubuntu, sans-serif; font-size: 14px; }
17
+ .pd { max-width: 640px; margin: 48px auto; padding: 0 16px; }
18
+ .pd h1 { font-size: 28px; margin: 16px 0 4px; }
19
+ .pd-lead { color: #697386; margin: 0 0 24px; }
20
+ .pd-card { background: #ffffff; border: 1px solid #e3e8ee; border-radius: 8px; padding: 24px; }
21
+ .pd-card label { display: block; font-weight: 600; margin-bottom: 6px; }
22
+ .pd-card small { display: block; color: #697386; margin-bottom: 8px; }
23
+ .pd-card input { width: 100%; padding: 8px 10px; border: 1px solid #c1c9d2; border-radius: 6px; font: inherit; }
24
+ .pd-actions { margin-top: 20px; display: flex; justify-content: flex-end; }
25
+ .pd-actions button { background: #635bff; color: #ffffff; border: 0; border-radius: 6px; padding: 8px 16px; font: inherit; font-weight: 600; cursor: pointer; }
26
+ .pd-notice { padding: 10px 12px; border-radius: 6px; margin-bottom: 16px; }
27
+ .pd-error { background: #fff0f3; color: #df1b41; }
28
+ .pd-saved { background: #e7f8ef; color: #0e6245; }
29
+ `;
30
+ function detailsPage(account, opts = {}) {
31
+ const named = account?.business_profile?.name;
32
+ const value = opts.value ?? (typeof named === 'string' ? named : '');
33
+ return flowPage({
34
+ title: 'Public details – Stripe',
35
+ css: [DETAILS_CSS],
36
+ ...(opts.error ? { status: 400 } : {}),
37
+ body: (_jsxs("main", { className: "pd", children: [_jsx("h1", { children: "Public details" }), _jsxs("p", { className: "pd-lead", children: ["What your customers see on Checkout, the customer portal, receipts and invoices. Currently shown: ", _jsx("strong", { "data-testid": "public-business-name", children: publicBusinessName(account) })] }), opts.error ? _jsx("p", { className: "pd-notice pd-error", role: "alert", children: opts.error }) : null, opts.saved ? _jsx("p", { className: "pd-notice pd-saved", role: "status", children: "Your public details were saved." }) : null, _jsxs("form", { className: "pd-card", method: "post", children: [_jsx("label", { htmlFor: "business_name", children: "Business name" }), _jsx("small", { children: "The name customers see when they pay you." }), _jsx("input", { id: "business_name", name: "business_name", autoComplete: "organization", defaultValue: value, required: true, maxLength: MAX_NAME }), _jsx("div", { className: "pd-actions", children: _jsx("button", { type: "submit", children: "Save" }) })] })] })),
38
+ });
39
+ }
40
+ /** Save the business name on the platform's own account: the stored record, made from Stripe's default the first
41
+ * time anything is saved, and otherwise changed in place (its other public details kept). */
42
+ async function save(ctx, name) {
43
+ const held = ctx.get('account', PLATFORM_ACCOUNT_ID);
44
+ const profile = { business_profile: { ...((held ?? platformAccountDefault()).business_profile ?? {}), name } };
45
+ await (held ? ctx.write('account', PLATFORM_ACCOUNT_ID, profile, 'account.update') : created(ctx, 'account', { ...platformAccountDefault(), ...profile }, {}, { operation: 'account.update' }));
46
+ }
47
+ /** What the page says of a name no browser sends (the field is required and bounded), for a client that sends it. */
48
+ function nameRefused(name) {
49
+ return !name ? 'Enter your business name.' : name.length > MAX_NAME ? `Your business name must be at most ${MAX_NAME} characters.` : undefined;
50
+ }
51
+ async function publicDetails(ctx) {
52
+ const request = ctx.call.request;
53
+ const url = new URL(request.url);
54
+ if (request.method !== 'GET' && request.method !== 'POST')
55
+ return notFound();
56
+ const form = request.method === 'POST' ? await formOf(request.clone()) : {};
57
+ if (request.method === 'GET')
58
+ return detailsPage(ctx.get('account', PLATFORM_ACCOUNT_ID), { saved: url.searchParams.has('saved') });
59
+ const name = (form.business_name ?? '').trim();
60
+ const refused = nameRefused(name);
61
+ if (refused)
62
+ return detailsPage(ctx.get('account', PLATFORM_ACCOUNT_ID), { value: form.business_name ?? '', error: refused });
63
+ await save(ctx, name);
64
+ // a query-only reference: the page's own path, wherever it is mounted and with or without its trailing slash
65
+ return new Response(null, { status: 303, headers: { location: '?saved=1' } });
66
+ }
67
+ /** Dashboard settings, Radar list creation and test Issuing funding; other pages answer Stripe's 404. */
68
+ export async function screen(ctx) {
69
+ const path = new URL(ctx.call.request.url).pathname.replace(/\/+$/, '');
70
+ // source: https://docs.stripe.com/radar/lists "Use the Dashboard or the API to create lists."
71
+ // source: https://docs.stripe.com/radar/lists "Enter a name for the list"
72
+ // source: https://docs.stripe.com/radar/lists "Select the type of list to create."
73
+ // source: https://docs.stripe.com/radar/lists "Click Add to save your new list."
74
+ // Where the documentation stops: it names Dashboard controls, not their browser request routes or form wire.
75
+ // This authored page uses /radar/lists for GET and POST, /radar/lists/{id} for its redirect/detail address,
76
+ // and a 303 redirect after saving. The HTML field names name, alias and item_type are the twin's choices;
77
+ // item_type values are the pinned API's enums, not published Dashboard form values. The alias fallback's
78
+ // lowercase/underscore algorithm is also authored; Stripe says it generates an alias but gives no algorithm.
79
+ if (/^\/radar\/lists(?:\/[^/]+)?$/.test(path)) {
80
+ if (ctx.call.request.method === 'POST') {
81
+ const form = await formOf(ctx.call.request);
82
+ const types = ['string', 'case_sensitive_string', 'card_fingerprint', 'card_bin', 'customer_id', 'email', 'ip_address', 'country', 'sepa_debit_fingerprint', 'us_bank_account_fingerprint'];
83
+ if (!form.name?.trim() || !types.includes(form.item_type ?? ''))
84
+ return invalid('Enter a name and select a list type.');
85
+ const alias = form.alias?.trim() || form.name.toLowerCase().replace(/[^a-z0-9]+/g, '_');
86
+ // Where the page stops: an operator has no login identity in the World; created_by is its synthetic operator.
87
+ const list = await created(ctx, VL, { name: form.name.trim(), alias, item_type: form.item_type }, {
88
+ created_by: 'twin', livemode: false, metadata: {},
89
+ list_items: { object: 'list', data: [], has_more: false, url: '/v1/radar/value_list_items' },
90
+ });
91
+ await ctx.write(VL, String(list.id), { list_items: { object: 'list', data: [], has_more: false, url: `/v1/radar/value_list_items?value_list=${list.id}` } }, 'radar.value_list.updated');
92
+ return redirect(`/radar/lists/${list.id}`);
93
+ }
94
+ return flowPage({ css: [], title: 'Lists – Radar – Stripe', body: _jsxs("main", { children: [_jsx("h1", { children: "Radar Lists" }), _jsxs("form", { method: "post", action: "/radar/lists", children: [_jsx("h2", { children: "New list" }), _jsxs("label", { children: ["Name", _jsx("input", { name: "name", required: true })] }), _jsxs("label", { children: ["Alias", _jsx("input", { name: "alias" })] }), _jsxs("label", { children: ["List type", _jsxs("select", { name: "item_type", children: [_jsx("option", { value: "customer_id", children: "Customer ID" }), _jsx("option", { value: "email", children: "Email" }), _jsx("option", { value: "card_fingerprint", children: "Card fingerprint" }), _jsx("option", { value: "ip_address", children: "IP address" }), _jsx("option", { value: "string", children: "String" })] })] }), _jsx("button", { children: "Add" })] }), _jsx("ul", { children: ctx.rows(VL).map(row => _jsxs("li", { children: [String(row.name), " (", String(row.alias), ")"] }, String(row.id))) })] }) });
95
+ }
96
+ // source: https://docs.stripe.com/issuing/testing "Before you create test transactions, you must add test funds to the Issuing balance on your account."
97
+ // source: https://docs.stripe.com/issuing/testing "You can create test top-ups in the Dashboard, or with the Top-ups API"
98
+ // Where the documentation stops: it states the Dashboard funding act, not a browser request path or form wire.
99
+ // The authored GET/POST address and 303 redirect are /test/issuing/balance. The form name amount, its positive
100
+ // integer validation and its units (USD cents for this US sandbox) are the twin's choices, not published form
101
+ // fields. Its POST records the balance change in the vendor ledger, with source=null (permitted by the pinned
102
+ // schema) and immediate availability for this sandbox control; the page states neither that source nor timing.
103
+ // No private Topup object is seeded or answered, and the uncalled Top-ups API remains the gap.
104
+ if (path === '/test/issuing/balance') {
105
+ if (ctx.call.request.method === 'POST') {
106
+ const form = await formOf(ctx.call.request);
107
+ const amount = Number(form.amount);
108
+ if (!Number.isInteger(amount) || amount <= 0)
109
+ return invalid('Enter a positive amount in cents.');
110
+ await created(ctx, 'balance_transaction', {}, { amount, currency: 'usd', fee: 0, net: amount, type: 'topup', reporting_category: 'topup', source: null, status: 'available', balance_type: 'issuing', available_on: Number(nowUnix(ctx.occurredAt)), fee_details: [] });
111
+ return redirect('/test/issuing/balance');
112
+ }
113
+ return flowPage({ css: [], title: 'Test Issuing balance – Stripe', body: _jsxs("main", { children: [_jsx("h1", { children: "Test Issuing balance" }), _jsxs("form", { method: "post", children: [_jsxs("label", { children: ["Amount", _jsx("input", { name: "amount", type: "number", min: "1", required: true })] }), _jsx("button", { children: "Add test funds" })] })] }) });
114
+ }
115
+ return path === PUBLIC_DETAILS_PATH ? publicDetails(ctx) : notFound();
116
+ }
@@ -0,0 +1,27 @@
1
+ export declare const STRIPE_CONSENT_SKIN = "\nbody { background: #f6f9fc; color: #1a1f36; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Ubuntu, sans-serif; }\n.consent-badge { background: #ffffff; border: 1px solid #e3e8ee; color: #1a1f36; }\n.consent-vendor { background: #635bff; border-color: #635bff; }\n.consent-link { border-color: #c1c9d2; }\n.consent-box { background: #ffffff; border-color: #e3e8ee; }\n.consent-who small, .consent-permission summary small, .consent-note { color: #697386; }\n.consent-actions { border-color: #e3e8ee; }\n.consent-deny { background: #ffffff; border-color: #c1c9d2; color: #1a1f36; }\n.consent-allow { background: #635bff; border-color: #635bff; color: #ffffff; }\n";
2
+ /** The form fields a page's POST carries, as a browser posts them. */
3
+ export declare function formOf(request: Request): Promise<Record<string, string>>;
4
+ export declare const seeOther: (location: string) => Response;
5
+ export declare const DECLINES: Record<string, string>;
6
+ export declare const luhn: (digits: string) => boolean;
7
+ export declare const money: (minor: unknown, currency: unknown) => string;
8
+ export declare const COUNTRIES: {
9
+ value: string;
10
+ label: string;
11
+ }[];
12
+ /** Cards that are saved but decline when charged. */
13
+ export declare const DECLINES_WHEN_CHARGED: Record<string, string>;
14
+ /** The expiry as the customer typed it. Stripe's field formats itself as it is typed: the digits `1230` read `12 / 30`,
15
+ * and a first digit above 1 is a month on its own (`5` reads `05 / `), so a person types the digits straight through
16
+ * (Checkout's own field, seen on checkout.stripe.com; docs.stripe.com/payments/checkout names no format to type). The
17
+ * page's script (@volter/world-ui Payment, `format: 'card-expiry'`) shows it formatted; this reads what is posted either
18
+ * way: `MM / YY`, `MM/YYYY`, or the bare digits `MMYY`, `MYY` (`530`) and `MMYYYY`; a month past 12 is read, and refused
19
+ * as invalid by cardAnswer. */
20
+ export declare function parseExpiry(raw: string): {
21
+ month: number;
22
+ year: number;
23
+ } | undefined;
24
+ /** The card's answer: undefined when it pays, else what Checkout says under the button. */
25
+ export declare function cardAnswer(v: Record<string, string>, nowSeconds: number, chargesNow: boolean): string | undefined;
26
+ /** Stripe's answer for a page of one of its hosts the twin does not serve. */
27
+ export declare const notFound: () => Response;
@@ -0,0 +1,87 @@
1
+ // What Stripe's screens share (docs/contributing/architecture.md, "Pack layout": screens/shared.tsx): the skin over
2
+ // @volter/world-ui's consent piece for the flows where a person agrees to something (connected-account onboarding,
3
+ // identity verification, linking a bank), a page's form, and the card a customer types on Checkout and the portal.
4
+ export const STRIPE_CONSENT_SKIN = `
5
+ body { background: #f6f9fc; color: #1a1f36; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Ubuntu, sans-serif; }
6
+ .consent-badge { background: #ffffff; border: 1px solid #e3e8ee; color: #1a1f36; }
7
+ .consent-vendor { background: #635bff; border-color: #635bff; }
8
+ .consent-link { border-color: #c1c9d2; }
9
+ .consent-box { background: #ffffff; border-color: #e3e8ee; }
10
+ .consent-who small, .consent-permission summary small, .consent-note { color: #697386; }
11
+ .consent-actions { border-color: #e3e8ee; }
12
+ .consent-deny { background: #ffffff; border-color: #c1c9d2; color: #1a1f36; }
13
+ .consent-allow { background: #635bff; border-color: #635bff; color: #ffffff; }
14
+ `;
15
+ /** The form fields a page's POST carries, as a browser posts them. */
16
+ export async function formOf(request) {
17
+ return Object.fromEntries(new URLSearchParams(await request.text()));
18
+ }
19
+ export const seeOther = (location) => new Response(null, { status: 302, headers: { location } });
20
+ export const DECLINES = {
21
+ '4000000000000002': 'Your card was declined.',
22
+ '4000000000009995': 'Your card has insufficient funds.',
23
+ '4000000000000069': 'Your card has expired.',
24
+ '4000000000000127': "Your card's security code is incorrect.",
25
+ };
26
+ export const luhn = (digits) => {
27
+ let sum = 0;
28
+ for (let i = 0; i < digits.length; i++) {
29
+ let d = Number(digits[digits.length - 1 - i]);
30
+ if (i % 2 === 1) {
31
+ d *= 2;
32
+ if (d > 9)
33
+ d -= 9;
34
+ }
35
+ sum += d;
36
+ }
37
+ return digits.length >= 12 && sum % 10 === 0;
38
+ };
39
+ export const money = (minor, currency) => {
40
+ const c = typeof currency === 'string' ? currency.toUpperCase() : 'USD';
41
+ const zeroDecimal = ['JPY', 'KRW', 'VND', 'CLP', 'ISK', 'UGX', 'XAF', 'XOF'].includes(c);
42
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: c }).format((Number(minor) || 0) / (zeroDecimal ? 1 : 100));
43
+ };
44
+ export const COUNTRIES = [['US', 'United States'], ['GB', 'United Kingdom'], ['CA', 'Canada'], ['DE', 'Germany'], ['FR', 'France'], ['AU', 'Australia'], ['JP', 'Japan']].map(([value, label]) => ({ value: value, label: label }));
45
+ /** Cards that are saved but decline when charged. */
46
+ export const DECLINES_WHEN_CHARGED = { '4000000000000341': 'Your card was declined.' };
47
+ /** The expiry as the customer typed it. Stripe's field formats itself as it is typed: the digits `1230` read `12 / 30`,
48
+ * and a first digit above 1 is a month on its own (`5` reads `05 / `), so a person types the digits straight through
49
+ * (Checkout's own field, seen on checkout.stripe.com; docs.stripe.com/payments/checkout names no format to type). The
50
+ * page's script (@volter/world-ui Payment, `format: 'card-expiry'`) shows it formatted; this reads what is posted either
51
+ * way: `MM / YY`, `MM/YYYY`, or the bare digits `MMYY`, `MYY` (`530`) and `MMYYYY`; a month past 12 is read, and refused
52
+ * as invalid by cardAnswer. */
53
+ export function parseExpiry(raw) {
54
+ const split = /^\s*(\d{1,2})\s*\/\s*(\d{2}|\d{4})\s*$/.exec(raw);
55
+ const digits = raw.replace(/\s/g, '');
56
+ const bare = split ? undefined : /^(\d{2})(\d{2}|\d{4})$/.exec(digits) ?? /^([2-9])(\d{2})$/.exec(digits);
57
+ const m = split ?? bare;
58
+ if (!m)
59
+ return undefined;
60
+ return { month: Number(m[1]), year: m[2].length === 2 ? 2000 + Number(m[2]) : Number(m[2]) };
61
+ }
62
+ /** The card's answer: undefined when it pays, else what Checkout says under the button. */
63
+ export function cardAnswer(v, nowSeconds, chargesNow) {
64
+ const digits = (v.cardNumber ?? '').replace(/\D/g, '');
65
+ if (!digits)
66
+ return 'Your card number is incomplete.';
67
+ if (DECLINES[digits])
68
+ return DECLINES[digits];
69
+ if (chargesNow && DECLINES_WHEN_CHARGED[digits])
70
+ return DECLINES_WHEN_CHARGED[digits];
71
+ if (!luhn(digits))
72
+ return 'Your card number is invalid.';
73
+ const exp = parseExpiry(v.cardExpiry ?? '');
74
+ if (!exp)
75
+ return "Your card's expiration date is incomplete.";
76
+ const { month, year } = exp;
77
+ if (month < 1 || month > 12)
78
+ return "Your card's expiration date is invalid.";
79
+ const now = new Date(nowSeconds * 1000);
80
+ if (year < now.getUTCFullYear() || (year === now.getUTCFullYear() && month < now.getUTCMonth() + 1))
81
+ return "Your card's expiration year is in the past.";
82
+ if (!/^\d{3,4}$/.test(v.cardCvc ?? ''))
83
+ return "Your card's security code is incomplete.";
84
+ return undefined;
85
+ }
86
+ /** Stripe's answer for a page of one of its hosts the twin does not serve. */
87
+ export const notFound = () => new Response('Not Found', { status: 404, headers: { 'content-type': 'text/plain; charset=utf-8' } });
@@ -0,0 +1,3 @@
1
+ import type { HandlerContext } from '@volter/world-core';
2
+ /** js.stripe.com's script for a GET of its path; Stripe's 404 for anything else there. */
3
+ export declare function screen(ctx: HandlerContext): Promise<Response>;
@@ -0,0 +1,95 @@
1
+ import { notFound } from "./shared.js";
2
+ /** The release train a script path names: `/v3`, `/v3/` or `/v3/stripe.js` → 3, `/<train>/stripe.js` → the train. */
3
+ function stripeJsTrain(pathname) {
4
+ return /^\/v3(\/|\/stripe\.js)?$/.test(pathname) ? 3 : /^\/([a-z]+)\/stripe\.js$/.exec(pathname)?.[1];
5
+ }
6
+ // source: archive:https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-8.6.0.tgz#sha256=13e61419e9076ab440cc636e34d3ea5df3fc62bbf2d6a80d3fd718e8cd47f0a2!/package/dist/stripe-js/checkout.d.ts "loadActions: () => Promise<StripeCheckoutLoadActionsResult>"
7
+ function script(version) {
8
+ return `(function () {
9
+ var version = ${JSON.stringify(version)};
10
+ function gap(name) {
11
+ return function () { throw new Error("Stripe.js (Volter twin): stripe." + name + " is not modelled by this twin yet"); };
12
+ }
13
+ function initCheckout(options) {
14
+ var session, root, fields = {}, listeners = {};
15
+ function emit(name, value) { (listeners[name] || []).forEach(function (fn) { fn(value); }); }
16
+ function call(action, values) {
17
+ var secret = options.clientSecret;
18
+ var id = secret.split("_secret_")[0];
19
+ return fetch("https://api.stripe.com/v1/payment_pages/" + encodeURIComponent(id) + "/" + action, {
20
+ method: "POST", headers: { "content-type": "application/json" },
21
+ body: JSON.stringify(Object.assign({ client_secret: secret }, values || {}))
22
+ }).then(function (r) { return r.json(); }).then(function (result) {
23
+ if (result.type === "success") { session = result.session; emit("change", session); }
24
+ return result;
25
+ });
26
+ }
27
+ var actions = {
28
+ getSession: function () { return session; },
29
+ applyPromotionCode: function (code) { return call("apply_promotion_code", { promotion_code: code }); },
30
+ removePromotionCode: function () { return call("remove_promotion_code"); },
31
+ confirm: function (params) {
32
+ return call("confirm", Object.assign({}, params || {}, { payment_details: fields })).then(function (result) {
33
+ if (result.type === "success" && (!params || params.redirect !== "if_required") && result.return_url) window.location.assign((params && params.returnUrl) || result.return_url);
34
+ return result;
35
+ });
36
+ }
37
+ };
38
+ var elementListeners = {};
39
+ function elementEmit(name, value) { (elementListeners[name] || []).forEach(function (fn) { fn(value); }); }
40
+ var element = {
41
+ on: function (name, fn) { (elementListeners[name] || (elementListeners[name] = [])).push(fn); return element; },
42
+ off: function (name, fn) { elementListeners[name] = (elementListeners[name] || []).filter(function (f) { return f !== fn; }); return element; },
43
+ mount: function (target) {
44
+ root = typeof target === "string" ? document.querySelector(target) : target;
45
+ [["cardNumber", "Card number", "cc-number"], ["cardExpiry", "Expiration", "cc-exp"], ["cardCvc", "CVC", "cc-csc"], ["billingName", "Cardholder name", "cc-name"]].forEach(function (field) {
46
+ var label = document.createElement("label"), input = document.createElement("input");
47
+ label.textContent = field[1]; input.name = field[0]; input.autocomplete = field[2];
48
+ input.addEventListener("input", function () { fields[field[0]] = input.value; elementEmit("change", { empty: !fields.cardNumber, complete: !!(fields.cardNumber && fields.cardExpiry && fields.cardCvc), value: { type: "card" } }); });
49
+ label.appendChild(input); root.appendChild(label);
50
+ });
51
+ Promise.resolve().then(function () { elementEmit("ready", element); });
52
+ },
53
+ unmount: function () { if (root) root.replaceChildren(); root = null; },
54
+ destroy: function () { element.unmount(); elementListeners = {}; },
55
+ update: function () {}
56
+ };
57
+ return {
58
+ on: function (name, fn) { (listeners[name] || (listeners[name] = [])).push(fn); },
59
+ loadActions: function () { return call("init").then(function (r) { return r.type === "success" ? { type: "success", actions: actions } : r; }); },
60
+ createPaymentElement: function () { return element; }, getPaymentElement: function () { return element; },
61
+ changeAppearance: function () {}, loadFonts: function () {}
62
+ };
63
+ }
64
+ function Stripe(publishableKey) {
65
+ if (typeof publishableKey !== "string" || !publishableKey) throw new Error("Stripe.js (Volter twin): Stripe() needs a publishable key");
66
+ var known = {
67
+ initCheckout: initCheckout,
68
+ _registerWrapper: function () {},
69
+ redirectToCheckout: function (options) {
70
+ if (!options || typeof options.sessionId !== "string" || !options.sessionId) return gap("redirectToCheckout without a sessionId")();
71
+ window.location.assign("https://checkout.stripe.com/c/pay/" + encodeURIComponent(options.sessionId));
72
+ return new Promise(function () {});
73
+ }
74
+ };
75
+ return new Proxy(known, {
76
+ get: function (target, name) {
77
+ if (name in target) return target[name];
78
+ if (typeof name === "symbol" || name === "then") return undefined;
79
+ return gap(String(name));
80
+ }
81
+ });
82
+ }
83
+ Stripe.version = version;
84
+ window.Stripe = Stripe;
85
+ })();
86
+ `;
87
+ }
88
+ /** js.stripe.com's script for a GET of its path; Stripe's 404 for anything else there. */
89
+ export async function screen(ctx) {
90
+ const request = ctx.call.request;
91
+ const train = request.method === 'GET' || request.method === 'HEAD' ? stripeJsTrain(new URL(request.url).pathname) : undefined;
92
+ if (train === undefined)
93
+ return notFound();
94
+ return new Response(script(train), { headers: { 'content-type': 'application/javascript; charset=utf-8', 'access-control-allow-origin': '*' } });
95
+ }
@@ -0,0 +1,2 @@
1
+ import type { HandlerContext } from '@volter/world-core';
2
+ export declare function GetAccount(ctx: HandlerContext): Promise<Response>;
@@ -0,0 +1,8 @@
1
+ import { PLATFORM_ACCOUNT_ID } from "../engine/stripe.js";
2
+ import { actingAccount, accountMissing } from "./shared.js";
3
+ // source: spec:GetAccount "Retrieves the details of an account."
4
+ export async function GetAccount(ctx) {
5
+ const id = actingAccount(ctx) ?? PLATFORM_ACCOUNT_ID;
6
+ const account = ctx.get('account', id);
7
+ return account ? ctx.reply(ctx.expand('account', account)) : accountMissing(ctx, id);
8
+ }
@@ -0,0 +1,2 @@
1
+ import type { HandlerContext } from '@volter/world-core';
2
+ export declare function PostAccountLinks(ctx: HandlerContext): Promise<Response>;
@@ -0,0 +1,22 @@
1
+ import { nowUnix } from "../engine/stripe.js";
2
+ import { accountMissing, fail } from "./shared.js";
3
+ // ── hosted onboarding links and embedded-component sessions ──
4
+ export async function PostAccountLinks(ctx) {
5
+ const account = typeof ctx.params.account === 'string' ? ctx.params.account : '';
6
+ if (!account)
7
+ return fail(ctx, 'Missing required param: account.', 400, 'parameter_missing');
8
+ if (!ctx.get('account', account))
9
+ return accountMissing(ctx, account, 400);
10
+ const linkType = typeof ctx.params.type === 'string' ? ctx.params.type : '';
11
+ if (linkType !== 'account_onboarding' && linkType !== 'account_update')
12
+ return fail(ctx, 'Invalid account link type: must be account_onboarding or account_update.', 400, 'parameter_invalid_string_enum');
13
+ // the owner is sent back to return_url when done, and to refresh_url for a new link once this one is used or expired
14
+ for (const k of ['refresh_url', 'return_url'])
15
+ if (typeof ctx.params[k] !== 'string' || !ctx.params[k])
16
+ return fail(ctx, `Missing required param: ${k}.`, 400, 'parameter_missing');
17
+ const now = Number(nowUnix(ctx.occurredAt));
18
+ const id = ctx.mint('account_link');
19
+ const link = { object: 'account_link', created: now, expires_at: now + 300, url: `https://connect.stripe.com/setup/${id}` };
20
+ await ctx.write('account_link', id, { ...link, account, type: linkType, refresh_url: ctx.params.refresh_url, return_url: ctx.params.return_url, used: false }, 'account_link.create');
21
+ return ctx.reply(link);
22
+ }
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '@volter/world-core';
2
+ export declare function GetAccountsAccountExternalAccounts(ctx: HandlerContext): Promise<Response>;
3
+ export declare function PostAccounts(ctx: HandlerContext): Promise<Response>;
4
+ export declare function PostAccountsAccountLoginLinks(ctx: HandlerContext): Promise<Response>;