@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.
- package/README.md +179 -0
- package/android/build.gradle +64 -0
- package/android/consumer-rules.pro +3 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/java/com/whop/elements/WhopAddressSuggestionsModule.kt +102 -0
- package/android/src/main/java/com/whop/elements/WhopAuthSessionModule.kt +126 -0
- package/android/src/main/java/com/whop/elements/WhopElementsPackage.kt +39 -0
- package/android/src/main/java/com/whop/elements/WhopPayButtonManager.kt +74 -0
- package/android/src/main/java/com/whop/elements/WhopPayButtonPressEvent.kt +17 -0
- package/android/src/main/java/com/whop/elements/WhopPayButtonView.kt +178 -0
- package/android/src/main/java/com/whop/elements/WhopPlatformPayModule.kt +212 -0
- package/assets/whop-wordmark-dark.png +0 -0
- package/assets/whop-wordmark-dark@2x.png +0 -0
- package/assets/whop-wordmark-dark@3x.png +0 -0
- package/assets/whop-wordmark-light.png +0 -0
- package/assets/whop-wordmark-light@2x.png +0 -0
- package/assets/whop-wordmark-light@3x.png +0 -0
- package/ios/Package.swift +59 -0
- package/ios/Sources/ElementsReactNative/WhopAddressSuggestionsImpl.swift +189 -0
- package/ios/Sources/ElementsReactNative/WhopAuthSessionImpl.swift +106 -0
- package/ios/Sources/ElementsReactNative/WhopPayButtonImpl.swift +89 -0
- package/ios/Sources/ElementsReactNative/WhopPlatformPayImpl.swift +181 -0
- package/ios/generated/WhopAddressSuggestionsModule.h +11 -0
- package/ios/generated/WhopAddressSuggestionsModule.mm +46 -0
- package/ios/generated/WhopAuthSessionModule.h +11 -0
- package/ios/generated/WhopAuthSessionModule.mm +45 -0
- package/ios/generated/WhopElementsShims.h +35 -0
- package/ios/generated/WhopPayButtonComponentView.h +12 -0
- package/ios/generated/WhopPayButtonComponentView.mm +82 -0
- package/ios/generated/WhopPlatformPayModule.h +11 -0
- package/ios/generated/WhopPlatformPayModule.mm +51 -0
- package/lib/module/api/client.js +139 -0
- package/lib/module/api/client.js.map +1 -0
- package/lib/module/api/client.test.js +137 -0
- package/lib/module/api/client.test.js.map +1 -0
- package/lib/module/api/types.js +2 -0
- package/lib/module/api/types.js.map +1 -0
- package/lib/module/controller/actions.js +148 -0
- package/lib/module/controller/actions.js.map +1 -0
- package/lib/module/controller/actions.test.js +102 -0
- package/lib/module/controller/actions.test.js.map +1 -0
- package/lib/module/controller/store.js +158 -0
- package/lib/module/controller/store.js.map +1 -0
- package/lib/module/core/address/country-data.js +1370 -0
- package/lib/module/core/address/country-data.js.map +1 -0
- package/lib/module/core/address/country-name.js +38 -0
- package/lib/module/core/address/country-name.js.map +1 -0
- package/lib/module/core/address/country-names.generated.js +249 -0
- package/lib/module/core/address/country-names.generated.js.map +1 -0
- package/lib/module/core/address/engine.js +245 -0
- package/lib/module/core/address/engine.js.map +1 -0
- package/lib/module/core/address/engine.test.js +216 -0
- package/lib/module/core/address/engine.test.js.map +1 -0
- package/lib/module/core/address/form-state.js +409 -0
- package/lib/module/core/address/form-state.js.map +1 -0
- package/lib/module/core/address/form-state.test.js +516 -0
- package/lib/module/core/address/form-state.test.js.map +1 -0
- package/lib/module/core/address/places.js +36 -0
- package/lib/module/core/address/places.js.map +1 -0
- package/lib/module/core/address/required-coverage.test.js +57 -0
- package/lib/module/core/address/required-coverage.test.js.map +1 -0
- package/lib/module/core/address/suggest.js +102 -0
- package/lib/module/core/address/suggest.js.map +1 -0
- package/lib/module/core/address/suggest.test.js +156 -0
- package/lib/module/core/address/suggest.test.js.map +1 -0
- package/lib/module/core/address/use-suggestions.js +33 -0
- package/lib/module/core/address/use-suggestions.js.map +1 -0
- package/lib/module/core/payments/address-change.js +91 -0
- package/lib/module/core/payments/address-change.js.map +1 -0
- package/lib/module/core/payments/billing-merge.js +62 -0
- package/lib/module/core/payments/billing-merge.js.map +1 -0
- package/lib/module/core/payments/browser-info.js +42 -0
- package/lib/module/core/payments/browser-info.js.map +1 -0
- package/lib/module/core/payments/bt.js +151 -0
- package/lib/module/core/payments/bt.js.map +1 -0
- package/lib/module/core/payments/bt.test.js +23 -0
- package/lib/module/core/payments/bt.test.js.map +1 -0
- package/lib/module/core/payments/currency.js +37 -0
- package/lib/module/core/payments/currency.js.map +1 -0
- package/lib/module/core/payments/detail.js +159 -0
- package/lib/module/core/payments/detail.js.map +1 -0
- package/lib/module/core/payments/detail.test.js +225 -0
- package/lib/module/core/payments/detail.test.js.map +1 -0
- package/lib/module/core/payments/errors.js +115 -0
- package/lib/module/core/payments/errors.js.map +1 -0
- package/lib/module/core/payments/mint.js +89 -0
- package/lib/module/core/payments/mint.js.map +1 -0
- package/lib/module/core/payments/plan.js +131 -0
- package/lib/module/core/payments/plan.js.map +1 -0
- package/lib/module/core/payments/refusals.js +17 -0
- package/lib/module/core/payments/refusals.js.map +1 -0
- package/lib/module/core/payments/secure-fields.js +87 -0
- package/lib/module/core/payments/secure-fields.js.map +1 -0
- package/lib/module/core/payments/tax-id-catalog.js +616 -0
- package/lib/module/core/payments/tax-id-catalog.js.map +1 -0
- package/lib/module/core/payments/wallet-contact.js +42 -0
- package/lib/module/core/payments/wallet-contact.js.map +1 -0
- package/lib/module/core/payments/wallet-contact.test.js +77 -0
- package/lib/module/core/payments/wallet-contact.test.js.map +1 -0
- package/lib/module/elements/address.js +467 -0
- package/lib/module/elements/address.js.map +1 -0
- package/lib/module/elements/branding.js +114 -0
- package/lib/module/elements/branding.js.map +1 -0
- package/lib/module/elements/card-fields/context.js +254 -0
- package/lib/module/elements/card-fields/context.js.map +1 -0
- package/lib/module/elements/card-fields/fields.js +309 -0
- package/lib/module/elements/card-fields/fields.js.map +1 -0
- package/lib/module/elements/card-fields/index.js +30 -0
- package/lib/module/elements/card-fields/index.js.map +1 -0
- package/lib/module/elements/card.js +107 -0
- package/lib/module/elements/card.js.map +1 -0
- package/lib/module/elements/email.js +84 -0
- package/lib/module/elements/email.js.map +1 -0
- package/lib/module/elements/payment/declared-fields.js +186 -0
- package/lib/module/elements/payment/declared-fields.js.map +1 -0
- package/lib/module/elements/payment/index.js +448 -0
- package/lib/module/elements/payment/index.js.map +1 -0
- package/lib/module/elements/payment/wallet.js +132 -0
- package/lib/module/elements/payment/wallet.js.map +1 -0
- package/lib/module/elements/tax-id.js +100 -0
- package/lib/module/elements/tax-id.js.map +1 -0
- package/lib/module/i18n/catalogs.js +103 -0
- package/lib/module/i18n/catalogs.js.map +1 -0
- package/lib/module/i18n/index.js +45 -0
- package/lib/module/i18n/index.js.map +1 -0
- package/lib/module/index.js +27 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/namespace.js +174 -0
- package/lib/module/namespace.js.map +1 -0
- package/lib/module/native/index.js +128 -0
- package/lib/module/native/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/provider.js +56 -0
- package/lib/module/provider.js.map +1 -0
- package/lib/module/specs/NativeWhopAddressSuggestions.js +6 -0
- package/lib/module/specs/NativeWhopAddressSuggestions.js.map +1 -0
- package/lib/module/specs/NativeWhopAuthSession.js +10 -0
- package/lib/module/specs/NativeWhopAuthSession.js.map +1 -0
- package/lib/module/specs/NativeWhopPlatformPay.js +10 -0
- package/lib/module/specs/NativeWhopPlatformPay.js.map +1 -0
- package/lib/module/specs/WhopPayButtonNativeComponent.ts +22 -0
- package/lib/module/theme/appearance.js +85 -0
- package/lib/module/theme/appearance.js.map +1 -0
- package/lib/module/theme/appearance.test.js +124 -0
- package/lib/module/theme/appearance.test.js.map +1 -0
- package/lib/module/theme/provider.js +25 -0
- package/lib/module/theme/provider.js.map +1 -0
- package/lib/module/theme/resolve.js +69 -0
- package/lib/module/theme/resolve.js.map +1 -0
- package/lib/module/theme/scales.generated.js +419 -0
- package/lib/module/theme/scales.generated.js.map +1 -0
- package/lib/module/ui/element-frame.js +83 -0
- package/lib/module/ui/element-frame.js.map +1 -0
- package/lib/module/ui/payment-method-icon.js +69 -0
- package/lib/module/ui/payment-method-icon.js.map +1 -0
- package/lib/module/ui/primitives.js +266 -0
- package/lib/module/ui/primitives.js.map +1 -0
- package/lib/module/ui/select.js +281 -0
- package/lib/module/ui/select.js.map +1 -0
- package/lib/module/ui/suggestions.js +71 -0
- package/lib/module/ui/suggestions.js.map +1 -0
- package/lib/module/ui/whop-logo.js +42 -0
- package/lib/module/ui/whop-logo.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/api/client.d.ts +87 -0
- package/lib/typescript/src/api/client.d.ts.map +1 -0
- package/lib/typescript/src/api/types.d.ts +175 -0
- package/lib/typescript/src/api/types.d.ts.map +1 -0
- package/lib/typescript/src/controller/actions.d.ts +42 -0
- package/lib/typescript/src/controller/actions.d.ts.map +1 -0
- package/lib/typescript/src/controller/store.d.ts +90 -0
- package/lib/typescript/src/controller/store.d.ts.map +1 -0
- package/lib/typescript/src/core/address/country-data.d.ts +4 -0
- package/lib/typescript/src/core/address/country-data.d.ts.map +1 -0
- package/lib/typescript/src/core/address/country-name.d.ts +2 -0
- package/lib/typescript/src/core/address/country-name.d.ts.map +1 -0
- package/lib/typescript/src/core/address/country-names.generated.d.ts +2 -0
- package/lib/typescript/src/core/address/country-names.generated.d.ts.map +1 -0
- package/lib/typescript/src/core/address/engine.d.ts +97 -0
- package/lib/typescript/src/core/address/engine.d.ts.map +1 -0
- package/lib/typescript/src/core/address/form-state.d.ts +144 -0
- package/lib/typescript/src/core/address/form-state.d.ts.map +1 -0
- package/lib/typescript/src/core/address/places.d.ts +23 -0
- package/lib/typescript/src/core/address/places.d.ts.map +1 -0
- package/lib/typescript/src/core/address/suggest.d.ts +47 -0
- package/lib/typescript/src/core/address/suggest.d.ts.map +1 -0
- package/lib/typescript/src/core/address/use-suggestions.d.ts +3 -0
- package/lib/typescript/src/core/address/use-suggestions.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/address-change.d.ts +52 -0
- package/lib/typescript/src/core/payments/address-change.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/billing-merge.d.ts +39 -0
- package/lib/typescript/src/core/payments/billing-merge.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/browser-info.d.ts +12 -0
- package/lib/typescript/src/core/payments/browser-info.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/bt.d.ts +55 -0
- package/lib/typescript/src/core/payments/bt.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/currency.d.ts +7 -0
- package/lib/typescript/src/core/payments/currency.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/detail.d.ts +53 -0
- package/lib/typescript/src/core/payments/detail.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/errors.d.ts +62 -0
- package/lib/typescript/src/core/payments/errors.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/mint.d.ts +74 -0
- package/lib/typescript/src/core/payments/mint.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/plan.d.ts +69 -0
- package/lib/typescript/src/core/payments/plan.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/refusals.d.ts +12 -0
- package/lib/typescript/src/core/payments/refusals.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/secure-fields.d.ts +33 -0
- package/lib/typescript/src/core/payments/secure-fields.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/tax-id-catalog.d.ts +481 -0
- package/lib/typescript/src/core/payments/tax-id-catalog.d.ts.map +1 -0
- package/lib/typescript/src/core/payments/wallet-contact.d.ts +40 -0
- package/lib/typescript/src/core/payments/wallet-contact.d.ts.map +1 -0
- package/lib/typescript/src/elements/address.d.ts +55 -0
- package/lib/typescript/src/elements/address.d.ts.map +1 -0
- package/lib/typescript/src/elements/branding.d.ts +18 -0
- package/lib/typescript/src/elements/branding.d.ts.map +1 -0
- package/lib/typescript/src/elements/card-fields/context.d.ts +67 -0
- package/lib/typescript/src/elements/card-fields/context.d.ts.map +1 -0
- package/lib/typescript/src/elements/card-fields/fields.d.ts +15 -0
- package/lib/typescript/src/elements/card-fields/fields.d.ts.map +1 -0
- package/lib/typescript/src/elements/card-fields/index.d.ts +21 -0
- package/lib/typescript/src/elements/card-fields/index.d.ts.map +1 -0
- package/lib/typescript/src/elements/card.d.ts +21 -0
- package/lib/typescript/src/elements/card.d.ts.map +1 -0
- package/lib/typescript/src/elements/email.d.ts +31 -0
- package/lib/typescript/src/elements/email.d.ts.map +1 -0
- package/lib/typescript/src/elements/payment/declared-fields.d.ts +43 -0
- package/lib/typescript/src/elements/payment/declared-fields.d.ts.map +1 -0
- package/lib/typescript/src/elements/payment/index.d.ts +37 -0
- package/lib/typescript/src/elements/payment/index.d.ts.map +1 -0
- package/lib/typescript/src/elements/payment/wallet.d.ts +39 -0
- package/lib/typescript/src/elements/payment/wallet.d.ts.map +1 -0
- package/lib/typescript/src/elements/tax-id.d.ts +41 -0
- package/lib/typescript/src/elements/tax-id.d.ts.map +1 -0
- package/lib/typescript/src/i18n/catalogs.d.ts +7 -0
- package/lib/typescript/src/i18n/catalogs.d.ts.map +1 -0
- package/lib/typescript/src/i18n/index.d.ts +17 -0
- package/lib/typescript/src/i18n/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +42 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/namespace.d.ts +36 -0
- package/lib/typescript/src/namespace.d.ts.map +1 -0
- package/lib/typescript/src/native/index.d.ts +62 -0
- package/lib/typescript/src/native/index.d.ts.map +1 -0
- package/lib/typescript/src/provider.d.ts +37 -0
- package/lib/typescript/src/provider.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts +13 -0
- package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts +20 -0
- package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts +24 -0
- package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts.map +1 -0
- package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts +21 -0
- package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/theme/appearance.d.ts +71 -0
- package/lib/typescript/src/theme/appearance.d.ts.map +1 -0
- package/lib/typescript/src/theme/provider.d.ts +11 -0
- package/lib/typescript/src/theme/provider.d.ts.map +1 -0
- package/lib/typescript/src/theme/resolve.d.ts +19 -0
- package/lib/typescript/src/theme/resolve.d.ts.map +1 -0
- package/lib/typescript/src/theme/scales.generated.d.ts +18 -0
- package/lib/typescript/src/theme/scales.generated.d.ts.map +1 -0
- package/lib/typescript/src/ui/element-frame.d.ts +32 -0
- package/lib/typescript/src/ui/element-frame.d.ts.map +1 -0
- package/lib/typescript/src/ui/payment-method-icon.d.ts +21 -0
- package/lib/typescript/src/ui/payment-method-icon.d.ts.map +1 -0
- package/lib/typescript/src/ui/primitives.d.ts +94 -0
- package/lib/typescript/src/ui/primitives.d.ts.map +1 -0
- package/lib/typescript/src/ui/select.d.ts +25 -0
- package/lib/typescript/src/ui/select.d.ts.map +1 -0
- package/lib/typescript/src/ui/suggestions.d.ts +15 -0
- package/lib/typescript/src/ui/suggestions.d.ts.map +1 -0
- package/lib/typescript/src/ui/whop-logo.d.ts +4 -0
- package/lib/typescript/src/ui/whop-logo.d.ts.map +1 -0
- package/package.json +121 -0
- package/scripts/check-swift.sh +27 -0
- package/scripts/codegen-shims.mjs +409 -0
- package/scripts/gen-country-names.mjs +58 -0
- package/scripts/gen-theme.mjs +124 -0
- package/scripts/gen-wordmark.mjs +68 -0
- package/scripts/mirror.mjs +112 -0
- package/src/api/client.ts +202 -0
- package/src/api/types.ts +166 -0
- package/src/controller/actions.ts +197 -0
- package/src/controller/store.ts +201 -0
- package/src/core/address/country-data.ts +927 -0
- package/src/core/address/country-name.ts +36 -0
- package/src/core/address/country-names.generated.ts +246 -0
- package/src/core/address/engine.ts +349 -0
- package/src/core/address/form-state.ts +479 -0
- package/src/core/address/places.ts +39 -0
- package/src/core/address/suggest.ts +153 -0
- package/src/core/address/use-suggestions.ts +37 -0
- package/src/core/payments/address-change.ts +121 -0
- package/src/core/payments/billing-merge.ts +91 -0
- package/src/core/payments/browser-info.ts +43 -0
- package/src/core/payments/bt.ts +176 -0
- package/src/core/payments/currency.ts +54 -0
- package/src/core/payments/detail.ts +204 -0
- package/src/core/payments/errors.ts +171 -0
- package/src/core/payments/mint.ts +130 -0
- package/src/core/payments/plan.ts +198 -0
- package/src/core/payments/refusals.ts +24 -0
- package/src/core/payments/secure-fields.ts +85 -0
- package/src/core/payments/tax-id-catalog.ts +320 -0
- package/src/core/payments/wallet-contact.ts +56 -0
- package/src/elements/address.tsx +570 -0
- package/src/elements/branding.tsx +87 -0
- package/src/elements/card-fields/context.tsx +310 -0
- package/src/elements/card-fields/fields.tsx +281 -0
- package/src/elements/card-fields/index.tsx +37 -0
- package/src/elements/card.tsx +101 -0
- package/src/elements/email.tsx +100 -0
- package/src/elements/payment/declared-fields.tsx +238 -0
- package/src/elements/payment/index.tsx +497 -0
- package/src/elements/payment/wallet.tsx +155 -0
- package/src/elements/tax-id.tsx +143 -0
- package/src/i18n/catalogs.ts +100 -0
- package/src/i18n/index.tsx +46 -0
- package/src/index.ts +68 -0
- package/src/namespace.tsx +231 -0
- package/src/native/index.ts +173 -0
- package/src/provider.tsx +76 -0
- package/src/specs/NativeWhopAddressSuggestions.ts +14 -0
- package/src/specs/NativeWhopAuthSession.ts +21 -0
- package/src/specs/NativeWhopPlatformPay.ts +25 -0
- package/src/specs/WhopPayButtonNativeComponent.ts +22 -0
- package/src/theme/appearance.ts +150 -0
- package/src/theme/provider.tsx +28 -0
- package/src/theme/resolve.ts +68 -0
- package/src/theme/scales.generated.ts +427 -0
- package/src/ui/element-frame.tsx +94 -0
- package/src/ui/payment-method-icon.tsx +55 -0
- package/src/ui/primitives.tsx +269 -0
- package/src/ui/select.tsx +255 -0
- package/src/ui/suggestions.tsx +69 -0
- package/src/ui/whop-logo.tsx +37 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// ported verbatim from frontend/apps/web/src/elements/payments/lib/plan.ts — keep in sync when the web element changes
|
|
2
|
+
|
|
3
|
+
import type { PaymentMethodConfigurationShape, PlanResource } from '../../api/types';
|
|
4
|
+
import type { WhopClient } from '../../api/client';
|
|
5
|
+
|
|
6
|
+
import { currencyMinorDigits } from './currency';
|
|
7
|
+
import { ChargeConfigError, apiErrorMessage } from './errors';
|
|
8
|
+
|
|
9
|
+
/** The widened props bag both runtimes hand to the charge resolution — the controller seed's
|
|
10
|
+
* `ctx.props` and the host sources' live props view are each one branch of the plan-vs-inline
|
|
11
|
+
* union, so every key is optional here and `chargeInputOf` decides which branch is in force. */
|
|
12
|
+
export interface ChargeProps {
|
|
13
|
+
accountId?: string;
|
|
14
|
+
plan?: string;
|
|
15
|
+
currency?: string;
|
|
16
|
+
amount?: number;
|
|
17
|
+
setupFutureUsage?: 'off_session' | 'on_session';
|
|
18
|
+
paymentMethodConfiguration?: PaymentMethodConfigurationShape;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** `config = resolve(plan)`: the ONE normalized charge config every downstream path consumes —
|
|
22
|
+
* the inline props ARE the degenerate case (`plan` undefined, fields verbatim), and a plan is
|
|
23
|
+
* a preset that resolves to this same shape. */
|
|
24
|
+
export interface ResolvedCharge {
|
|
25
|
+
configured: true;
|
|
26
|
+
plan: string | undefined;
|
|
27
|
+
currency: string;
|
|
28
|
+
amount: number;
|
|
29
|
+
setupFutureUsage: 'off_session' | 'on_session' | undefined;
|
|
30
|
+
paymentMethodConfiguration: PaymentMethodConfigurationShape | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The optionless statics-rail boot (`WhopElements.payments.handleNextAction` — the namespace
|
|
34
|
+
* handle mounts with an EMPTY options bag): no charge input exists and none is needed, the
|
|
35
|
+
* static flow never collects. Collection-face projections refuse on it, loudly. */
|
|
36
|
+
export interface UnconfiguredCharge {
|
|
37
|
+
configured: false;
|
|
38
|
+
plan: undefined;
|
|
39
|
+
currency: undefined;
|
|
40
|
+
amount: undefined;
|
|
41
|
+
setupFutureUsage: undefined;
|
|
42
|
+
paymentMethodConfiguration: undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type ChargeState = ResolvedCharge | UnconfiguredCharge;
|
|
46
|
+
|
|
47
|
+
export const UNCONFIGURED_CHARGE: UnconfiguredCharge = Object.freeze({
|
|
48
|
+
configured: false,
|
|
49
|
+
plan: undefined,
|
|
50
|
+
currency: undefined,
|
|
51
|
+
amount: undefined,
|
|
52
|
+
setupFutureUsage: undefined,
|
|
53
|
+
paymentMethodConfiguration: undefined,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/** ZERO charge inputs mounted — the only shape that resolves to UNCONFIGURED_CHARGE; any
|
|
57
|
+
* PARTIAL config still throws the loud ChargeConfigError (a half-config is a consumer bug). */
|
|
58
|
+
export function chargeless(props: ChargeProps): boolean {
|
|
59
|
+
return (
|
|
60
|
+
props.accountId === undefined &&
|
|
61
|
+
props.plan === undefined &&
|
|
62
|
+
props.currency === undefined &&
|
|
63
|
+
props.amount === undefined &&
|
|
64
|
+
props.paymentMethodConfiguration === undefined
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ChargeInput {
|
|
69
|
+
accountId: string | undefined;
|
|
70
|
+
plan: string | undefined;
|
|
71
|
+
currency: string | undefined;
|
|
72
|
+
amount: number | undefined;
|
|
73
|
+
setupFutureUsage: 'off_session' | 'on_session' | undefined;
|
|
74
|
+
paymentMethodConfiguration: PaymentMethodConfigurationShape | undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The plan-XOR-inline gate: `plan` alongside any inline charge key is a conflict, and neither
|
|
78
|
+
* is a missing config — both refuse LOUDLY with a coded `ChargeConfigError`. */
|
|
79
|
+
export function chargeInputOf(props: ChargeProps): ChargeInput {
|
|
80
|
+
const input: ChargeInput = {
|
|
81
|
+
accountId: props.accountId,
|
|
82
|
+
plan: props.plan,
|
|
83
|
+
currency: props.currency,
|
|
84
|
+
amount: props.amount,
|
|
85
|
+
setupFutureUsage: props.setupFutureUsage,
|
|
86
|
+
paymentMethodConfiguration: props.paymentMethodConfiguration,
|
|
87
|
+
};
|
|
88
|
+
if (input.plan !== undefined) {
|
|
89
|
+
const inlineKeys = (['currency', 'amount', 'paymentMethodConfiguration'] as const).filter(
|
|
90
|
+
(key) => input[key] !== undefined,
|
|
91
|
+
);
|
|
92
|
+
if (inlineKeys.length > 0) {
|
|
93
|
+
throw new ChargeConfigError(
|
|
94
|
+
'PLAN_INLINE_CONFLICT',
|
|
95
|
+
`Pass a plan OR an inline charge config, never both — ${inlineKeys.join(', ')} conflict with plan ${input.plan}. The plan resolves to the same shape client-side.`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return input;
|
|
99
|
+
}
|
|
100
|
+
if (input.currency === undefined || input.amount === undefined) {
|
|
101
|
+
throw new ChargeConfigError(
|
|
102
|
+
'CHARGE_CONFIG_MISSING',
|
|
103
|
+
'Pass a plan id, or an inline charge config with both currency and amount — neither is mounted.',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return input;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function inlineCharge(input: ChargeInput): ResolvedCharge {
|
|
110
|
+
return {
|
|
111
|
+
configured: true,
|
|
112
|
+
plan: undefined,
|
|
113
|
+
currency: (input.currency ?? '').toLowerCase(),
|
|
114
|
+
amount: input.amount ?? 0,
|
|
115
|
+
setupFutureUsage: input.setupFutureUsage,
|
|
116
|
+
paymentMethodConfiguration: input.paymentMethodConfiguration,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The slice of the SDK's own plan the charge resolution reads — the schema is the author,
|
|
121
|
+
* nothing hand-typed to drift. `effective_payment_method_configuration` is the GOVERNING
|
|
122
|
+
* configuration resolved server-side through every layer; `null` means platform defaults, and
|
|
123
|
+
* there is no fallback to the plan's own editable override — the backend publishing the field
|
|
124
|
+
* deploys BENEATH this code by PR stacking. */
|
|
125
|
+
export type RetrievedPlan = Pick<
|
|
126
|
+
PlanResource,
|
|
127
|
+
'id' | 'currency' | 'initial_price' | 'plan_type' | 'account' | 'effective_payment_method_configuration'
|
|
128
|
+
>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Normalize a retrieved plan into the inline shape. The plan's `account_id` must MATCH the
|
|
132
|
+
* mounted `accountId` — the handle's account stays the mount key, and a foreign plan is a coded
|
|
133
|
+
* refusal, never a silent re-key. A renewing plan attests `off_session` save-consent unless the
|
|
134
|
+
* consumer set the attestation explicitly (the charge vaults the method for renewals).
|
|
135
|
+
*/
|
|
136
|
+
export function chargeFromPlan(resource: RetrievedPlan, input: ChargeInput): ResolvedCharge {
|
|
137
|
+
const planId = resource.id ?? input.plan;
|
|
138
|
+
const planAccount = resource.account?.id;
|
|
139
|
+
if (!planAccount || planAccount !== input.accountId) {
|
|
140
|
+
throw new ChargeConfigError(
|
|
141
|
+
'PLAN_ACCOUNT_MISMATCH',
|
|
142
|
+
`Plan ${planId} belongs to ${planAccount ?? 'no account'}, not the mounted accountId ${input.accountId} — mount the element on the plan's own account.`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const currency = resource.currency?.toLowerCase();
|
|
146
|
+
const initialPrice = resource.initial_price;
|
|
147
|
+
if (!currency || typeof initialPrice !== 'number' || !Number.isFinite(initialPrice)) {
|
|
148
|
+
throw new ChargeConfigError(
|
|
149
|
+
'PLAN_RESOLUTION_FAILED',
|
|
150
|
+
`Plan ${planId} resolved without a currency and initial price.`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
configured: true,
|
|
155
|
+
plan: planId,
|
|
156
|
+
currency,
|
|
157
|
+
// Truncate, never round — the backend's Utils.amount_in_cents is (amount.to_d * factor).to_i;
|
|
158
|
+
// toFixed(4) re-quantizes the float product to the exact decimal before the truncate.
|
|
159
|
+
amount: Math.trunc(Number((initialPrice * 10 ** currencyMinorDigits(currency)).toFixed(4))),
|
|
160
|
+
setupFutureUsage: input.setupFutureUsage ?? (resource.plan_type === 'renewal' ? 'off_session' : undefined),
|
|
161
|
+
// The GOVERNING configuration, resolved server-side — the same read a session mount gets
|
|
162
|
+
// from its own payment_method_configuration. A null answer is trusted as platform defaults;
|
|
163
|
+
// the elements NEVER re-derive a configuration layer client-side. Sliced to exactly the
|
|
164
|
+
// three known keys.
|
|
165
|
+
paymentMethodConfiguration: slicedConfiguration(resource.effective_payment_method_configuration),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function slicedConfiguration(
|
|
170
|
+
configuration: PaymentMethodConfigurationShape | null | undefined,
|
|
171
|
+
): PaymentMethodConfigurationShape | undefined {
|
|
172
|
+
if (!configuration) return undefined;
|
|
173
|
+
return {
|
|
174
|
+
enabled: configuration.enabled,
|
|
175
|
+
disabled: configuration.disabled,
|
|
176
|
+
include_platform_defaults: configuration.include_platform_defaults,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The one charge resolution. Inline props resolve with no network; a plan costs one GET. */
|
|
181
|
+
export async function resolveCharge(client: WhopClient, props: ChargeProps): Promise<ChargeState> {
|
|
182
|
+
// The statics rail's lazy open-handle boots with an EMPTY options bag and DOES run the
|
|
183
|
+
// h.sync charge gate — a chargeless boot resolves the unconfigured sentinel there exactly
|
|
184
|
+
// like the controller seed does. Partial config still throws.
|
|
185
|
+
if (chargeless(props)) return UNCONFIGURED_CHARGE;
|
|
186
|
+
const input = chargeInputOf(props);
|
|
187
|
+
if (input.plan === undefined) return inlineCharge(input);
|
|
188
|
+
let plan: PlanResource;
|
|
189
|
+
try {
|
|
190
|
+
plan = await client.plans.retrieve(input.plan);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
throw new ChargeConfigError(
|
|
193
|
+
'PLAN_RESOLUTION_FAILED',
|
|
194
|
+
apiErrorMessage(err, `plan ${input.plan} could not be resolved`),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return chargeFromPlan(plan, input);
|
|
198
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refusals that exist only on React Native, kept out of the ported `errors.ts` so that file stays a
|
|
3
|
+
* clean mirror of the web element's.
|
|
4
|
+
*
|
|
5
|
+
* Every one is coded, so a caller switches on `code` and never string-matches a message.
|
|
6
|
+
*/
|
|
7
|
+
export type PaymentsRefusalCode =
|
|
8
|
+
| 'CONFIRMATION_IN_FLIGHT'
|
|
9
|
+
| 'NEXT_ACTION_IN_FLIGHT'
|
|
10
|
+
| 'NEXT_ACTION_PRESENTATION_FAILED'
|
|
11
|
+
| 'CHARGE_CONFIG_MISSING'
|
|
12
|
+
| 'NO_COLLECTION_SURFACE'
|
|
13
|
+
| 'NATIVE_MODULE_MISSING'
|
|
14
|
+
| 'WALLET_UNAVAILABLE';
|
|
15
|
+
|
|
16
|
+
export class PaymentsRefusalError extends Error {
|
|
17
|
+
readonly code: PaymentsRefusalCode;
|
|
18
|
+
|
|
19
|
+
constructor(code: PaymentsRefusalCode, message: string) {
|
|
20
|
+
super(`[${code}] ${message}`);
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.name = 'PaymentsRefusalError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// ported verbatim from frontend/apps/web/src/elements/payments/lib/secure-fields.ts — keep in sync when the web element changes
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The secure declared-field rail (payment-api.md §4): a matrix field marked `secure: true`
|
|
5
|
+
* renders as a Basis Theory hosted text element wearing the suite's input chrome, so the raw
|
|
6
|
+
* value NEVER exists in our JavaScript — at collect time the selected method's element
|
|
7
|
+
* INSTANCES are tokenized into ONE Basis Theory token intent, keyed by field name
|
|
8
|
+
* (`{ type: 'token', data: { bank_account: <element> } }`; BT interpolates each element's value
|
|
9
|
+
* server-side, producing exactly the legacy checkout's additional-data shape, so the
|
|
10
|
+
* checkout.com mandate interpolation `$.data.<name>` consumes it unchanged), and the category
|
|
11
|
+
* payload carries `{ token }`. The create rides the web-elements SDK's element path under the
|
|
12
|
+
* method's config-block publishable key (`tokenizer` + `public_key`, the card block's source).
|
|
13
|
+
* Intents expire on BT's default clock (24h) — the confirm consumes one long before that.
|
|
14
|
+
*/
|
|
15
|
+
import type { BasisTheoryElements } from './bt';
|
|
16
|
+
|
|
17
|
+
import { SecureFieldError } from './errors';
|
|
18
|
+
|
|
19
|
+
/** A hosted text element instance. Opaque by construction — it carries an id and a formatter, never
|
|
20
|
+
* the value the buyer typed. */
|
|
21
|
+
type ITextElement = unknown;
|
|
22
|
+
|
|
23
|
+
const BT_TOKEN_INTENTS_URL = 'https://api.basistheory.com/token-intents';
|
|
24
|
+
|
|
25
|
+
/** Applied by BT inside the hosted element at tokenization (String.replace semantics):
|
|
26
|
+
* whitespace stripped — buyers type the spaced IBAN print format, the electronic format rides. */
|
|
27
|
+
export const secureValueTransform = /\s+/g;
|
|
28
|
+
|
|
29
|
+
// BT runs the validation regex against the value AS TYPED, while `transform` applies only at
|
|
30
|
+
// tokenization (developers.basistheory.com/docs/sdks/web/web-elements/components). The matrix
|
|
31
|
+
// publishes both truths ready to use: `input_formats` (what a buyer may type — whitespace
|
|
32
|
+
// tolerant, authored star-height 1 because BT's frame rejects nested quantifiers as unsafe)
|
|
33
|
+
// and `formats` (the stored electronic value, the fallback where no typing tolerance is
|
|
34
|
+
// declared). Patterns ride verbatim — the backend is the only author.
|
|
35
|
+
export function secureFieldValidation(field: {
|
|
36
|
+
formats?: Record<string, string> | null;
|
|
37
|
+
input_formats?: Record<string, string> | null;
|
|
38
|
+
}): RegExp | undefined {
|
|
39
|
+
const typed = Object.values(field.input_formats ?? {});
|
|
40
|
+
const patterns = typed.length > 0 ? typed : Object.values(field.formats ?? {});
|
|
41
|
+
if (patterns.length === 0) return undefined;
|
|
42
|
+
try {
|
|
43
|
+
return new RegExp(patterns.map((pattern) => `(?:${pattern})`).join('|'));
|
|
44
|
+
} catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Raw-data token intents ride BT's REST surface with the publishable key — the wallet sheets'
|
|
50
|
+
* leg alone (Google's tokenizationData.token is a raw string, no hosted element exists to
|
|
51
|
+
* reference). */
|
|
52
|
+
export async function createRawTokenIntent(publicKey: string, data: unknown): Promise<string> {
|
|
53
|
+
const response = await fetch(BT_TOKEN_INTENTS_URL, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'BT-API-KEY': publicKey, 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify({ type: 'token', data }),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) throw new Error(`token intent creation failed (${response.status})`);
|
|
59
|
+
const intent = (await response.json().catch(() => null)) as { id?: string } | null;
|
|
60
|
+
if (!intent?.id) throw new Error('token intent creation returned no id');
|
|
61
|
+
return intent.id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function tokenizeSecureFields(
|
|
65
|
+
bt: BasisTheoryElements | undefined,
|
|
66
|
+
elements: Record<string, ITextElement>,
|
|
67
|
+
/** The method's OWN config-block key. Passed per call because Basis Theory's client is a
|
|
68
|
+
* module-level singleton — the first key constructed wins for the whole process. */
|
|
69
|
+
apiKey: string,
|
|
70
|
+
): Promise<string> {
|
|
71
|
+
if (!bt) {
|
|
72
|
+
throw new SecureFieldError(
|
|
73
|
+
'SECURE_FIELD_TOKENIZER_UNAVAILABLE',
|
|
74
|
+
'This method declares a secure field but its tokenizer is not available',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const intent = await bt.tokenIntents.create({ type: 'token', data: elements as never }, { apiKey });
|
|
79
|
+
if (!intent?.id) throw new Error('token intent creation returned no id');
|
|
80
|
+
return intent.id;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const detail = err instanceof Error && err.message ? `: ${err.message}` : '';
|
|
83
|
+
throw new SecureFieldError('SECURE_FIELD_TOKENIZE_FAILED', `Secure field tokenization failed${detail}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// ported verbatim from frontend/apps/web/src/elements/payments/lib/tax-id-catalog.ts — keep in sync when the web element changes
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useT } from '../../i18n';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The registration types the API accepts for `tax_id.type`. The backend owns the set
|
|
9
|
+
* (`TaxIdentifier::TAX_ID_TYPES`); this package has no generated enum to derive it from, so the
|
|
10
|
+
* catalog below IS the set. `useTaxIdTypeLabels` keeps the half of the exhaustiveness gate that
|
|
11
|
+
* still applies: a catalog row with no label refuses to compile.
|
|
12
|
+
*/
|
|
13
|
+
export type TaxIdType = keyof typeof TAX_ID_TYPE_META;
|
|
14
|
+
|
|
15
|
+
interface TaxIdTypeMeta {
|
|
16
|
+
/** The ISO 3166-1 alpha-2 region the registration belongs to — `EU` for the union-wide
|
|
17
|
+
* registrations, `XI` for Northern Ireland's post-Brexit VAT scheme. */
|
|
18
|
+
region: string;
|
|
19
|
+
/** A format specimen for the value input's placeholder — a shape, never prose, so it is not
|
|
20
|
+
* translated. Sourced from the dashboard's tax-settings country map (the legacy per-type
|
|
21
|
+
* metadata) where a row exists; `pl_nip` and `xi_vat` follow the same published formats. */
|
|
22
|
+
placeholder: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Display metadata per wire type. */
|
|
26
|
+
export const TAX_ID_TYPE_META = {
|
|
27
|
+
ad_nrt: { region: 'AD', placeholder: 'A-123456-B' },
|
|
28
|
+
ae_trn: { region: 'AE', placeholder: '123456789012345' },
|
|
29
|
+
al_tin: { region: 'AL', placeholder: 'J12345678N' },
|
|
30
|
+
am_tin: { region: 'AM', placeholder: '12345678' },
|
|
31
|
+
ao_tin: { region: 'AO', placeholder: '1234567890' },
|
|
32
|
+
ar_cuit: { region: 'AR', placeholder: '20-12345678-9' },
|
|
33
|
+
au_abn: { region: 'AU', placeholder: '12345678901' },
|
|
34
|
+
au_arn: { region: 'AU', placeholder: '12345678901' },
|
|
35
|
+
aw_tin: { region: 'AW', placeholder: '1234567' },
|
|
36
|
+
az_tin: { region: 'AZ', placeholder: '1234567890' },
|
|
37
|
+
ba_tin: { region: 'BA', placeholder: '1234567890123' },
|
|
38
|
+
bb_tin: { region: 'BB', placeholder: '1234567890' },
|
|
39
|
+
bd_bin: { region: 'BD', placeholder: '123456789' },
|
|
40
|
+
bf_ifu: { region: 'BF', placeholder: '12345678901234' },
|
|
41
|
+
bg_uic: { region: 'BG', placeholder: '123456789' },
|
|
42
|
+
bh_vat: { region: 'BH', placeholder: '123456789012345' },
|
|
43
|
+
bj_ifu: { region: 'BJ', placeholder: '1234567890123' },
|
|
44
|
+
bo_tin: { region: 'BO', placeholder: '1234567' },
|
|
45
|
+
br_cnpj: { region: 'BR', placeholder: '12.345.678/0001-00' },
|
|
46
|
+
br_cpf: { region: 'BR', placeholder: '123.456.789-00' },
|
|
47
|
+
bs_tin: { region: 'BS', placeholder: '123456789' },
|
|
48
|
+
by_tin: { region: 'BY', placeholder: '123456789' },
|
|
49
|
+
ca_bn: { region: 'CA', placeholder: '123456789' },
|
|
50
|
+
ca_gst_hst: { region: 'CA', placeholder: '123456789RT0001' },
|
|
51
|
+
ca_pst_bc: { region: 'CA', placeholder: 'PST-1234-5678' },
|
|
52
|
+
ca_pst_mb: { region: 'CA', placeholder: '123456-7' },
|
|
53
|
+
ca_pst_sk: { region: 'CA', placeholder: '1234567' },
|
|
54
|
+
ca_qst: { region: 'CA', placeholder: '1234567890TQ1234' },
|
|
55
|
+
cd_nif: { region: 'CD', placeholder: 'A1234567B' },
|
|
56
|
+
ch_uid: { region: 'CH', placeholder: 'CHE-123.456.789' },
|
|
57
|
+
ch_vat: { region: 'CH', placeholder: 'CHE-123.456.789 MWST' },
|
|
58
|
+
cl_tin: { region: 'CL', placeholder: '12.345.678-9' },
|
|
59
|
+
cm_niu: { region: 'CM', placeholder: 'M012345678901A' },
|
|
60
|
+
cn_tin: { region: 'CN', placeholder: '123456789012345678' },
|
|
61
|
+
co_nit: { region: 'CO', placeholder: '123.456.789-0' },
|
|
62
|
+
cr_tin: { region: 'CR', placeholder: '1-234-567890' },
|
|
63
|
+
cv_nif: { region: 'CV', placeholder: '123456789' },
|
|
64
|
+
de_stn: { region: 'DE', placeholder: '12/345/67890' },
|
|
65
|
+
do_rcn: { region: 'DO', placeholder: '123-4567890-1' },
|
|
66
|
+
ec_ruc: { region: 'EC', placeholder: '1234567890001' },
|
|
67
|
+
eg_tin: { region: 'EG', placeholder: '123456789' },
|
|
68
|
+
es_cif: { region: 'ES', placeholder: 'A12345678' },
|
|
69
|
+
et_tin: { region: 'ET', placeholder: '1234567890' },
|
|
70
|
+
eu_oss_vat: { region: 'EU', placeholder: 'FR12345678901' },
|
|
71
|
+
eu_vat: { region: 'EU', placeholder: 'DE123456789' },
|
|
72
|
+
gb_vat: { region: 'GB', placeholder: 'GB123456789' },
|
|
73
|
+
ge_vat: { region: 'GE', placeholder: '123456789' },
|
|
74
|
+
gh_tin: { region: 'GH', placeholder: 'C0001234567' },
|
|
75
|
+
gn_nif: { region: 'GN', placeholder: '123456789' },
|
|
76
|
+
hk_br: { region: 'HK', placeholder: '12345678' },
|
|
77
|
+
hr_oib: { region: 'HR', placeholder: '12345678901' },
|
|
78
|
+
hu_tin: { region: 'HU', placeholder: '12345678' },
|
|
79
|
+
id_npwp: { region: 'ID', placeholder: '12.345.678.9-012.345' },
|
|
80
|
+
il_vat: { region: 'IL', placeholder: '123456789' },
|
|
81
|
+
in_gst: { region: 'IN', placeholder: '22AAAAA0000A1Z5' },
|
|
82
|
+
is_vat: { region: 'IS', placeholder: '123456' },
|
|
83
|
+
jp_cn: { region: 'JP', placeholder: '1234-56-7890' },
|
|
84
|
+
jp_rn: { region: 'JP', placeholder: '12345' },
|
|
85
|
+
jp_trn: { region: 'JP', placeholder: 'T1234567890123' },
|
|
86
|
+
ke_pin: { region: 'KE', placeholder: 'P000123456A' },
|
|
87
|
+
kg_tin: { region: 'KG', placeholder: '12345678' },
|
|
88
|
+
kh_tin: { region: 'KH', placeholder: 'K001-234567890' },
|
|
89
|
+
kr_brn: { region: 'KR', placeholder: '123-45-67890' },
|
|
90
|
+
kz_bin: { region: 'KZ', placeholder: '123456789012' },
|
|
91
|
+
la_tin: { region: 'LA', placeholder: '1234567890' },
|
|
92
|
+
li_uid: { region: 'LI', placeholder: 'CHE-123.456.789' },
|
|
93
|
+
li_vat: { region: 'LI', placeholder: '12345' },
|
|
94
|
+
ma_vat: { region: 'MA', placeholder: '12345678' },
|
|
95
|
+
md_vat: { region: 'MD', placeholder: '1234567' },
|
|
96
|
+
me_pib: { region: 'ME', placeholder: '12345678' },
|
|
97
|
+
mk_vat: { region: 'MK', placeholder: 'MK1234567890123' },
|
|
98
|
+
mr_nif: { region: 'MR', placeholder: '12345678' },
|
|
99
|
+
mx_rfc: { region: 'MX', placeholder: 'XAXX010101000' },
|
|
100
|
+
my_frp: { region: 'MY', placeholder: '12345678' },
|
|
101
|
+
my_itn: { region: 'MY', placeholder: 'C1234567890' },
|
|
102
|
+
my_sst: { region: 'MY', placeholder: 'A12-3456-78901234' },
|
|
103
|
+
ng_tin: { region: 'NG', placeholder: '12345678-0001' },
|
|
104
|
+
no_vat: { region: 'NO', placeholder: '123456789MVA' },
|
|
105
|
+
no_voec: { region: 'NO', placeholder: '1234567' },
|
|
106
|
+
np_pan: { region: 'NP', placeholder: '123456789' },
|
|
107
|
+
nz_gst: { region: 'NZ', placeholder: '123456789' },
|
|
108
|
+
om_vat: { region: 'OM', placeholder: 'OM1234567890' },
|
|
109
|
+
pe_ruc: { region: 'PE', placeholder: '12345678901' },
|
|
110
|
+
ph_tin: { region: 'PH', placeholder: '123-456-789-000' },
|
|
111
|
+
pl_nip: { region: 'PL', placeholder: '1234567890' },
|
|
112
|
+
ro_tin: { region: 'RO', placeholder: '1234567890' },
|
|
113
|
+
rs_pib: { region: 'RS', placeholder: '123456789' },
|
|
114
|
+
ru_inn: { region: 'RU', placeholder: '1234567890' },
|
|
115
|
+
ru_kpp: { region: 'RU', placeholder: '123456789' },
|
|
116
|
+
sa_vat: { region: 'SA', placeholder: '123456789012345' },
|
|
117
|
+
sg_gst: { region: 'SG', placeholder: 'M12345678X' },
|
|
118
|
+
sg_uen: { region: 'SG', placeholder: '123456789A' },
|
|
119
|
+
si_tin: { region: 'SI', placeholder: '12345678' },
|
|
120
|
+
sn_ninea: { region: 'SN', placeholder: '12345678901' },
|
|
121
|
+
sr_fin: { region: 'SR', placeholder: '12345678' },
|
|
122
|
+
sv_nit: { region: 'SV', placeholder: '0614-123456-123-4' },
|
|
123
|
+
th_vat: { region: 'TH', placeholder: '1234567890123' },
|
|
124
|
+
tj_tin: { region: 'TJ', placeholder: '123456789' },
|
|
125
|
+
tr_tin: { region: 'TR', placeholder: '1234567890' },
|
|
126
|
+
tw_vat: { region: 'TW', placeholder: '12345678' },
|
|
127
|
+
tz_vat: { region: 'TZ', placeholder: '12-345-6789' },
|
|
128
|
+
ua_vat: { region: 'UA', placeholder: '1234567890' },
|
|
129
|
+
ug_tin: { region: 'UG', placeholder: '1234567890' },
|
|
130
|
+
us_ein: { region: 'US', placeholder: '12-3456789' },
|
|
131
|
+
uy_ruc: { region: 'UY', placeholder: '123456789012' },
|
|
132
|
+
uz_tin: { region: 'UZ', placeholder: '123456789' },
|
|
133
|
+
uz_vat: { region: 'UZ', placeholder: '123456789012' },
|
|
134
|
+
ve_rif: { region: 'VE', placeholder: 'J-12345678-9' },
|
|
135
|
+
vn_tin: { region: 'VN', placeholder: '1234567890' },
|
|
136
|
+
xi_vat: { region: 'XI', placeholder: 'XI123456789' },
|
|
137
|
+
za_vat: { region: 'ZA', placeholder: '4123456789' },
|
|
138
|
+
zm_tin: { region: 'ZM', placeholder: '1234567890' },
|
|
139
|
+
zw_tin: { region: 'ZW', placeholder: '1234567890' },
|
|
140
|
+
} satisfies Record<string, TaxIdTypeMeta>;
|
|
141
|
+
|
|
142
|
+
export const TAX_ID_TYPES = Object.keys(TAX_ID_TYPE_META) as readonly TaxIdType[];
|
|
143
|
+
|
|
144
|
+
export function isTaxIdType(value: string): value is TaxIdType {
|
|
145
|
+
return Object.prototype.hasOwnProperty.call(TAX_ID_TYPE_META, value);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const EU_COUNTRIES = new Set([
|
|
149
|
+
'AT',
|
|
150
|
+
'BE',
|
|
151
|
+
'BG',
|
|
152
|
+
'HR',
|
|
153
|
+
'CY',
|
|
154
|
+
'CZ',
|
|
155
|
+
'DK',
|
|
156
|
+
'EE',
|
|
157
|
+
'FI',
|
|
158
|
+
'FR',
|
|
159
|
+
'DE',
|
|
160
|
+
'GR',
|
|
161
|
+
'HU',
|
|
162
|
+
'IE',
|
|
163
|
+
'IT',
|
|
164
|
+
'LV',
|
|
165
|
+
'LT',
|
|
166
|
+
'LU',
|
|
167
|
+
'MT',
|
|
168
|
+
'NL',
|
|
169
|
+
'PL',
|
|
170
|
+
'PT',
|
|
171
|
+
'RO',
|
|
172
|
+
'SK',
|
|
173
|
+
'SI',
|
|
174
|
+
'ES',
|
|
175
|
+
'SE',
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The registration type a buyer country preselects: EU countries stay on `eu_vat` (a member
|
|
180
|
+
* state's domestic registration like `de_stn` is the rarer intent, and `eu_vat` was the only
|
|
181
|
+
* type the surface ever sent, so it is the floor); a country with exactly ONE catalog type
|
|
182
|
+
* preselects it (`AR` → `ar_cuit`, `US` → `us_ein`); everything else — unknown country, or one
|
|
183
|
+
* with several registrations to pick from — falls back to `eu_vat`, today's bare default.
|
|
184
|
+
*/
|
|
185
|
+
export function defaultTaxIdType(country: string | undefined): TaxIdType {
|
|
186
|
+
if (!country) return 'eu_vat';
|
|
187
|
+
const iso = country.toUpperCase();
|
|
188
|
+
if (EU_COUNTRIES.has(iso)) return 'eu_vat';
|
|
189
|
+
const matches = TAX_ID_TYPES.filter((type) => TAX_ID_TYPE_META[type].region === iso);
|
|
190
|
+
return matches.length === 1 ? (matches[0] ?? 'eu_vat') : 'eu_vat';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The buyer-facing name of each registration, the way the world abbreviates them (`EU VAT`,
|
|
195
|
+
* `AR CUIT`, `US EIN`) — the same labels the dashboard's tax settings use. Localized copy, so a
|
|
196
|
+
* hook: the labels ride the element's message catalog like every other user-facing string. The
|
|
197
|
+
* `Record<TaxIdType, …>` return type is the second half of the exhaustiveness gate — a new wire
|
|
198
|
+
* type without a label refuses to compile.
|
|
199
|
+
*/
|
|
200
|
+
export function useTaxIdTypeLabels(): Record<TaxIdType, string> {
|
|
201
|
+
const t = useT();
|
|
202
|
+
return useMemo(
|
|
203
|
+
() => ({
|
|
204
|
+
ad_nrt: t('AD NRT'),
|
|
205
|
+
ae_trn: t('AE TRN'),
|
|
206
|
+
al_tin: t('AL TIN'),
|
|
207
|
+
am_tin: t('AM TIN'),
|
|
208
|
+
ao_tin: t('AO TIN'),
|
|
209
|
+
ar_cuit: t('AR CUIT'),
|
|
210
|
+
au_abn: t('AU ABN'),
|
|
211
|
+
au_arn: t('AU ARN'),
|
|
212
|
+
aw_tin: t('AW TIN'),
|
|
213
|
+
az_tin: t('AZ TIN'),
|
|
214
|
+
ba_tin: t('BA TIN'),
|
|
215
|
+
bb_tin: t('BB TIN'),
|
|
216
|
+
bd_bin: t('BD BIN'),
|
|
217
|
+
bf_ifu: t('BF IFU'),
|
|
218
|
+
bg_uic: t('BG UIC'),
|
|
219
|
+
bh_vat: t('BH VAT'),
|
|
220
|
+
bj_ifu: t('BJ IFU'),
|
|
221
|
+
bo_tin: t('BO TIN'),
|
|
222
|
+
br_cnpj: t('BR CNPJ'),
|
|
223
|
+
br_cpf: t('BR CPF'),
|
|
224
|
+
bs_tin: t('BS TIN'),
|
|
225
|
+
by_tin: t('BY TIN'),
|
|
226
|
+
ca_bn: t('CA BN'),
|
|
227
|
+
ca_gst_hst: t('CA GST/HST'),
|
|
228
|
+
ca_pst_bc: t('CA PST BC'),
|
|
229
|
+
ca_pst_mb: t('CA PST MB'),
|
|
230
|
+
ca_pst_sk: t('CA PST SK'),
|
|
231
|
+
ca_qst: t('CA QST'),
|
|
232
|
+
cd_nif: t('CD NIF'),
|
|
233
|
+
ch_uid: t('CH UID'),
|
|
234
|
+
ch_vat: t('CH VAT'),
|
|
235
|
+
cl_tin: t('CL TIN'),
|
|
236
|
+
cm_niu: t('CM NIU'),
|
|
237
|
+
cn_tin: t('CN TIN'),
|
|
238
|
+
co_nit: t('CO NIT'),
|
|
239
|
+
cr_tin: t('CR TIN'),
|
|
240
|
+
cv_nif: t('CV NIF'),
|
|
241
|
+
de_stn: t('DE STN'),
|
|
242
|
+
do_rcn: t('DO RCN'),
|
|
243
|
+
ec_ruc: t('EC RUC'),
|
|
244
|
+
eg_tin: t('EG TIN'),
|
|
245
|
+
es_cif: t('ES CIF'),
|
|
246
|
+
et_tin: t('ET TIN'),
|
|
247
|
+
eu_oss_vat: t('EU OSS VAT'),
|
|
248
|
+
eu_vat: t('EU VAT'),
|
|
249
|
+
gb_vat: t('GB VAT'),
|
|
250
|
+
ge_vat: t('GE VAT'),
|
|
251
|
+
gh_tin: t('GH TIN'),
|
|
252
|
+
gn_nif: t('GN NIF'),
|
|
253
|
+
hk_br: t('HK BR'),
|
|
254
|
+
hr_oib: t('HR OIB'),
|
|
255
|
+
hu_tin: t('HU TIN'),
|
|
256
|
+
id_npwp: t('ID NPWP'),
|
|
257
|
+
il_vat: t('IL VAT'),
|
|
258
|
+
in_gst: t('IN GST'),
|
|
259
|
+
is_vat: t('IS VAT'),
|
|
260
|
+
jp_cn: t('JP CN'),
|
|
261
|
+
jp_rn: t('JP RN'),
|
|
262
|
+
jp_trn: t('JP TRN'),
|
|
263
|
+
ke_pin: t('KE PIN'),
|
|
264
|
+
kg_tin: t('KG TIN'),
|
|
265
|
+
kh_tin: t('KH TIN'),
|
|
266
|
+
kr_brn: t('KR BRN'),
|
|
267
|
+
kz_bin: t('KZ BIN'),
|
|
268
|
+
la_tin: t('LA TIN'),
|
|
269
|
+
li_uid: t('LI UID'),
|
|
270
|
+
li_vat: t('LI VAT'),
|
|
271
|
+
ma_vat: t('MA VAT'),
|
|
272
|
+
md_vat: t('MD VAT'),
|
|
273
|
+
me_pib: t('ME PIB'),
|
|
274
|
+
mk_vat: t('MK VAT'),
|
|
275
|
+
mr_nif: t('MR NIF'),
|
|
276
|
+
mx_rfc: t('MX RFC'),
|
|
277
|
+
my_frp: t('MY FRP'),
|
|
278
|
+
my_itn: t('MY ITN'),
|
|
279
|
+
my_sst: t('MY SST'),
|
|
280
|
+
ng_tin: t('NG TIN'),
|
|
281
|
+
no_vat: t('NO VAT'),
|
|
282
|
+
no_voec: t('NO VOEC'),
|
|
283
|
+
np_pan: t('NP PAN'),
|
|
284
|
+
nz_gst: t('NZ GST'),
|
|
285
|
+
om_vat: t('OM VAT'),
|
|
286
|
+
pe_ruc: t('PE RUC'),
|
|
287
|
+
ph_tin: t('PH TIN'),
|
|
288
|
+
pl_nip: t('PL NIP'),
|
|
289
|
+
ro_tin: t('RO TIN'),
|
|
290
|
+
rs_pib: t('RS PIB'),
|
|
291
|
+
ru_inn: t('RU INN'),
|
|
292
|
+
ru_kpp: t('RU KPP'),
|
|
293
|
+
sa_vat: t('SA VAT'),
|
|
294
|
+
sg_gst: t('SG GST'),
|
|
295
|
+
sg_uen: t('SG UEN'),
|
|
296
|
+
si_tin: t('SI TIN'),
|
|
297
|
+
sn_ninea: t('SN NINEA'),
|
|
298
|
+
sr_fin: t('SR FIN'),
|
|
299
|
+
sv_nit: t('SV NIT'),
|
|
300
|
+
th_vat: t('TH VAT'),
|
|
301
|
+
tj_tin: t('TJ TIN'),
|
|
302
|
+
tr_tin: t('TR TIN'),
|
|
303
|
+
tw_vat: t('TW VAT'),
|
|
304
|
+
tz_vat: t('TZ VAT'),
|
|
305
|
+
ua_vat: t('UA VAT'),
|
|
306
|
+
ug_tin: t('UG TIN'),
|
|
307
|
+
us_ein: t('US EIN'),
|
|
308
|
+
uy_ruc: t('UY RUC'),
|
|
309
|
+
uz_tin: t('UZ TIN'),
|
|
310
|
+
uz_vat: t('UZ VAT'),
|
|
311
|
+
ve_rif: t('VE RIF'),
|
|
312
|
+
vn_tin: t('VN TIN'),
|
|
313
|
+
xi_vat: t('XI VAT'),
|
|
314
|
+
za_vat: t('ZA VAT'),
|
|
315
|
+
zm_tin: t('ZM TIN'),
|
|
316
|
+
zw_tin: t('ZW TIN'),
|
|
317
|
+
}),
|
|
318
|
+
[t],
|
|
319
|
+
);
|
|
320
|
+
}
|