@hello-pay/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @hello-pay/react
2
+
3
+ HelloPay Elements の React バインディング。加盟店ページ上で任意のレイアウトのカード入力フォームを構築できる。
4
+
5
+ 実体のカード情報は HelloPay 管理ドメイン (js.hello-pay.app) から配信される iframe 内に閉じ込められるため、PCI DSS SAQ A を維持したままネイティブ React で UI を組める。
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @hello-pay/react
11
+ # or: pnpm add @hello-pay/react / bun add @hello-pay/react
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```tsx
17
+ import {
18
+ loadHelloPay,
19
+ Elements,
20
+ CardNumberElement,
21
+ CardExpiryElement,
22
+ CardCvcElement,
23
+ useHelloPay,
24
+ useElements,
25
+ } from '@hello-pay/react';
26
+
27
+ // SDK のロードは module スコープで 1 回だけ(Stripe と同じ挙動)。
28
+ // SSR / Next.js でも安全に module top-level に置ける。
29
+ const helloPayPromise = loadHelloPay('pk_test_xxx');
30
+
31
+ export default function Checkout() {
32
+ return (
33
+ <Elements hellopay={helloPayPromise}>
34
+ <PaymentForm />
35
+ </Elements>
36
+ );
37
+ }
38
+
39
+ function PaymentForm() {
40
+ const hellopay = useHelloPay();
41
+ const elements = useElements();
42
+
43
+ async function handleSubmit(e: React.FormEvent) {
44
+ e.preventDefault();
45
+ if (!hellopay || !elements) return;
46
+
47
+ const cardNumber = elements.getElement('cardNumber');
48
+ const { token, card, error } = await hellopay.createToken(cardNumber);
49
+
50
+ if (error) {
51
+ // error.type は 'validation_error' | 'api_error' | 'config_error'
52
+ showError(error.message);
53
+ return;
54
+ }
55
+ // 以降は token / card が必ず non-null(Discriminated Union)
56
+ await fetch('/api/charge', {
57
+ method: 'POST',
58
+ body: JSON.stringify({ token, last4: card.last4 }),
59
+ });
60
+ }
61
+
62
+ return (
63
+ <form onSubmit={handleSubmit}>
64
+ <CardNumberElement />
65
+ <CardExpiryElement />
66
+ <CardCvcElement />
67
+ <button type="submit">支払う</button>
68
+ </form>
69
+ );
70
+ }
71
+ ```
72
+
73
+ ## Scripts
74
+
75
+ | Script | 内容 |
76
+ |---|---|
77
+ | `bun run build` | tsup で `dist/` に ESM + CJS + `.d.ts` を出力 |
78
+ | `bun run demo` | ローカル demo サーバー (http://localhost:18901) を起動。先に `just dev` が必要 |
79
+ | `bun run typecheck` | 型チェックのみ(`tsc --noEmit`) |
80
+ | `bun run test` | `bun test` 実行 |
81
+
82
+ ## Demo
83
+
84
+ ```bash
85
+ # ターミナル1
86
+ just dev # HelloPay サーバー起動(js.lvh.me / api.lvh.me / cde.lvh.me を公開)
87
+
88
+ # ターミナル2
89
+ cd js_sdk/react && bun run demo
90
+ open http://localhost:18901
91
+ ```
92
+
93
+ `.dev-ports` からサーバーポートを自動検出するため、worktree を跨いで設定不要で動作する。
94
+
95
+ ## Architecture
96
+
97
+ - コア SDK (`hellopay-elements.js`) は HelloPay 管理ドメイン (`js.hello-pay.app`) から配信される。npm パッケージは **ローダーと型定義のみ**を提供する(PCI DSS 要件)。
98
+ - 各 Element コンポーネントは 1 つの `<div>` を描画するだけで、カード情報の入力 iframe はコア SDK が差し込む。
99
+ - iframe 間通信は `BroadcastChannel`(同一オリジン)で行い、親ページ(加盟店)は一切経由しない。
100
+ - トークン化は iframe 内で直接 CDE API に POST する。
101
+
102
+ より詳細な設計は `docs/src/content/docs/integration/custom-form.md` を参照。
package/dist/index.cjs ADDED
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CardCvcElement: () => CardCvcElement,
24
+ CardExpiryElement: () => CardExpiryElement,
25
+ CardNumberElement: () => CardNumberElement,
26
+ Elements: () => Elements,
27
+ loadHelloPay: () => loadHelloPay,
28
+ useElements: () => useElements,
29
+ useHelloPay: () => useHelloPay
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/loadHelloPay.ts
34
+ var DEFAULT_TEST_SDK_URL = "https://js.test.hello-pay.app/v1/hellopay-elements.js";
35
+ var DEFAULT_LIVE_SDK_URL = "https://js.hello-pay.app/v1/hellopay-elements.js";
36
+ function resolveDefaultSdkUrl(publicKey) {
37
+ return publicKey.startsWith("pk_test_") ? DEFAULT_TEST_SDK_URL : DEFAULT_LIVE_SDK_URL;
38
+ }
39
+ var scriptPromiseCache = /* @__PURE__ */ new Map();
40
+ var sdkCache = /* @__PURE__ */ new Map();
41
+ function normalizeSdkUrl(url) {
42
+ if (typeof window === "undefined") return url;
43
+ try {
44
+ return new URL(url, window.location.href).href;
45
+ } catch {
46
+ return url;
47
+ }
48
+ }
49
+ function normalizeLocale(locale) {
50
+ return locale || "ja";
51
+ }
52
+ function loadScript(url) {
53
+ if (typeof window === "undefined") return Promise.resolve(null);
54
+ const normalizedUrl = normalizeSdkUrl(url);
55
+ return new Promise((resolve, reject) => {
56
+ const managedScripts = Array.from(
57
+ document.querySelectorAll("script[data-hellopay-sdk]")
58
+ );
59
+ const existing = managedScripts.find((script2) => {
60
+ const rawUrl = script2.dataset.hellopayUrl || script2.src;
61
+ if (!rawUrl) return false;
62
+ try {
63
+ return new URL(rawUrl, window.location.href).href === normalizedUrl;
64
+ } catch {
65
+ return rawUrl === normalizedUrl;
66
+ }
67
+ });
68
+ if (existing) {
69
+ const state = existing.dataset.hellopayState;
70
+ if (state === "loaded") {
71
+ resolve(window.HelloPay ?? null);
72
+ return;
73
+ }
74
+ if (state === "loading") {
75
+ existing.addEventListener("load", () => resolve(window.HelloPay ?? null), {
76
+ once: true
77
+ });
78
+ existing.addEventListener(
79
+ "error",
80
+ () => reject(new Error("HelloPay SDK failed to load")),
81
+ { once: true }
82
+ );
83
+ return;
84
+ }
85
+ existing.remove();
86
+ }
87
+ const script = document.createElement("script");
88
+ script.src = normalizedUrl;
89
+ script.async = true;
90
+ script.dataset.hellopaySdk = "true";
91
+ script.dataset.hellopayUrl = normalizedUrl;
92
+ script.dataset.hellopayState = "loading";
93
+ script.addEventListener(
94
+ "load",
95
+ () => {
96
+ if (!window.HelloPay) {
97
+ script.dataset.hellopayState = "error";
98
+ reject(new Error("HelloPay SDK loaded but window.HelloPay is missing"));
99
+ return;
100
+ }
101
+ script.dataset.hellopayState = "loaded";
102
+ resolve(window.HelloPay);
103
+ },
104
+ { once: true }
105
+ );
106
+ script.addEventListener(
107
+ "error",
108
+ () => {
109
+ script.dataset.hellopayState = "error";
110
+ reject(new Error("HelloPay SDK failed to load"));
111
+ },
112
+ { once: true }
113
+ );
114
+ document.head.appendChild(script);
115
+ });
116
+ }
117
+ function loadHelloPay(publicKey, options = {}) {
118
+ const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? resolveDefaultSdkUrl(publicKey));
119
+ const locale = normalizeLocale(options.locale);
120
+ const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;
121
+ const cached = sdkCache.get(cacheKey);
122
+ if (cached) return cached;
123
+ let scriptPromise = scriptPromiseCache.get(sdkUrl);
124
+ if (!scriptPromise) {
125
+ scriptPromise = loadScript(sdkUrl).catch((err) => {
126
+ scriptPromiseCache.delete(sdkUrl);
127
+ throw err;
128
+ });
129
+ scriptPromiseCache.set(sdkUrl, scriptPromise);
130
+ }
131
+ const sdkPromise = scriptPromise.then((factory) => {
132
+ if (!factory) return null;
133
+ return factory(publicKey, { locale: options.locale });
134
+ }).catch((err) => {
135
+ sdkCache.delete(cacheKey);
136
+ throw err;
137
+ });
138
+ sdkCache.set(cacheKey, sdkPromise);
139
+ return sdkPromise;
140
+ }
141
+
142
+ // src/Elements.tsx
143
+ var import_react = require("react");
144
+ var import_jsx_runtime = require("react/jsx-runtime");
145
+ var EMPTY_CONTEXT = {
146
+ hellopay: null,
147
+ elements: null
148
+ };
149
+ var ElementsContext = (0, import_react.createContext)(EMPTY_CONTEXT);
150
+ function Elements({ hellopay: hellopayProp, children }) {
151
+ const [ctx, setCtx] = (0, import_react.useState)(EMPTY_CONTEXT);
152
+ (0, import_react.useEffect)(() => {
153
+ let cancelled = false;
154
+ setCtx(EMPTY_CONTEXT);
155
+ Promise.resolve(hellopayProp).then((sdk) => {
156
+ if (cancelled) return;
157
+ if (!sdk) {
158
+ setCtx(EMPTY_CONTEXT);
159
+ return;
160
+ }
161
+ setCtx({ hellopay: sdk, elements: sdk.elements() });
162
+ }).catch(() => {
163
+ if (cancelled) return;
164
+ setCtx(EMPTY_CONTEXT);
165
+ });
166
+ return () => {
167
+ cancelled = true;
168
+ };
169
+ }, [hellopayProp]);
170
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ElementsContext.Provider, { value: ctx, children });
171
+ }
172
+
173
+ // src/ElementComponents.tsx
174
+ var import_react3 = require("react");
175
+
176
+ // src/hooks.ts
177
+ var import_react2 = require("react");
178
+ function useHelloPay() {
179
+ return (0, import_react2.useContext)(ElementsContext).hellopay;
180
+ }
181
+ function useElements() {
182
+ return (0, import_react2.useContext)(ElementsContext).elements;
183
+ }
184
+
185
+ // src/ElementComponents.tsx
186
+ var import_jsx_runtime2 = require("react/jsx-runtime");
187
+ function createElementComponent(type) {
188
+ function ElementComponent({
189
+ options,
190
+ className,
191
+ style,
192
+ onReady,
193
+ onChange,
194
+ onFocus,
195
+ onBlur
196
+ }) {
197
+ const elements = useElements();
198
+ const containerRef = (0, import_react3.useRef)(null);
199
+ const handlersRef = (0, import_react3.useRef)({ onReady, onChange, onFocus, onBlur });
200
+ handlersRef.current = { onReady, onChange, onFocus, onBlur };
201
+ (0, import_react3.useEffect)(() => {
202
+ if (!elements || !containerRef.current) return;
203
+ const element = elements.create(type, options);
204
+ element.mount(containerRef.current);
205
+ const unsubs = [
206
+ element.on("ready", (e) => handlersRef.current.onReady?.(e)),
207
+ element.on("change", (e) => handlersRef.current.onChange?.(e)),
208
+ element.on("focus", (e) => handlersRef.current.onFocus?.(e)),
209
+ element.on("blur", (e) => handlersRef.current.onBlur?.(e))
210
+ ];
211
+ return () => {
212
+ unsubs.forEach((u) => u());
213
+ element.unmount();
214
+ };
215
+ }, [elements]);
216
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
217
+ "div",
218
+ {
219
+ ref: containerRef,
220
+ className,
221
+ style: { width: "100%", height: "100%", ...style }
222
+ }
223
+ );
224
+ }
225
+ ElementComponent.displayName = `HelloPay(${type})`;
226
+ ElementComponent.__elementType = type;
227
+ return ElementComponent;
228
+ }
229
+ var CardNumberElement = createElementComponent("cardNumber");
230
+ var CardExpiryElement = createElementComponent("cardExpiry");
231
+ var CardCvcElement = createElementComponent("cardCvc");
232
+ // Annotate the CommonJS export names for ESM import in node:
233
+ 0 && (module.exports = {
234
+ CardCvcElement,
235
+ CardExpiryElement,
236
+ CardNumberElement,
237
+ Elements,
238
+ loadHelloPay,
239
+ useElements,
240
+ useHelloPay
241
+ });
242
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts"],"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} from './ElementComponents';\nexport type { ElementProps } from './ElementComponents';\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 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\nconst DEFAULT_TEST_SDK_URL = 'https://js.test.hello-pay.app/v1/hellopay-elements.js';\nconst DEFAULT_LIVE_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n/** publicKey のプレフィックスに基づいてデフォルト SDK URL を解決する。 */\nfunction resolveDefaultSdkUrl(publicKey: string): string {\n return publicKey.startsWith('pk_test_') ? DEFAULT_TEST_SDK_URL : DEFAULT_LIVE_SDK_URL;\n}\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 で差し替える用。省略時は publicKey に基づいて test/live を自動解決) */\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 ?? resolveDefaultSdkUrl(publicKey));\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');\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"],"mappings":"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","names":["script","import_react","import_react","import_jsx_runtime"]}
@@ -0,0 +1,176 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ /**
5
+ * HelloPay Elements SDK の型定義
6
+ *
7
+ * コア SDK (hellopay-elements.js) はグローバル `window.HelloPay` として
8
+ * 読み込まれる。これは PCI DSS の要件(カード入力関連スクリプトを
9
+ * HelloPay 管理ドメインから配信)を満たすため。
10
+ * npm パッケージは型定義とローダーのみを提供する。
11
+ */
12
+ type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc';
13
+ interface ElementStyle {
14
+ base?: {
15
+ color?: string;
16
+ fontSize?: string;
17
+ fontFamily?: string;
18
+ fontWeight?: string;
19
+ fontStyle?: string;
20
+ letterSpacing?: string;
21
+ lineHeight?: string;
22
+ textAlign?: string;
23
+ textTransform?: string;
24
+ '::placeholder'?: {
25
+ color?: string;
26
+ };
27
+ };
28
+ }
29
+ interface ElementOptions {
30
+ style?: ElementStyle;
31
+ }
32
+ interface ElementChangeEvent {
33
+ elementType: ElementType;
34
+ empty: boolean;
35
+ complete: boolean;
36
+ brand?: string | null;
37
+ }
38
+ interface ElementFocusEvent {
39
+ elementType: ElementType;
40
+ }
41
+ interface ElementReadyEvent {
42
+ elementType: ElementType;
43
+ }
44
+ type ElementEventMap = {
45
+ ready: ElementReadyEvent;
46
+ change: ElementChangeEvent;
47
+ focus: ElementFocusEvent;
48
+ blur: ElementFocusEvent;
49
+ };
50
+ type Unsubscribe = () => void;
51
+ interface Element {
52
+ type: ElementType;
53
+ mount(target: string | HTMLElement): void;
54
+ unmount(): void;
55
+ on<K extends keyof ElementEventMap>(event: K, handler: (e: ElementEventMap[K]) => void): Unsubscribe;
56
+ off<K extends keyof ElementEventMap>(event: K, handler: (e: ElementEventMap[K]) => void): void;
57
+ }
58
+ interface Elements$1 {
59
+ create(type: ElementType, options?: ElementOptions): Element;
60
+ getElement(type: ElementType): Element | null;
61
+ }
62
+ interface TokenCard {
63
+ brand: string;
64
+ last4: string;
65
+ expMonth: string;
66
+ expYear: string;
67
+ }
68
+ interface HelloPayError {
69
+ type: 'validation_error' | 'api_error' | 'config_error';
70
+ code: string;
71
+ message: string;
72
+ }
73
+ /**
74
+ * createToken の返り値。Discriminated Union により、success 時は
75
+ * token/card のみ、error 時は error のみが型として存在する。
76
+ *
77
+ * ```ts
78
+ * const { token, card, error } = await hellopay.createToken(el);
79
+ * if (error) return showError(error.message);
80
+ * sendToServer(token, card);
81
+ * ```
82
+ */
83
+ type CreateTokenResult = {
84
+ token: string;
85
+ card: TokenCard;
86
+ error?: undefined;
87
+ } | {
88
+ token?: undefined;
89
+ card?: undefined;
90
+ error: HelloPayError;
91
+ };
92
+ interface HelloPay {
93
+ elements(): Elements$1;
94
+ createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
95
+ }
96
+ type HelloPayFactory = (publicKey: string, options?: {
97
+ locale?: string;
98
+ }) => HelloPay;
99
+ declare global {
100
+ interface Window {
101
+ HelloPay?: HelloPayFactory;
102
+ /**
103
+ * ローカル開発・E2E 用: SDK 本体の配信 URL を上書きする。
104
+ * 本番では設定不要(`loadHelloPay(sdkUrl)` のデフォルトが使われる)。
105
+ */
106
+ HELLOPAY_ELEMENTS_URL?: string;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * HelloPay SDK ローダー。
112
+ *
113
+ * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、
114
+ * `window.HelloPay` ファクトリを解決する。
115
+ *
116
+ * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)
117
+ * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)
118
+ * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)
119
+ * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能
120
+ */
121
+
122
+ interface LoadHelloPayOptions {
123
+ /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は publicKey に基づいて test/live を自動解決) */
124
+ sdkUrl?: string;
125
+ locale?: string;
126
+ }
127
+ /**
128
+ * HelloPay SDK をロードして初期化する。
129
+ * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。
130
+ */
131
+ declare function loadHelloPay(publicKey: string, options?: LoadHelloPayOptions): Promise<HelloPay | null>;
132
+
133
+ interface ElementsProps {
134
+ /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */
135
+ hellopay: Promise<HelloPay | null> | HelloPay | null;
136
+ children: ReactNode;
137
+ }
138
+ declare function Elements({ hellopay: hellopayProp, children }: ElementsProps): react_jsx_runtime.JSX.Element;
139
+
140
+ interface ElementProps {
141
+ options?: ElementOptions;
142
+ className?: string;
143
+ style?: CSSProperties;
144
+ onReady?: (e: ElementReadyEvent) => void;
145
+ onChange?: (e: ElementChangeEvent) => void;
146
+ onFocus?: (e: ElementFocusEvent) => void;
147
+ onBlur?: (e: ElementFocusEvent) => void;
148
+ }
149
+ declare const CardNumberElement: {
150
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
151
+ displayName: string;
152
+ __elementType: ElementType;
153
+ };
154
+ declare const CardExpiryElement: {
155
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
156
+ displayName: string;
157
+ __elementType: ElementType;
158
+ };
159
+ declare const CardCvcElement: {
160
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
161
+ displayName: string;
162
+ __elementType: ElementType;
163
+ };
164
+
165
+ /**
166
+ * React hooks.
167
+ *
168
+ * useHelloPay / useElements は <Elements> Provider の Context から
169
+ * SDK インスタンスと Elements セッションを取り出す。
170
+ * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。
171
+ */
172
+
173
+ declare function useHelloPay(): HelloPay | null;
174
+ declare function useElements(): Elements$1 | null;
175
+
176
+ export { CardCvcElement, CardExpiryElement, CardNumberElement, 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 Unsubscribe, loadHelloPay, useElements, useHelloPay };
@@ -0,0 +1,176 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ /**
5
+ * HelloPay Elements SDK の型定義
6
+ *
7
+ * コア SDK (hellopay-elements.js) はグローバル `window.HelloPay` として
8
+ * 読み込まれる。これは PCI DSS の要件(カード入力関連スクリプトを
9
+ * HelloPay 管理ドメインから配信)を満たすため。
10
+ * npm パッケージは型定義とローダーのみを提供する。
11
+ */
12
+ type ElementType = 'cardNumber' | 'cardExpiry' | 'cardCvc';
13
+ interface ElementStyle {
14
+ base?: {
15
+ color?: string;
16
+ fontSize?: string;
17
+ fontFamily?: string;
18
+ fontWeight?: string;
19
+ fontStyle?: string;
20
+ letterSpacing?: string;
21
+ lineHeight?: string;
22
+ textAlign?: string;
23
+ textTransform?: string;
24
+ '::placeholder'?: {
25
+ color?: string;
26
+ };
27
+ };
28
+ }
29
+ interface ElementOptions {
30
+ style?: ElementStyle;
31
+ }
32
+ interface ElementChangeEvent {
33
+ elementType: ElementType;
34
+ empty: boolean;
35
+ complete: boolean;
36
+ brand?: string | null;
37
+ }
38
+ interface ElementFocusEvent {
39
+ elementType: ElementType;
40
+ }
41
+ interface ElementReadyEvent {
42
+ elementType: ElementType;
43
+ }
44
+ type ElementEventMap = {
45
+ ready: ElementReadyEvent;
46
+ change: ElementChangeEvent;
47
+ focus: ElementFocusEvent;
48
+ blur: ElementFocusEvent;
49
+ };
50
+ type Unsubscribe = () => void;
51
+ interface Element {
52
+ type: ElementType;
53
+ mount(target: string | HTMLElement): void;
54
+ unmount(): void;
55
+ on<K extends keyof ElementEventMap>(event: K, handler: (e: ElementEventMap[K]) => void): Unsubscribe;
56
+ off<K extends keyof ElementEventMap>(event: K, handler: (e: ElementEventMap[K]) => void): void;
57
+ }
58
+ interface Elements$1 {
59
+ create(type: ElementType, options?: ElementOptions): Element;
60
+ getElement(type: ElementType): Element | null;
61
+ }
62
+ interface TokenCard {
63
+ brand: string;
64
+ last4: string;
65
+ expMonth: string;
66
+ expYear: string;
67
+ }
68
+ interface HelloPayError {
69
+ type: 'validation_error' | 'api_error' | 'config_error';
70
+ code: string;
71
+ message: string;
72
+ }
73
+ /**
74
+ * createToken の返り値。Discriminated Union により、success 時は
75
+ * token/card のみ、error 時は error のみが型として存在する。
76
+ *
77
+ * ```ts
78
+ * const { token, card, error } = await hellopay.createToken(el);
79
+ * if (error) return showError(error.message);
80
+ * sendToServer(token, card);
81
+ * ```
82
+ */
83
+ type CreateTokenResult = {
84
+ token: string;
85
+ card: TokenCard;
86
+ error?: undefined;
87
+ } | {
88
+ token?: undefined;
89
+ card?: undefined;
90
+ error: HelloPayError;
91
+ };
92
+ interface HelloPay {
93
+ elements(): Elements$1;
94
+ createToken(cardNumberElement: Element): Promise<CreateTokenResult>;
95
+ }
96
+ type HelloPayFactory = (publicKey: string, options?: {
97
+ locale?: string;
98
+ }) => HelloPay;
99
+ declare global {
100
+ interface Window {
101
+ HelloPay?: HelloPayFactory;
102
+ /**
103
+ * ローカル開発・E2E 用: SDK 本体の配信 URL を上書きする。
104
+ * 本番では設定不要(`loadHelloPay(sdkUrl)` のデフォルトが使われる)。
105
+ */
106
+ HELLOPAY_ELEMENTS_URL?: string;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * HelloPay SDK ローダー。
112
+ *
113
+ * HelloPay 管理下のドメイン(js.hello-pay.app 等)から SDK を動的ロードし、
114
+ * `window.HelloPay` ファクトリを解決する。
115
+ *
116
+ * - 同一 sdkUrl への呼び出しは script tag を 1 回だけ挿入(dedup)
117
+ * - 異なる sdkUrl は独立したロードを維持(E2E / ローカル切り替え対応)
118
+ * - SSR 環境では Promise<null> を解決(加盟店側で null チェック)
119
+ * - 一時的な失敗はキャッシュせず次回呼び出しで再試行可能
120
+ */
121
+
122
+ interface LoadHelloPayOptions {
123
+ /** SDK 配信 URL(ローカル開発・E2E で差し替える用。省略時は publicKey に基づいて test/live を自動解決) */
124
+ sdkUrl?: string;
125
+ locale?: string;
126
+ }
127
+ /**
128
+ * HelloPay SDK をロードして初期化する。
129
+ * モジュールスコープで 1 回だけ呼び、返り値の Promise を `<Elements>` に渡す。
130
+ */
131
+ declare function loadHelloPay(publicKey: string, options?: LoadHelloPayOptions): Promise<HelloPay | null>;
132
+
133
+ interface ElementsProps {
134
+ /** loadHelloPay() の戻り値。モジュールスコープで 1 回だけ生成すること */
135
+ hellopay: Promise<HelloPay | null> | HelloPay | null;
136
+ children: ReactNode;
137
+ }
138
+ declare function Elements({ hellopay: hellopayProp, children }: ElementsProps): react_jsx_runtime.JSX.Element;
139
+
140
+ interface ElementProps {
141
+ options?: ElementOptions;
142
+ className?: string;
143
+ style?: CSSProperties;
144
+ onReady?: (e: ElementReadyEvent) => void;
145
+ onChange?: (e: ElementChangeEvent) => void;
146
+ onFocus?: (e: ElementFocusEvent) => void;
147
+ onBlur?: (e: ElementFocusEvent) => void;
148
+ }
149
+ declare const CardNumberElement: {
150
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
151
+ displayName: string;
152
+ __elementType: ElementType;
153
+ };
154
+ declare const CardExpiryElement: {
155
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
156
+ displayName: string;
157
+ __elementType: ElementType;
158
+ };
159
+ declare const CardCvcElement: {
160
+ ({ options, className, style, onReady, onChange, onFocus, onBlur, }: ElementProps): react_jsx_runtime.JSX.Element;
161
+ displayName: string;
162
+ __elementType: ElementType;
163
+ };
164
+
165
+ /**
166
+ * React hooks.
167
+ *
168
+ * useHelloPay / useElements は <Elements> Provider の Context から
169
+ * SDK インスタンスと Elements セッションを取り出す。
170
+ * SDK ロード完了までは null を返すので、ハンドラ内で null チェックする。
171
+ */
172
+
173
+ declare function useHelloPay(): HelloPay | null;
174
+ declare function useElements(): Elements$1 | null;
175
+
176
+ export { CardCvcElement, CardExpiryElement, CardNumberElement, 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 Unsubscribe, loadHelloPay, useElements, useHelloPay };
package/dist/index.js ADDED
@@ -0,0 +1,209 @@
1
+ // src/loadHelloPay.ts
2
+ var DEFAULT_TEST_SDK_URL = "https://js.test.hello-pay.app/v1/hellopay-elements.js";
3
+ var DEFAULT_LIVE_SDK_URL = "https://js.hello-pay.app/v1/hellopay-elements.js";
4
+ function resolveDefaultSdkUrl(publicKey) {
5
+ return publicKey.startsWith("pk_test_") ? DEFAULT_TEST_SDK_URL : DEFAULT_LIVE_SDK_URL;
6
+ }
7
+ var scriptPromiseCache = /* @__PURE__ */ new Map();
8
+ var sdkCache = /* @__PURE__ */ new Map();
9
+ function normalizeSdkUrl(url) {
10
+ if (typeof window === "undefined") return url;
11
+ try {
12
+ return new URL(url, window.location.href).href;
13
+ } catch {
14
+ return url;
15
+ }
16
+ }
17
+ function normalizeLocale(locale) {
18
+ return locale || "ja";
19
+ }
20
+ function loadScript(url) {
21
+ if (typeof window === "undefined") return Promise.resolve(null);
22
+ const normalizedUrl = normalizeSdkUrl(url);
23
+ return new Promise((resolve, reject) => {
24
+ const managedScripts = Array.from(
25
+ document.querySelectorAll("script[data-hellopay-sdk]")
26
+ );
27
+ const existing = managedScripts.find((script2) => {
28
+ const rawUrl = script2.dataset.hellopayUrl || script2.src;
29
+ if (!rawUrl) return false;
30
+ try {
31
+ return new URL(rawUrl, window.location.href).href === normalizedUrl;
32
+ } catch {
33
+ return rawUrl === normalizedUrl;
34
+ }
35
+ });
36
+ if (existing) {
37
+ const state = existing.dataset.hellopayState;
38
+ if (state === "loaded") {
39
+ resolve(window.HelloPay ?? null);
40
+ return;
41
+ }
42
+ if (state === "loading") {
43
+ existing.addEventListener("load", () => resolve(window.HelloPay ?? null), {
44
+ once: true
45
+ });
46
+ existing.addEventListener(
47
+ "error",
48
+ () => reject(new Error("HelloPay SDK failed to load")),
49
+ { once: true }
50
+ );
51
+ return;
52
+ }
53
+ existing.remove();
54
+ }
55
+ const script = document.createElement("script");
56
+ script.src = normalizedUrl;
57
+ script.async = true;
58
+ script.dataset.hellopaySdk = "true";
59
+ script.dataset.hellopayUrl = normalizedUrl;
60
+ script.dataset.hellopayState = "loading";
61
+ script.addEventListener(
62
+ "load",
63
+ () => {
64
+ if (!window.HelloPay) {
65
+ script.dataset.hellopayState = "error";
66
+ reject(new Error("HelloPay SDK loaded but window.HelloPay is missing"));
67
+ return;
68
+ }
69
+ script.dataset.hellopayState = "loaded";
70
+ resolve(window.HelloPay);
71
+ },
72
+ { once: true }
73
+ );
74
+ script.addEventListener(
75
+ "error",
76
+ () => {
77
+ script.dataset.hellopayState = "error";
78
+ reject(new Error("HelloPay SDK failed to load"));
79
+ },
80
+ { once: true }
81
+ );
82
+ document.head.appendChild(script);
83
+ });
84
+ }
85
+ function loadHelloPay(publicKey, options = {}) {
86
+ const sdkUrl = normalizeSdkUrl(options.sdkUrl ?? resolveDefaultSdkUrl(publicKey));
87
+ const locale = normalizeLocale(options.locale);
88
+ const cacheKey = `${publicKey}::${locale}::${sdkUrl}`;
89
+ const cached = sdkCache.get(cacheKey);
90
+ if (cached) return cached;
91
+ let scriptPromise = scriptPromiseCache.get(sdkUrl);
92
+ if (!scriptPromise) {
93
+ scriptPromise = loadScript(sdkUrl).catch((err) => {
94
+ scriptPromiseCache.delete(sdkUrl);
95
+ throw err;
96
+ });
97
+ scriptPromiseCache.set(sdkUrl, scriptPromise);
98
+ }
99
+ const sdkPromise = scriptPromise.then((factory) => {
100
+ if (!factory) return null;
101
+ return factory(publicKey, { locale: options.locale });
102
+ }).catch((err) => {
103
+ sdkCache.delete(cacheKey);
104
+ throw err;
105
+ });
106
+ sdkCache.set(cacheKey, sdkPromise);
107
+ return sdkPromise;
108
+ }
109
+
110
+ // src/Elements.tsx
111
+ import { createContext, useEffect, useState } from "react";
112
+ import { jsx } from "react/jsx-runtime";
113
+ var EMPTY_CONTEXT = {
114
+ hellopay: null,
115
+ elements: null
116
+ };
117
+ var ElementsContext = createContext(EMPTY_CONTEXT);
118
+ function Elements({ hellopay: hellopayProp, children }) {
119
+ const [ctx, setCtx] = useState(EMPTY_CONTEXT);
120
+ useEffect(() => {
121
+ let cancelled = false;
122
+ setCtx(EMPTY_CONTEXT);
123
+ Promise.resolve(hellopayProp).then((sdk) => {
124
+ if (cancelled) return;
125
+ if (!sdk) {
126
+ setCtx(EMPTY_CONTEXT);
127
+ return;
128
+ }
129
+ setCtx({ hellopay: sdk, elements: sdk.elements() });
130
+ }).catch(() => {
131
+ if (cancelled) return;
132
+ setCtx(EMPTY_CONTEXT);
133
+ });
134
+ return () => {
135
+ cancelled = true;
136
+ };
137
+ }, [hellopayProp]);
138
+ return /* @__PURE__ */ jsx(ElementsContext.Provider, { value: ctx, children });
139
+ }
140
+
141
+ // src/ElementComponents.tsx
142
+ import { useEffect as useEffect2, useRef } from "react";
143
+
144
+ // src/hooks.ts
145
+ import { useContext } from "react";
146
+ function useHelloPay() {
147
+ return useContext(ElementsContext).hellopay;
148
+ }
149
+ function useElements() {
150
+ return useContext(ElementsContext).elements;
151
+ }
152
+
153
+ // src/ElementComponents.tsx
154
+ import { jsx as jsx2 } from "react/jsx-runtime";
155
+ function createElementComponent(type) {
156
+ function ElementComponent({
157
+ options,
158
+ className,
159
+ style,
160
+ onReady,
161
+ onChange,
162
+ onFocus,
163
+ onBlur
164
+ }) {
165
+ const elements = useElements();
166
+ const containerRef = useRef(null);
167
+ const handlersRef = useRef({ onReady, onChange, onFocus, onBlur });
168
+ handlersRef.current = { onReady, onChange, onFocus, onBlur };
169
+ useEffect2(() => {
170
+ if (!elements || !containerRef.current) return;
171
+ const element = elements.create(type, options);
172
+ element.mount(containerRef.current);
173
+ const unsubs = [
174
+ element.on("ready", (e) => handlersRef.current.onReady?.(e)),
175
+ element.on("change", (e) => handlersRef.current.onChange?.(e)),
176
+ element.on("focus", (e) => handlersRef.current.onFocus?.(e)),
177
+ element.on("blur", (e) => handlersRef.current.onBlur?.(e))
178
+ ];
179
+ return () => {
180
+ unsubs.forEach((u) => u());
181
+ element.unmount();
182
+ };
183
+ }, [elements]);
184
+ return /* @__PURE__ */ jsx2(
185
+ "div",
186
+ {
187
+ ref: containerRef,
188
+ className,
189
+ style: { width: "100%", height: "100%", ...style }
190
+ }
191
+ );
192
+ }
193
+ ElementComponent.displayName = `HelloPay(${type})`;
194
+ ElementComponent.__elementType = type;
195
+ return ElementComponent;
196
+ }
197
+ var CardNumberElement = createElementComponent("cardNumber");
198
+ var CardExpiryElement = createElementComponent("cardExpiry");
199
+ var CardCvcElement = createElementComponent("cardCvc");
200
+ export {
201
+ CardCvcElement,
202
+ CardExpiryElement,
203
+ CardNumberElement,
204
+ Elements,
205
+ loadHelloPay,
206
+ useElements,
207
+ useHelloPay
208
+ };
209
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/loadHelloPay.ts","../src/Elements.tsx","../src/ElementComponents.tsx","../src/hooks.ts"],"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\nconst DEFAULT_TEST_SDK_URL = 'https://js.test.hello-pay.app/v1/hellopay-elements.js';\nconst DEFAULT_LIVE_SDK_URL = 'https://js.hello-pay.app/v1/hellopay-elements.js';\n\n/** publicKey のプレフィックスに基づいてデフォルト SDK URL を解決する。 */\nfunction resolveDefaultSdkUrl(publicKey: string): string {\n return publicKey.startsWith('pk_test_') ? DEFAULT_TEST_SDK_URL : DEFAULT_LIVE_SDK_URL;\n}\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 で差し替える用。省略時は publicKey に基づいて test/live を自動解決) */\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 ?? resolveDefaultSdkUrl(publicKey));\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');\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"],"mappings":"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","names":["script","useEffect","jsx","useEffect"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@hello-pay/react",
3
+ "version": "0.1.0",
4
+ "description": "HelloPay Elements React bindings (custom card form for merchants)",
5
+ "license": "UNLICENSED",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "provenance": true
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/hello-ai/hello_pay.git",
13
+ "directory": "js_sdk/react"
14
+ },
15
+ "homepage": "https://github.com/hello-ai/hello_pay/tree/master/js_sdk/react",
16
+ "bugs": {
17
+ "url": "https://github.com/hello-ai/hello_pay/issues"
18
+ },
19
+ "keywords": [
20
+ "hellopay",
21
+ "payments",
22
+ "elements",
23
+ "react",
24
+ "card",
25
+ "tokenization"
26
+ ],
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "require": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "demo": "bun ./demo-server.ts",
45
+ "typecheck": "tsc --noEmit",
46
+ "test": "bun test"
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=18",
50
+ "react-dom": ">=18"
51
+ },
52
+ "devDependencies": {
53
+ "@happy-dom/global-registrator": "^20.8.9",
54
+ "@types/react": "^19.0.0",
55
+ "@types/react-dom": "^19.0.0",
56
+ "bun-types": "^1.3.12",
57
+ "happy-dom": "^20.8.9",
58
+ "react": "^19.0.0",
59
+ "react-dom": "^19.0.0",
60
+ "tsup": "^8.5.0",
61
+ "typescript": "^5.5.0"
62
+ }
63
+ }