@hello-pay/react 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  CardExpiryElement,
22
22
  CardCvcElement,
23
23
  CardholderNameElement,
24
+ CardPasswordElement,
24
25
  useHelloPay,
25
26
  useElements,
26
27
  } from '@hello-pay/react';
@@ -71,6 +72,8 @@ function PaymentForm() {
71
72
  <CardExpiryElement />
72
73
  <CardCvcElement />
73
74
  <CardholderNameElement />
75
+ {/* 韓国発行カードのみ: 暗証番号の先頭 2 桁 */}
76
+ <CardPasswordElement />
74
77
  <button type="submit">支払う</button>
75
78
  </form>
76
79
  );
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  CardCvcElement: () => CardCvcElement,
25
25
  CardExpiryElement: () => CardExpiryElement,
26
26
  CardNumberElement: () => CardNumberElement,
27
+ CardPasswordElement: () => CardPasswordElement,
27
28
  CardholderNameElement: () => CardholderNameElement,
28
29
  Elements: () => Elements,
29
30
  loadHelloPay: () => loadHelloPay,
@@ -182,6 +183,24 @@ function useElements() {
182
183
 
183
184
  // src/ElementComponents.tsx
184
185
  var import_jsx_runtime2 = require("react/jsx-runtime");
186
+ var PROBE_PX = 1e5;
187
+ function contentSizedAxes(container) {
188
+ if (container.getClientRects().length === 0) {
189
+ const computed = getComputedStyle(container);
190
+ const isAuto = (v) => v === "" || v === "auto";
191
+ return { width: isAuto(computed.width), height: isAuto(computed.height) };
192
+ }
193
+ const before = { width: container.offsetWidth, height: container.offsetHeight };
194
+ const probe = container.ownerDocument.createElement("div");
195
+ probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;
196
+ container.appendChild(probe);
197
+ const grows = {
198
+ width: container.offsetWidth > before.width,
199
+ height: container.offsetHeight > before.height
200
+ };
201
+ probe.remove();
202
+ return grows;
203
+ }
185
204
  function createElementComponent(type) {
186
205
  function ElementComponent({
187
206
  options,
@@ -194,12 +213,19 @@ function createElementComponent(type) {
194
213
  }) {
195
214
  const elements = useElements();
196
215
  const containerRef = (0, import_react3.useRef)(null);
216
+ const [fill, setFill] = (0, import_react3.useState)({ width: false, height: false });
197
217
  const handlersRef = (0, import_react3.useRef)({ onReady, onChange, onFocus, onBlur });
198
218
  handlersRef.current = { onReady, onChange, onFocus, onBlur };
199
219
  (0, import_react3.useEffect)(() => {
200
- if (!elements || !containerRef.current) return;
220
+ const container = containerRef.current;
221
+ if (!elements || !container) return;
222
+ const unset = contentSizedAxes(container);
223
+ setFill((prev) => ({
224
+ width: prev.width || unset.width,
225
+ height: prev.height || unset.height
226
+ }));
201
227
  const element = elements.create(type, options);
202
- element.mount(containerRef.current);
228
+ element.mount(container);
203
229
  const unsubs = [
204
230
  element.on("ready", (e) => handlersRef.current.onReady?.(e)),
205
231
  element.on("change", (e) => handlersRef.current.onChange?.(e)),
@@ -216,7 +242,11 @@ function createElementComponent(type) {
216
242
  {
217
243
  ref: containerRef,
218
244
  className,
219
- style: { width: "100%", height: "100%", ...style }
245
+ style: {
246
+ ...fill.width && { width: "100%" },
247
+ ...fill.height && { height: "100%" },
248
+ ...style
249
+ }
220
250
  }
221
251
  );
222
252
  }
@@ -228,6 +258,7 @@ var CardNumberElement = createElementComponent("cardNumber");
228
258
  var CardExpiryElement = createElementComponent("cardExpiry");
229
259
  var CardCvcElement = createElementComponent("cardCvc");
230
260
  var CardholderNameElement = createElementComponent("cardholderName");
261
+ var CardPasswordElement = createElementComponent("cardPassword");
231
262
 
232
263
  // src/ApplePayButton.tsx
233
264
  var import_react4 = require("react");
@@ -297,6 +328,7 @@ ApplePayButton.displayName = "HelloPay(ApplePayButton)";
297
328
  CardCvcElement,
298
329
  CardExpiryElement,
299
330
  CardNumberElement,
331
+ CardPasswordElement,
300
332
  CardholderNameElement,
301
333
  Elements,
302
334
  loadHelloPay,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts","../src/ApplePayButton.tsx"],"sourcesContent":["export { loadHelloPay } from './loadHelloPay';\nexport type { LoadHelloPayOptions } from './loadHelloPay';\n\nexport { Elements } from './Elements';\nexport type { ElementsProps } from './Elements';\n\nexport {\n CardNumberElement,\n CardExpiryElement,\n CardCvcElement,\n CardholderNameElement,\n} from './ElementComponents';\nexport type { ElementProps } from './ElementComponents';\n\nexport { ApplePayButton } from './ApplePayButton';\nexport type { ApplePayButtonProps } from './ApplePayButton';\n\nexport { useHelloPay, useElements } from './hooks';\n\nexport type {\n HelloPay,\n Elements as ElementsSession,\n Element,\n ElementType,\n ElementOptions,\n ElementStyle,\n ElementChangeEvent,\n ElementFocusEvent,\n ElementReadyEvent,\n TokenCard,\n HelloPayError,\n CreateTokenResult,\n TokenProvider,\n ApplePayCard,\n ApplePayOptions,\n ApplePayResult,\n ApplePayError,\n ApplePayCancelError,\n Unsubscribe,\n} from './types';\n","/**\n * HelloPay SDK ローダー。\n *\n * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、\n * `window.HelloPay` ファクトリを解決する。\n *\n * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)\n * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)\n * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)\n * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能\n */\n\nimport type { HelloPay, HelloPayFactory } from './types';\n\n// SDK 本体は test / live どちらの publishable key でも同一ドメイン\n// (js.hello-pay.app) から配信する。test / live mode の切り替えは SDK 内部で\n// publishable key の prefix から解決し、test mode では接続先を test 環境\n// (*.stg.hello-pay.app) に向ける(hellopay.js と同じ方式)。\nconst DEFAULT_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n// sdkUrl 単位で script ロード Promise をキャッシュ。\n// loadPromise を単一変数にするとすべての URL で共有されてしまい、\n// 異なる sdkUrl(E2E / ローカル開発)が誤って同一ロードを共有する。\nconst scriptPromiseCache = new Map<string, Promise<HelloPayFactory | null>>();\n\n// publicKey + locale + sdkUrl の組み合わせごとに `HelloPaySDK` インスタンスをキャッシュ。\n// 加盟店が誤って inline で loadHelloPay を呼び出した場合でも同一 SDK インスタンスが\n// 返るようにして、Elements セッションの BroadcastChannel session ID ミスマッチを防ぐ。\nconst sdkCache = new Map<string, Promise<HelloPay | null>>();\n\n/** テスト専用: module-scope のキャッシュをすべて初期状態に戻す。 */\nexport function __resetLoadPromiseForTest(): void {\n scriptPromiseCache.clear();\n sdkCache.clear();\n}\n\nfunction normalizeSdkUrl(url: string): string {\n if (typeof window === 'undefined') return url;\n\n try {\n return new URL(url, window.location.href).href;\n } catch {\n return url;\n }\n}\n\nfunction normalizeLocale(locale?: string): string {\n return locale || 'ja';\n}\n\nfunction loadScript(url: string): Promise<HelloPayFactory | null> {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n // URL を正規化してプロトコル省略形などを統一する。\n // テスト / SSR 環境では window.location が http URL でない場合があるため try-catch で保護。\n const normalizedUrl = normalizeSdkUrl(url);\n\n return new Promise((resolve, reject) => {\n // script は URL 単位で管理する。異なる URL の script を削除せず共存させることで、\n // E2E / ローカル開発で異なる sdkUrl が並列で呼ばれても Promise が宙吊りにならない。\n // 状態は dataset.hellopayState に \"loading\" | \"loaded\" | \"error\" で刻む。\n const managedScripts = Array.from(\n document.querySelectorAll<HTMLScriptElement>('script[data-hellopay-sdk]'),\n );\n\n const existing = managedScripts.find((script) => {\n const rawUrl = script.dataset.hellopayUrl || script.src;\n if (!rawUrl) return false;\n try {\n return new URL(rawUrl, window.location.href).href === normalizedUrl;\n } catch {\n return rawUrl === normalizedUrl;\n }\n });\n\n if (existing) {\n const state = existing.dataset.hellopayState;\n if (state === 'loaded') {\n resolve(window.HelloPay ?? null);\n return;\n }\n if (state === 'loading') {\n existing.addEventListener('load', () => resolve(window.HelloPay ?? null), {\n once: true,\n });\n existing.addEventListener(\n 'error',\n () => reject(new Error('HelloPay SDK failed to load')),\n { once: true },\n );\n return;\n }\n // state === \"error\" or undefined: 失敗済み or 未知の状態なので作り直す\n existing.remove();\n }\n\n const script = document.createElement('script');\n script.src = normalizedUrl;\n script.async = true;\n script.dataset.hellopaySdk = 'true';\n script.dataset.hellopayUrl = normalizedUrl; // URL 比較用\n script.dataset.hellopayState = 'loading';\n script.addEventListener(\n 'load',\n () => {\n if (!window.HelloPay) {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK loaded but window.HelloPay is missing'));\n return;\n }\n script.dataset.hellopayState = 'loaded';\n resolve(window.HelloPay);\n },\n { once: true },\n );\n script.addEventListener(\n 'error',\n () => {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK failed to load'));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n}\n\nexport interface LoadHelloPayOptions {\n /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は js.hello-pay.app から配信し、test/live は SDK 内部で key prefix から解決) */\n sdkUrl?: string;\n locale?: string;\n}\n\n/**\n * HelloPay SDK をロードして初期化する。\n * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。\n */\nexport function loadHelloPay(\n publicKey: string,\n options: LoadHelloPayOptions = {},\n): Promise<HelloPay | null> {\n const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? DEFAULT_SDK_URL);\n const locale = normalizeLocale(options.locale);\n const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;\n const cached = sdkCache.get(cacheKey);\n if (cached) return cached;\n\n let scriptPromise = scriptPromiseCache.get(sdkUrl);\n if (!scriptPromise) {\n scriptPromise = loadScript(sdkUrl).catch((err) => {\n // 一時的なロード失敗はキャッシュせず次回呼び出しで再試行可能にする\n scriptPromiseCache.delete(sdkUrl);\n throw err;\n });\n scriptPromiseCache.set(sdkUrl, scriptPromise);\n }\n\n const sdkPromise = scriptPromise\n .then((factory) => {\n if (!factory) return null; // SSR\n return factory(publicKey, { locale: options.locale });\n })\n .catch((err) => {\n // SDK インスタンス生成失敗は sdkCache から退避して次回呼び出しで再試行可能にする\n sdkCache.delete(cacheKey);\n throw err;\n });\n sdkCache.set(cacheKey, sdkPromise);\n return sdkPromise;\n}\n","/**\n * <Elements> Provider.\n *\n * loadHelloPay() の Promise を受け取り、SDK インスタンスと Elements セッションを\n * 子孫に Context 経由で供給する。\n */\n\nimport { createContext, useEffect, useState, type ReactNode } from 'react';\nimport type { HelloPay, Elements as ElementsSession } from './types';\n\nexport interface ElementsContextValue {\n hellopay: HelloPay | null;\n elements: ElementsSession | null;\n}\n\nconst EMPTY_CONTEXT: ElementsContextValue = {\n hellopay: null,\n elements: null,\n};\n\nexport const ElementsContext = createContext<ElementsContextValue>(EMPTY_CONTEXT);\n\nexport interface ElementsProps {\n /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */\n hellopay: Promise<HelloPay | null> | HelloPay | null;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, children }: ElementsProps) {\n const [ctx, setCtx] = useState<ElementsContextValue>(EMPTY_CONTEXT);\n\n useEffect(() => {\n let cancelled = false;\n setCtx(EMPTY_CONTEXT);\n\n Promise.resolve(hellopayProp)\n .then((sdk) => {\n if (cancelled) return;\n if (!sdk) {\n setCtx(EMPTY_CONTEXT);\n return;\n }\n setCtx({ hellopay: sdk, elements: sdk.elements() });\n })\n .catch(() => {\n // SDK ロード失敗時は null のまま(unhandled rejection を防ぐ)\n if (cancelled) return;\n setCtx(EMPTY_CONTEXT);\n });\n\n return () => {\n cancelled = true;\n };\n }, [hellopayProp]);\n\n return <ElementsContext.Provider value={ctx}>{children}</ElementsContext.Provider>;\n}\n","/**\n * Element コンポーネント。\n *\n * 各コンポーネントは 1 つの `<div>` コンテナを描画するだけで、\n * 実体(カード情報が入る iframe)はコア SDK が差し込む。\n *\n * React Strict Mode の二重 mount 対応:\n * - effect の cleanup で element.unmount() を必ず呼ぶ\n * - unmount 後は Elements._elements 配列から除外される(コア SDK 側で対応済)\n * - 結果として Strict Mode で effect が再実行されても古い iframe は残らない\n */\n\nimport { useEffect, useRef, type CSSProperties } from 'react';\nimport { useElements } from './hooks';\nimport type {\n ElementType,\n ElementOptions,\n ElementChangeEvent,\n ElementFocusEvent,\n ElementReadyEvent,\n} from './types';\n\nexport interface ElementProps {\n options?: ElementOptions;\n className?: string;\n style?: CSSProperties;\n onReady?: (e: ElementReadyEvent) => void;\n onChange?: (e: ElementChangeEvent) => void;\n onFocus?: (e: ElementFocusEvent) => void;\n onBlur?: (e: ElementFocusEvent) => void;\n}\n\n/**\n * 指定 type の Element を管理する React コンポーネントを生成する。\n */\nfunction createElementComponent(type: ElementType) {\n function ElementComponent({\n options,\n className,\n style,\n onReady,\n onChange,\n onFocus,\n onBlur,\n }: ElementProps) {\n const elements = useElements();\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // 最新のハンドラを ref で保持(effect を毎回 re-run させないため)\n const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });\n handlersRef.current = { onReady, onChange, onFocus, onBlur };\n\n useEffect(() => {\n if (!elements || !containerRef.current) return;\n\n const element = elements.create(type, options);\n element.mount(containerRef.current);\n\n const unsubs = [\n element.on('ready', (e) => handlersRef.current.onReady?.(e)),\n element.on('change', (e) => handlersRef.current.onChange?.(e)),\n element.on('focus', (e) => handlersRef.current.onFocus?.(e)),\n element.on('blur', (e) => handlersRef.current.onBlur?.(e)),\n ];\n\n return () => {\n unsubs.forEach((u) => u());\n element.unmount();\n };\n // options はマウント時にしか反映しない方針(Stripe と同じ挙動)。\n // 動的に style を変えたい場合は後続 PR で element.update() を追加。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [elements]);\n\n // 親コンテナ(加盟店が指定した `.element-container` 等)の高さを\n // iframe に伝播させるため、wrapper は親のサイズを塗りつぶす。\n // style prop で上書き可能。\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n }\n ElementComponent.displayName = `HelloPay(${type})`;\n // getElement(CardNumberElement) のようにコンポーネント自身を型指定子として使えるよう、\n // 静的プロパティに type を生やしておく\n ElementComponent.__elementType = type as ElementType;\n return ElementComponent;\n}\n\nexport const CardNumberElement = createElementComponent('cardNumber');\nexport const CardExpiryElement = createElementComponent('cardExpiry');\nexport const CardCvcElement = createElementComponent('cardCvc');\nexport const CardholderNameElement = createElementComponent('cardholderName');\n\nexport type ElementComponent = typeof CardNumberElement;\n","/**\n * React hooks.\n *\n * useHelloPay / useElements は <Elements> Provider の Context から\n * SDK インスタンスと Elements セッションを取り出す。\n * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。\n */\n\nimport { useContext } from 'react';\nimport { ElementsContext } from './Elements';\nimport type { HelloPay, Elements } from './types';\n\nexport function useHelloPay(): HelloPay | null {\n return useContext(ElementsContext).hellopay;\n}\n\nexport function useElements(): Elements | null {\n return useContext(ElementsContext).elements;\n}\n","/**\n * Apple Pay ボタン。\n *\n * `useHelloPay()` から SDK を取り、対応端末(Safari + Apple Pay 利用可能)でのみ\n * Apple 公式スタイルのボタンを描画する。非対応端末では何も描画しない。\n *\n * click で `hellopay.applePay()` を同期呼び出しし(`new ApplePaySession` の\n * user gesture 要件のため)、結果を `onToken` / `onError` / `onCancel` に振り分ける。\n * SDK は Apple Pay トークンを返すだけで課金はしないので、加盟店は `onToken` で\n * 受けたトークンを自社バックエンドから決済 API に送って課金する。\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useHelloPay } from './hooks';\nimport type { ApplePayCard, ApplePayOptions, HelloPayError } from './types';\n\nexport interface ApplePayButtonProps {\n options: ApplePayOptions;\n /** トークン取得成功時。トークンを自社バックエンド経由で決済 API に送る。 */\n onToken: (token: string, card: ApplePayCard) => void;\n /** validation / network エラー時。 */\n onError?: (error: HelloPayError) => void;\n /** ユーザーが Apple Pay シートを閉じた時。 */\n onCancel?: () => void;\n /** Apple Pay ボタンの色。既定 `black`。 */\n buttonStyle?: 'black' | 'white' | 'white-outline';\n /** Apple Pay ボタンの種別(`plain` / `buy` / `pay` 等)。既定 `plain`。 */\n buttonType?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ApplePayButton({\n options,\n onToken,\n onError,\n onCancel,\n buttonStyle = 'black',\n buttonType = 'plain',\n className,\n style,\n}: ApplePayButtonProps) {\n const hellopay = useHelloPay();\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const [supported, setSupported] = useState(false);\n\n // canMakeApplePayPayments は ApplePaySession 依存で client-only のため effect で判定。\n useEffect(() => {\n setSupported(!!hellopay && hellopay.canMakeApplePayPayments());\n }, [hellopay]);\n\n // Apple Pay ボタンの見た目はベンダー CSS プロパティで指定する。React の style 型に\n // 無いため ref 経由で setProperty する。\n useEffect(() => {\n const el = buttonRef.current;\n if (!el) return;\n el.style.setProperty('-webkit-appearance', '-apple-pay-button');\n el.style.setProperty('-apple-pay-button-type', buttonType);\n el.style.setProperty('-apple-pay-button-style', buttonStyle);\n }, [supported, buttonType, buttonStyle]);\n\n // 最新のハンドラ / options を ref に保持し、click ハンドラを毎回作り直さない。\n const latest = useRef({ options, onToken, onError, onCancel, hellopay });\n latest.current = { options, onToken, onError, onCancel, hellopay };\n\n if (!supported) return null;\n\n const handleClick = () => {\n const current = latest.current;\n if (!current.hellopay) return;\n // user gesture 要件のため click ハンドラから同期呼び出しする。\n current.hellopay\n .applePay(current.options)\n .then((result) => {\n if (result.error) {\n if (result.error.type === 'cancel') {\n current.onCancel?.();\n } else {\n current.onError?.(result.error);\n }\n return;\n }\n current.onToken(result.token, result.card);\n })\n .catch((err: unknown) => {\n current.onError?.({\n type: 'config_error',\n code: 'apple_pay_error',\n message: err instanceof Error ? err.message : 'Apple Pay error',\n });\n });\n };\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleClick}\n className={className}\n style={{ width: '100%', height: '44px', cursor: 'pointer', ...style }}\n />\n );\n}\n\nApplePayButton.displayName = 'HelloPay(ApplePayButton)';\n"],"mappings":"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","names":["script","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts","../src/ApplePayButton.tsx"],"sourcesContent":["export { loadHelloPay } from './loadHelloPay';\nexport type { LoadHelloPayOptions } from './loadHelloPay';\n\nexport { Elements } from './Elements';\nexport type { ElementsProps } from './Elements';\n\nexport {\n CardNumberElement,\n CardExpiryElement,\n CardCvcElement,\n CardholderNameElement,\n CardPasswordElement,\n} from './ElementComponents';\nexport type { ElementProps } from './ElementComponents';\n\nexport { ApplePayButton } from './ApplePayButton';\nexport type { ApplePayButtonProps } from './ApplePayButton';\n\nexport { useHelloPay, useElements } from './hooks';\n\nexport type {\n HelloPay,\n Elements as ElementsSession,\n Element,\n ElementType,\n ElementOptions,\n ElementStyle,\n ElementChangeEvent,\n CardNumberChangeEvent,\n OtherElementChangeEvent,\n IssuerCountryResult,\n ElementFocusEvent,\n ElementReadyEvent,\n TokenCard,\n HelloPayError,\n CreateTokenResult,\n TokenProvider,\n ApplePayCard,\n ApplePayOptions,\n ApplePayResult,\n ApplePayError,\n ApplePayCancelError,\n Unsubscribe,\n} from './types';\n","/**\n * HelloPay SDK ローダー。\n *\n * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、\n * `window.HelloPay` ファクトリを解決する。\n *\n * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)\n * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)\n * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)\n * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能\n */\n\nimport type { HelloPay, HelloPayFactory } from './types';\n\n// SDK 本体は test / live どちらの publishable key でも同一ドメイン\n// (js.hello-pay.app) から配信する。test / live mode の切り替えは SDK 内部で\n// publishable key の prefix から解決し、test mode では接続先を test 環境\n// (*.stg.hello-pay.app) に向ける(hellopay.js と同じ方式)。\nconst DEFAULT_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n// sdkUrl 単位で script ロード Promise をキャッシュ。\n// loadPromise を単一変数にするとすべての URL で共有されてしまい、\n// 異なる sdkUrl(E2E / ローカル開発)が誤って同一ロードを共有する。\nconst scriptPromiseCache = new Map<string, Promise<HelloPayFactory | null>>();\n\n// publicKey + locale + sdkUrl の組み合わせごとに `HelloPaySDK` インスタンスをキャッシュ。\n// 加盟店が誤って inline で loadHelloPay を呼び出した場合でも同一 SDK インスタンスが\n// 返るようにして、Elements セッションの BroadcastChannel session ID ミスマッチを防ぐ。\nconst sdkCache = new Map<string, Promise<HelloPay | null>>();\n\n/** テスト専用: module-scope のキャッシュをすべて初期状態に戻す。 */\nexport function __resetLoadPromiseForTest(): void {\n scriptPromiseCache.clear();\n sdkCache.clear();\n}\n\nfunction normalizeSdkUrl(url: string): string {\n if (typeof window === 'undefined') return url;\n\n try {\n return new URL(url, window.location.href).href;\n } catch {\n return url;\n }\n}\n\nfunction normalizeLocale(locale?: string): string {\n return locale || 'ja';\n}\n\nfunction loadScript(url: string): Promise<HelloPayFactory | null> {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n // URL を正規化してプロトコル省略形などを統一する。\n // テスト / SSR 環境では window.location が http URL でない場合があるため try-catch で保護。\n const normalizedUrl = normalizeSdkUrl(url);\n\n return new Promise((resolve, reject) => {\n // script は URL 単位で管理する。異なる URL の script を削除せず共存させることで、\n // E2E / ローカル開発で異なる sdkUrl が並列で呼ばれても Promise が宙吊りにならない。\n // 状態は dataset.hellopayState に \"loading\" | \"loaded\" | \"error\" で刻む。\n const managedScripts = Array.from(\n document.querySelectorAll<HTMLScriptElement>('script[data-hellopay-sdk]'),\n );\n\n const existing = managedScripts.find((script) => {\n const rawUrl = script.dataset.hellopayUrl || script.src;\n if (!rawUrl) return false;\n try {\n return new URL(rawUrl, window.location.href).href === normalizedUrl;\n } catch {\n return rawUrl === normalizedUrl;\n }\n });\n\n if (existing) {\n const state = existing.dataset.hellopayState;\n if (state === 'loaded') {\n resolve(window.HelloPay ?? null);\n return;\n }\n if (state === 'loading') {\n existing.addEventListener('load', () => resolve(window.HelloPay ?? null), {\n once: true,\n });\n existing.addEventListener(\n 'error',\n () => reject(new Error('HelloPay SDK failed to load')),\n { once: true },\n );\n return;\n }\n // state === \"error\" or undefined: 失敗済み or 未知の状態なので作り直す\n existing.remove();\n }\n\n const script = document.createElement('script');\n script.src = normalizedUrl;\n script.async = true;\n script.dataset.hellopaySdk = 'true';\n script.dataset.hellopayUrl = normalizedUrl; // URL 比較用\n script.dataset.hellopayState = 'loading';\n script.addEventListener(\n 'load',\n () => {\n if (!window.HelloPay) {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK loaded but window.HelloPay is missing'));\n return;\n }\n script.dataset.hellopayState = 'loaded';\n resolve(window.HelloPay);\n },\n { once: true },\n );\n script.addEventListener(\n 'error',\n () => {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK failed to load'));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n}\n\nexport interface LoadHelloPayOptions {\n /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は js.hello-pay.app から配信し、test/live は SDK 内部で key prefix から解決) */\n sdkUrl?: string;\n locale?: string;\n}\n\n/**\n * HelloPay SDK をロードして初期化する。\n * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。\n */\nexport function loadHelloPay(\n publicKey: string,\n options: LoadHelloPayOptions = {},\n): Promise<HelloPay | null> {\n const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? DEFAULT_SDK_URL);\n const locale = normalizeLocale(options.locale);\n const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;\n const cached = sdkCache.get(cacheKey);\n if (cached) return cached;\n\n let scriptPromise = scriptPromiseCache.get(sdkUrl);\n if (!scriptPromise) {\n scriptPromise = loadScript(sdkUrl).catch((err) => {\n // 一時的なロード失敗はキャッシュせず次回呼び出しで再試行可能にする\n scriptPromiseCache.delete(sdkUrl);\n throw err;\n });\n scriptPromiseCache.set(sdkUrl, scriptPromise);\n }\n\n const sdkPromise = scriptPromise\n .then((factory) => {\n if (!factory) return null; // SSR\n return factory(publicKey, { locale: options.locale });\n })\n .catch((err) => {\n // SDK インスタンス生成失敗は sdkCache から退避して次回呼び出しで再試行可能にする\n sdkCache.delete(cacheKey);\n throw err;\n });\n sdkCache.set(cacheKey, sdkPromise);\n return sdkPromise;\n}\n","/**\n * <Elements> Provider.\n *\n * loadHelloPay() の Promise を受け取り、SDK インスタンスと Elements セッションを\n * 子孫に Context 経由で供給する。\n */\n\nimport { createContext, useEffect, useState, type ReactNode } from 'react';\nimport type { HelloPay, Elements as ElementsSession } from './types';\n\nexport interface ElementsContextValue {\n hellopay: HelloPay | null;\n elements: ElementsSession | null;\n}\n\nconst EMPTY_CONTEXT: ElementsContextValue = {\n hellopay: null,\n elements: null,\n};\n\nexport const ElementsContext = createContext<ElementsContextValue>(EMPTY_CONTEXT);\n\nexport interface ElementsProps {\n /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */\n hellopay: Promise<HelloPay | null> | HelloPay | null;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, children }: ElementsProps) {\n const [ctx, setCtx] = useState<ElementsContextValue>(EMPTY_CONTEXT);\n\n useEffect(() => {\n let cancelled = false;\n setCtx(EMPTY_CONTEXT);\n\n Promise.resolve(hellopayProp)\n .then((sdk) => {\n if (cancelled) return;\n if (!sdk) {\n setCtx(EMPTY_CONTEXT);\n return;\n }\n setCtx({ hellopay: sdk, elements: sdk.elements() });\n })\n .catch(() => {\n // SDK ロード失敗時は null のまま(unhandled rejection を防ぐ)\n if (cancelled) return;\n setCtx(EMPTY_CONTEXT);\n });\n\n return () => {\n cancelled = true;\n };\n }, [hellopayProp]);\n\n return <ElementsContext.Provider value={ctx}>{children}</ElementsContext.Provider>;\n}\n","/**\n * Element コンポーネント。\n *\n * 各コンポーネントは 1 つの `<div>` コンテナを描画するだけで、\n * 実体(カード情報が入る iframe)はコア SDK が差し込む。\n *\n * React Strict Mode の二重 mount 対応:\n * - effect の cleanup で element.unmount() を必ず呼ぶ\n * - unmount 後は Elements._elements 配列から除外される(コア SDK 側で対応済)\n * - 結果として Strict Mode で effect が再実行されても古い iframe は残らない\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useElements } from './hooks';\nimport type {\n ElementType,\n ElementOptions,\n ElementChangeEvent,\n ElementFocusEvent,\n ElementReadyEvent,\n} from './types';\n\nexport interface ElementProps {\n options?: ElementOptions;\n className?: string;\n style?: CSSProperties;\n onReady?: (e: ElementReadyEvent) => void;\n onChange?: (e: ElementChangeEvent) => void;\n onFocus?: (e: ElementFocusEvent) => void;\n onBlur?: (e: ElementFocusEvent) => void;\n}\n\nconst PROBE_PX = 100000;\n\n/**\n * コンテナの幅・高さのうち、中身に追従する(加盟店が寸法を決めていない)軸を返す。\n */\nfunction contentSizedAxes(container: HTMLElement) {\n // display: none の祖先の中ではレイアウトが無いので、computed value で見る。\n if (container.getClientRects().length === 0) {\n const computed = getComputedStyle(container);\n const isAuto = (v: string) => v === '' || v === 'auto';\n return { width: isAuto(computed.width), height: isAuto(computed.height) };\n }\n // computed の height で判定すると、border-box の padding・border や min-height で\n // 0 にならず見誤るので、大きな子を一時的に入れて寸法が伸びるかで確かめる。\n // 祖先の transform(モーダルの scale のアニメーションなど)に左右されないよう、\n // getBoundingClientRect ではなくレイアウト上の寸法の offset* で比べる。\n const before = { width: container.offsetWidth, height: container.offsetHeight };\n const probe = container.ownerDocument.createElement('div');\n probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;\n container.appendChild(probe);\n const grows = {\n width: container.offsetWidth > before.width,\n height: container.offsetHeight > before.height,\n };\n probe.remove();\n return grows;\n}\n\n/**\n * 指定 type の Element を管理する React コンポーネントを生成する。\n */\nfunction createElementComponent(type: ElementType) {\n function ElementComponent({\n options,\n className,\n style,\n onReady,\n onChange,\n onFocus,\n onBlur,\n }: ElementProps) {\n const elements = useElements();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [fill, setFill] = useState({ width: false, height: false });\n\n // 最新のハンドラを ref で保持(effect を毎回 re-run させないため)\n const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });\n handlersRef.current = { onReady, onChange, onFocus, onBlur };\n\n useEffect(() => {\n const container = containerRef.current;\n if (!elements || !container) return;\n\n // 100% を当てた後に測り直すと親の寸法に追従して見えるので、立てた軸は戻さない。\n const unset = contentSizedAxes(container);\n setFill((prev) => ({\n width: prev.width || unset.width,\n height: prev.height || unset.height,\n }));\n\n const element = elements.create(type, options);\n element.mount(container);\n\n const unsubs = [\n element.on('ready', (e) => handlersRef.current.onReady?.(e)),\n element.on('change', (e) => handlersRef.current.onChange?.(e)),\n element.on('focus', (e) => handlersRef.current.onFocus?.(e)),\n element.on('blur', (e) => handlersRef.current.onBlur?.(e)),\n ];\n\n return () => {\n unsubs.forEach((u) => u());\n element.unmount();\n };\n // options はマウント時にしか反映しない方針(Stripe と同じ挙動)。\n // 動的に style を変えたい場合は後続 PR で element.update() を追加。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [elements]);\n\n // インラインの既定値は className の寸法に勝つので、加盟店が決めていない軸だけに付ける。\n // 判定は mount 時の 1 回だけなので、後から寸法を変えるなら style で渡す。\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n ...(fill.width && { width: '100%' }),\n ...(fill.height && { height: '100%' }),\n ...style,\n }}\n />\n );\n }\n ElementComponent.displayName = `HelloPay(${type})`;\n // getElement(CardNumberElement) のようにコンポーネント自身を型指定子として使えるよう、\n // 静的プロパティに type を生やしておく\n ElementComponent.__elementType = type as ElementType;\n return ElementComponent;\n}\n\nexport const CardNumberElement = createElementComponent('cardNumber');\nexport const CardExpiryElement = createElementComponent('cardExpiry');\nexport const CardCvcElement = createElementComponent('cardCvc');\nexport const CardholderNameElement = createElementComponent('cardholderName');\n// 韓国発行カードの Toss 登録に必要な暗証番号の先頭 2 桁。表示・非表示は\n// 発行国の判定結果に応じて呼び出し側が mount / unmount で制御する。\nexport const CardPasswordElement = createElementComponent('cardPassword');\n\nexport type ElementComponent = typeof CardNumberElement;\n","/**\n * React hooks.\n *\n * useHelloPay / useElements は <Elements> Provider の Context から\n * SDK インスタンスと Elements セッションを取り出す。\n * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。\n */\n\nimport { useContext } from 'react';\nimport { ElementsContext } from './Elements';\nimport type { HelloPay, Elements } from './types';\n\nexport function useHelloPay(): HelloPay | null {\n return useContext(ElementsContext).hellopay;\n}\n\nexport function useElements(): Elements | null {\n return useContext(ElementsContext).elements;\n}\n","/**\n * Apple Pay ボタン。\n *\n * `useHelloPay()` から SDK を取り、対応端末(Safari + Apple Pay 利用可能)でのみ\n * Apple 公式スタイルのボタンを描画する。非対応端末では何も描画しない。\n *\n * click で `hellopay.applePay()` を同期呼び出しし(`new ApplePaySession` の\n * user gesture 要件のため)、結果を `onToken` / `onError` / `onCancel` に振り分ける。\n * SDK は Apple Pay トークンを返すだけで課金はしないので、加盟店は `onToken` で\n * 受けたトークンを自社バックエンドから決済 API に送って課金する。\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useHelloPay } from './hooks';\nimport type { ApplePayCard, ApplePayOptions, HelloPayError } from './types';\n\nexport interface ApplePayButtonProps {\n options: ApplePayOptions;\n /** トークン取得成功時。トークンを自社バックエンド経由で決済 API に送る。 */\n onToken: (token: string, card: ApplePayCard) => void;\n /** validation / network エラー時。 */\n onError?: (error: HelloPayError) => void;\n /** ユーザーが Apple Pay シートを閉じた時。 */\n onCancel?: () => void;\n /** Apple Pay ボタンの色。既定 `black`。 */\n buttonStyle?: 'black' | 'white' | 'white-outline';\n /** Apple Pay ボタンの種別(`plain` / `buy` / `pay` 等)。既定 `plain`。 */\n buttonType?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ApplePayButton({\n options,\n onToken,\n onError,\n onCancel,\n buttonStyle = 'black',\n buttonType = 'plain',\n className,\n style,\n}: ApplePayButtonProps) {\n const hellopay = useHelloPay();\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const [supported, setSupported] = useState(false);\n\n // canMakeApplePayPayments は ApplePaySession 依存で client-only のため effect で判定。\n useEffect(() => {\n setSupported(!!hellopay && hellopay.canMakeApplePayPayments());\n }, [hellopay]);\n\n // Apple Pay ボタンの見た目はベンダー CSS プロパティで指定する。React の style 型に\n // 無いため ref 経由で setProperty する。\n useEffect(() => {\n const el = buttonRef.current;\n if (!el) return;\n el.style.setProperty('-webkit-appearance', '-apple-pay-button');\n el.style.setProperty('-apple-pay-button-type', buttonType);\n el.style.setProperty('-apple-pay-button-style', buttonStyle);\n }, [supported, buttonType, buttonStyle]);\n\n // 最新のハンドラ / options を ref に保持し、click ハンドラを毎回作り直さない。\n const latest = useRef({ options, onToken, onError, onCancel, hellopay });\n latest.current = { options, onToken, onError, onCancel, hellopay };\n\n if (!supported) return null;\n\n const handleClick = () => {\n const current = latest.current;\n if (!current.hellopay) return;\n // user gesture 要件のため click ハンドラから同期呼び出しする。\n current.hellopay\n .applePay(current.options)\n .then((result) => {\n if (result.error) {\n if (result.error.type === 'cancel') {\n current.onCancel?.();\n } else {\n current.onError?.(result.error);\n }\n return;\n }\n current.onToken(result.token, result.card);\n })\n .catch((err: unknown) => {\n current.onError?.({\n type: 'config_error',\n code: 'apple_pay_error',\n message: err instanceof Error ? err.message : 'Apple Pay error',\n });\n });\n };\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleClick}\n className={className}\n style={{ width: '100%', height: '44px', cursor: 'pointer', ...style }}\n />\n );\n}\n\nApplePayButton.displayName = 'HelloPay(ApplePayButton)';\n"],"mappings":"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","names":["script","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -9,7 +9,7 @@ import { ReactNode, CSSProperties } from 'react';
9
9
  * HelloPay 管理ドメインから配信)を満たすため。
10
10
  * npm パッケージは型定義とローダーのみを提供する。
11
11
  */
12
- type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc' | 'cardholderName';
12
+ type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc' | 'cardholderName' | 'cardPassword';
13
13
  interface ElementStyle {
14
14
  base?: {
15
15
  color?: string;
@@ -29,12 +29,45 @@ interface ElementStyle {
29
29
  interface ElementOptions {
30
30
  style?: ElementStyle;
31
31
  }
32
- interface ElementChangeEvent {
33
- elementType: ElementType;
32
+ /**
33
+ * カード発行国の判定結果。
34
+ *
35
+ * `null` を使わず 3 状態で返すのは、呼び出し側の挙動が異なるため。
36
+ * `pending` はフォームを組み替えずに現状の表示を維持し、`unknown` は
37
+ * どの発行国グループにも該当しないもの(`default`)として組み替える。
38
+ *
39
+ * 現在 `determined` になるのは韓国の国内専用カード(先頭 4 桁 `9410`)のみ。
40
+ * それ以外の発行国と、韓国発行の国際ブランドカードは `unknown` を返す。
41
+ * 誤検知を避けるため確実に判定できる範囲だけに絞っており、対応範囲は
42
+ * 今後 `country` を増やす形で広げる。
43
+ */
44
+ type IssuerCountryResult = {
45
+ status: 'pending';
46
+ } | {
47
+ status: 'unknown';
48
+ } | {
49
+ status: 'determined';
50
+ country: string;
51
+ };
52
+ interface CardNumberChangeEvent {
53
+ elementType: 'cardNumber';
54
+ empty: boolean;
55
+ complete: boolean;
56
+ brand?: string | null;
57
+ issuerCountry: IssuerCountryResult;
58
+ }
59
+ interface OtherElementChangeEvent {
60
+ elementType: Exclude<ElementType, 'cardNumber'>;
34
61
  empty: boolean;
35
62
  complete: boolean;
36
63
  brand?: string | null;
37
64
  }
65
+ /**
66
+ * `elementType` で分岐する判別可能ユニオン。`issuerCountry` は cardNumber
67
+ * にしか存在しないため、読むには `elementType === 'cardNumber'` での
68
+ * narrowing が必要。
69
+ */
70
+ type ElementChangeEvent = CardNumberChangeEvent | OtherElementChangeEvent;
38
71
  interface ElementFocusEvent {
39
72
  elementType: ElementType;
40
73
  }
@@ -110,6 +143,53 @@ type CreateTokenResult = {
110
143
  tokenExpiryDate?: undefined;
111
144
  error: HelloPayError;
112
145
  };
146
+ /**
147
+ * 複数プロバイダへのカード登録 (#2747) の返り値。
148
+ *
149
+ * `CreateTokenResult` と違い **トークンを持たない** — 登録結果 (Toss の BillingKey など) は
150
+ * 加盟店バックエンドがサーバ間 API で取得する。判別は `status` で行う。
151
+ *
152
+ * ```ts
153
+ * const result = await hellopay.createToken(el, { sessionClientSecret });
154
+ * if (result.status === 'failed') {
155
+ * // 発行国の申告と BIN 判定が食い違った場合はフォームを組み替える
156
+ * if (result.error.code === 'issuer_country_mismatch') {
157
+ * rebuildForm(result.error.issuerCountryGroup);
158
+ * }
159
+ * return showError(result.error.message);
160
+ * }
161
+ * showRegisteredCard(result.card);
162
+ * ```
163
+ */
164
+ type CardRegistrationResult = {
165
+ status: 'succeeded';
166
+ card: CardRegistrationCard | null;
167
+ error?: undefined;
168
+ } | {
169
+ status: 'failed';
170
+ card: null;
171
+ error: CardRegistrationError;
172
+ };
173
+ /**
174
+ * 登録できたカードのメタデータ。
175
+ *
176
+ * `brand` が `TokenCard` と違って `null` を許すのは、韓国の国内専用カード (`9410` 始まり) が
177
+ * 国際ブランドを持たないため。サーバーの BIN 判定がブランドを確定できないとそのまま `null` で
178
+ * 返るので、型でも表す。
179
+ */
180
+ interface CardRegistrationCard {
181
+ brand: string | null;
182
+ last4: string;
183
+ expMonth: string;
184
+ expYear: string;
185
+ }
186
+ interface CardRegistrationError extends HelloPayError {
187
+ /**
188
+ * `issuer_country_mismatch` のときだけ入る、BIN 判定で確定した発行国グループ
189
+ * (該当なしは `'default'`)。呼び出し側はこれを使って入力フォームを組み替える。
190
+ */
191
+ issuerCountryGroup?: string;
192
+ }
113
193
  /**
114
194
  * Apple Pay で決済したカードのメタデータ。Apple Pay は client 側で last4 /
115
195
  * 有効期限を確実に取得できないため、カード決済の `TokenCard` とは別形状。
@@ -166,6 +246,16 @@ type ApplePayResult = {
166
246
  interface HelloPay {
167
247
  elements(): Elements$1;
168
248
  createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
249
+ /**
250
+ * 登録セッション経路 (#2747)。`sessionClientSecret` を渡すと、加盟店バックエンドが
251
+ * 宣言した複数の決済プロバイダへまとめてカードを登録する。
252
+ *
253
+ * **トークンは返らない。** BillingKey も PSP のトークンもブラウザには渡らず、
254
+ * 加盟店バックエンドがサーバ間 API で取得する。
255
+ */
256
+ createToken(cardNumberElement: Element, options: {
257
+ sessionClientSecret: string;
258
+ }): Promise<CardRegistrationResult>;
169
259
  /** 対応端末(Safari + Apple Pay 利用可能)でのみ true。 */
170
260
  canMakeApplePayPayments(): boolean;
171
261
  /** Apple Pay シートを開き、都度課金トークンを取得する。 */
@@ -244,6 +334,11 @@ declare const CardholderNameElement: {
244
334
  displayName: string;
245
335
  __elementType: ElementType;
246
336
  };
337
+ declare const CardPasswordElement: {
338
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
339
+ displayName: string;
340
+ __elementType: ElementType;
341
+ };
247
342
 
248
343
  interface ApplePayButtonProps {
249
344
  options: ApplePayOptions;
@@ -276,4 +371,4 @@ declare namespace ApplePayButton {
276
371
  declare function useHelloPay(): HelloPay | null;
277
372
  declare function useElements(): Elements$1 | null;
278
373
 
279
- export { ApplePayButton, type ApplePayButtonProps, type ApplePayCancelError, type ApplePayCard, type ApplePayError, type ApplePayOptions, type ApplePayResult, CardCvcElement, CardExpiryElement, CardNumberElement, CardholderNameElement, type CreateTokenResult, type Element, type ElementChangeEvent, type ElementFocusEvent, type ElementOptions, type ElementProps, type ElementReadyEvent, type ElementStyle, type ElementType, Elements, type ElementsProps, type Elements$1 as ElementsSession, type HelloPay, type HelloPayError, type LoadHelloPayOptions, type TokenCard, type TokenProvider, type Unsubscribe, loadHelloPay, useElements, useHelloPay };
374
+ export { ApplePayButton, type ApplePayButtonProps, type ApplePayCancelError, type ApplePayCard, type ApplePayError, type ApplePayOptions, type ApplePayResult, CardCvcElement, CardExpiryElement, type CardNumberChangeEvent, CardNumberElement, CardPasswordElement, CardholderNameElement, type CreateTokenResult, type Element, type ElementChangeEvent, type ElementFocusEvent, type ElementOptions, type ElementProps, type ElementReadyEvent, type ElementStyle, type ElementType, Elements, type ElementsProps, type Elements$1 as ElementsSession, type HelloPay, type HelloPayError, type IssuerCountryResult, type LoadHelloPayOptions, type OtherElementChangeEvent, type TokenCard, type TokenProvider, type Unsubscribe, loadHelloPay, useElements, useHelloPay };
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { ReactNode, CSSProperties } from 'react';
9
9
  * HelloPay 管理ドメインから配信)を満たすため。
10
10
  * npm パッケージは型定義とローダーのみを提供する。
11
11
  */
12
- type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc' | 'cardholderName';
12
+ type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc' | 'cardholderName' | 'cardPassword';
13
13
  interface ElementStyle {
14
14
  base?: {
15
15
  color?: string;
@@ -29,12 +29,45 @@ interface ElementStyle {
29
29
  interface ElementOptions {
30
30
  style?: ElementStyle;
31
31
  }
32
- interface ElementChangeEvent {
33
- elementType: ElementType;
32
+ /**
33
+ * カード発行国の判定結果。
34
+ *
35
+ * `null` を使わず 3 状態で返すのは、呼び出し側の挙動が異なるため。
36
+ * `pending` はフォームを組み替えずに現状の表示を維持し、`unknown` は
37
+ * どの発行国グループにも該当しないもの(`default`)として組み替える。
38
+ *
39
+ * 現在 `determined` になるのは韓国の国内専用カード(先頭 4 桁 `9410`)のみ。
40
+ * それ以外の発行国と、韓国発行の国際ブランドカードは `unknown` を返す。
41
+ * 誤検知を避けるため確実に判定できる範囲だけに絞っており、対応範囲は
42
+ * 今後 `country` を増やす形で広げる。
43
+ */
44
+ type IssuerCountryResult = {
45
+ status: 'pending';
46
+ } | {
47
+ status: 'unknown';
48
+ } | {
49
+ status: 'determined';
50
+ country: string;
51
+ };
52
+ interface CardNumberChangeEvent {
53
+ elementType: 'cardNumber';
54
+ empty: boolean;
55
+ complete: boolean;
56
+ brand?: string | null;
57
+ issuerCountry: IssuerCountryResult;
58
+ }
59
+ interface OtherElementChangeEvent {
60
+ elementType: Exclude<ElementType, 'cardNumber'>;
34
61
  empty: boolean;
35
62
  complete: boolean;
36
63
  brand?: string | null;
37
64
  }
65
+ /**
66
+ * `elementType` で分岐する判別可能ユニオン。`issuerCountry` は cardNumber
67
+ * にしか存在しないため、読むには `elementType === 'cardNumber'` での
68
+ * narrowing が必要。
69
+ */
70
+ type ElementChangeEvent = CardNumberChangeEvent | OtherElementChangeEvent;
38
71
  interface ElementFocusEvent {
39
72
  elementType: ElementType;
40
73
  }
@@ -110,6 +143,53 @@ type CreateTokenResult = {
110
143
  tokenExpiryDate?: undefined;
111
144
  error: HelloPayError;
112
145
  };
146
+ /**
147
+ * 複数プロバイダへのカード登録 (#2747) の返り値。
148
+ *
149
+ * `CreateTokenResult` と違い **トークンを持たない** — 登録結果 (Toss の BillingKey など) は
150
+ * 加盟店バックエンドがサーバ間 API で取得する。判別は `status` で行う。
151
+ *
152
+ * ```ts
153
+ * const result = await hellopay.createToken(el, { sessionClientSecret });
154
+ * if (result.status === 'failed') {
155
+ * // 発行国の申告と BIN 判定が食い違った場合はフォームを組み替える
156
+ * if (result.error.code === 'issuer_country_mismatch') {
157
+ * rebuildForm(result.error.issuerCountryGroup);
158
+ * }
159
+ * return showError(result.error.message);
160
+ * }
161
+ * showRegisteredCard(result.card);
162
+ * ```
163
+ */
164
+ type CardRegistrationResult = {
165
+ status: 'succeeded';
166
+ card: CardRegistrationCard | null;
167
+ error?: undefined;
168
+ } | {
169
+ status: 'failed';
170
+ card: null;
171
+ error: CardRegistrationError;
172
+ };
173
+ /**
174
+ * 登録できたカードのメタデータ。
175
+ *
176
+ * `brand` が `TokenCard` と違って `null` を許すのは、韓国の国内専用カード (`9410` 始まり) が
177
+ * 国際ブランドを持たないため。サーバーの BIN 判定がブランドを確定できないとそのまま `null` で
178
+ * 返るので、型でも表す。
179
+ */
180
+ interface CardRegistrationCard {
181
+ brand: string | null;
182
+ last4: string;
183
+ expMonth: string;
184
+ expYear: string;
185
+ }
186
+ interface CardRegistrationError extends HelloPayError {
187
+ /**
188
+ * `issuer_country_mismatch` のときだけ入る、BIN 判定で確定した発行国グループ
189
+ * (該当なしは `'default'`)。呼び出し側はこれを使って入力フォームを組み替える。
190
+ */
191
+ issuerCountryGroup?: string;
192
+ }
113
193
  /**
114
194
  * Apple Pay で決済したカードのメタデータ。Apple Pay は client 側で last4 /
115
195
  * 有効期限を確実に取得できないため、カード決済の `TokenCard` とは別形状。
@@ -166,6 +246,16 @@ type ApplePayResult = {
166
246
  interface HelloPay {
167
247
  elements(): Elements$1;
168
248
  createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
249
+ /**
250
+ * 登録セッション経路 (#2747)。`sessionClientSecret` を渡すと、加盟店バックエンドが
251
+ * 宣言した複数の決済プロバイダへまとめてカードを登録する。
252
+ *
253
+ * **トークンは返らない。** BillingKey も PSP のトークンもブラウザには渡らず、
254
+ * 加盟店バックエンドがサーバ間 API で取得する。
255
+ */
256
+ createToken(cardNumberElement: Element, options: {
257
+ sessionClientSecret: string;
258
+ }): Promise<CardRegistrationResult>;
169
259
  /** 対応端末(Safari + Apple Pay 利用可能)でのみ true。 */
170
260
  canMakeApplePayPayments(): boolean;
171
261
  /** Apple Pay シートを開き、都度課金トークンを取得する。 */
@@ -244,6 +334,11 @@ declare const CardholderNameElement: {
244
334
  displayName: string;
245
335
  __elementType: ElementType;
246
336
  };
337
+ declare const CardPasswordElement: {
338
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
339
+ displayName: string;
340
+ __elementType: ElementType;
341
+ };
247
342
 
248
343
  interface ApplePayButtonProps {
249
344
  options: ApplePayOptions;
@@ -276,4 +371,4 @@ declare namespace ApplePayButton {
276
371
  declare function useHelloPay(): HelloPay | null;
277
372
  declare function useElements(): Elements$1 | null;
278
373
 
279
- export { ApplePayButton, type ApplePayButtonProps, type ApplePayCancelError, type ApplePayCard, type ApplePayError, type ApplePayOptions, type ApplePayResult, CardCvcElement, CardExpiryElement, CardNumberElement, CardholderNameElement, type CreateTokenResult, type Element, type ElementChangeEvent, type ElementFocusEvent, type ElementOptions, type ElementProps, type ElementReadyEvent, type ElementStyle, type ElementType, Elements, type ElementsProps, type Elements$1 as ElementsSession, type HelloPay, type HelloPayError, type LoadHelloPayOptions, type TokenCard, type TokenProvider, type Unsubscribe, loadHelloPay, useElements, useHelloPay };
374
+ export { ApplePayButton, type ApplePayButtonProps, type ApplePayCancelError, type ApplePayCard, type ApplePayError, type ApplePayOptions, type ApplePayResult, CardCvcElement, CardExpiryElement, type CardNumberChangeEvent, CardNumberElement, CardPasswordElement, CardholderNameElement, type CreateTokenResult, type Element, type ElementChangeEvent, type ElementFocusEvent, type ElementOptions, type ElementProps, type ElementReadyEvent, type ElementStyle, type ElementType, Elements, type ElementsProps, type Elements$1 as ElementsSession, type HelloPay, type HelloPayError, type IssuerCountryResult, type LoadHelloPayOptions, type OtherElementChangeEvent, type TokenCard, type TokenProvider, type Unsubscribe, loadHelloPay, useElements, useHelloPay };
package/dist/index.js CHANGED
@@ -135,7 +135,7 @@ function Elements({ hellopay: hellopayProp, children }) {
135
135
  }
136
136
 
137
137
  // src/ElementComponents.tsx
138
- import { useEffect as useEffect2, useRef } from "react";
138
+ import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
139
139
 
140
140
  // src/hooks.ts
141
141
  import { useContext } from "react";
@@ -148,6 +148,24 @@ function useElements() {
148
148
 
149
149
  // src/ElementComponents.tsx
150
150
  import { jsx as jsx2 } from "react/jsx-runtime";
151
+ var PROBE_PX = 1e5;
152
+ function contentSizedAxes(container) {
153
+ if (container.getClientRects().length === 0) {
154
+ const computed = getComputedStyle(container);
155
+ const isAuto = (v) => v === "" || v === "auto";
156
+ return { width: isAuto(computed.width), height: isAuto(computed.height) };
157
+ }
158
+ const before = { width: container.offsetWidth, height: container.offsetHeight };
159
+ const probe = container.ownerDocument.createElement("div");
160
+ probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;
161
+ container.appendChild(probe);
162
+ const grows = {
163
+ width: container.offsetWidth > before.width,
164
+ height: container.offsetHeight > before.height
165
+ };
166
+ probe.remove();
167
+ return grows;
168
+ }
151
169
  function createElementComponent(type) {
152
170
  function ElementComponent({
153
171
  options,
@@ -160,12 +178,19 @@ function createElementComponent(type) {
160
178
  }) {
161
179
  const elements = useElements();
162
180
  const containerRef = useRef(null);
181
+ const [fill, setFill] = useState2({ width: false, height: false });
163
182
  const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });
164
183
  handlersRef.current = { onReady, onChange, onFocus, onBlur };
165
184
  useEffect2(() => {
166
- if (!elements || !containerRef.current) return;
185
+ const container = containerRef.current;
186
+ if (!elements || !container) return;
187
+ const unset = contentSizedAxes(container);
188
+ setFill((prev) => ({
189
+ width: prev.width || unset.width,
190
+ height: prev.height || unset.height
191
+ }));
167
192
  const element = elements.create(type, options);
168
- element.mount(containerRef.current);
193
+ element.mount(container);
169
194
  const unsubs = [
170
195
  element.on("ready", (e) => handlersRef.current.onReady?.(e)),
171
196
  element.on("change", (e) => handlersRef.current.onChange?.(e)),
@@ -182,7 +207,11 @@ function createElementComponent(type) {
182
207
  {
183
208
  ref: containerRef,
184
209
  className,
185
- style: { width: "100%", height: "100%", ...style }
210
+ style: {
211
+ ...fill.width && { width: "100%" },
212
+ ...fill.height && { height: "100%" },
213
+ ...style
214
+ }
186
215
  }
187
216
  );
188
217
  }
@@ -194,9 +223,10 @@ var CardNumberElement = createElementComponent("cardNumber");
194
223
  var CardExpiryElement = createElementComponent("cardExpiry");
195
224
  var CardCvcElement = createElementComponent("cardCvc");
196
225
  var CardholderNameElement = createElementComponent("cardholderName");
226
+ var CardPasswordElement = createElementComponent("cardPassword");
197
227
 
198
228
  // src/ApplePayButton.tsx
199
- import { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
229
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
200
230
  import { jsx as jsx3 } from "react/jsx-runtime";
201
231
  function ApplePayButton({
202
232
  options,
@@ -210,7 +240,7 @@ function ApplePayButton({
210
240
  }) {
211
241
  const hellopay = useHelloPay();
212
242
  const buttonRef = useRef2(null);
213
- const [supported, setSupported] = useState2(false);
243
+ const [supported, setSupported] = useState3(false);
214
244
  useEffect3(() => {
215
245
  setSupported(!!hellopay && hellopay.canMakeApplePayPayments());
216
246
  }, [hellopay]);
@@ -262,6 +292,7 @@ export {
262
292
  CardCvcElement,
263
293
  CardExpiryElement,
264
294
  CardNumberElement,
295
+ CardPasswordElement,
265
296
  CardholderNameElement,
266
297
  Elements,
267
298
  loadHelloPay,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts","../src/ApplePayButton.tsx"],"sourcesContent":["/**\n * HelloPay SDK ローダー。\n *\n * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、\n * `window.HelloPay` ファクトリを解決する。\n *\n * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)\n * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)\n * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)\n * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能\n */\n\nimport type { HelloPay, HelloPayFactory } from './types';\n\n// SDK 本体は test / live どちらの publishable key でも同一ドメイン\n// (js.hello-pay.app) から配信する。test / live mode の切り替えは SDK 内部で\n// publishable key の prefix から解決し、test mode では接続先を test 環境\n// (*.stg.hello-pay.app) に向ける(hellopay.js と同じ方式)。\nconst DEFAULT_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n// sdkUrl 単位で script ロード Promise をキャッシュ。\n// loadPromise を単一変数にするとすべての URL で共有されてしまい、\n// 異なる sdkUrl(E2E / ローカル開発)が誤って同一ロードを共有する。\nconst scriptPromiseCache = new Map<string, Promise<HelloPayFactory | null>>();\n\n// publicKey + locale + sdkUrl の組み合わせごとに `HelloPaySDK` インスタンスをキャッシュ。\n// 加盟店が誤って inline で loadHelloPay を呼び出した場合でも同一 SDK インスタンスが\n// 返るようにして、Elements セッションの BroadcastChannel session ID ミスマッチを防ぐ。\nconst sdkCache = new Map<string, Promise<HelloPay | null>>();\n\n/** テスト専用: module-scope のキャッシュをすべて初期状態に戻す。 */\nexport function __resetLoadPromiseForTest(): void {\n scriptPromiseCache.clear();\n sdkCache.clear();\n}\n\nfunction normalizeSdkUrl(url: string): string {\n if (typeof window === 'undefined') return url;\n\n try {\n return new URL(url, window.location.href).href;\n } catch {\n return url;\n }\n}\n\nfunction normalizeLocale(locale?: string): string {\n return locale || 'ja';\n}\n\nfunction loadScript(url: string): Promise<HelloPayFactory | null> {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n // URL を正規化してプロトコル省略形などを統一する。\n // テスト / SSR 環境では window.location が http URL でない場合があるため try-catch で保護。\n const normalizedUrl = normalizeSdkUrl(url);\n\n return new Promise((resolve, reject) => {\n // script は URL 単位で管理する。異なる URL の script を削除せず共存させることで、\n // E2E / ローカル開発で異なる sdkUrl が並列で呼ばれても Promise が宙吊りにならない。\n // 状態は dataset.hellopayState に \"loading\" | \"loaded\" | \"error\" で刻む。\n const managedScripts = Array.from(\n document.querySelectorAll<HTMLScriptElement>('script[data-hellopay-sdk]'),\n );\n\n const existing = managedScripts.find((script) => {\n const rawUrl = script.dataset.hellopayUrl || script.src;\n if (!rawUrl) return false;\n try {\n return new URL(rawUrl, window.location.href).href === normalizedUrl;\n } catch {\n return rawUrl === normalizedUrl;\n }\n });\n\n if (existing) {\n const state = existing.dataset.hellopayState;\n if (state === 'loaded') {\n resolve(window.HelloPay ?? null);\n return;\n }\n if (state === 'loading') {\n existing.addEventListener('load', () => resolve(window.HelloPay ?? null), {\n once: true,\n });\n existing.addEventListener(\n 'error',\n () => reject(new Error('HelloPay SDK failed to load')),\n { once: true },\n );\n return;\n }\n // state === \"error\" or undefined: 失敗済み or 未知の状態なので作り直す\n existing.remove();\n }\n\n const script = document.createElement('script');\n script.src = normalizedUrl;\n script.async = true;\n script.dataset.hellopaySdk = 'true';\n script.dataset.hellopayUrl = normalizedUrl; // URL 比較用\n script.dataset.hellopayState = 'loading';\n script.addEventListener(\n 'load',\n () => {\n if (!window.HelloPay) {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK loaded but window.HelloPay is missing'));\n return;\n }\n script.dataset.hellopayState = 'loaded';\n resolve(window.HelloPay);\n },\n { once: true },\n );\n script.addEventListener(\n 'error',\n () => {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK failed to load'));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n}\n\nexport interface LoadHelloPayOptions {\n /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は js.hello-pay.app から配信し、test/live は SDK 内部で key prefix から解決) */\n sdkUrl?: string;\n locale?: string;\n}\n\n/**\n * HelloPay SDK をロードして初期化する。\n * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。\n */\nexport function loadHelloPay(\n publicKey: string,\n options: LoadHelloPayOptions = {},\n): Promise<HelloPay | null> {\n const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? DEFAULT_SDK_URL);\n const locale = normalizeLocale(options.locale);\n const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;\n const cached = sdkCache.get(cacheKey);\n if (cached) return cached;\n\n let scriptPromise = scriptPromiseCache.get(sdkUrl);\n if (!scriptPromise) {\n scriptPromise = loadScript(sdkUrl).catch((err) => {\n // 一時的なロード失敗はキャッシュせず次回呼び出しで再試行可能にする\n scriptPromiseCache.delete(sdkUrl);\n throw err;\n });\n scriptPromiseCache.set(sdkUrl, scriptPromise);\n }\n\n const sdkPromise = scriptPromise\n .then((factory) => {\n if (!factory) return null; // SSR\n return factory(publicKey, { locale: options.locale });\n })\n .catch((err) => {\n // SDK インスタンス生成失敗は sdkCache から退避して次回呼び出しで再試行可能にする\n sdkCache.delete(cacheKey);\n throw err;\n });\n sdkCache.set(cacheKey, sdkPromise);\n return sdkPromise;\n}\n","/**\n * <Elements> Provider.\n *\n * loadHelloPay() の Promise を受け取り、SDK インスタンスと Elements セッションを\n * 子孫に Context 経由で供給する。\n */\n\nimport { createContext, useEffect, useState, type ReactNode } from 'react';\nimport type { HelloPay, Elements as ElementsSession } from './types';\n\nexport interface ElementsContextValue {\n hellopay: HelloPay | null;\n elements: ElementsSession | null;\n}\n\nconst EMPTY_CONTEXT: ElementsContextValue = {\n hellopay: null,\n elements: null,\n};\n\nexport const ElementsContext = createContext<ElementsContextValue>(EMPTY_CONTEXT);\n\nexport interface ElementsProps {\n /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */\n hellopay: Promise<HelloPay | null> | HelloPay | null;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, children }: ElementsProps) {\n const [ctx, setCtx] = useState<ElementsContextValue>(EMPTY_CONTEXT);\n\n useEffect(() => {\n let cancelled = false;\n setCtx(EMPTY_CONTEXT);\n\n Promise.resolve(hellopayProp)\n .then((sdk) => {\n if (cancelled) return;\n if (!sdk) {\n setCtx(EMPTY_CONTEXT);\n return;\n }\n setCtx({ hellopay: sdk, elements: sdk.elements() });\n })\n .catch(() => {\n // SDK ロード失敗時は null のまま(unhandled rejection を防ぐ)\n if (cancelled) return;\n setCtx(EMPTY_CONTEXT);\n });\n\n return () => {\n cancelled = true;\n };\n }, [hellopayProp]);\n\n return <ElementsContext.Provider value={ctx}>{children}</ElementsContext.Provider>;\n}\n","/**\n * Element コンポーネント。\n *\n * 各コンポーネントは 1 つの `<div>` コンテナを描画するだけで、\n * 実体(カード情報が入る iframe)はコア SDK が差し込む。\n *\n * React Strict Mode の二重 mount 対応:\n * - effect の cleanup で element.unmount() を必ず呼ぶ\n * - unmount 後は Elements._elements 配列から除外される(コア SDK 側で対応済)\n * - 結果として Strict Mode で effect が再実行されても古い iframe は残らない\n */\n\nimport { useEffect, useRef, type CSSProperties } from 'react';\nimport { useElements } from './hooks';\nimport type {\n ElementType,\n ElementOptions,\n ElementChangeEvent,\n ElementFocusEvent,\n ElementReadyEvent,\n} from './types';\n\nexport interface ElementProps {\n options?: ElementOptions;\n className?: string;\n style?: CSSProperties;\n onReady?: (e: ElementReadyEvent) => void;\n onChange?: (e: ElementChangeEvent) => void;\n onFocus?: (e: ElementFocusEvent) => void;\n onBlur?: (e: ElementFocusEvent) => void;\n}\n\n/**\n * 指定 type の Element を管理する React コンポーネントを生成する。\n */\nfunction createElementComponent(type: ElementType) {\n function ElementComponent({\n options,\n className,\n style,\n onReady,\n onChange,\n onFocus,\n onBlur,\n }: ElementProps) {\n const elements = useElements();\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // 最新のハンドラを ref で保持(effect を毎回 re-run させないため)\n const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });\n handlersRef.current = { onReady, onChange, onFocus, onBlur };\n\n useEffect(() => {\n if (!elements || !containerRef.current) return;\n\n const element = elements.create(type, options);\n element.mount(containerRef.current);\n\n const unsubs = [\n element.on('ready', (e) => handlersRef.current.onReady?.(e)),\n element.on('change', (e) => handlersRef.current.onChange?.(e)),\n element.on('focus', (e) => handlersRef.current.onFocus?.(e)),\n element.on('blur', (e) => handlersRef.current.onBlur?.(e)),\n ];\n\n return () => {\n unsubs.forEach((u) => u());\n element.unmount();\n };\n // options はマウント時にしか反映しない方針(Stripe と同じ挙動)。\n // 動的に style を変えたい場合は後続 PR で element.update() を追加。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [elements]);\n\n // 親コンテナ(加盟店が指定した `.element-container` 等)の高さを\n // iframe に伝播させるため、wrapper は親のサイズを塗りつぶす。\n // style prop で上書き可能。\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n }\n ElementComponent.displayName = `HelloPay(${type})`;\n // getElement(CardNumberElement) のようにコンポーネント自身を型指定子として使えるよう、\n // 静的プロパティに type を生やしておく\n ElementComponent.__elementType = type as ElementType;\n return ElementComponent;\n}\n\nexport const CardNumberElement = createElementComponent('cardNumber');\nexport const CardExpiryElement = createElementComponent('cardExpiry');\nexport const CardCvcElement = createElementComponent('cardCvc');\nexport const CardholderNameElement = createElementComponent('cardholderName');\n\nexport type ElementComponent = typeof CardNumberElement;\n","/**\n * React hooks.\n *\n * useHelloPay / useElements は <Elements> Provider の Context から\n * SDK インスタンスと Elements セッションを取り出す。\n * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。\n */\n\nimport { useContext } from 'react';\nimport { ElementsContext } from './Elements';\nimport type { HelloPay, Elements } from './types';\n\nexport function useHelloPay(): HelloPay | null {\n return useContext(ElementsContext).hellopay;\n}\n\nexport function useElements(): Elements | null {\n return useContext(ElementsContext).elements;\n}\n","/**\n * Apple Pay ボタン。\n *\n * `useHelloPay()` から SDK を取り、対応端末(Safari + Apple Pay 利用可能)でのみ\n * Apple 公式スタイルのボタンを描画する。非対応端末では何も描画しない。\n *\n * click で `hellopay.applePay()` を同期呼び出しし(`new ApplePaySession` の\n * user gesture 要件のため)、結果を `onToken` / `onError` / `onCancel` に振り分ける。\n * SDK は Apple Pay トークンを返すだけで課金はしないので、加盟店は `onToken` で\n * 受けたトークンを自社バックエンドから決済 API に送って課金する。\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useHelloPay } from './hooks';\nimport type { ApplePayCard, ApplePayOptions, HelloPayError } from './types';\n\nexport interface ApplePayButtonProps {\n options: ApplePayOptions;\n /** トークン取得成功時。トークンを自社バックエンド経由で決済 API に送る。 */\n onToken: (token: string, card: ApplePayCard) => void;\n /** validation / network エラー時。 */\n onError?: (error: HelloPayError) => void;\n /** ユーザーが Apple Pay シートを閉じた時。 */\n onCancel?: () => void;\n /** Apple Pay ボタンの色。既定 `black`。 */\n buttonStyle?: 'black' | 'white' | 'white-outline';\n /** Apple Pay ボタンの種別(`plain` / `buy` / `pay` 等)。既定 `plain`。 */\n buttonType?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ApplePayButton({\n options,\n onToken,\n onError,\n onCancel,\n buttonStyle = 'black',\n buttonType = 'plain',\n className,\n style,\n}: ApplePayButtonProps) {\n const hellopay = useHelloPay();\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const [supported, setSupported] = useState(false);\n\n // canMakeApplePayPayments は ApplePaySession 依存で client-only のため effect で判定。\n useEffect(() => {\n setSupported(!!hellopay && hellopay.canMakeApplePayPayments());\n }, [hellopay]);\n\n // Apple Pay ボタンの見た目はベンダー CSS プロパティで指定する。React の style 型に\n // 無いため ref 経由で setProperty する。\n useEffect(() => {\n const el = buttonRef.current;\n if (!el) return;\n el.style.setProperty('-webkit-appearance', '-apple-pay-button');\n el.style.setProperty('-apple-pay-button-type', buttonType);\n el.style.setProperty('-apple-pay-button-style', buttonStyle);\n }, [supported, buttonType, buttonStyle]);\n\n // 最新のハンドラ / options を ref に保持し、click ハンドラを毎回作り直さない。\n const latest = useRef({ options, onToken, onError, onCancel, hellopay });\n latest.current = { options, onToken, onError, onCancel, hellopay };\n\n if (!supported) return null;\n\n const handleClick = () => {\n const current = latest.current;\n if (!current.hellopay) return;\n // user gesture 要件のため click ハンドラから同期呼び出しする。\n current.hellopay\n .applePay(current.options)\n .then((result) => {\n if (result.error) {\n if (result.error.type === 'cancel') {\n current.onCancel?.();\n } else {\n current.onError?.(result.error);\n }\n return;\n }\n current.onToken(result.token, result.card);\n })\n .catch((err: unknown) => {\n current.onError?.({\n type: 'config_error',\n code: 'apple_pay_error',\n message: err instanceof Error ? err.message : 'Apple Pay error',\n });\n });\n };\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleClick}\n className={className}\n style={{ width: '100%', height: '44px', cursor: 'pointer', ...style }}\n />\n );\n}\n\nApplePayButton.displayName = 'HelloPay(ApplePayButton)';\n"],"mappings":"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","names":["script","useEffect","jsx","useEffect","useEffect","useRef","useState","jsx","useRef","useState","useEffect"]}
1
+ {"version":3,"sources":["../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts","../src/ApplePayButton.tsx"],"sourcesContent":["/**\n * HelloPay SDK ローダー。\n *\n * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、\n * `window.HelloPay` ファクトリを解決する。\n *\n * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)\n * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)\n * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)\n * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能\n */\n\nimport type { HelloPay, HelloPayFactory } from './types';\n\n// SDK 本体は test / live どちらの publishable key でも同一ドメイン\n// (js.hello-pay.app) から配信する。test / live mode の切り替えは SDK 内部で\n// publishable key の prefix から解決し、test mode では接続先を test 環境\n// (*.stg.hello-pay.app) に向ける(hellopay.js と同じ方式)。\nconst DEFAULT_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n// sdkUrl 単位で script ロード Promise をキャッシュ。\n// loadPromise を単一変数にするとすべての URL で共有されてしまい、\n// 異なる sdkUrl(E2E / ローカル開発)が誤って同一ロードを共有する。\nconst scriptPromiseCache = new Map<string, Promise<HelloPayFactory | null>>();\n\n// publicKey + locale + sdkUrl の組み合わせごとに `HelloPaySDK` インスタンスをキャッシュ。\n// 加盟店が誤って inline で loadHelloPay を呼び出した場合でも同一 SDK インスタンスが\n// 返るようにして、Elements セッションの BroadcastChannel session ID ミスマッチを防ぐ。\nconst sdkCache = new Map<string, Promise<HelloPay | null>>();\n\n/** テスト専用: module-scope のキャッシュをすべて初期状態に戻す。 */\nexport function __resetLoadPromiseForTest(): void {\n scriptPromiseCache.clear();\n sdkCache.clear();\n}\n\nfunction normalizeSdkUrl(url: string): string {\n if (typeof window === 'undefined') return url;\n\n try {\n return new URL(url, window.location.href).href;\n } catch {\n return url;\n }\n}\n\nfunction normalizeLocale(locale?: string): string {\n return locale || 'ja';\n}\n\nfunction loadScript(url: string): Promise<HelloPayFactory | null> {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n // URL を正規化してプロトコル省略形などを統一する。\n // テスト / SSR 環境では window.location が http URL でない場合があるため try-catch で保護。\n const normalizedUrl = normalizeSdkUrl(url);\n\n return new Promise((resolve, reject) => {\n // script は URL 単位で管理する。異なる URL の script を削除せず共存させることで、\n // E2E / ローカル開発で異なる sdkUrl が並列で呼ばれても Promise が宙吊りにならない。\n // 状態は dataset.hellopayState に \"loading\" | \"loaded\" | \"error\" で刻む。\n const managedScripts = Array.from(\n document.querySelectorAll<HTMLScriptElement>('script[data-hellopay-sdk]'),\n );\n\n const existing = managedScripts.find((script) => {\n const rawUrl = script.dataset.hellopayUrl || script.src;\n if (!rawUrl) return false;\n try {\n return new URL(rawUrl, window.location.href).href === normalizedUrl;\n } catch {\n return rawUrl === normalizedUrl;\n }\n });\n\n if (existing) {\n const state = existing.dataset.hellopayState;\n if (state === 'loaded') {\n resolve(window.HelloPay ?? null);\n return;\n }\n if (state === 'loading') {\n existing.addEventListener('load', () => resolve(window.HelloPay ?? null), {\n once: true,\n });\n existing.addEventListener(\n 'error',\n () => reject(new Error('HelloPay SDK failed to load')),\n { once: true },\n );\n return;\n }\n // state === \"error\" or undefined: 失敗済み or 未知の状態なので作り直す\n existing.remove();\n }\n\n const script = document.createElement('script');\n script.src = normalizedUrl;\n script.async = true;\n script.dataset.hellopaySdk = 'true';\n script.dataset.hellopayUrl = normalizedUrl; // URL 比較用\n script.dataset.hellopayState = 'loading';\n script.addEventListener(\n 'load',\n () => {\n if (!window.HelloPay) {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK loaded but window.HelloPay is missing'));\n return;\n }\n script.dataset.hellopayState = 'loaded';\n resolve(window.HelloPay);\n },\n { once: true },\n );\n script.addEventListener(\n 'error',\n () => {\n script.dataset.hellopayState = 'error';\n reject(new Error('HelloPay SDK failed to load'));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n}\n\nexport interface LoadHelloPayOptions {\n /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は js.hello-pay.app から配信し、test/live は SDK 内部で key prefix から解決) */\n sdkUrl?: string;\n locale?: string;\n}\n\n/**\n * HelloPay SDK をロードして初期化する。\n * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。\n */\nexport function loadHelloPay(\n publicKey: string,\n options: LoadHelloPayOptions = {},\n): Promise<HelloPay | null> {\n const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? DEFAULT_SDK_URL);\n const locale = normalizeLocale(options.locale);\n const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;\n const cached = sdkCache.get(cacheKey);\n if (cached) return cached;\n\n let scriptPromise = scriptPromiseCache.get(sdkUrl);\n if (!scriptPromise) {\n scriptPromise = loadScript(sdkUrl).catch((err) => {\n // 一時的なロード失敗はキャッシュせず次回呼び出しで再試行可能にする\n scriptPromiseCache.delete(sdkUrl);\n throw err;\n });\n scriptPromiseCache.set(sdkUrl, scriptPromise);\n }\n\n const sdkPromise = scriptPromise\n .then((factory) => {\n if (!factory) return null; // SSR\n return factory(publicKey, { locale: options.locale });\n })\n .catch((err) => {\n // SDK インスタンス生成失敗は sdkCache から退避して次回呼び出しで再試行可能にする\n sdkCache.delete(cacheKey);\n throw err;\n });\n sdkCache.set(cacheKey, sdkPromise);\n return sdkPromise;\n}\n","/**\n * <Elements> Provider.\n *\n * loadHelloPay() の Promise を受け取り、SDK インスタンスと Elements セッションを\n * 子孫に Context 経由で供給する。\n */\n\nimport { createContext, useEffect, useState, type ReactNode } from 'react';\nimport type { HelloPay, Elements as ElementsSession } from './types';\n\nexport interface ElementsContextValue {\n hellopay: HelloPay | null;\n elements: ElementsSession | null;\n}\n\nconst EMPTY_CONTEXT: ElementsContextValue = {\n hellopay: null,\n elements: null,\n};\n\nexport const ElementsContext = createContext<ElementsContextValue>(EMPTY_CONTEXT);\n\nexport interface ElementsProps {\n /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */\n hellopay: Promise<HelloPay | null> | HelloPay | null;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, children }: ElementsProps) {\n const [ctx, setCtx] = useState<ElementsContextValue>(EMPTY_CONTEXT);\n\n useEffect(() => {\n let cancelled = false;\n setCtx(EMPTY_CONTEXT);\n\n Promise.resolve(hellopayProp)\n .then((sdk) => {\n if (cancelled) return;\n if (!sdk) {\n setCtx(EMPTY_CONTEXT);\n return;\n }\n setCtx({ hellopay: sdk, elements: sdk.elements() });\n })\n .catch(() => {\n // SDK ロード失敗時は null のまま(unhandled rejection を防ぐ)\n if (cancelled) return;\n setCtx(EMPTY_CONTEXT);\n });\n\n return () => {\n cancelled = true;\n };\n }, [hellopayProp]);\n\n return <ElementsContext.Provider value={ctx}>{children}</ElementsContext.Provider>;\n}\n","/**\n * Element コンポーネント。\n *\n * 各コンポーネントは 1 つの `<div>` コンテナを描画するだけで、\n * 実体(カード情報が入る iframe)はコア SDK が差し込む。\n *\n * React Strict Mode の二重 mount 対応:\n * - effect の cleanup で element.unmount() を必ず呼ぶ\n * - unmount 後は Elements._elements 配列から除外される(コア SDK 側で対応済)\n * - 結果として Strict Mode で effect が再実行されても古い iframe は残らない\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useElements } from './hooks';\nimport type {\n ElementType,\n ElementOptions,\n ElementChangeEvent,\n ElementFocusEvent,\n ElementReadyEvent,\n} from './types';\n\nexport interface ElementProps {\n options?: ElementOptions;\n className?: string;\n style?: CSSProperties;\n onReady?: (e: ElementReadyEvent) => void;\n onChange?: (e: ElementChangeEvent) => void;\n onFocus?: (e: ElementFocusEvent) => void;\n onBlur?: (e: ElementFocusEvent) => void;\n}\n\nconst PROBE_PX = 100000;\n\n/**\n * コンテナの幅・高さのうち、中身に追従する(加盟店が寸法を決めていない)軸を返す。\n */\nfunction contentSizedAxes(container: HTMLElement) {\n // display: none の祖先の中ではレイアウトが無いので、computed value で見る。\n if (container.getClientRects().length === 0) {\n const computed = getComputedStyle(container);\n const isAuto = (v: string) => v === '' || v === 'auto';\n return { width: isAuto(computed.width), height: isAuto(computed.height) };\n }\n // computed の height で判定すると、border-box の padding・border や min-height で\n // 0 にならず見誤るので、大きな子を一時的に入れて寸法が伸びるかで確かめる。\n // 祖先の transform(モーダルの scale のアニメーションなど)に左右されないよう、\n // getBoundingClientRect ではなくレイアウト上の寸法の offset* で比べる。\n const before = { width: container.offsetWidth, height: container.offsetHeight };\n const probe = container.ownerDocument.createElement('div');\n probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;\n container.appendChild(probe);\n const grows = {\n width: container.offsetWidth > before.width,\n height: container.offsetHeight > before.height,\n };\n probe.remove();\n return grows;\n}\n\n/**\n * 指定 type の Element を管理する React コンポーネントを生成する。\n */\nfunction createElementComponent(type: ElementType) {\n function ElementComponent({\n options,\n className,\n style,\n onReady,\n onChange,\n onFocus,\n onBlur,\n }: ElementProps) {\n const elements = useElements();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [fill, setFill] = useState({ width: false, height: false });\n\n // 最新のハンドラを ref で保持(effect を毎回 re-run させないため)\n const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });\n handlersRef.current = { onReady, onChange, onFocus, onBlur };\n\n useEffect(() => {\n const container = containerRef.current;\n if (!elements || !container) return;\n\n // 100% を当てた後に測り直すと親の寸法に追従して見えるので、立てた軸は戻さない。\n const unset = contentSizedAxes(container);\n setFill((prev) => ({\n width: prev.width || unset.width,\n height: prev.height || unset.height,\n }));\n\n const element = elements.create(type, options);\n element.mount(container);\n\n const unsubs = [\n element.on('ready', (e) => handlersRef.current.onReady?.(e)),\n element.on('change', (e) => handlersRef.current.onChange?.(e)),\n element.on('focus', (e) => handlersRef.current.onFocus?.(e)),\n element.on('blur', (e) => handlersRef.current.onBlur?.(e)),\n ];\n\n return () => {\n unsubs.forEach((u) => u());\n element.unmount();\n };\n // options はマウント時にしか反映しない方針(Stripe と同じ挙動)。\n // 動的に style を変えたい場合は後続 PR で element.update() を追加。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [elements]);\n\n // インラインの既定値は className の寸法に勝つので、加盟店が決めていない軸だけに付ける。\n // 判定は mount 時の 1 回だけなので、後から寸法を変えるなら style で渡す。\n return (\n <div\n ref={containerRef}\n className={className}\n style={{\n ...(fill.width && { width: '100%' }),\n ...(fill.height && { height: '100%' }),\n ...style,\n }}\n />\n );\n }\n ElementComponent.displayName = `HelloPay(${type})`;\n // getElement(CardNumberElement) のようにコンポーネント自身を型指定子として使えるよう、\n // 静的プロパティに type を生やしておく\n ElementComponent.__elementType = type as ElementType;\n return ElementComponent;\n}\n\nexport const CardNumberElement = createElementComponent('cardNumber');\nexport const CardExpiryElement = createElementComponent('cardExpiry');\nexport const CardCvcElement = createElementComponent('cardCvc');\nexport const CardholderNameElement = createElementComponent('cardholderName');\n// 韓国発行カードの Toss 登録に必要な暗証番号の先頭 2 桁。表示・非表示は\n// 発行国の判定結果に応じて呼び出し側が mount / unmount で制御する。\nexport const CardPasswordElement = createElementComponent('cardPassword');\n\nexport type ElementComponent = typeof CardNumberElement;\n","/**\n * React hooks.\n *\n * useHelloPay / useElements は <Elements> Provider の Context から\n * SDK インスタンスと Elements セッションを取り出す。\n * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。\n */\n\nimport { useContext } from 'react';\nimport { ElementsContext } from './Elements';\nimport type { HelloPay, Elements } from './types';\n\nexport function useHelloPay(): HelloPay | null {\n return useContext(ElementsContext).hellopay;\n}\n\nexport function useElements(): Elements | null {\n return useContext(ElementsContext).elements;\n}\n","/**\n * Apple Pay ボタン。\n *\n * `useHelloPay()` から SDK を取り、対応端末(Safari + Apple Pay 利用可能)でのみ\n * Apple 公式スタイルのボタンを描画する。非対応端末では何も描画しない。\n *\n * click で `hellopay.applePay()` を同期呼び出しし(`new ApplePaySession` の\n * user gesture 要件のため)、結果を `onToken` / `onError` / `onCancel` に振り分ける。\n * SDK は Apple Pay トークンを返すだけで課金はしないので、加盟店は `onToken` で\n * 受けたトークンを自社バックエンドから決済 API に送って課金する。\n */\n\nimport { useEffect, useRef, useState, type CSSProperties } from 'react';\nimport { useHelloPay } from './hooks';\nimport type { ApplePayCard, ApplePayOptions, HelloPayError } from './types';\n\nexport interface ApplePayButtonProps {\n options: ApplePayOptions;\n /** トークン取得成功時。トークンを自社バックエンド経由で決済 API に送る。 */\n onToken: (token: string, card: ApplePayCard) => void;\n /** validation / network エラー時。 */\n onError?: (error: HelloPayError) => void;\n /** ユーザーが Apple Pay シートを閉じた時。 */\n onCancel?: () => void;\n /** Apple Pay ボタンの色。既定 `black`。 */\n buttonStyle?: 'black' | 'white' | 'white-outline';\n /** Apple Pay ボタンの種別(`plain` / `buy` / `pay` 等)。既定 `plain`。 */\n buttonType?: string;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ApplePayButton({\n options,\n onToken,\n onError,\n onCancel,\n buttonStyle = 'black',\n buttonType = 'plain',\n className,\n style,\n}: ApplePayButtonProps) {\n const hellopay = useHelloPay();\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const [supported, setSupported] = useState(false);\n\n // canMakeApplePayPayments は ApplePaySession 依存で client-only のため effect で判定。\n useEffect(() => {\n setSupported(!!hellopay && hellopay.canMakeApplePayPayments());\n }, [hellopay]);\n\n // Apple Pay ボタンの見た目はベンダー CSS プロパティで指定する。React の style 型に\n // 無いため ref 経由で setProperty する。\n useEffect(() => {\n const el = buttonRef.current;\n if (!el) return;\n el.style.setProperty('-webkit-appearance', '-apple-pay-button');\n el.style.setProperty('-apple-pay-button-type', buttonType);\n el.style.setProperty('-apple-pay-button-style', buttonStyle);\n }, [supported, buttonType, buttonStyle]);\n\n // 最新のハンドラ / options を ref に保持し、click ハンドラを毎回作り直さない。\n const latest = useRef({ options, onToken, onError, onCancel, hellopay });\n latest.current = { options, onToken, onError, onCancel, hellopay };\n\n if (!supported) return null;\n\n const handleClick = () => {\n const current = latest.current;\n if (!current.hellopay) return;\n // user gesture 要件のため click ハンドラから同期呼び出しする。\n current.hellopay\n .applePay(current.options)\n .then((result) => {\n if (result.error) {\n if (result.error.type === 'cancel') {\n current.onCancel?.();\n } else {\n current.onError?.(result.error);\n }\n return;\n }\n current.onToken(result.token, result.card);\n })\n .catch((err: unknown) => {\n current.onError?.({\n type: 'config_error',\n code: 'apple_pay_error',\n message: err instanceof Error ? err.message : 'Apple Pay error',\n });\n });\n };\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleClick}\n className={className}\n style={{ width: '100%', height: '44px', cursor: 'pointer', ...style }}\n />\n );\n}\n\nApplePayButton.displayName = 'HelloPay(ApplePayButton)';\n"],"mappings":"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","names":["script","useEffect","useState","jsx","useState","useEffect","useEffect","useRef","useState","jsx","useRef","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hello-pay/react",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "React components for accepting payments with HelloPay",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {