@whop/elements-react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/README.md +179 -0
  2. package/android/build.gradle +64 -0
  3. package/android/consumer-rules.pro +3 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/java/com/whop/elements/WhopAddressSuggestionsModule.kt +102 -0
  6. package/android/src/main/java/com/whop/elements/WhopAuthSessionModule.kt +126 -0
  7. package/android/src/main/java/com/whop/elements/WhopElementsPackage.kt +39 -0
  8. package/android/src/main/java/com/whop/elements/WhopPayButtonManager.kt +74 -0
  9. package/android/src/main/java/com/whop/elements/WhopPayButtonPressEvent.kt +17 -0
  10. package/android/src/main/java/com/whop/elements/WhopPayButtonView.kt +178 -0
  11. package/android/src/main/java/com/whop/elements/WhopPlatformPayModule.kt +212 -0
  12. package/assets/whop-wordmark-dark.png +0 -0
  13. package/assets/whop-wordmark-dark@2x.png +0 -0
  14. package/assets/whop-wordmark-dark@3x.png +0 -0
  15. package/assets/whop-wordmark-light.png +0 -0
  16. package/assets/whop-wordmark-light@2x.png +0 -0
  17. package/assets/whop-wordmark-light@3x.png +0 -0
  18. package/ios/Package.swift +59 -0
  19. package/ios/Sources/ElementsReactNative/WhopAddressSuggestionsImpl.swift +189 -0
  20. package/ios/Sources/ElementsReactNative/WhopAuthSessionImpl.swift +106 -0
  21. package/ios/Sources/ElementsReactNative/WhopPayButtonImpl.swift +89 -0
  22. package/ios/Sources/ElementsReactNative/WhopPlatformPayImpl.swift +181 -0
  23. package/ios/generated/WhopAddressSuggestionsModule.h +11 -0
  24. package/ios/generated/WhopAddressSuggestionsModule.mm +46 -0
  25. package/ios/generated/WhopAuthSessionModule.h +11 -0
  26. package/ios/generated/WhopAuthSessionModule.mm +45 -0
  27. package/ios/generated/WhopElementsShims.h +35 -0
  28. package/ios/generated/WhopPayButtonComponentView.h +12 -0
  29. package/ios/generated/WhopPayButtonComponentView.mm +82 -0
  30. package/ios/generated/WhopPlatformPayModule.h +11 -0
  31. package/ios/generated/WhopPlatformPayModule.mm +51 -0
  32. package/lib/module/api/client.js +139 -0
  33. package/lib/module/api/client.js.map +1 -0
  34. package/lib/module/api/client.test.js +137 -0
  35. package/lib/module/api/client.test.js.map +1 -0
  36. package/lib/module/api/types.js +2 -0
  37. package/lib/module/api/types.js.map +1 -0
  38. package/lib/module/controller/actions.js +148 -0
  39. package/lib/module/controller/actions.js.map +1 -0
  40. package/lib/module/controller/actions.test.js +102 -0
  41. package/lib/module/controller/actions.test.js.map +1 -0
  42. package/lib/module/controller/store.js +158 -0
  43. package/lib/module/controller/store.js.map +1 -0
  44. package/lib/module/core/address/country-data.js +1370 -0
  45. package/lib/module/core/address/country-data.js.map +1 -0
  46. package/lib/module/core/address/country-name.js +38 -0
  47. package/lib/module/core/address/country-name.js.map +1 -0
  48. package/lib/module/core/address/country-names.generated.js +249 -0
  49. package/lib/module/core/address/country-names.generated.js.map +1 -0
  50. package/lib/module/core/address/engine.js +245 -0
  51. package/lib/module/core/address/engine.js.map +1 -0
  52. package/lib/module/core/address/engine.test.js +216 -0
  53. package/lib/module/core/address/engine.test.js.map +1 -0
  54. package/lib/module/core/address/form-state.js +409 -0
  55. package/lib/module/core/address/form-state.js.map +1 -0
  56. package/lib/module/core/address/form-state.test.js +516 -0
  57. package/lib/module/core/address/form-state.test.js.map +1 -0
  58. package/lib/module/core/address/places.js +36 -0
  59. package/lib/module/core/address/places.js.map +1 -0
  60. package/lib/module/core/address/required-coverage.test.js +57 -0
  61. package/lib/module/core/address/required-coverage.test.js.map +1 -0
  62. package/lib/module/core/address/suggest.js +102 -0
  63. package/lib/module/core/address/suggest.js.map +1 -0
  64. package/lib/module/core/address/suggest.test.js +156 -0
  65. package/lib/module/core/address/suggest.test.js.map +1 -0
  66. package/lib/module/core/address/use-suggestions.js +33 -0
  67. package/lib/module/core/address/use-suggestions.js.map +1 -0
  68. package/lib/module/core/payments/address-change.js +91 -0
  69. package/lib/module/core/payments/address-change.js.map +1 -0
  70. package/lib/module/core/payments/billing-merge.js +62 -0
  71. package/lib/module/core/payments/billing-merge.js.map +1 -0
  72. package/lib/module/core/payments/browser-info.js +42 -0
  73. package/lib/module/core/payments/browser-info.js.map +1 -0
  74. package/lib/module/core/payments/bt.js +151 -0
  75. package/lib/module/core/payments/bt.js.map +1 -0
  76. package/lib/module/core/payments/bt.test.js +23 -0
  77. package/lib/module/core/payments/bt.test.js.map +1 -0
  78. package/lib/module/core/payments/currency.js +37 -0
  79. package/lib/module/core/payments/currency.js.map +1 -0
  80. package/lib/module/core/payments/detail.js +159 -0
  81. package/lib/module/core/payments/detail.js.map +1 -0
  82. package/lib/module/core/payments/detail.test.js +225 -0
  83. package/lib/module/core/payments/detail.test.js.map +1 -0
  84. package/lib/module/core/payments/errors.js +115 -0
  85. package/lib/module/core/payments/errors.js.map +1 -0
  86. package/lib/module/core/payments/mint.js +89 -0
  87. package/lib/module/core/payments/mint.js.map +1 -0
  88. package/lib/module/core/payments/plan.js +131 -0
  89. package/lib/module/core/payments/plan.js.map +1 -0
  90. package/lib/module/core/payments/refusals.js +17 -0
  91. package/lib/module/core/payments/refusals.js.map +1 -0
  92. package/lib/module/core/payments/secure-fields.js +87 -0
  93. package/lib/module/core/payments/secure-fields.js.map +1 -0
  94. package/lib/module/core/payments/tax-id-catalog.js +616 -0
  95. package/lib/module/core/payments/tax-id-catalog.js.map +1 -0
  96. package/lib/module/core/payments/wallet-contact.js +42 -0
  97. package/lib/module/core/payments/wallet-contact.js.map +1 -0
  98. package/lib/module/core/payments/wallet-contact.test.js +77 -0
  99. package/lib/module/core/payments/wallet-contact.test.js.map +1 -0
  100. package/lib/module/elements/address.js +467 -0
  101. package/lib/module/elements/address.js.map +1 -0
  102. package/lib/module/elements/branding.js +114 -0
  103. package/lib/module/elements/branding.js.map +1 -0
  104. package/lib/module/elements/card-fields/context.js +254 -0
  105. package/lib/module/elements/card-fields/context.js.map +1 -0
  106. package/lib/module/elements/card-fields/fields.js +309 -0
  107. package/lib/module/elements/card-fields/fields.js.map +1 -0
  108. package/lib/module/elements/card-fields/index.js +30 -0
  109. package/lib/module/elements/card-fields/index.js.map +1 -0
  110. package/lib/module/elements/card.js +107 -0
  111. package/lib/module/elements/card.js.map +1 -0
  112. package/lib/module/elements/email.js +84 -0
  113. package/lib/module/elements/email.js.map +1 -0
  114. package/lib/module/elements/payment/declared-fields.js +186 -0
  115. package/lib/module/elements/payment/declared-fields.js.map +1 -0
  116. package/lib/module/elements/payment/index.js +448 -0
  117. package/lib/module/elements/payment/index.js.map +1 -0
  118. package/lib/module/elements/payment/wallet.js +132 -0
  119. package/lib/module/elements/payment/wallet.js.map +1 -0
  120. package/lib/module/elements/tax-id.js +100 -0
  121. package/lib/module/elements/tax-id.js.map +1 -0
  122. package/lib/module/i18n/catalogs.js +103 -0
  123. package/lib/module/i18n/catalogs.js.map +1 -0
  124. package/lib/module/i18n/index.js +45 -0
  125. package/lib/module/i18n/index.js.map +1 -0
  126. package/lib/module/index.js +27 -0
  127. package/lib/module/index.js.map +1 -0
  128. package/lib/module/namespace.js +174 -0
  129. package/lib/module/namespace.js.map +1 -0
  130. package/lib/module/native/index.js +128 -0
  131. package/lib/module/native/index.js.map +1 -0
  132. package/lib/module/package.json +1 -0
  133. package/lib/module/provider.js +56 -0
  134. package/lib/module/provider.js.map +1 -0
  135. package/lib/module/specs/NativeWhopAddressSuggestions.js +6 -0
  136. package/lib/module/specs/NativeWhopAddressSuggestions.js.map +1 -0
  137. package/lib/module/specs/NativeWhopAuthSession.js +10 -0
  138. package/lib/module/specs/NativeWhopAuthSession.js.map +1 -0
  139. package/lib/module/specs/NativeWhopPlatformPay.js +10 -0
  140. package/lib/module/specs/NativeWhopPlatformPay.js.map +1 -0
  141. package/lib/module/specs/WhopPayButtonNativeComponent.ts +22 -0
  142. package/lib/module/theme/appearance.js +85 -0
  143. package/lib/module/theme/appearance.js.map +1 -0
  144. package/lib/module/theme/appearance.test.js +124 -0
  145. package/lib/module/theme/appearance.test.js.map +1 -0
  146. package/lib/module/theme/provider.js +25 -0
  147. package/lib/module/theme/provider.js.map +1 -0
  148. package/lib/module/theme/resolve.js +69 -0
  149. package/lib/module/theme/resolve.js.map +1 -0
  150. package/lib/module/theme/scales.generated.js +419 -0
  151. package/lib/module/theme/scales.generated.js.map +1 -0
  152. package/lib/module/ui/element-frame.js +83 -0
  153. package/lib/module/ui/element-frame.js.map +1 -0
  154. package/lib/module/ui/payment-method-icon.js +69 -0
  155. package/lib/module/ui/payment-method-icon.js.map +1 -0
  156. package/lib/module/ui/primitives.js +266 -0
  157. package/lib/module/ui/primitives.js.map +1 -0
  158. package/lib/module/ui/select.js +281 -0
  159. package/lib/module/ui/select.js.map +1 -0
  160. package/lib/module/ui/suggestions.js +71 -0
  161. package/lib/module/ui/suggestions.js.map +1 -0
  162. package/lib/module/ui/whop-logo.js +42 -0
  163. package/lib/module/ui/whop-logo.js.map +1 -0
  164. package/lib/typescript/package.json +1 -0
  165. package/lib/typescript/src/api/client.d.ts +87 -0
  166. package/lib/typescript/src/api/client.d.ts.map +1 -0
  167. package/lib/typescript/src/api/types.d.ts +175 -0
  168. package/lib/typescript/src/api/types.d.ts.map +1 -0
  169. package/lib/typescript/src/controller/actions.d.ts +42 -0
  170. package/lib/typescript/src/controller/actions.d.ts.map +1 -0
  171. package/lib/typescript/src/controller/store.d.ts +90 -0
  172. package/lib/typescript/src/controller/store.d.ts.map +1 -0
  173. package/lib/typescript/src/core/address/country-data.d.ts +4 -0
  174. package/lib/typescript/src/core/address/country-data.d.ts.map +1 -0
  175. package/lib/typescript/src/core/address/country-name.d.ts +2 -0
  176. package/lib/typescript/src/core/address/country-name.d.ts.map +1 -0
  177. package/lib/typescript/src/core/address/country-names.generated.d.ts +2 -0
  178. package/lib/typescript/src/core/address/country-names.generated.d.ts.map +1 -0
  179. package/lib/typescript/src/core/address/engine.d.ts +97 -0
  180. package/lib/typescript/src/core/address/engine.d.ts.map +1 -0
  181. package/lib/typescript/src/core/address/form-state.d.ts +144 -0
  182. package/lib/typescript/src/core/address/form-state.d.ts.map +1 -0
  183. package/lib/typescript/src/core/address/places.d.ts +23 -0
  184. package/lib/typescript/src/core/address/places.d.ts.map +1 -0
  185. package/lib/typescript/src/core/address/suggest.d.ts +47 -0
  186. package/lib/typescript/src/core/address/suggest.d.ts.map +1 -0
  187. package/lib/typescript/src/core/address/use-suggestions.d.ts +3 -0
  188. package/lib/typescript/src/core/address/use-suggestions.d.ts.map +1 -0
  189. package/lib/typescript/src/core/payments/address-change.d.ts +52 -0
  190. package/lib/typescript/src/core/payments/address-change.d.ts.map +1 -0
  191. package/lib/typescript/src/core/payments/billing-merge.d.ts +39 -0
  192. package/lib/typescript/src/core/payments/billing-merge.d.ts.map +1 -0
  193. package/lib/typescript/src/core/payments/browser-info.d.ts +12 -0
  194. package/lib/typescript/src/core/payments/browser-info.d.ts.map +1 -0
  195. package/lib/typescript/src/core/payments/bt.d.ts +55 -0
  196. package/lib/typescript/src/core/payments/bt.d.ts.map +1 -0
  197. package/lib/typescript/src/core/payments/currency.d.ts +7 -0
  198. package/lib/typescript/src/core/payments/currency.d.ts.map +1 -0
  199. package/lib/typescript/src/core/payments/detail.d.ts +53 -0
  200. package/lib/typescript/src/core/payments/detail.d.ts.map +1 -0
  201. package/lib/typescript/src/core/payments/errors.d.ts +62 -0
  202. package/lib/typescript/src/core/payments/errors.d.ts.map +1 -0
  203. package/lib/typescript/src/core/payments/mint.d.ts +74 -0
  204. package/lib/typescript/src/core/payments/mint.d.ts.map +1 -0
  205. package/lib/typescript/src/core/payments/plan.d.ts +69 -0
  206. package/lib/typescript/src/core/payments/plan.d.ts.map +1 -0
  207. package/lib/typescript/src/core/payments/refusals.d.ts +12 -0
  208. package/lib/typescript/src/core/payments/refusals.d.ts.map +1 -0
  209. package/lib/typescript/src/core/payments/secure-fields.d.ts +33 -0
  210. package/lib/typescript/src/core/payments/secure-fields.d.ts.map +1 -0
  211. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts +481 -0
  212. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts.map +1 -0
  213. package/lib/typescript/src/core/payments/wallet-contact.d.ts +40 -0
  214. package/lib/typescript/src/core/payments/wallet-contact.d.ts.map +1 -0
  215. package/lib/typescript/src/elements/address.d.ts +55 -0
  216. package/lib/typescript/src/elements/address.d.ts.map +1 -0
  217. package/lib/typescript/src/elements/branding.d.ts +18 -0
  218. package/lib/typescript/src/elements/branding.d.ts.map +1 -0
  219. package/lib/typescript/src/elements/card-fields/context.d.ts +67 -0
  220. package/lib/typescript/src/elements/card-fields/context.d.ts.map +1 -0
  221. package/lib/typescript/src/elements/card-fields/fields.d.ts +15 -0
  222. package/lib/typescript/src/elements/card-fields/fields.d.ts.map +1 -0
  223. package/lib/typescript/src/elements/card-fields/index.d.ts +21 -0
  224. package/lib/typescript/src/elements/card-fields/index.d.ts.map +1 -0
  225. package/lib/typescript/src/elements/card.d.ts +21 -0
  226. package/lib/typescript/src/elements/card.d.ts.map +1 -0
  227. package/lib/typescript/src/elements/email.d.ts +31 -0
  228. package/lib/typescript/src/elements/email.d.ts.map +1 -0
  229. package/lib/typescript/src/elements/payment/declared-fields.d.ts +43 -0
  230. package/lib/typescript/src/elements/payment/declared-fields.d.ts.map +1 -0
  231. package/lib/typescript/src/elements/payment/index.d.ts +37 -0
  232. package/lib/typescript/src/elements/payment/index.d.ts.map +1 -0
  233. package/lib/typescript/src/elements/payment/wallet.d.ts +39 -0
  234. package/lib/typescript/src/elements/payment/wallet.d.ts.map +1 -0
  235. package/lib/typescript/src/elements/tax-id.d.ts +41 -0
  236. package/lib/typescript/src/elements/tax-id.d.ts.map +1 -0
  237. package/lib/typescript/src/i18n/catalogs.d.ts +7 -0
  238. package/lib/typescript/src/i18n/catalogs.d.ts.map +1 -0
  239. package/lib/typescript/src/i18n/index.d.ts +17 -0
  240. package/lib/typescript/src/i18n/index.d.ts.map +1 -0
  241. package/lib/typescript/src/index.d.ts +42 -0
  242. package/lib/typescript/src/index.d.ts.map +1 -0
  243. package/lib/typescript/src/namespace.d.ts +36 -0
  244. package/lib/typescript/src/namespace.d.ts.map +1 -0
  245. package/lib/typescript/src/native/index.d.ts +62 -0
  246. package/lib/typescript/src/native/index.d.ts.map +1 -0
  247. package/lib/typescript/src/provider.d.ts +37 -0
  248. package/lib/typescript/src/provider.d.ts.map +1 -0
  249. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts +13 -0
  250. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts.map +1 -0
  251. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts +20 -0
  252. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts.map +1 -0
  253. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts +24 -0
  254. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts.map +1 -0
  255. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts +21 -0
  256. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts.map +1 -0
  257. package/lib/typescript/src/theme/appearance.d.ts +71 -0
  258. package/lib/typescript/src/theme/appearance.d.ts.map +1 -0
  259. package/lib/typescript/src/theme/provider.d.ts +11 -0
  260. package/lib/typescript/src/theme/provider.d.ts.map +1 -0
  261. package/lib/typescript/src/theme/resolve.d.ts +19 -0
  262. package/lib/typescript/src/theme/resolve.d.ts.map +1 -0
  263. package/lib/typescript/src/theme/scales.generated.d.ts +18 -0
  264. package/lib/typescript/src/theme/scales.generated.d.ts.map +1 -0
  265. package/lib/typescript/src/ui/element-frame.d.ts +32 -0
  266. package/lib/typescript/src/ui/element-frame.d.ts.map +1 -0
  267. package/lib/typescript/src/ui/payment-method-icon.d.ts +21 -0
  268. package/lib/typescript/src/ui/payment-method-icon.d.ts.map +1 -0
  269. package/lib/typescript/src/ui/primitives.d.ts +94 -0
  270. package/lib/typescript/src/ui/primitives.d.ts.map +1 -0
  271. package/lib/typescript/src/ui/select.d.ts +25 -0
  272. package/lib/typescript/src/ui/select.d.ts.map +1 -0
  273. package/lib/typescript/src/ui/suggestions.d.ts +15 -0
  274. package/lib/typescript/src/ui/suggestions.d.ts.map +1 -0
  275. package/lib/typescript/src/ui/whop-logo.d.ts +4 -0
  276. package/lib/typescript/src/ui/whop-logo.d.ts.map +1 -0
  277. package/package.json +121 -0
  278. package/scripts/check-swift.sh +27 -0
  279. package/scripts/codegen-shims.mjs +409 -0
  280. package/scripts/gen-country-names.mjs +58 -0
  281. package/scripts/gen-theme.mjs +124 -0
  282. package/scripts/gen-wordmark.mjs +68 -0
  283. package/scripts/mirror.mjs +112 -0
  284. package/src/api/client.ts +202 -0
  285. package/src/api/types.ts +166 -0
  286. package/src/controller/actions.ts +197 -0
  287. package/src/controller/store.ts +201 -0
  288. package/src/core/address/country-data.ts +927 -0
  289. package/src/core/address/country-name.ts +36 -0
  290. package/src/core/address/country-names.generated.ts +246 -0
  291. package/src/core/address/engine.ts +349 -0
  292. package/src/core/address/form-state.ts +479 -0
  293. package/src/core/address/places.ts +39 -0
  294. package/src/core/address/suggest.ts +153 -0
  295. package/src/core/address/use-suggestions.ts +37 -0
  296. package/src/core/payments/address-change.ts +121 -0
  297. package/src/core/payments/billing-merge.ts +91 -0
  298. package/src/core/payments/browser-info.ts +43 -0
  299. package/src/core/payments/bt.ts +176 -0
  300. package/src/core/payments/currency.ts +54 -0
  301. package/src/core/payments/detail.ts +204 -0
  302. package/src/core/payments/errors.ts +171 -0
  303. package/src/core/payments/mint.ts +130 -0
  304. package/src/core/payments/plan.ts +198 -0
  305. package/src/core/payments/refusals.ts +24 -0
  306. package/src/core/payments/secure-fields.ts +85 -0
  307. package/src/core/payments/tax-id-catalog.ts +320 -0
  308. package/src/core/payments/wallet-contact.ts +56 -0
  309. package/src/elements/address.tsx +570 -0
  310. package/src/elements/branding.tsx +87 -0
  311. package/src/elements/card-fields/context.tsx +310 -0
  312. package/src/elements/card-fields/fields.tsx +281 -0
  313. package/src/elements/card-fields/index.tsx +37 -0
  314. package/src/elements/card.tsx +101 -0
  315. package/src/elements/email.tsx +100 -0
  316. package/src/elements/payment/declared-fields.tsx +238 -0
  317. package/src/elements/payment/index.tsx +497 -0
  318. package/src/elements/payment/wallet.tsx +155 -0
  319. package/src/elements/tax-id.tsx +143 -0
  320. package/src/i18n/catalogs.ts +100 -0
  321. package/src/i18n/index.tsx +46 -0
  322. package/src/index.ts +68 -0
  323. package/src/namespace.tsx +231 -0
  324. package/src/native/index.ts +173 -0
  325. package/src/provider.tsx +76 -0
  326. package/src/specs/NativeWhopAddressSuggestions.ts +14 -0
  327. package/src/specs/NativeWhopAuthSession.ts +21 -0
  328. package/src/specs/NativeWhopPlatformPay.ts +25 -0
  329. package/src/specs/WhopPayButtonNativeComponent.ts +22 -0
  330. package/src/theme/appearance.ts +150 -0
  331. package/src/theme/provider.tsx +28 -0
  332. package/src/theme/resolve.ts +68 -0
  333. package/src/theme/scales.generated.ts +427 -0
  334. package/src/ui/element-frame.tsx +94 -0
  335. package/src/ui/payment-method-icon.tsx +55 -0
  336. package/src/ui/primitives.tsx +269 -0
  337. package/src/ui/select.tsx +255 -0
  338. package/src/ui/suggestions.tsx +69 -0
  339. package/src/ui/whop-logo.tsx +37 -0
@@ -0,0 +1,204 @@
1
+ // gating rules ported from frontend/apps/web/src/elements/payments/lib/detail.tsx — keep in sync
2
+
3
+ import type { PaymentMethodConfigurationShape, PaymentMethodTypeResource } from '../../api/types';
4
+ import { currencyMinorDigits } from './currency';
5
+
6
+ export type MatrixEntry = PaymentMethodTypeResource & {
7
+ next_action_render_modes?: string[];
8
+ supports_fresh_collection?: boolean;
9
+ submit_button?: { label?: string; kind?: string };
10
+ };
11
+
12
+ export type { PaymentMethodConfigurationShape };
13
+
14
+ /**
15
+ * Which checkout is asking. The matrix leaves out methods that do not serve the named surface, and
16
+ * the axis is who BUILT the checkout, not where it runs.
17
+ *
18
+ * A React Native mount is `direct_checkout`: it owns its own presentation the way a first-party
19
+ * whop.com document does, and unlike an iframe it can hand a full-page step to the system browser.
20
+ * Sending `external_checkout` would drop every `full_page`-only method from the list.
21
+ */
22
+ export type CheckoutSurface = 'direct_checkout' | 'embedded_checkout' | 'modal_checkout' | 'external_checkout';
23
+
24
+ const TAB_OWNING_SURFACES: readonly CheckoutSurface[] = ['direct_checkout', 'modal_checkout'];
25
+
26
+ /**
27
+ * The categories this build can actually collect. An entry from a newer matrix in a category this
28
+ * build does not implement is a rail it cannot run, so it is skipped rather than rendered as a
29
+ * broken tile — the same gate web added in #25658 (apps/web/src/elements/payments/lib/detail.tsx).
30
+ *
31
+ * `balance` is web's list minus one: ledger-balance payment is not in this release (README), so
32
+ * claiming it here would render a tile that cannot complete.
33
+ */
34
+ const IMPLEMENTED_CATEGORIES: readonly string[] = [
35
+ 'card',
36
+ 'wallet',
37
+ 'bank_debit',
38
+ 'bank_transfer',
39
+ 'voucher',
40
+ 'redirect',
41
+ 'crypto',
42
+ ];
43
+
44
+ const TEMPLATES = ['redirect', 'bnpl_confirm', 'bank_connect', 'bank_wire', 'wallet_sheet', 'none'] as const;
45
+ export type DetailTemplate = (typeof TEMPLATES)[number];
46
+
47
+ export interface WalletAvailability {
48
+ applePay?: boolean;
49
+ googlePay?: boolean;
50
+ }
51
+
52
+ export function detailTemplate(entry: MatrixEntry): DetailTemplate {
53
+ const declared = entry.detail?.template;
54
+ if (declared && (TEMPLATES as readonly string[]).includes(declared)) return declared as DetailTemplate;
55
+ // An unknown template is a newer matrix than this build; redirect copy is the safe read.
56
+ return 'redirect';
57
+ }
58
+
59
+ export function passesConfiguration(
60
+ type: string,
61
+ configuration: PaymentMethodConfigurationShape | undefined,
62
+ platformDefault?: boolean,
63
+ ): boolean {
64
+ if (!configuration) return platformDefault ?? true;
65
+ const enabled = configuration.enabled ?? [];
66
+ if (!(configuration.include_platform_defaults ?? true)) return enabled.includes(type);
67
+ // (platform defaults − disabled + enabled): an off-by-default type needs an explicit enable,
68
+ // so "not disabled" alone is not an allow.
69
+ return ((platformDefault ?? true) && !(configuration.disabled ?? []).includes(type)) || enabled.includes(type);
70
+ }
71
+
72
+ /** The entry's category-keyed config block, read generically. */
73
+ export function methodConfigBlock(entry: MatrixEntry): Record<string, unknown> {
74
+ const block = (entry as unknown as Record<string, unknown>)[entry.category];
75
+ return block && typeof block === 'object' ? (block as Record<string, unknown>) : {};
76
+ }
77
+
78
+ export function methodTokenizerKey(entry: MatrixEntry): string | undefined {
79
+ const key = methodConfigBlock(entry).public_key;
80
+ return typeof key === 'string' ? key : undefined;
81
+ }
82
+
83
+ /**
84
+ * Whether the entry is offered for this charge on this surface.
85
+ *
86
+ * Amount-gated methods are excluded outright rather than rendered disabled, and every absent field
87
+ * reads as unrestricted so an older matrix keeps its behavior.
88
+ */
89
+ export function presentMethod(
90
+ entry: MatrixEntry,
91
+ currency: string,
92
+ amount: number,
93
+ setupFutureUsage: 'off_session' | 'on_session' | undefined,
94
+ walletAvailability: WalletAvailability | undefined,
95
+ configuration: PaymentMethodConfigurationShape | undefined,
96
+ surface: CheckoutSurface,
97
+ country?: string,
98
+ ): boolean {
99
+ // A category this build does not implement is a rail it cannot collect — an entry from a newer
100
+ // matrix must be skipped, never rendered as a broken tile.
101
+ if (!IMPLEMENTED_CATEGORIES.includes(entry.category)) return false;
102
+ if (
103
+ !TAB_OWNING_SURFACES.includes(surface) &&
104
+ entry.next_action_render_modes &&
105
+ !entry.next_action_render_modes.includes('inline')
106
+ ) {
107
+ return false;
108
+ }
109
+ if (!passesConfiguration(entry.type, configuration, entry.platform_default)) return false;
110
+ if (!entry.usage?.one_time) return false;
111
+ if (setupFutureUsage === 'off_session' && !entry.usage?.off_session) return false;
112
+ if (setupFutureUsage === 'on_session' && !entry.usage?.on_session) return false;
113
+ if (entry.category === 'card' && !entry.card?.public_key) return false;
114
+ // A secure declared field tokenizes into the config block's key at collect time; without the key
115
+ // the method is broken at confirm, so it is never offered.
116
+ if ((entry.required_fields ?? []).some((field) => field.secure) && !methodTokenizerKey(entry)) return false;
117
+ if (detailTemplate(entry) === 'wallet_sheet') {
118
+ if (entry.type === 'apple_pay' && !walletAvailability?.applePay) return false;
119
+ if (entry.type === 'google_pay' && !walletAvailability?.googlePay) return false;
120
+ }
121
+ const bounds = entry.currencies?.[currency.toLowerCase()];
122
+ if (!bounds) return false;
123
+ const { min, max } = amountBounds(bounds, country);
124
+ if (min != null && amount < min) return false;
125
+ if (max != null && amount > max) return false;
126
+ return true;
127
+ }
128
+
129
+ /**
130
+ * A currency's limits, narrowed to the billing country when the matrix publishes a row for it.
131
+ *
132
+ * Reading only the currency-level pair offered methods that the selected country cannot actually
133
+ * take at this amount, which the buyer discovers at confirm rather than in the list. Mirrors
134
+ * `amountBounds` in apps/web/src/elements/payments/lib/detail.tsx.
135
+ */
136
+ function amountBounds(
137
+ bounds: NonNullable<MatrixEntry['currencies']>[string],
138
+ country: string | undefined,
139
+ ): { min: number | null | undefined; max: number | null | undefined } {
140
+ const code = country?.toLowerCase();
141
+ const row = code ? bounds.countries?.find((candidate) => candidate.country?.toLowerCase() === code) : undefined;
142
+ if (!row) return { min: bounds.min_amount, max: bounds.max_amount };
143
+ return { min: row.min_amount, max: row.max_amount };
144
+ }
145
+
146
+ /** Listed types take their list position; unlisted types share the tail rank, so a stable sort
147
+ * preserves their incoming order. */
148
+ export function orderRank(type: string, order: readonly string[]): number {
149
+ const index = order.indexOf(type);
150
+ return index === -1 ? order.length : index;
151
+ }
152
+
153
+ export function formatMinorAmount(amount: number, currency: string, locale: string): string {
154
+ // The divisor is the backend-mirrored factor; the display digits are Intl's. They disagree for
155
+ // currencies like huf, and the backend's answer is the one that priced the charge.
156
+ const major = amount / 10 ** currencyMinorDigits(currency);
157
+ try {
158
+ return new Intl.NumberFormat(locale, { style: 'currency', currency: currency.toUpperCase() }).format(major);
159
+ } catch {
160
+ return `${major} ${currency.toUpperCase()}`;
161
+ }
162
+ }
163
+
164
+ export function fieldLabel(name: string): string {
165
+ const spaced = name.replace(/_/g, ' ').replace(/\bid\b/gi, 'ID');
166
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
167
+ }
168
+
169
+ /** A declared field's country-keyed pattern, `*` as the fallback. Never throws on a bad pattern. */
170
+ export function fieldMatches(
171
+ formats: Record<string, string> | undefined,
172
+ country: string | undefined,
173
+ value: string,
174
+ ): boolean {
175
+ if (!formats) return true;
176
+ const trimmed = value.trim();
177
+ const patterns = [formats[(country ?? '').toLowerCase()], formats['*']].filter(
178
+ (p): p is string => typeof p === 'string',
179
+ );
180
+ return patterns.length === 0 || patterns.some((pattern) => safeTest(pattern, trimmed));
181
+ }
182
+
183
+ function safeTest(pattern: string, value: string): boolean {
184
+ try {
185
+ return new RegExp(pattern).test(value);
186
+ } catch {
187
+ return true;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * The billing countries a method constrains the buyer to, or null when it constrains nothing.
193
+ *
194
+ * `*` anywhere in the entry's per-currency list means unrestricted, and so does an empty list — a
195
+ * method that names countries names ALL of the ones it serves. The address element intersects the
196
+ * result with the integrator's own `allowedCountries` rather than replacing it.
197
+ */
198
+ export function methodBillingCountries(
199
+ entry: Pick<PaymentMethodTypeResource, 'currencies'> | undefined,
200
+ currency: string,
201
+ ): string[] | null {
202
+ const codes = (entry?.currencies?.[currency]?.countries ?? []).map((row) => row.country);
203
+ return codes.length === 0 || codes.includes('*') ? null : codes;
204
+ }
@@ -0,0 +1,171 @@
1
+ // ported verbatim from frontend/apps/web/src/elements/payments/lib/errors.ts — keep in sync when the web element changes
2
+
3
+ import { WhopError } from '../../api/client';
4
+
5
+ export type ChargeConfigErrorCode =
6
+ | 'PLAN_INLINE_CONFLICT'
7
+ | 'CHARGE_CONFIG_MISSING'
8
+ | 'PLAN_ACCOUNT_MISMATCH'
9
+ | 'PLAN_RESOLUTION_FAILED';
10
+
11
+ /** The plan/inline charge-config refusals — CODED like the framework's lifecycle refusals
12
+ * (switch on `code`, never string-match). The message carries the code prefix too because a
13
+ * wire boot failure (`controller-failed`) delivers the MESSAGE alone to the handle's onError. */
14
+ export class ChargeConfigError extends Error {
15
+ readonly code: ChargeConfigErrorCode;
16
+
17
+ constructor(code: ChargeConfigErrorCode, message: string) {
18
+ super(`[${code}] ${message}`);
19
+ this.code = code;
20
+ this.name = 'ChargeConfigError';
21
+ }
22
+ }
23
+
24
+ export type BrandingRequiredErrorCode = 'BRANDING_NOT_MOUNTED' | 'BRANDING_HIDDEN';
25
+
26
+ /** The merchant-of-record refusal. `BRANDING_NOT_MOUNTED` remains in the error-code contract for
27
+ * clients compiled against 1.0.0-beta.1; the compatibility runtime only emits `BRANDING_HIDDEN`
28
+ * when a mounted notice has been CSS-hidden or shrunk. */
29
+ export class BrandingRequiredError extends Error {
30
+ readonly code: BrandingRequiredErrorCode;
31
+
32
+ constructor(code: BrandingRequiredErrorCode, message: string) {
33
+ super(`[${code}] ${message}`);
34
+ this.code = code;
35
+ this.name = 'BrandingRequiredError';
36
+ }
37
+ }
38
+
39
+ export type SelectErrorCode = 'METHOD_NOT_OFFERED';
40
+
41
+ /** The `select` action's refusals — the same coded shape as ChargeConfigError (switch on `code`;
42
+ * the message carries the code prefix because the wire rpc rail delivers the message alone). */
43
+ export class SelectError extends Error {
44
+ readonly code: SelectErrorCode;
45
+
46
+ constructor(code: SelectErrorCode, message: string) {
47
+ super(`[${code}] ${message}`);
48
+ this.code = code;
49
+ this.name = 'SelectError';
50
+ }
51
+ }
52
+
53
+ export type SecureFieldErrorCode = 'SECURE_FIELD_TOKENIZER_UNAVAILABLE' | 'SECURE_FIELD_TOKENIZE_FAILED';
54
+
55
+ /** The secure declared-field refusals (a secure value could not be tokenized, so the confirm
56
+ * never ran) — the same coded shape as the other refusal families. */
57
+ export class SecureFieldError extends Error {
58
+ readonly code: SecureFieldErrorCode;
59
+
60
+ constructor(code: SecureFieldErrorCode, message: string) {
61
+ super(`[${code}] ${message}`);
62
+ this.code = code;
63
+ this.name = 'SecureFieldError';
64
+ }
65
+ }
66
+
67
+ export type PaymentTokenizationFailureStage =
68
+ | 'card_fields'
69
+ | 'card_tokenizer_session'
70
+ | 'card_number'
71
+ | 'card_session_authorization'
72
+ | 'card_expiry'
73
+ | 'card_cvc'
74
+ | 'confirmation_token_create';
75
+
76
+ export type PaymentTokenizationErrorCode =
77
+ | 'card_fields_not_ready'
78
+ | 'card_tokenizer_session_failed'
79
+ | 'card_number_validation_failed'
80
+ | 'card_number_tokenization_failed'
81
+ | 'card_session_authorization_failed'
82
+ | 'card_expiry_validation_failed'
83
+ | 'card_expiry_tokenization_failed'
84
+ | 'card_cvc_validation_failed'
85
+ | 'card_cvc_tokenization_failed'
86
+ | 'confirmation_token_create_failed';
87
+
88
+ interface PaymentTokenizationFailure {
89
+ code: PaymentTokenizationErrorCode;
90
+ failureStage: PaymentTokenizationFailureStage;
91
+ message: string;
92
+ }
93
+
94
+ const tokenizationStageByCode: Record<PaymentTokenizationErrorCode, PaymentTokenizationFailureStage> = {
95
+ card_fields_not_ready: 'card_fields',
96
+ card_tokenizer_session_failed: 'card_tokenizer_session',
97
+ card_number_validation_failed: 'card_number',
98
+ card_number_tokenization_failed: 'card_number',
99
+ card_session_authorization_failed: 'card_session_authorization',
100
+ card_expiry_validation_failed: 'card_expiry',
101
+ card_expiry_tokenization_failed: 'card_expiry',
102
+ card_cvc_validation_failed: 'card_cvc',
103
+ card_cvc_tokenization_failed: 'card_cvc',
104
+ confirmation_token_create_failed: 'confirmation_token_create',
105
+ };
106
+
107
+ export class PaymentTokenizationError extends Error {
108
+ readonly code: PaymentTokenizationErrorCode;
109
+ readonly failureStage: PaymentTokenizationFailureStage;
110
+ readonly buyerMessage: string;
111
+
112
+ constructor(code: PaymentTokenizationErrorCode, message: string) {
113
+ super(`[${code}] ${message}`);
114
+ this.code = code;
115
+ this.failureStage = tokenizationStageByCode[code];
116
+ this.buyerMessage = message;
117
+ this.name = 'PaymentTokenizationError';
118
+ }
119
+ }
120
+
121
+ export function paymentTokenizationFailureOf(error: unknown): PaymentTokenizationFailure | undefined {
122
+ if (error instanceof PaymentTokenizationError) {
123
+ return { code: error.code, failureStage: error.failureStage, message: error.buyerMessage };
124
+ }
125
+ const message = error instanceof Error ? error.message : '';
126
+ const match = /^\[([a-z_]+)\] ([\s\S]*)$/.exec(message);
127
+ const code = match?.[1];
128
+ if (!code || !Object.hasOwn(tokenizationStageByCode, code)) return undefined;
129
+ const typedCode = code as PaymentTokenizationErrorCode;
130
+ return { code: typedCode, failureStage: tokenizationStageByCode[typedCode], message: match[2] ?? '' };
131
+ }
132
+
133
+ export function paymentTokenizationBuyerMessage(error: unknown, fallback: string): string {
134
+ return (
135
+ paymentTokenizationFailureOf(error)?.message ?? (error instanceof Error && error.message ? error.message : fallback)
136
+ );
137
+ }
138
+
139
+ export type NextActionErrorCode = 'FULL_PAGE_STEP_IN_FRAME';
140
+
141
+ /** handleNextAction's posture refusal (a full_page-only step reached a document that cannot
142
+ * navigate the whole tab, so nothing ran) — the same coded shape as the other refusal
143
+ * families. */
144
+ export class NextActionError extends Error {
145
+ readonly code: NextActionErrorCode;
146
+
147
+ constructor(code: NextActionErrorCode, message: string) {
148
+ super(`[${code}] ${message}`);
149
+ this.code = code;
150
+ this.name = 'NextActionError';
151
+ }
152
+ }
153
+
154
+ /** Whether a caught error is this family's refusal for `code`, wherever it was caught: the
155
+ * structured `code` survives an in-process throw, and the `[CODE]` message prefix is the
156
+ * family's own WIRE encoding — the rpc rail re-mints a rejection from `.message` alone, which
157
+ * is the whole reason these constructors bake the code into the message. Decoding the prefix
158
+ * here is reading that contract, not string-matching prose. */
159
+ export function isNextActionRefusal(error: unknown, code: NextActionErrorCode): boolean {
160
+ if ((error as { code?: unknown } | null)?.code === code) return true;
161
+ const message = error instanceof Error ? error.message : '';
162
+ return message.startsWith(`[${code}]`);
163
+ }
164
+
165
+ export function apiErrorMessage(err: unknown, fallback: string): string {
166
+ if (err instanceof WhopError) {
167
+ const body = err.body as { error?: { message?: string } } | null | undefined;
168
+ return body?.error?.message ?? `${fallback} (${err.statusCode})`;
169
+ }
170
+ return err instanceof Error ? err.message : fallback;
171
+ }
@@ -0,0 +1,130 @@
1
+ // ported verbatim from frontend/apps/web/src/elements/payments/lib/mint.ts — keep in sync when the web element changes
2
+
3
+ import type { WhopClient } from '../../api/client';
4
+
5
+ import { capturedBrowserInfo } from './browser-info';
6
+ import { PaymentTokenizationError, apiErrorMessage } from './errors';
7
+
8
+ export interface MintBillingDetails {
9
+ email: string;
10
+ name?: string;
11
+ phone?: string;
12
+ address?: { country?: string; line1?: string; city?: string; postal_code?: string };
13
+ }
14
+
15
+ export interface MintContext {
16
+ accountId: string | undefined;
17
+ returnUrl: string | undefined;
18
+ setupFutureUsage: 'off_session' | 'on_session' | undefined;
19
+ }
20
+
21
+ /** The collected bag a surface's `collect` verb returns — everything the CONTROLLER needs to
22
+ * mint: the assembled payment_method slice, the surface-validated billing block, and any
23
+ * declared inputs that belong on billing_details. Serializable — it crosses the surface's
24
+ * frame into the controller frame. */
25
+ export interface CollectedPayment {
26
+ paymentMethod: { type: string; category: string } & Record<string, unknown>;
27
+ billingDetails: MintBillingDetails;
28
+ billingExtra?: Record<string, string>;
29
+ }
30
+
31
+ /** The ONE confirmation-token mint — run by the CONTROLLER's internal action only, in the
32
+ * controller frame, with the controller's sdk + accountId/returnUrl/setupFutureUsage context.
33
+ * (Exception: WhopPaymentRequest tokenizes + mints host-side — it must run without a mounted
34
+ * controller.) */
35
+ export async function mintConfirmationToken(
36
+ sdk: WhopClient,
37
+ context: MintContext,
38
+ paymentMethod: { type: string; category: string } & Record<string, unknown>,
39
+ billingDetails: MintBillingDetails,
40
+ billingExtra?: Record<string, string>,
41
+ ): Promise<{ confirmationToken: string; type: string }> {
42
+ if (!context.accountId) throw new Error('No seller account is in scope for this surface');
43
+ // A SAVED reference states less than a collected one, and the API REFUSES the difference rather
44
+ // than ignoring it: the type is read off the stored row, and consent to store was given when the
45
+ // method was first saved. Sending either back is a 400 telling us the element misread the flow —
46
+ // so the type is dropped from the body (it is still returned to the caller from the selection)
47
+ // and `setup_future_usage` never rides along. A BALANCE reference keeps its type (single-valued,
48
+ // server-verified) but is never vaulted, so the attested consent is refused there too.
49
+ const saved = paymentMethod.category === 'saved';
50
+ const referenced = saved || paymentMethod.category === 'balance';
51
+ const { type: _selectedType, ...savedPayload } = paymentMethod;
52
+ const token = await sdk.confirmationTokens
53
+ .create({
54
+ account_id: context.accountId,
55
+ // The generated params type CLOSES payment_method over the category blocks, but the
56
+ // design of record sends method-DECLARED dynamic fields as top-level keys (INVENTORY
57
+ // backend obligation: the schema must admit them; strong params must not drop them).
58
+ payment_method: (saved ? savedPayload : paymentMethod) as unknown as Parameters<
59
+ typeof sdk.confirmationTokens.create
60
+ >[0]['payment_method'],
61
+ billing_details: {
62
+ ...billingDetails,
63
+ ...billingExtra,
64
+ },
65
+ return_url: context.returnUrl,
66
+ setup_future_usage: referenced ? undefined : context.setupFutureUsage,
67
+ browser_info: capturedBrowserInfo(),
68
+ })
69
+ .catch((err: unknown) => {
70
+ throw new PaymentTokenizationError('confirmation_token_create_failed', apiErrorMessage(err, 'collection failed'));
71
+ });
72
+ return { confirmationToken: token.id, type: paymentMethod.type };
73
+ }
74
+
75
+ export function validateBillingEmail(billing: { email?: string } | undefined): MintBillingDetails {
76
+ if (!billing?.email) throw new Error('billingDetails.email is required');
77
+ return { ...billing, email: billing.email };
78
+ }
79
+
80
+ export function assertCardBilling(billing: MintBillingDetails): void {
81
+ if (!billing.name) throw new Error('billingDetails.name is required for card payments');
82
+ }
83
+
84
+ interface CollectionSurface {
85
+ /** advisory CLAIM-axis liveness (`ctx.children.<key>.created`) — "may I address it". */
86
+ created: boolean;
87
+ collect: (input: unknown) => Promise<unknown>;
88
+ }
89
+
90
+ export interface CollectionSurfaces {
91
+ /** the payment element's collect (a.controllerOnly — it orchestrates its own selection). */
92
+ payment: CollectionSurface;
93
+ /** the fused card element's collect (a.controllerOnly). */
94
+ card: CollectionSurface;
95
+ /** the exploded card-fields sub's collect (a.controller — the only parent-reachable action
96
+ * kind on a sub: `ctx.children.<sub>` routes through `runtime.dispatch`, which refuses
97
+ * internal). */
98
+ cardFields: CollectionSurface;
99
+ }
100
+
101
+ /** The framework's CODED lifecycle refusals (`childRefusal`, controller-core): switch on
102
+ * `code`, never string-match — the message is the human channel. */
103
+ export function isChildRefusal(err: unknown): boolean {
104
+ const code = (err as { code?: unknown } | null)?.code;
105
+ return code === 'CHILD_NOT_CREATED' || code === 'CHILD_DESTROYED';
106
+ }
107
+
108
+ /**
109
+ * The delegation cascade: COLLECTION belongs to the active surface, tried in precedence order —
110
+ * payment element, fused card element, exploded card-fields unit; each validates its own
111
+ * requirements and returns the serializable collected bag, and the CONTROLLER mints from it.
112
+ * Occupancy is DECIDED, not caught: `.created` (the advisory claim-axis probe) picks the live
113
+ * face; a coded lifecycle refusal (the check→invoke race — a face destroyed in between) falls
114
+ * through to the next; a live surface's own error re-throws untouched — its refusal is the
115
+ * answer.
116
+ */
117
+ export async function collectThroughLiveSurface(
118
+ surfaces: CollectionSurfaces,
119
+ input: unknown,
120
+ ): Promise<CollectedPayment> {
121
+ for (const surface of [surfaces.payment, surfaces.card, surfaces.cardFields]) {
122
+ if (!surface.created) continue;
123
+ try {
124
+ return (await surface.collect(input)) as CollectedPayment;
125
+ } catch (err) {
126
+ if (!isChildRefusal(err)) throw err;
127
+ }
128
+ }
129
+ throw new Error('Mount a collection surface first — the payment element, the card element, or the card-fields unit');
130
+ }