@hello-pay/react 0.4.0 → 0.6.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 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,
@@ -145,7 +146,8 @@ var EMPTY_CONTEXT = {
145
146
  elements: null
146
147
  };
147
148
  var ElementsContext = (0, import_react.createContext)(EMPTY_CONTEXT);
148
- function Elements({ hellopay: hellopayProp, children }) {
149
+ function Elements({ hellopay: hellopayProp, options, children }) {
150
+ const sessionClientSecret = options?.sessionClientSecret;
149
151
  const [ctx, setCtx] = (0, import_react.useState)(EMPTY_CONTEXT);
150
152
  (0, import_react.useEffect)(() => {
151
153
  let cancelled = false;
@@ -156,7 +158,10 @@ function Elements({ hellopay: hellopayProp, children }) {
156
158
  setCtx(EMPTY_CONTEXT);
157
159
  return;
158
160
  }
159
- setCtx({ hellopay: sdk, elements: sdk.elements() });
161
+ setCtx({
162
+ hellopay: sdk,
163
+ elements: sdk.elements(sessionClientSecret ? { sessionClientSecret } : void 0)
164
+ });
160
165
  }).catch(() => {
161
166
  if (cancelled) return;
162
167
  setCtx(EMPTY_CONTEXT);
@@ -164,7 +169,7 @@ function Elements({ hellopay: hellopayProp, children }) {
164
169
  return () => {
165
170
  cancelled = true;
166
171
  };
167
- }, [hellopayProp]);
172
+ }, [hellopayProp, sessionClientSecret]);
168
173
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ElementsContext.Provider, { value: ctx, children });
169
174
  }
170
175
 
@@ -182,6 +187,24 @@ function useElements() {
182
187
 
183
188
  // src/ElementComponents.tsx
184
189
  var import_jsx_runtime2 = require("react/jsx-runtime");
190
+ var PROBE_PX = 1e5;
191
+ function contentSizedAxes(container) {
192
+ if (container.getClientRects().length === 0) {
193
+ const computed = getComputedStyle(container);
194
+ const isAuto = (v) => v === "" || v === "auto";
195
+ return { width: isAuto(computed.width), height: isAuto(computed.height) };
196
+ }
197
+ const before = { width: container.offsetWidth, height: container.offsetHeight };
198
+ const probe = container.ownerDocument.createElement("div");
199
+ probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;
200
+ container.appendChild(probe);
201
+ const grows = {
202
+ width: container.offsetWidth > before.width,
203
+ height: container.offsetHeight > before.height
204
+ };
205
+ probe.remove();
206
+ return grows;
207
+ }
185
208
  function createElementComponent(type) {
186
209
  function ElementComponent({
187
210
  options,
@@ -194,12 +217,19 @@ function createElementComponent(type) {
194
217
  }) {
195
218
  const elements = useElements();
196
219
  const containerRef = (0, import_react3.useRef)(null);
220
+ const [fill, setFill] = (0, import_react3.useState)({ width: false, height: false });
197
221
  const handlersRef = (0, import_react3.useRef)({ onReady, onChange, onFocus, onBlur });
198
222
  handlersRef.current = { onReady, onChange, onFocus, onBlur };
199
223
  (0, import_react3.useEffect)(() => {
200
- if (!elements || !containerRef.current) return;
224
+ const container = containerRef.current;
225
+ if (!elements || !container) return;
226
+ const unset = contentSizedAxes(container);
227
+ setFill((prev) => ({
228
+ width: prev.width || unset.width,
229
+ height: prev.height || unset.height
230
+ }));
201
231
  const element = elements.create(type, options);
202
- element.mount(containerRef.current);
232
+ element.mount(container);
203
233
  const unsubs = [
204
234
  element.on("ready", (e) => handlersRef.current.onReady?.(e)),
205
235
  element.on("change", (e) => handlersRef.current.onChange?.(e)),
@@ -216,7 +246,11 @@ function createElementComponent(type) {
216
246
  {
217
247
  ref: containerRef,
218
248
  className,
219
- style: { width: "100%", height: "100%", ...style }
249
+ style: {
250
+ ...fill.width && { width: "100%" },
251
+ ...fill.height && { height: "100%" },
252
+ ...style
253
+ }
220
254
  }
221
255
  );
222
256
  }
@@ -228,6 +262,7 @@ var CardNumberElement = createElementComponent("cardNumber");
228
262
  var CardExpiryElement = createElementComponent("cardExpiry");
229
263
  var CardCvcElement = createElementComponent("cardCvc");
230
264
  var CardholderNameElement = createElementComponent("cardholderName");
265
+ var CardPasswordElement = createElementComponent("cardPassword");
231
266
 
232
267
  // src/ApplePayButton.tsx
233
268
  var import_react4 = require("react");
@@ -297,6 +332,7 @@ ApplePayButton.displayName = "HelloPay(ApplePayButton)";
297
332
  CardCvcElement,
298
333
  CardExpiryElement,
299
334
  CardNumberElement,
335
+ CardPasswordElement,
300
336
  CardholderNameElement,
301
337
  Elements,
302
338
  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 ElementsOptions,\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, ElementsOptions } 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 /** `sessionClientSecret` を渡すと、預かりカードのセッションで要る欄だけを出す */\n options?: ElementsOptions;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, options, children }: ElementsProps) {\n const sessionClientSecret = options?.sessionClientSecret;\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({\n hellopay: sdk,\n elements: sdk.elements(sessionClientSecret ? { sessionClientSecret } : undefined),\n });\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, sessionClientSecret]);\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBA,IAAM,kBAAkB;AAKxB,IAAM,qBAAqB,oBAAI,IAA6C;AAK5E,IAAM,WAAW,oBAAI,IAAsC;AAQ3D,SAAS,gBAAgB,KAAqB;AAC5C,MAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,MAAI;AACF,WAAO,IAAI,IAAI,KAAK,OAAO,SAAS,IAAI,EAAE;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,QAAyB;AAChD,SAAO,UAAU;AACnB;AAEA,SAAS,WAAW,KAA8C;AAChE,MAAI,OAAO,WAAW,YAAa,QAAO,QAAQ,QAAQ,IAAI;AAI9D,QAAM,gBAAgB,gBAAgB,GAAG;AAEzC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAItC,UAAM,iBAAiB,MAAM;AAAA,MAC3B,SAAS,iBAAoC,2BAA2B;AAAA,IAC1E;AAEA,UAAM,WAAW,eAAe,KAAK,CAACA,YAAW;AAC/C,YAAM,SAASA,QAAO,QAAQ,eAAeA,QAAO;AACpD,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI;AACF,eAAO,IAAI,IAAI,QAAQ,OAAO,SAAS,IAAI,EAAE,SAAS;AAAA,MACxD,QAAQ;AACN,eAAO,WAAW;AAAA,MACpB;AAAA,IACF,CAAC;AAED,QAAI,UAAU;AACZ,YAAM,QAAQ,SAAS,QAAQ;AAC/B,UAAI,UAAU,UAAU;AACtB,gBAAQ,OAAO,YAAY,IAAI;AAC/B;AAAA,MACF;AACA,UAAI,UAAU,WAAW;AACvB,iBAAS,iBAAiB,QAAQ,MAAM,QAAQ,OAAO,YAAY,IAAI,GAAG;AAAA,UACxE,MAAM;AAAA,QACR,CAAC;AACD,iBAAS;AAAA,UACP;AAAA,UACA,MAAM,OAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,UACrD,EAAE,MAAM,KAAK;AAAA,QACf;AACA;AAAA,MACF;AAEA,eAAS,OAAO;AAAA,IAClB;AAEA,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,MAAM;AACb,WAAO,QAAQ;AACf,WAAO,QAAQ,cAAc;AAC7B,WAAO,QAAQ,cAAc;AAC7B,WAAO,QAAQ,gBAAgB;AAC/B,WAAO;AAAA,MACL;AAAA,MACA,MAAM;AACJ,YAAI,CAAC,OAAO,UAAU;AACpB,iBAAO,QAAQ,gBAAgB;AAC/B,iBAAO,IAAI,MAAM,oDAAoD,CAAC;AACtE;AAAA,QACF;AACA,eAAO,QAAQ,gBAAgB;AAC/B,gBAAQ,OAAO,QAAQ;AAAA,MACzB;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AACA,WAAO;AAAA,MACL;AAAA,MACA,MAAM;AACJ,eAAO,QAAQ,gBAAgB;AAC/B,eAAO,IAAI,MAAM,6BAA6B,CAAC;AAAA,MACjD;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AACA,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAYO,SAAS,aACd,WACA,UAA+B,CAAC,GACN;AAC1B,QAAM,SAAS,gBAAgB,QAAQ,UAAU,eAAe;AAChE,QAAM,SAAS,gBAAgB,QAAQ,MAAM;AAC7C,QAAM,WAAW,GAAG,SAAS,KAAK,MAAM,KAAK,MAAM;AACnD,QAAM,SAAS,SAAS,IAAI,QAAQ;AACpC,MAAI,OAAQ,QAAO;AAEnB,MAAI,gBAAgB,mBAAmB,IAAI,MAAM;AACjD,MAAI,CAAC,eAAe;AAClB,oBAAgB,WAAW,MAAM,EAAE,MAAM,CAAC,QAAQ;AAEhD,yBAAmB,OAAO,MAAM;AAChC,YAAM;AAAA,IACR,CAAC;AACD,uBAAmB,IAAI,QAAQ,aAAa;AAAA,EAC9C;AAEA,QAAM,aAAa,cAChB,KAAK,CAAC,YAAY;AACjB,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,QAAQ,WAAW,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACtD,CAAC,EACA,MAAM,CAAC,QAAQ;AAEd,aAAS,OAAO,QAAQ;AACxB,UAAM;AAAA,EACR,CAAC;AACH,WAAS,IAAI,UAAU,UAAU;AACjC,SAAO;AACT;;;AClKA,mBAAmE;AAsD1D;AA9CT,IAAM,gBAAsC;AAAA,EAC1C,UAAU;AAAA,EACV,UAAU;AACZ;AAEO,IAAM,sBAAkB,4BAAoC,aAAa;AAUzE,SAAS,SAAS,EAAE,UAAU,cAAc,SAAS,SAAS,GAAkB;AACrF,QAAM,sBAAsB,SAAS;AACrC,QAAM,CAAC,KAAK,MAAM,QAAI,uBAA+B,aAAa;AAElE,8BAAU,MAAM;AACd,QAAI,YAAY;AAChB,WAAO,aAAa;AAEpB,YAAQ,QAAQ,YAAY,EACzB,KAAK,CAAC,QAAQ;AACb,UAAI,UAAW;AACf,UAAI,CAAC,KAAK;AACR,eAAO,aAAa;AACpB;AAAA,MACF;AACA,aAAO;AAAA,QACL,UAAU;AAAA,QACV,UAAU,IAAI,SAAS,sBAAsB,EAAE,oBAAoB,IAAI,MAAS;AAAA,MAClF,CAAC;AAAA,IACH,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,UAAW;AACf,aAAO,aAAa;AAAA,IACtB,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,cAAc,mBAAmB,CAAC;AAEtC,SAAO,4CAAC,gBAAgB,UAAhB,EAAyB,OAAO,KAAM,UAAS;AACzD;;;AClDA,IAAAC,gBAAgE;;;ACJhE,IAAAC,gBAA2B;AAIpB,SAAS,cAA+B;AAC7C,aAAO,0BAAW,eAAe,EAAE;AACrC;AAEO,SAAS,cAA+B;AAC7C,aAAO,0BAAW,eAAe,EAAE;AACrC;;;ADgGM,IAAAC,sBAAA;AAlFN,IAAM,WAAW;AAKjB,SAAS,iBAAiB,WAAwB;AAEhD,MAAI,UAAU,eAAe,EAAE,WAAW,GAAG;AAC3C,UAAM,WAAW,iBAAiB,SAAS;AAC3C,UAAM,SAAS,CAAC,MAAc,MAAM,MAAM,MAAM;AAChD,WAAO,EAAE,OAAO,OAAO,SAAS,KAAK,GAAG,QAAQ,OAAO,SAAS,MAAM,EAAE;AAAA,EAC1E;AAKA,QAAM,SAAS,EAAE,OAAO,UAAU,aAAa,QAAQ,UAAU,aAAa;AAC9E,QAAM,QAAQ,UAAU,cAAc,cAAc,KAAK;AACzD,QAAM,MAAM,UAAU,UAAU,QAAQ,eAAe,QAAQ;AAC/D,YAAU,YAAY,KAAK;AAC3B,QAAM,QAAQ;AAAA,IACZ,OAAO,UAAU,cAAc,OAAO;AAAA,IACtC,QAAQ,UAAU,eAAe,OAAO;AAAA,EAC1C;AACA,QAAM,OAAO;AACb,SAAO;AACT;AAKA,SAAS,uBAAuB,MAAmB;AACjD,WAAS,iBAAiB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAiB;AACf,UAAM,WAAW,YAAY;AAC7B,UAAM,mBAAe,sBAA8B,IAAI;AACvD,UAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,EAAE,OAAO,OAAO,QAAQ,MAAM,CAAC;AAGhE,UAAM,kBAAc,sBAAO,EAAE,SAAS,UAAU,SAAS,OAAO,CAAC;AACjE,gBAAY,UAAU,EAAE,SAAS,UAAU,SAAS,OAAO;AAE3D,iCAAU,MAAM;AACd,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,YAAY,CAAC,UAAW;AAG7B,YAAM,QAAQ,iBAAiB,SAAS;AACxC,cAAQ,CAAC,UAAU;AAAA,QACjB,OAAO,KAAK,SAAS,MAAM;AAAA,QAC3B,QAAQ,KAAK,UAAU,MAAM;AAAA,MAC/B,EAAE;AAEF,YAAM,UAAU,SAAS,OAAO,MAAM,OAAO;AAC7C,cAAQ,MAAM,SAAS;AAEvB,YAAM,SAAS;AAAA,QACb,QAAQ,GAAG,SAAS,CAAC,MAAM,YAAY,QAAQ,UAAU,CAAC,CAAC;AAAA,QAC3D,QAAQ,GAAG,UAAU,CAAC,MAAM,YAAY,QAAQ,WAAW,CAAC,CAAC;AAAA,QAC7D,QAAQ,GAAG,SAAS,CAAC,MAAM,YAAY,QAAQ,UAAU,CAAC,CAAC;AAAA,QAC3D,QAAQ,GAAG,QAAQ,CAAC,MAAM,YAAY,QAAQ,SAAS,CAAC,CAAC;AAAA,MAC3D;AAEA,aAAO,MAAM;AACX,eAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AACzB,gBAAQ,QAAQ;AAAA,MAClB;AAAA,IAIF,GAAG,CAAC,QAAQ,CAAC;AAIb,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,OAAO;AAAA,UACL,GAAI,KAAK,SAAS,EAAE,OAAO,OAAO;AAAA,UAClC,GAAI,KAAK,UAAU,EAAE,QAAQ,OAAO;AAAA,UACpC,GAAG;AAAA,QACL;AAAA;AAAA,IACF;AAAA,EAEJ;AACA,mBAAiB,cAAc,YAAY,IAAI;AAG/C,mBAAiB,gBAAgB;AACjC,SAAO;AACT;AAEO,IAAM,oBAAoB,uBAAuB,YAAY;AAC7D,IAAM,oBAAoB,uBAAuB,YAAY;AAC7D,IAAM,iBAAiB,uBAAuB,SAAS;AACvD,IAAM,wBAAwB,uBAAuB,gBAAgB;AAGrE,IAAM,sBAAsB,uBAAuB,cAAc;;;AE9HxE,IAAAC,gBAAgE;AAkF5D,IAAAC,sBAAA;AA9DG,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAwB;AACtB,QAAM,WAAW,YAAY;AAC7B,QAAM,gBAAY,sBAAiC,IAAI;AACvD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAGhD,+BAAU,MAAM;AACd,iBAAa,CAAC,CAAC,YAAY,SAAS,wBAAwB,CAAC;AAAA,EAC/D,GAAG,CAAC,QAAQ,CAAC;AAIb,+BAAU,MAAM;AACd,UAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AACT,OAAG,MAAM,YAAY,sBAAsB,mBAAmB;AAC9D,OAAG,MAAM,YAAY,0BAA0B,UAAU;AACzD,OAAG,MAAM,YAAY,2BAA2B,WAAW;AAAA,EAC7D,GAAG,CAAC,WAAW,YAAY,WAAW,CAAC;AAGvC,QAAM,aAAS,sBAAO,EAAE,SAAS,SAAS,SAAS,UAAU,SAAS,CAAC;AACvE,SAAO,UAAU,EAAE,SAAS,SAAS,SAAS,UAAU,SAAS;AAEjE,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,cAAc,MAAM;AACxB,UAAM,UAAU,OAAO;AACvB,QAAI,CAAC,QAAQ,SAAU;AAEvB,YAAQ,SACL,SAAS,QAAQ,OAAO,EACxB,KAAK,CAAC,WAAW;AAChB,UAAI,OAAO,OAAO;AAChB,YAAI,OAAO,MAAM,SAAS,UAAU;AAClC,kBAAQ,WAAW;AAAA,QACrB,OAAO;AACL,kBAAQ,UAAU,OAAO,KAAK;AAAA,QAChC;AACA;AAAA,MACF;AACA,cAAQ,QAAQ,OAAO,OAAO,OAAO,IAAI;AAAA,IAC3C,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,cAAQ,UAAU;AAAA,QAChB,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,MAChD,CAAC;AAAA,IACH,CAAC;AAAA,EACL;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,MAAK;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,GAAG,MAAM;AAAA;AAAA,EACtE;AAEJ;AAEA,eAAe,cAAc;","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,17 +29,65 @@ 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
+ * カード番号の先頭 8 桁が揃うと HelloPay に問い合わせ、KSNET の国内カード
40
+ * BIN 一覧にあれば `determined`(`country: 'KR'`)、無ければ `unknown` になる。
41
+ * 8 桁未満の間と問い合わせ中は `pending`、問い合わせに失敗したら `unknown`。
42
+ */
43
+ type IssuerCountryResult = {
44
+ status: 'pending';
45
+ } | {
46
+ status: 'unknown';
47
+ } | {
48
+ status: 'determined';
49
+ country: string;
50
+ };
51
+ interface CardNumberChangeEvent {
52
+ elementType: 'cardNumber';
53
+ empty: boolean;
54
+ complete: boolean;
55
+ /**
56
+ * `'unionpay'` はこのイベントにだけ現れる。トークン化結果 (`TokenCard.brand`) は
57
+ * acquirer 側の見え方を返すので、同じカードで別の値になりうる。
58
+ */
59
+ brand?: string | null;
60
+ issuerCountry: IssuerCountryResult;
61
+ }
62
+ interface OtherElementChangeEvent {
63
+ elementType: Exclude<ElementType, 'cardNumber'>;
34
64
  empty: boolean;
35
65
  complete: boolean;
36
66
  brand?: string | null;
37
67
  }
68
+ /**
69
+ * `elementType` で分岐する判別可能ユニオン。`issuerCountry` は cardNumber
70
+ * にしか存在しないため、読むには `elementType === 'cardNumber'` での
71
+ * narrowing が必要。
72
+ */
73
+ type ElementChangeEvent = CardNumberChangeEvent | OtherElementChangeEvent;
38
74
  interface ElementFocusEvent {
39
75
  elementType: ElementType;
40
76
  }
41
77
  interface ElementReadyEvent {
42
78
  elementType: ElementType;
79
+ /**
80
+ * 預かりカードの登録セッションで、この欄を出さないとき true。SDK は iframe を隠すので、
81
+ * 加盟店は見出しなど周りの要素を隠す。
82
+ */
83
+ hidden: boolean;
84
+ }
85
+ interface ElementsOptions {
86
+ /**
87
+ * 登録セッションの `client_secret`。預かりカードのセッションなら、カード番号と有効期限の
88
+ * 欄を出さず、CVC か暗証番号の欄だけを出す。
89
+ */
90
+ sessionClientSecret?: string;
43
91
  }
44
92
  type ElementEventMap = {
45
93
  ready: ElementReadyEvent;
@@ -110,6 +158,59 @@ type CreateTokenResult = {
110
158
  tokenExpiryDate?: undefined;
111
159
  error: HelloPayError;
112
160
  };
161
+ /**
162
+ * 複数プロバイダへのカード登録 (#2747) の返り値。
163
+ *
164
+ * `CreateTokenResult` と違い **トークンを持たない** — 登録結果 (Toss の BillingKey など) は
165
+ * 加盟店バックエンドがサーバ間 API で取得する。判別は `status` で行う。
166
+ *
167
+ * ```ts
168
+ * const result = await hellopay.createToken(el, { sessionClientSecret });
169
+ * if (result.status === 'failed') {
170
+ * // 発行国の申告と BIN 判定が食い違った場合はフォームを組み替える
171
+ * if (result.error.code === 'issuer_country_mismatch') {
172
+ * rebuildForm(result.error.issuerCountryGroup);
173
+ * }
174
+ * return showError(result.error.message);
175
+ * }
176
+ * showRegisteredCard(result.card);
177
+ * ```
178
+ */
179
+ type CardRegistrationResult = {
180
+ status: 'succeeded';
181
+ card: CardRegistrationCard | null;
182
+ error?: undefined;
183
+ } | {
184
+ status: 'failed';
185
+ card: null;
186
+ error: CardRegistrationError;
187
+ };
188
+ /**
189
+ * 登録できたカードのメタデータ。
190
+ *
191
+ * `brand` が `TokenCard` と違って `null` を許すのは、韓国の国内専用カード (`9410` 始まり) が
192
+ * 国際ブランドを持たないため。サーバーの BIN 判定がブランドを確定できないとそのまま `null` で
193
+ * 返るので、型でも表す。
194
+ */
195
+ interface CardRegistrationCard {
196
+ brand: string | null;
197
+ last4: string;
198
+ expMonth: string;
199
+ expYear: string;
200
+ }
201
+ interface CardRegistrationError extends HelloPayError {
202
+ /**
203
+ * `issuer_country_mismatch` のときだけ入る、BIN 判定で確定した発行国グループ
204
+ * (該当なしは `'default'`)。呼び出し側はこれを使って入力フォームを組み替える。
205
+ */
206
+ issuerCountryGroup?: string;
207
+ /**
208
+ * `card_brand_mismatch` のときだけ入る、判定したカードブランド (`visa` / `unionpay` など)。
209
+ * ブランドを判別できないカード (`9410` 始まり) では入らない。呼び出し側はこのブランドで
210
+ * 登録セッションを作り直す。
211
+ */
212
+ cardBrand?: string;
213
+ }
113
214
  /**
114
215
  * Apple Pay で決済したカードのメタデータ。Apple Pay は client 側で last4 /
115
216
  * 有効期限を確実に取得できないため、カード決済の `TokenCard` とは別形状。
@@ -164,8 +265,18 @@ type ApplePayResult = {
164
265
  error: ApplePayError;
165
266
  };
166
267
  interface HelloPay {
167
- elements(): Elements$1;
268
+ elements(options?: ElementsOptions): Elements$1;
168
269
  createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
270
+ /**
271
+ * 登録セッション経路 (#2747)。`sessionClientSecret` を渡すと、加盟店バックエンドが
272
+ * 宣言した複数の決済プロバイダへまとめてカードを登録する。
273
+ *
274
+ * **トークンは返らない。** BillingKey も PSP のトークンもブラウザには渡らず、
275
+ * 加盟店バックエンドがサーバ間 API で取得する。
276
+ */
277
+ createToken(cardNumberElement: Element, options: {
278
+ sessionClientSecret: string;
279
+ }): Promise<CardRegistrationResult>;
169
280
  /** 対応端末(Safari + Apple Pay 利用可能)でのみ true。 */
170
281
  canMakeApplePayPayments(): boolean;
171
282
  /** Apple Pay シートを開き、都度課金トークンを取得する。 */
@@ -211,9 +322,11 @@ declare function loadHelloPay(publicKey: string, options?: LoadHelloPayOptions):
211
322
  interface ElementsProps {
212
323
  /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */
213
324
  hellopay: Promise<HelloPay | null> | HelloPay | null;
325
+ /** `sessionClientSecret` を渡すと、預かりカードのセッションで要る欄だけを出す */
326
+ options?: ElementsOptions;
214
327
  children: ReactNode;
215
328
  }
216
- declare function Elements({ hellopay: hellopayProp, children }: ElementsProps): react_jsx_runtime.JSX.Element;
329
+ declare function Elements({ hellopay: hellopayProp, options, children }: ElementsProps): react_jsx_runtime.JSX.Element;
217
330
 
218
331
  interface ElementProps {
219
332
  options?: ElementOptions;
@@ -244,6 +357,11 @@ declare const CardholderNameElement: {
244
357
  displayName: string;
245
358
  __elementType: ElementType;
246
359
  };
360
+ declare const CardPasswordElement: {
361
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
362
+ displayName: string;
363
+ __elementType: ElementType;
364
+ };
247
365
 
248
366
  interface ApplePayButtonProps {
249
367
  options: ApplePayOptions;
@@ -276,4 +394,4 @@ declare namespace ApplePayButton {
276
394
  declare function useHelloPay(): HelloPay | null;
277
395
  declare function useElements(): Elements$1 | null;
278
396
 
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 };
397
+ 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 ElementsOptions, 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,17 +29,65 @@ 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
+ * カード番号の先頭 8 桁が揃うと HelloPay に問い合わせ、KSNET の国内カード
40
+ * BIN 一覧にあれば `determined`(`country: 'KR'`)、無ければ `unknown` になる。
41
+ * 8 桁未満の間と問い合わせ中は `pending`、問い合わせに失敗したら `unknown`。
42
+ */
43
+ type IssuerCountryResult = {
44
+ status: 'pending';
45
+ } | {
46
+ status: 'unknown';
47
+ } | {
48
+ status: 'determined';
49
+ country: string;
50
+ };
51
+ interface CardNumberChangeEvent {
52
+ elementType: 'cardNumber';
53
+ empty: boolean;
54
+ complete: boolean;
55
+ /**
56
+ * `'unionpay'` はこのイベントにだけ現れる。トークン化結果 (`TokenCard.brand`) は
57
+ * acquirer 側の見え方を返すので、同じカードで別の値になりうる。
58
+ */
59
+ brand?: string | null;
60
+ issuerCountry: IssuerCountryResult;
61
+ }
62
+ interface OtherElementChangeEvent {
63
+ elementType: Exclude<ElementType, 'cardNumber'>;
34
64
  empty: boolean;
35
65
  complete: boolean;
36
66
  brand?: string | null;
37
67
  }
68
+ /**
69
+ * `elementType` で分岐する判別可能ユニオン。`issuerCountry` は cardNumber
70
+ * にしか存在しないため、読むには `elementType === 'cardNumber'` での
71
+ * narrowing が必要。
72
+ */
73
+ type ElementChangeEvent = CardNumberChangeEvent | OtherElementChangeEvent;
38
74
  interface ElementFocusEvent {
39
75
  elementType: ElementType;
40
76
  }
41
77
  interface ElementReadyEvent {
42
78
  elementType: ElementType;
79
+ /**
80
+ * 預かりカードの登録セッションで、この欄を出さないとき true。SDK は iframe を隠すので、
81
+ * 加盟店は見出しなど周りの要素を隠す。
82
+ */
83
+ hidden: boolean;
84
+ }
85
+ interface ElementsOptions {
86
+ /**
87
+ * 登録セッションの `client_secret`。預かりカードのセッションなら、カード番号と有効期限の
88
+ * 欄を出さず、CVC か暗証番号の欄だけを出す。
89
+ */
90
+ sessionClientSecret?: string;
43
91
  }
44
92
  type ElementEventMap = {
45
93
  ready: ElementReadyEvent;
@@ -110,6 +158,59 @@ type CreateTokenResult = {
110
158
  tokenExpiryDate?: undefined;
111
159
  error: HelloPayError;
112
160
  };
161
+ /**
162
+ * 複数プロバイダへのカード登録 (#2747) の返り値。
163
+ *
164
+ * `CreateTokenResult` と違い **トークンを持たない** — 登録結果 (Toss の BillingKey など) は
165
+ * 加盟店バックエンドがサーバ間 API で取得する。判別は `status` で行う。
166
+ *
167
+ * ```ts
168
+ * const result = await hellopay.createToken(el, { sessionClientSecret });
169
+ * if (result.status === 'failed') {
170
+ * // 発行国の申告と BIN 判定が食い違った場合はフォームを組み替える
171
+ * if (result.error.code === 'issuer_country_mismatch') {
172
+ * rebuildForm(result.error.issuerCountryGroup);
173
+ * }
174
+ * return showError(result.error.message);
175
+ * }
176
+ * showRegisteredCard(result.card);
177
+ * ```
178
+ */
179
+ type CardRegistrationResult = {
180
+ status: 'succeeded';
181
+ card: CardRegistrationCard | null;
182
+ error?: undefined;
183
+ } | {
184
+ status: 'failed';
185
+ card: null;
186
+ error: CardRegistrationError;
187
+ };
188
+ /**
189
+ * 登録できたカードのメタデータ。
190
+ *
191
+ * `brand` が `TokenCard` と違って `null` を許すのは、韓国の国内専用カード (`9410` 始まり) が
192
+ * 国際ブランドを持たないため。サーバーの BIN 判定がブランドを確定できないとそのまま `null` で
193
+ * 返るので、型でも表す。
194
+ */
195
+ interface CardRegistrationCard {
196
+ brand: string | null;
197
+ last4: string;
198
+ expMonth: string;
199
+ expYear: string;
200
+ }
201
+ interface CardRegistrationError extends HelloPayError {
202
+ /**
203
+ * `issuer_country_mismatch` のときだけ入る、BIN 判定で確定した発行国グループ
204
+ * (該当なしは `'default'`)。呼び出し側はこれを使って入力フォームを組み替える。
205
+ */
206
+ issuerCountryGroup?: string;
207
+ /**
208
+ * `card_brand_mismatch` のときだけ入る、判定したカードブランド (`visa` / `unionpay` など)。
209
+ * ブランドを判別できないカード (`9410` 始まり) では入らない。呼び出し側はこのブランドで
210
+ * 登録セッションを作り直す。
211
+ */
212
+ cardBrand?: string;
213
+ }
113
214
  /**
114
215
  * Apple Pay で決済したカードのメタデータ。Apple Pay は client 側で last4 /
115
216
  * 有効期限を確実に取得できないため、カード決済の `TokenCard` とは別形状。
@@ -164,8 +265,18 @@ type ApplePayResult = {
164
265
  error: ApplePayError;
165
266
  };
166
267
  interface HelloPay {
167
- elements(): Elements$1;
268
+ elements(options?: ElementsOptions): Elements$1;
168
269
  createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
270
+ /**
271
+ * 登録セッション経路 (#2747)。`sessionClientSecret` を渡すと、加盟店バックエンドが
272
+ * 宣言した複数の決済プロバイダへまとめてカードを登録する。
273
+ *
274
+ * **トークンは返らない。** BillingKey も PSP のトークンもブラウザには渡らず、
275
+ * 加盟店バックエンドがサーバ間 API で取得する。
276
+ */
277
+ createToken(cardNumberElement: Element, options: {
278
+ sessionClientSecret: string;
279
+ }): Promise<CardRegistrationResult>;
169
280
  /** 対応端末(Safari + Apple Pay 利用可能)でのみ true。 */
170
281
  canMakeApplePayPayments(): boolean;
171
282
  /** Apple Pay シートを開き、都度課金トークンを取得する。 */
@@ -211,9 +322,11 @@ declare function loadHelloPay(publicKey: string, options?: LoadHelloPayOptions):
211
322
  interface ElementsProps {
212
323
  /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */
213
324
  hellopay: Promise<HelloPay | null> | HelloPay | null;
325
+ /** `sessionClientSecret` を渡すと、預かりカードのセッションで要る欄だけを出す */
326
+ options?: ElementsOptions;
214
327
  children: ReactNode;
215
328
  }
216
- declare function Elements({ hellopay: hellopayProp, children }: ElementsProps): react_jsx_runtime.JSX.Element;
329
+ declare function Elements({ hellopay: hellopayProp, options, children }: ElementsProps): react_jsx_runtime.JSX.Element;
217
330
 
218
331
  interface ElementProps {
219
332
  options?: ElementOptions;
@@ -244,6 +357,11 @@ declare const CardholderNameElement: {
244
357
  displayName: string;
245
358
  __elementType: ElementType;
246
359
  };
360
+ declare const CardPasswordElement: {
361
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
362
+ displayName: string;
363
+ __elementType: ElementType;
364
+ };
247
365
 
248
366
  interface ApplePayButtonProps {
249
367
  options: ApplePayOptions;
@@ -276,4 +394,4 @@ declare namespace ApplePayButton {
276
394
  declare function useHelloPay(): HelloPay | null;
277
395
  declare function useElements(): Elements$1 | null;
278
396
 
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 };
397
+ 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 ElementsOptions, 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
@@ -111,7 +111,8 @@ var EMPTY_CONTEXT = {
111
111
  elements: null
112
112
  };
113
113
  var ElementsContext = createContext(EMPTY_CONTEXT);
114
- function Elements({ hellopay: hellopayProp, children }) {
114
+ function Elements({ hellopay: hellopayProp, options, children }) {
115
+ const sessionClientSecret = options?.sessionClientSecret;
115
116
  const [ctx, setCtx] = useState(EMPTY_CONTEXT);
116
117
  useEffect(() => {
117
118
  let cancelled = false;
@@ -122,7 +123,10 @@ function Elements({ hellopay: hellopayProp, children }) {
122
123
  setCtx(EMPTY_CONTEXT);
123
124
  return;
124
125
  }
125
- setCtx({ hellopay: sdk, elements: sdk.elements() });
126
+ setCtx({
127
+ hellopay: sdk,
128
+ elements: sdk.elements(sessionClientSecret ? { sessionClientSecret } : void 0)
129
+ });
126
130
  }).catch(() => {
127
131
  if (cancelled) return;
128
132
  setCtx(EMPTY_CONTEXT);
@@ -130,12 +134,12 @@ function Elements({ hellopay: hellopayProp, children }) {
130
134
  return () => {
131
135
  cancelled = true;
132
136
  };
133
- }, [hellopayProp]);
137
+ }, [hellopayProp, sessionClientSecret]);
134
138
  return /* @__PURE__ */ jsx(ElementsContext.Provider, { value: ctx, children });
135
139
  }
136
140
 
137
141
  // src/ElementComponents.tsx
138
- import { useEffect as useEffect2, useRef } from "react";
142
+ import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
139
143
 
140
144
  // src/hooks.ts
141
145
  import { useContext } from "react";
@@ -148,6 +152,24 @@ function useElements() {
148
152
 
149
153
  // src/ElementComponents.tsx
150
154
  import { jsx as jsx2 } from "react/jsx-runtime";
155
+ var PROBE_PX = 1e5;
156
+ function contentSizedAxes(container) {
157
+ if (container.getClientRects().length === 0) {
158
+ const computed = getComputedStyle(container);
159
+ const isAuto = (v) => v === "" || v === "auto";
160
+ return { width: isAuto(computed.width), height: isAuto(computed.height) };
161
+ }
162
+ const before = { width: container.offsetWidth, height: container.offsetHeight };
163
+ const probe = container.ownerDocument.createElement("div");
164
+ probe.style.cssText = `width: ${PROBE_PX}px; height: ${PROBE_PX}px;`;
165
+ container.appendChild(probe);
166
+ const grows = {
167
+ width: container.offsetWidth > before.width,
168
+ height: container.offsetHeight > before.height
169
+ };
170
+ probe.remove();
171
+ return grows;
172
+ }
151
173
  function createElementComponent(type) {
152
174
  function ElementComponent({
153
175
  options,
@@ -160,12 +182,19 @@ function createElementComponent(type) {
160
182
  }) {
161
183
  const elements = useElements();
162
184
  const containerRef = useRef(null);
185
+ const [fill, setFill] = useState2({ width: false, height: false });
163
186
  const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });
164
187
  handlersRef.current = { onReady, onChange, onFocus, onBlur };
165
188
  useEffect2(() => {
166
- if (!elements || !containerRef.current) return;
189
+ const container = containerRef.current;
190
+ if (!elements || !container) return;
191
+ const unset = contentSizedAxes(container);
192
+ setFill((prev) => ({
193
+ width: prev.width || unset.width,
194
+ height: prev.height || unset.height
195
+ }));
167
196
  const element = elements.create(type, options);
168
- element.mount(containerRef.current);
197
+ element.mount(container);
169
198
  const unsubs = [
170
199
  element.on("ready", (e) => handlersRef.current.onReady?.(e)),
171
200
  element.on("change", (e) => handlersRef.current.onChange?.(e)),
@@ -182,7 +211,11 @@ function createElementComponent(type) {
182
211
  {
183
212
  ref: containerRef,
184
213
  className,
185
- style: { width: "100%", height: "100%", ...style }
214
+ style: {
215
+ ...fill.width && { width: "100%" },
216
+ ...fill.height && { height: "100%" },
217
+ ...style
218
+ }
186
219
  }
187
220
  );
188
221
  }
@@ -194,9 +227,10 @@ var CardNumberElement = createElementComponent("cardNumber");
194
227
  var CardExpiryElement = createElementComponent("cardExpiry");
195
228
  var CardCvcElement = createElementComponent("cardCvc");
196
229
  var CardholderNameElement = createElementComponent("cardholderName");
230
+ var CardPasswordElement = createElementComponent("cardPassword");
197
231
 
198
232
  // src/ApplePayButton.tsx
199
- import { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
233
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
200
234
  import { jsx as jsx3 } from "react/jsx-runtime";
201
235
  function ApplePayButton({
202
236
  options,
@@ -210,7 +244,7 @@ function ApplePayButton({
210
244
  }) {
211
245
  const hellopay = useHelloPay();
212
246
  const buttonRef = useRef2(null);
213
- const [supported, setSupported] = useState2(false);
247
+ const [supported, setSupported] = useState3(false);
214
248
  useEffect3(() => {
215
249
  setSupported(!!hellopay && hellopay.canMakeApplePayPayments());
216
250
  }, [hellopay]);
@@ -262,6 +296,7 @@ export {
262
296
  CardCvcElement,
263
297
  CardExpiryElement,
264
298
  CardNumberElement,
299
+ CardPasswordElement,
265
300
  CardholderNameElement,
266
301
  Elements,
267
302
  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, ElementsOptions } 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 /** `sessionClientSecret` を渡すと、預かりカードのセッションで要る欄だけを出す */\n options?: ElementsOptions;\n children: ReactNode;\n}\n\nexport function Elements({ hellopay: hellopayProp, options, children }: ElementsProps) {\n const sessionClientSecret = options?.sessionClientSecret;\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({\n hellopay: sdk,\n elements: sdk.elements(sessionClientSecret ? { sessionClientSecret } : undefined),\n });\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, sessionClientSecret]);\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.6.0",
4
4
  "description": "React components for accepting payments with HelloPay",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {