emap-merchant-signup について
パートナーサイトにEMAPの加盟店登録を組み込む開発スキルです。全フォーム、リダイレクト、メール登録の3方式に対応し、実装後にスキーマ・セキュリティ・網羅性を検証します。
主な機能
- 01加盟店登録の3つの連携方式
- 02スタック検出とフォーム提供の案内
- 03ブラウザーからEMAPへ直接リクエスト
- 04スキーマ適合性の検証ゲート
- 05セキュリティと網羅性の検証案内
活用シーン
- 01パートナーサイトで全6段階の登録を提供
- 02EMAPのオンボーディングへリダイレクト
- 03メールリンクから登録を再開
- 04静的サイトやサーバー描画サイトへのフォーム埋め込み
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ daptondev2/ emap-skills --skill emap-merchant-signupインストール後、Claude Code で/emap-merchant-signupを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdsignup-steps-schema.jsonreferences/api-errors.mdapi-quirks.mddropdown-fallbacks.jsonfield-catalog.mdmode-1-fullform.mdmode-2-redirect.mdmode-3-api.mdsecurity-checklist.mdstack-guide.mdtemplates/integration-1/plain-html.htmlREADME.mdSignupForm.tsxtemplates/integration-2/plain-html.htmlREADME.mdSignupForm.tsxtemplates/integration-3/plain-html.htmlREADME.mdSignupForm.tsxverify/README.mdverify/hooks/claude-code-settings.jsonverify/scripts/emap_gate.pyverify_form.py詳細
Merchant Signup Integration
パートナーのウェブサイトに EMAP の加盟店登録フォームを埋め込むための開発者向けガイドです。 加盟店はパートナーのサイトで基本的な事業情報を入力します。 その後の手続き(会社情報、銀行口座情報、電子署名)はすべて EMAP が処理します。Integration 1 では、パートナーが全 6 ステップをホストします。
目次
- 始める前に — 必須の質問 ← 必ずここから開始してください
- リファレンスファイル — 対象の機能を実装するときにのみ読み込んでください
- ステップ 0:プロジェクトのスタックを検出する
- ステップ 1:検証ゲートをインストールする ← コードを書く前に必ず実施してください
- ステップ 2:パートナーのアトリビューション(任意)
- ステップ 3:統合モードを選択する
- Integration 1 の構築:フルフォーム
- Integration 2 の構築:リダイレクトによる引き継ぎ
- Integration 3 の構築:メールベースの登録
- 検証:スキーマへの準拠 ← 構築完了とする前に必ず実行してください
- 検証:セキュリティと網羅性
- ガードレール
始める前に — 必須の質問
停止してください。開発者が以下の 2 つの質問に回答するまでは、リファレンスファイルを読んだり、テンプレートを開いたり、コードを生成したりしないでください。 最初に尋ねる質問はこの 2 つだけです。スタックについては、ステップ 0 で検出できない場合にのみ尋ねてください。
質問 1 — どの統合方式を使用しますか?
開発者には、次の 3 つの選択肢から必ず 1 つだけ選ぶよう依頼してください。ほかの選択肢を提示してはいけません。エージェントに複数選択式の質問ツールがある場合は、それを使用し、太字のテキストを各選択肢のラベル、残りを説明として指定してください(ラベルに通し番号を付けないでください)。それ以外の場合は、番号付きリストをプレーンテキストで提示し、回答を待ってください。
- フルフォーム(Integration 1) — サイト上で加盟店登録の全 6 ステップをホストします。加盟店はプラットフォームを離れることなく、すべてのステップ(1〜6)を完了します。各ステップはブラウザーから EMAP の API に直接 POST されます。どのスタックでも動作し、EMAP は加盟店のブラウザーから呼び出されます。
- リダイレクトによる引き継ぎ(Integration 2) — 加盟店はサイト上で単一のステップ 1 のフォームに入力します。送信すると、残りのステップを完了するため、EMAP のオンボーディング登録フローに直接リダイレクトされます。どのスタックでも動作し、EMAP は加盟店のブラウザーから呼び出されます。
- メールベースの登録(Integration 3) — 加盟店はサイト上で単一のステップ 1 のフォームに入力し、ブラウザーから直接 EMAP に POST します。EMAP がアカウントを作成し、続きから再開できる安全なリンクをメールで送信します。加盟店はそのリンクをクリックし、受信トレイからオンボーディングの全フローを続行します。どのスタックでも動作し、EMAP は加盟店のブラウザーから呼び出されます。
開発者が 3 つの選択肢のいずれかを選ぶまで、先に進まないでください。
質問 2 — パートナーキー
開発者がバリアントを選んだら、質問 1 と同じ方法で、必ず次の 3 つの選択肢から 1 つを選ぶよう促してください(エージェントに選択式の質問ツールがある場合はそれを使い、ない場合はプレーンテキストで番号付きリストを提示します)。各選択肢の説明には、以下に記載された取得手順を一字一句そのまま含めてください。これは、開発者がキーの確認場所や入手方法を知るために読むものです。また、回答する前に、次のことを伝えてください。EMAP はマーチャントのブラウザーから呼び出されるため、キーはページの JavaScript に直接埋め込まれ、ページのソースを表示できる人なら誰でも確認できます。 EMAP はキーを純粋にアトリビューション/紹介の値として扱い(何かへのアクセスを許可する認証情報ではありません)、実際にキーが漏えいした場合のリスクは、別のサイトの登録がこのキーに誤って紐付けられることであり、セキュリティ侵害ではありません。ただし、判断する前にこの点を把握しておく必要があります。
- はい、パートナーキーを持っています — 説明:「パートナーポータルにログイン → Integration → API Integration → 表示されている API キーをコピー → ここに貼り付けます。」
- いいえ、パートナーキーを持っていません — 説明:「https://emap.easypaydirect.com/signup/partner でパートナーとして登録します。登録後、Integration → API Integration に移動し、パートナーキーをコピーして、ここに戻って貼り付けます。」
- スキップ(キーなしで続行) — 説明:「登録は引き続き機能しますが、パートナーアカウントには紐付けられません。ページのソースにキーを表示したくない場合は、この選択肢を選んでください。」
開発者が「はい、パートナーキーを持っています」を選択した場合:
キーを今すぐ貼り付けるよう依頼します。これを EMAP_PARTNER_KEY として記憶し、後で生成するファイル内の EMAP_PARTNER_KEY 定数に直接書き込みます。ステップ 0 に進みます。
開発者が「いいえ、パートナーキーを持っていません」を選択した場合:
上記の選択肢の説明にある登録リンクに従い、準備ができたら戻ってキーを貼り付けるよう伝えます。ひとまずキーなしでステップ 0 に進みます。後でキーを貼り付けた場合は、生成するファイル内の EMAP_PARTNER_KEY 定数に書き込みます。
開発者が「スキップ(キーなしで続行)」を選択した場合: パートナーキーなしでステップ 0 に進みます。
両方の質問に回答してもらった後にのみ、ステップ 0 に進み、参照ファイルの読み込みまたはコードの生成を開始してください。
参照ファイル
機能の実装時に必要な参照ファイルだけを読み込んでください。最初にすべて読む必要はありません。
| ファイル | 読み込むタイミング |
|---|---|
signup-steps-schema.json | すべてのフォーム(3つの連携すべて)を作成または検証するとき — 各フィールドのウィジェットtype(select/radio/checkbox-group/textなど)、選択肢のラベル(staticDropdowns)、動的ドロップダウンのエンドポイント、国ごとの検証ルール(countryVariants)を網羅した、信頼できる機械可読な情報源です。ウィジェットの種類や、ハードコードされたフィールドの選択肢ラベルを、フィールド名やMarkdownの表から推測してはいけません。必ずこのファイルで確認してください。 |
references/field-catalog.md | 連携2または3を実装するとき — フィールドの一覧、種類、制約、自動送信をトリガーするフィールドを網羅しています。連携1には適用されません — 国や業種の値の形式が異なるため、代わりにmode-1-fullform.mdと上記のスキーマを使用してください。 |
references/mode-1-fullform.md | 連携1を実装するとき — 6つのステップすべてのAPIコントラクト、フィールドのルール、UUIDのライフサイクル、条件分岐を記載しています。このファイルに記載されているのはAPIのペイロード/値のルールのみです。レンダリングするHTMLウィジェットや、国ごとに異なる検証ルールは記載されていないため、signup-steps-schema.jsonで確認してください。 |
references/mode-2-redirect.md | 連携2を実装するとき — URLの構築、自動送信ロジック、パートナーへのアトリビューション、UTMの引き継ぎを記載しています。 |
references/mode-3-api.md | 連携3を実装するとき — メールアドレスを使用した登録APIのコントラクト、リクエスト/レスポンスの形式、再開リンク、エラー処理を記載しています。 |
references/api-errors.md | エラーを処理するとき — すべてのHTTPステータスコードとレスポンスの形式、推奨される開発者の対応を記載しています。 |
references/stack-guide.md | ステップ0:プロジェクトのスタックでフォームを提供するとき。スタックの検出、React/Vue/Angular/Svelte/サーバーレンダリング/静的サイト/サイトビルダー/モバイルでの提供方法、ネイティブアプリへの移植ルール、EMAPを呼び出してはいけないサーバー側コードについて記載しています。 |
references/security-checklist.md | 公開前 — 満たす必要があるすべてのセキュリティ要件を記載しています。 |
references/dropdown-fallbacks.json | いずれかの連携を構築するとき — すべてのEMAP /api/partner/*ドロップダウンレスポンスの静的スナップショットです。各テンプレートのEMAP_DROPDOWN_FALLBACKS定数に埋め込まれ、ライブ呼び出しに失敗した場合や、使用可能なデータが返されない場合に使われます。EMAPのテストサーバーから取得したものです。本番環境でAPIが提供されるようになったら、再取得してください。 |
references/api-quirks.md | 連携1を構築するとき — EMAP独自の登録ページとは異なるAPIの挙動(納税者番号と登録番号の免除、SSNの国、紹介元と業種の照合)と、このファイルに記載されたルールの背景を説明しています。 |
verify/README.md | フォームファイルの作成または編集後、ビルド完了とする前 — verify_form.pyの実行方法、5つのチェックカテゴリで確認する内容、検出できない内容を記載しています。 |
ステップ 0:プロジェクトのスタックを判別する
このフォームはあらゆるスタックで動作します。アーキテクチャ上のルールは、どのフレームワークでページをレンダリングするかではなく、EMAP リクエストの送信元に関するものです。
すべての EMAP 呼び出し(すべての
POST、/api/partner/*のドロップダウン用GET、Integration 2 のリダイレクト)は、パートナーのサーバーではなく、加盟店のブラウザーから行います。 EMAP は、呼び出し元の IP に基づいて登録をレート制限し、不正利用スコアを算出します。パートナーのサーバー経由でプロキシすると、すべての加盟店がそのサーバーの IP を共有することになります。その結果、1 人の不正利用者によってパートナーのすべての登録がレート制限され、EMAP は加盟店ごとのスパム判定シグナルを失います。それ以外の用途には、どのようなバックエンドを使ってもかまいません。ページがサーバーサイドレンダリングされていても、ページ内の JavaScript から EMAP リクエストを送る限り問題ありません。次のいずれからも EMAP を呼び出さないでください。
- ルートハンドラー、API ルート、Server Actions
- ローダーやアクション
- SSR のデータ取得
- コントローラー、
curl/wp_remote_post、サーバーレス関数
references/stack-guide.mdには、フレームワークごとに避けるべきサーバーサイド API が記載されています。
プロジェクトのファイルからスタックを判別します。package.json の依存関係、composer.json、Gemfile、manage.py、*.csproj、SSG の設定などを確認します。判別に使うシグナルはスタックガイドに記載されています。シグナルが見つからない場合や、互いに矛盾する場合にのみ、開発者に質問してください。その後、次のようにフォームを組み込みます。
| プロジェクト | 提供内容 |
|---|---|
| React(Next.js、Vite、Remix / React Router、Gatsby、Astro + React) | templates/integration-<n>/SignupForm.tsx。任意のページからレンダリングします。JavaScript のみのプロジェクトでは、ファイル名を .jsx に変更し、型を削除します |
| Vue / Nuxt、Angular、Svelte / SvelteKit、Solid、その他のコンポーネントフレームワーク | SignupForm.tsx と同じ方法で、クライアント専用のマウントフック内に plain-html.html のマークアップとスクリプトをマウントします(推奨)。または、ネイティブに 1:1 で移植します |
| サーバーサイドレンダリング(PHP / WordPress / Laravel、Rails、Django / Flask、ASP.NET、Twig / Liquid など) | plain-html.html を埋め込みます。CSS とスクリプトは静的アセットファイルとして配置し、.emap-signup マークアップはページテンプレートに含めます |
| 静的サイト / SSG | plain-html.html を独立したページとして使用するか、上記のように埋め込みます |
| ホステッド型サイトビルダー(Webflow、Wix、Squarespace、Shopify、Framer) | カスタムコードの埋め込みブロックを使うか、plain-html.html をホストして iframe またはリンクで表示します |
| モバイルアプリ | ホストされた plain-html.html を WebView で読み込みます |
スタックごとの詳細(SSR の落とし穴、テンプレートエンジンのエスケープ処理、CSP、iframe、ネイティブ移植のルール)については、references/stack-guide.md をお読みください。
どちらのテンプレートも、同じテスト済みのフォームです。plain-html.html の CSS は .emap-signup の内側にスコープされ、スクリプトは IIFE でラップされて onReady() から開始するため、スタイルやグローバル変数を漏らさずに既存のページへ埋め込めます。生成したフォームのファイルはすべて、ほかのファイルを含まない 1 つの form_dir に配置してください。verify_form.py は、配下にあるすべてのフロントエンドソース形式を再帰的にスキャンします。
手順 1:検証ゲートをインストールする
停止してください。この手順が完了するまでは、新規プロジェクトでも継続中のプロジェクトでも、フォームファイルを 1 つも作成、編集、生成しないでください。 プロジェクトに既存のフォームコードがあるからといって、ゲートがインストール済みとは限りません。まず
.emap/emap_gate.pyがあるか確認してください。なければ、ほかに何がすでに構築されていても、この手順を実行してください。ゲートはビルドの完了を判断します。
emap_gate.pyはverify_form.pyを自ら実行して結果を記録し、作成者が記述したステータスを決して信用しません。通常の Python 3 で実装されているため、シェルコマンドを実行できるエージェントであれば、CI でも手動でも利用できます。スクリプトの確認項目については、検証:スキーマへの準拠を参照してください。
-
3 つのファイルを対象プロジェクトの
.emap/フォルダーに、バイト単位でそのままコピーします。verify/scripts/emap_gate.py→.emap/emap_gate.pyverify/scripts/verify_form.py→.emap/verify_form.pysignup-steps-schema.json→.emap/signup-steps-schema.json
.emap/はビルド用ツールであり、フォームの一部ではないため、デプロイしないでください。コミットしても問題ありません(CI でゲートを実行できます)。.emap/verify-cache.jsonは Git の追跡対象から除外できます。 -
**ビルドを進行中としてマークします。**選択した統合モードのコード生成を始めたらすぐに、
.emap/build-state.jsonを書き込みます。{ "status": "in_progress", "integration": "1", "form_dir": "merchant-signup" }構築中のモードに合わせて
"2"または"3"を使用します。form_dirには、生成したフォームファイルを格納するフォルダーをプロジェクトルートからの相対パスで指定します。そのフォルダーには、ほかのファイルを格納しないでください。 -
任意:エージェントにゲートを自動適用させます。
- Claude Code:
verify/hooks/claude-code-settings.jsonのhooksキーを対象プロジェクトの.claude/settings.jsonに統合します。ファイルがなければ作成してください。hooks.Stopにすでに項目がある場合は、この項目を追加し、ほかの項目は決して上書きしないでください。これにより Claude Code は、ゲートを通過するか、ビルドがblockedまたはcancelledとマークされるまで、処理を完了できなくなります。 - **エージェントの終了時に実行されるフックがあるその他のエージェント:**そのエージェントのフック形式で
python3 .emap/emap_gate.pyを登録します。ブロック対象がなければ終了コード 0、問題があれば標準出力に内容を表示して終了コード 1 を返します。 - **フックのないエージェント:**インストールするものはありません。ビルド完了とする前に、自分でゲートを実行します(検証:スキーマへの準拠を参照)。
- **すべてのエージェント:**開発者は CI または pre-commit フックで
python3 .emap/emap_gate.pyを実行することもできるため、未検証のフォームがマージされるのを防げます。
- Claude Code:
**続行する前に確認:**3 つのファイルと .emap/build-state.json がディスク上に存在すること(設定項目を追加した場合は、その項目も存在すること)を確認してください。書き込みが成功したと決めつけないでください。その後、手順 2に進むか、コードの生成を始めてください。
この環境で Python 3 を実行できない場合(たとえば、ブラウザー上で動作するアプリビルダーの場合)は、ここではゲートを実行できないこと、およびビルドを受け入れる前に Python 3 がインストールされたマシンで python3 .emap/emap_gate.py を実行する必要があることを開発者に伝えてください。手順 1 を黙って省略しないでください。
ステップ 2:パートナー帰属(任意)
パートナー帰属を設定すると、加盟店の登録が EMAP 内のパートナーアカウントに紐付けられます。 任意の設定です。設定しなくても登録はできますが、パートナーには実績が付与されません。
すべてのテンプレートで同じ定数 EMAP_PARTNER_KEY を使用し、<script> ブロック内に設定します。
これは帰属情報であり、秘密情報ではありません。どのような連携を構築しても、ページの JavaScript から確認できます。
- 連携 1 と 3(API): テンプレートはステップ 1 の POST の JSON 本文に
partner_keyとして送信します。 - 連携 2(リダイレクト): テンプレートは EMAP の
secretKeyURL パラメーターとして送信します。パラメーター名に反して、値は同じパートナーキーであり、リダイレクト URL からも確認できます。
デプロイ後はクライアント側で確認できる値ですが、公開リポジトリのソース管理には実際のキー値を絶対にコミットしないでください。「デプロイ済みページ内」と「git 履歴内」は、別々の露出対象として扱ってください。
開発者がまだパートナーキーを持っていない場合は、 開始する前に — 必須の質問 の手順を案内してください。 キーがなくても連携は機能します。登録に帰属情報が付与されないだけです。
ステップ 3:連携モードを選択
| 連携 1 — フルフォーム | 連携 2 — リダイレクトによる引き継ぎ | 連携 3 — メールベースの登録 | |
|---|---|---|---|
| 仕組み | パートナーが全 6 ステップをホストし、ブラウザーが各ステップを EMAP の API に直接 POST します | パートナーが単一ステップのフォーム(連携 1 のステップ 1 と同じフィールド)をホストします。送信時にすべてのフィールドが URL パラメーターとして追加され、ブラウザーが EMAP の /signup?params にリダイレクトします | パートナーが単一ステップのフォームをホストし、ブラウザーがステップ 1 のフィールドを EMAP の REST API に直接 POST します。EMAP は加盟店に、申し込みを完了するための安全なリンクをメールで送信します |
| 加盟店が続行する場所 | パートナーのサイト — 全 6 ステップ | EMAP、ステップ 2 以降(リダイレクト直後) | EMAP、ステップ 2 以降(メールリンクをクリックした後) |
| サーバーコードが必要? | いいえ。ブラウザーが EMAP を直接呼び出します | いいえ。リダイレクトはブラウザーの画面遷移です | いいえ。ブラウザーが EMAP を直接呼び出します |
| EMAP で自動送信? | 該当なし — 加盟店は EMAP にアクセスしません | first_name、last_name、company_name、phone、email がすべて入力され、EMAP のページが事前入力データを受け付ける場合は自動送信します。それ以外の場合、加盟店は EMAP の事前入力済みフォームを送信します | 該当なし — ブラウザーがすでにステップ 1 を送信済みです |
| 最適な用途 | 全 6 ステップでの完全なブランディング制御、エンタープライズ連携 | シンプルな埋め込み、静的サイト、最速の連携 | パートナー側でのステップ 1 のスムーズな UX。加盟店はメールリンクをクリックした後、残りの手続きを EMAP で完了します |
開発者はすでに質問 1 でモードを選択しています。再度質問せず、この表は各モードの違いに関する質問に答える場合にのみ使用してください。
連携 1 の構築:フルフォーム
以下のファイルを作成する前に必ず確認してください: このプロジェクトに .emap/emap_gate.py が存在するか確認します。存在しない場合は、merchant-signup/ やその他のビルド出力がすでにあっても、まずステップ 1を実行してください。
続行する前に references/mode-1-fullform.md を読んでください。
必ず
signup-steps-schema.jsonに基づいて構築してください。 フィールドの作成や移植を行う前に、signup-steps-schema.jsonでそのフィールドを確認し、ウィジェットのtype、API のフィールド名/ルート、すべての検証ルール(国ごとのcountryVariantsを含む)、および依存/条件付きフィールド(dependsOn/visibleIf)に厳密に従ってください。これらをフィールド名、Markdown の表、文章から推測してはいけません。フォームの構築で唯一の信頼できる情報源となるのは、この JSON ファイルです。
手順
-
templates/integration-1/のテンプレートを使用します。-
plain-html.html:ビルド手順なしで使える、全6ステップのフォームです。標準となる参照実装です。 そのまま使うか、プロジェクトのページに埋め込みます( Step 0を参照)。 -
SignupForm.tsx:同じフォームをReactコンポーネントとして実装したもので、任意のReactフレームワークで使用できます。 -
それ以外のコンポーネントフレームワークでは、文章による参照情報だけをもとにフォームを再生成するのではなく、参照実装をマウントするか、
plain-html.htmlを1:1で移植してください。移植では、次の内容を維持します。- 同じフィールド一覧
- 各フィールドで同じウィジェットタイプ
- 同じバリデーション
- 同じ条件分岐ロジック
- EMAPへの同じブラウザー側
fetch()呼び出し
フィールドごとの仕様には
signup-steps-schema.jsonを使用し、references/stack-guide.mdに記載された移植ルールに従ってください。 -
スタックにかかわらず、EMAPの呼び出しはブラウザーコード内に置いてください。EMAPを呼び出すサーバールート、アクション、コントローラーは追加しないでください。
-
-
ファイルの
<script>ブロックの先頭にある2つの定数を設定します(「EDIT THESE」を検索してください。SignupForm.tsxではFORM_LOGIC文字列の先頭にあります)。var EMAP_BASE_URL = 'https://emap.epd.dev'; // EMAP's TEST server var EMAP_PARTNER_KEY = ''; // optional — see Step 2 above設定する環境変数や
.envファイル、サーバープロセスはありません。これが唯一の設定手順です。https://emap.epd.devはEMAPのテストサーバーです。このURLを使ってビルドとテストを行い、公開前にEasy Pay Directから案内された本番URLに切り替えてください。 -
ドロップダウンのデータはEMAPから直接取得します(
GET {EMAP_BASE_URL}/api/partner/countriesなど)。テンプレートに用意されているfetchDropdownData()関数が取得するため、プロキシルートを実装する必要はありません。ライブ呼び出しでエラーやタイムアウトが発生した場合、またはdata配列が空もしくは存在しない場合は、埋め込み済みのEMAP_DROPDOWN_FALLBACKS定数(references/dropdown-fallbacks.jsonを参照)にフォールバックします。そのため、選択肢が0件のままドロップダウンが表示されることはありません。別のスタックに移植する場合は、ファイルの残りの部分とあわせてfetchDropdownData()と埋め込みのフォールバックデータも移植してください。 -
ステップの流れを確認します。
- すべてのステップのリクエストでは、JSON本文に
step_count(1〜6)が含まれます。Step 1とStep 4も例外ではありません。 - Step 1(
POST /api/v1/signup、step_count: 1)はuuidとsignup_tokenを返します。収集する項目は、名、姓、メールアドレス、電話番号、会社名、ウェブサイト、国、年間売上高、業種タイプ、および(米国の場合は)事業州です。uuidはlocalStorage('emap_uuid')に、トークンはlocalStorage('emap_signup_token')に保存します。 - **Step 1の自動保存:**名、姓、メールアドレス、電話番号を入力した後、そのうちいずれかのフィールドからフォーカスを外すと、バックグラウンドで
/api/v1/signup/auto-saveにPOSTされます(EMAPがユーザーとHubSpotの連絡先を作成します。EMAP独自の登録ページと同様です)。エラーが表示されることはありません。Step 1の送信時には、実行中の自動保存の完了を待ちます。references/mode-1-fullform.mdを参照してください。 - Steps 2、3、5、6では、
uuid、最新のsignup_token、適切なstep_countを指定してPOST /api/v1/application/stepを呼び出します。 - Step 4では、
uuid、最新のsignup_token、step_count: 4、ドット記法によるオーナーフィールドを指定してPOST /api/v1/ownershipを呼び出します。 - **各ステップの成功時に新しい
signup_tokenが返されます。古いトークンを上書きして保存してください。**トークンがない、誤っている、または期限切れ(最後に保存したステップから24時間経過)になっている場合、EMAPはSteps 2〜6にHTTP 403を返します。ページの読み込み時に、トークンの期限が切れた保存済みの進捗は再開しないでください。references/mode-1-fullform.mdを参照してください。 - Step 6が成功したら、
localStorageをクリアし、トップレベルウィンドウをEMAPの{EMAP_BASE_URL}/upload-document/{uuid}?redirect=1ページにリダイレクトします。そこで加盟店が書類をアップロードします。
- すべてのステップのリクエストでは、JSON本文に
-
ステップ 1 のデータをステップ 2 に事前入力します: ステップ 1 が成功し、フォームがステップ 2 に進んだら、次の項目に自動入力します:
legal_name← ステップ 1 のcompany_nameの値name(DBA/「doing business as」)← ステップ 1 のcompany_nameの値 法的名称が商号と異なる場合、加盟店はステップ 2 でこれらの項目を編集できます。 項目が空の場合にのみ事前入力します(加盟店がすでに入力している場合や、セッションが localStorage から復元された場合は上書きしません)。 また、EMAP の独自フォームと同様に、「主な連絡担当者ですか?」が「はい」の場合、ステップ 4 の所有者 1 の携帯電話番号(phone.1)にステップ 1 のphoneを事前入力します。「いいえ」に切り替えると、ステップ 1 の番号が残っている場合は消去されます。「はい」に戻すと復元されます。
電話番号項目(
phone、customer_service_telephone_number、phone.1、phone.2)には、EMAP の独自の登録ページで使用されているバージョンの intl-tel-input 22.0.2 を使用します。国の選択(米国が先、次にカナダ)、入力中の書式設定(例:(202) 555-1234)、プレースホルダーとしての入力例を提供します。isValidNumber()が true でない限り送信をブロックし、E.164 形式のgetNumber()(+12025551234)を送信します。専用の SRI ピン留め済み<script>タグでutils.jsを読み込み、window.intlTelInput.utils = window.intlTelInputUtilsを設定します。このライブラリのutilsScriptオプションは、整合性ハッシュなしでスクリプトを挿入します。 -
ステップ間の戻る操作: プログレスバーの番号付きステップは、このページのセッション中に到達済みのステップであればクリックできます(以前の回答が失われる、ページ再読み込み後を除く)。すべての
:disabledの入力欄、選択欄、テキストエリア、ラジオボタン、チェックボックスに、見た目で分かるスタイル(灰色の背景、控えめな文字色、not-allowedカーソル)を設定し、読み取り専用の項目が明確に分かるようにします。 ステップ 2~6 には「戻る」ボタンを表示します。戻っても加盟店が入力した内容は保持され、編集してそのステップを再送信できます。API は同じuuidと最新のsignup_tokenを使用して、ステップを再度受け付けます。- ステップ 1 は、戻ったとき読み取り専用です。
POST /api/v1/signupは再実行できません(メールアドレスはすでに登録済みです)。そのため、入力項目を無効にし、「続行」を押すと API を呼び出さずにステップ 2 に進むようにします。 - ステップ 4:所有者の本人確認情報は、EMAP の単一ページ登録と同様、一度保存した後は読み取り専用です。「あなたは事業主ですか?」、各所有者の名、姓、メールアドレス、および各所有割合を無効にします。ステップ 4 のその他すべて(役職、電話番号、生年月日、SSN、住所、身分証明書)は編集可能なままにします。無効化された入力項目は
FormDataに含まれないため、再送信ペイロードの作成時に、ID を使ってロックされた項目の値を読み取ります。無効化されたすべてのコントロールを一律にコピーしてはいけません。各 State テキストボックス/米国の州選択欄ペアの非表示側も無効化され、同じnameを持っているため、コピーすると選択済みの州が空の値で上書きされます(EMAP では「State is required」と回答されます)。ステップ 2、3、5、6 にはロックされた項目はありません。 - 再送信ごとに、そのステップ固有の
step_countを送信します。再送信を省略して先のステップに進んではいけません。 - ページ再読み込み後は以前の回答が失われるため、復元されたステップでは「戻る」を非表示にします。
- 書類アップロードページには「戻る」はありません。ステップ 6 の成功後、EMAP にリダイレクトされます。
- ステップ 1 は、戻ったとき読み取り専用です。
-
条件付きフィールドの処理:
条件付きフィールドごとに、
visibleIf(表示条件)とdependsOn(必須条件)を個別に確認してください。 これらは異なる式です。EMAP の API と独自の登録ページの内容が異なる場合は、API に従ってください。 以下のルールはすでに API に準拠しています。各ルールの根拠はreferences/api-quirks.mdを参照してください。industry_typeはステップ 1(ステップ 2 ではありません)で収集し、POST /api/v1/signupとともに送信します。industry_type = otherの場合はindustry_type_otherを表示します。照合では大文字小文字を区別しません。ライブの/api/partner/industry-typesエンドポイントの catch-all スラッグはotherではなく、Other(大文字)です(ステップ 1)。country=US→business_state(ステップ 1)とstate.1(ステップ 4)を表示します。emap_country(ステップ 1)=CAまたはbusiness_organized=Sole-Proprietorship→federal_tax_id(ステップ 2)を非表示/無効化し、必須条件を解除します。それ以外の場合は表示し、必須とし、countryVariantsに従ってマスキングします(ポイント 9)。いずれかのフィールドが変更されるたびに再評価してください。emap_country(ステップ 1)=US→business_register_number(ステップ 2)を非表示にし、必須条件を解除します。API が認める免除はこれだけです。プエルトリコや CA+Sole-Proprietorship を免除対象に含めないでください。is_physical_address_same_as_legal_address=0→ 物理住所ブロック(ステップ 2)を表示します。marketingModelに2が含まれる →subscription_frequencyを表示します。frequency=3の場合はsubscription_frequency_otherを表示します(ステップ 2)。fulfillment_byがVendorまたはOthers→fullfillment_company(l が 2 つ)を表示します(ステップ 3)。primary_contact=0→first_name.1、last_name.1、email.1、primary_contact_job_titleを表示します(ステップ 4)。ownership_percentage.1 < 51→ Owner 2 セクションを表示します(ステップ 4)。country.1=US→driver_license_state.1とdriver_license_expiration_date.1を表示します(ステップ 4)。emap_country(ステップ 1)=CA→institution_numberとcustomer_pay_currencyを表示します(ステップ 5)。bad_experience=true→bad_experience_happenedを表示します(ステップ 6)。howdidyouhearが「Other」、「Friend」、「Live Event / Trade Show」のいずれか →hear_about_us_otherを表示します (ステップ 6)。スラッグまたは名前のテキスト(Other、Friend、Live-Event-/-Trade-Show)で照合してください。エンドポイントは ID を返しません。
-
ウィジェットの種類 — 推測せず、必ずスキーマで確認してください。 これらのフィールドはハードコードされた値セットであり、動的なドロップダウンデータではありません。これまで何度も、誤って生の値を並べた
<select>として生成されています。レンダリング前にsignup-steps-schema.jsonで各フィールドのtypeを確認してください。marketingModel→ チェックボックスグループ(staticDropdowns.marketing_model)です。セレクトではありません。labelのテキスト(例:「Recurring/Continuity/Subscription」)を表示し、整数のvalueを送信します。is_physical_address_same_as_legal_address、primary_contact、bankruptcy_filed.1/.2、bankruptcy_discharged.1/.2、current_processing、bad_experience、multiple_merchant_accounts、leave_deposit→ ラジオボタン(Yes/No)です。セレクトではありません。terms_and_conditions_agreed→ チェックボックスです。セレクトではありません。optionsSource: dynamicDropdownEndpoints.*を使うフィールド(country、industry_type、shopping_cart、howdidyouhearなど)は、<select>として正しくレンダリングされます。
-
国ごとのフィールド検証 — ヒントとして表示するだけでなく、必ず適用してください。 これらのフィールドについては
signup-steps-schema.jsonのcountryVariantsを確認し、対応するmaxLength/minLength/patternを入力欄の HTML 属性と送信時の JavaScript チェックの両方に設定してください(下記の SSN マスクを適用するのと同じ方法です)。ラベルやプレースホルダーに「9 桁」と書いてあるだけでは何の効果もありません。routing_number— US:数字 9 桁ちょうど(^[0-9]{9}$)。account_number— US:8~17 文字。federal_tax_id— 数字のみとし、US/CA/PR ではXXX-XX-XXXX(3-2-4 の区切り)、それ以外のすべての国ではXX-XXXXXXX(2-7 の区切り)の形式にします。これは、フィールドの汎用パターン^[0-9A-Za-z\-]+$やプレースホルダーの「EIN」から想像される内容とは逆です。ラベルが US/CA/PR 以外では「...または同等の法人税番号」と表示されていても、どの国でも英数字を自由に入力できる形式にはなりません。signup-steps-schema.jsonのこのフィールドのcountryVariantsを参照してください。institution_number— スキーマのcountryVariants/patternを参照してください。
-
カード決済の割合(ステップ 3):
card_swiped + customer_entered + staff_enteredの合計は 100 でなければなりません。クライアント側で検証し、条件を満たさない場合は送信をブロックしてください。 -
SSN/SIN(ステップ 4): US/CA/PR では、Cleave.js のマスク
blocks:[3,2,4] delimiters:["-","-"]を適用し、XXX-XX-XXXX形式にします。ラベルは「SSN/SIN」としてください。検証条件はssn.replace(/-/g,'').length >= 9です。 それ以外のすべての国では、書式チェックは必須入力以外行わず、マスクなしのテキストフィールドを表示し、ラベルは「SSN(または同等の個人用納税者番号)」としてください。この判定に使用する国は
country_from_step1、つまりステップ 1 で選択した単一の法人設立国です。ssn.1とssn.2の両方に適用されます。各所有者のcountry.1/country.2ではありません。これらは運転免許証のフィールドの表示条件にのみ使います。- Owner 1 の SSN(
ssn.1)は常に必須です。Owner 2 の SSN(ssn.2)は、Owner 2 のセクションが表示されている場合(ownership_percentage.1 < 51)にのみ必須です。
- Owner 1 の SSN(
-
生年月日(ステップ 4): 所有者の年齢は 18 歳以上、100 歳以下でなければなりません。
maxDate = today − 18 years、minDate = today − 100 years。 クライアント側で適用してください(カレンダーピッカーのmin/max属性に加え、送信時にもチェックします)。EMAP の API はこれをチェックしないため、フォームのチェックだけが有効です。詳細はreferences/api-quirks.mdを参照してください。- すべての日付フィールドは
YYYY-MM-DD形式で表示します(business_formed、dob.*、driver_license_expiration_date.*、bankruptcy_discharged_date.*)。そのままの<input type="date">を表示してはいけません。ブラウザーは訪問者のロケールに合わせた形式(例:US ではMM/DD/YYYY)で表示するためです。テンプレートでは Cleave.js(date: true, datePattern: ['Y','m','d'], delimiter: '-')でマスクしたテキストボックスと、カレンダーアイコンの上に重ねた透明で名前のないネイティブの日付入力を使います。選択した日付はテキストボックスにコピーします。各ステップの送信時に、実在する日付ではないYYYY-MM-DDの入力値を拒否してください。
- すべての日付フィールドは
-
各ステップであらゆるレスポンス形式に対応してください(
references/api-errors.mdを参照)。{"status":true}→ 次のステップに進みます。- ステップ 2~6 で HTTP 403 が返された場合 → 保存済みの進行状況をクリアし、EMAP から届いたメールに従って続行するか、新しい申請を開始するよう加盟店に伝えます。
- HTTP 422 → フィールドごとのエラーを表示します。フォームに表示されていないフィールド(例:
partner_key)のエラーは、ページレベルのアラートに表示します。 - HTTP 429 → 時間をおいて再試行するよう加盟店に伝えます。
- HTTP 5xx → 汎用の「もう一度お試しください」というメッセージを表示します。
-
手動でスキーマとの適合性を確認せず、
verify_form.pyを実行してください。 スキーマと照らし合わせて個々のフィールドを手作業で抜き取り確認しないでください(ウィジェットの種類、ハードコードされたラベル、国別の検証パターン、条件付きの表示/非表示の連携、ドロップダウンの遷移先/フォールバックなど)。代わりにverify_form.pyを実行し(検証:スキーマ適合性を参照)、報告された内容をそのまま修正してください。このチェックは決定的でほぼ瞬時に完了します。手作業でやり直しても、同じ確認を二度行うことになります。 このチェックで実環境のテストが不要になるわけではありません。 スクリプトが検出するのは、スキーマとコードの不一致であり、APIについて両方が誤っているケースではありません(verify/README.mdの「このスクリプトで検出できないこと」を参照)。チェックに合格したら、EMAPのテストサーバーに対して、代表的なエッジケース(例:カナダ以外の個人事業主)で、6ステップの申し込みを1件送信してください。自分で管理しているメールアドレスを使用してください。EMAPの本番サーバーでテスト申し込みを作成してはいけません。- 開発者にフォームの完成を伝える前に、検証:スキーマ適合性の手順を実行し、続けて検証:セキュリティと網羅性を実行してください。
python3 .emap/emap_gate.pyがPASSを出力するまで、ビルドは完了していません。
- 開発者にフォームの完成を伝える前に、検証:スキーマ適合性の手順を実行し、続けて検証:セキュリティと網羅性を実行してください。
ビルド連携 2:リダイレクトによる引き継ぎ
以下のファイルを書き始める前に: このプロジェクトに.emap/emap_gate.pyが存在することを確認してください。存在しない場合は、merchant-signup/などのビルド出力がすでにあっても、まず手順1を実行してください。
作業を進める前にreferences/mode-2-redirect.mdを読んでください。
必ず
signup-steps-schema.jsonに基づいてビルドしてください。 フィールドを記述する前に、signup-steps-schema.jsonでそのフィールドを確認し、定義に厳密に従ってください。ウィジェットのtype、APIフィールド名、すべての検証ルール(国ごとのcountryVariantsを含む)、および依存フィールド/条件付きフィールド(dependsOn/visibleIf)が対象です。フィールド名、Markdownの表、説明文から推測してはいけません。このJSONファイルが、フォーム構築の唯一の信頼できる情報源です。連携2での例外が1つあります:
countryとindustry_typeについては、コードやスラッグではなく、スキーマのフィールドにあるintegration2UrlValueで指定された値(表示用の名前)を送信してください。EMAPの/signupページは名前で照合します。フォーム独自の条件分岐ロジック用に、コード/スラッグはdata-code/data-slug属性に保持してください。
概要
連携2は単一ステップのフォームです。連携1のステップ1(加盟店の基本情報)とまったく同じフィールドだけを収集します。利用規約のチェックボックスはありません。 送信時、すべてのフォームフィールドがURLクエリパラメーターとしてシリアル化され、ブラウザーは{EMAP_BASE_URL}/signup?{params}にリダイレクトします。EMAPはパラメーターを読み取り、フォームに事前入力します。first_name、last_name、company_name、phone、emailがすべて存在する場合、EMAPのページは事前入力されたデータを検証し、問題がなければ加盟店に代わって送信します。その後、加盟店はステップ2に進みます。それ以外の場合は、加盟店がEMAPの事前入力済みフォームを確認して送信します。(EMAPのテストサーバーで確認済みです。詳しくはreferences/mode-2-redirect.mdを参照してください。)
収集するフィールド(Integration 1 の Step 1 と同じ)
| フィールド | 型 | 備考 |
|---|---|---|
first_name | テキスト | 必須 |
last_name | テキスト | 必須 |
email | メールアドレス | 必須 |
phone | 電話番号 | 必須 |
company_name | テキスト | 必須 |
website | URL | 必須 |
country | 選択 | 必須 — 2 文字のコードではなく、国名全体(例:United States)を渡してください |
business_state | 選択 | country = United States の場合のみ必須 |
annual_sales | 数値 | 必須 |
industry_type | 選択 | 必須 — EMAP API(/api/partner/industry-types)から読み込み、値には name を使用してください |
industry_type_other | テキスト | 条件付き — industry_type = Other の場合に表示 |
promo_code | テキスト | 任意 |
利用規約への同意チェックボックスはありません。 追加しないでください — 加盟店は Step 6 の後、EMAP で利用規約に同意します。
手順
-
templates/integration-2/のテンプレートを使用します:plain-html.html:リダイレクト URL を作成して遷移する、クライアント側のフォームです。標準となるリファレンスです。そのまま使用するか、埋め込んでください(Step 0 を参照)。SignupForm.tsx:同じフォームを React コンポーネントにしたものです。あらゆる React フレームワークで使用できます。- その他のコンポーネントフレームワークでは、リファレンスをマウントするか、1 対 1 で移植してください(
references/stack-guide.mdを参照)。リダイレクトはブラウザーでの遷移にしてください。送信されたデータから作成したサーバー側のリダイレクトは使用しないでください。 - Step 1 の自動保存: 名、姓、メールアドレス、電話番号を入力した後、そのいずれかの入力欄から離れると、バックグラウンドで
/api/v1/signup/auto-saveに POST します。Integration 1 と 3 と同じ動作です。EMAP の/signupページでは、リダイレクト経由で訪問したユーザーの自動保存がスキップされるため、詳細を入力しても送信しなかった加盟店の情報を保存するには、この処理が必要です。エラーは表示されません。また、送信時には、実行中の自動保存が完了してからリダイレクトします。references/mode-2-redirect.mdを参照してください。
-
送信時 — リダイレクト URL を作成します:
const params = new URLSearchParams(); params.set('first_name', formData.get('first_name')); params.set('last_name', formData.get('last_name')); params.set('email', formData.get('email')); params.set('phone', formData.get('phone')); params.set('company_name', formData.get('company_name')); params.set('website', formData.get('website')); params.set('country', formData.get('country')); // full name if (formData.get('business_state')) params.set('business_state', formData.get('business_state')); params.set('annual_sales', formData.get('annual_sales')); params.set('industry_type', formData.get('industry_type')); if (formData.get('promo_code')) params.set('promo_code', formData.get('promo_code')); // UTM pass-through (see step 3) // navigateTop() navigates the top-level window (falls back to this frame), so the // handoff also works when the form is embedded in an iframe. navigateTop(EMAP_BASE_URL + '/signup?' + params.toString()); -
UTM パラメーターを引き継ぎます。 現在のページ URL から
utm_campaign、utm_source、utm_medium、utm_term、utm_content、およびクリック ID のgclid、gbraid、wbraid、fbclidを読み取り、リダイレクト URL に追加します。テンプレートではこれが自動的に行われます。Integration 1 と 3 では代わりに API を呼び出すため、テンプレートは同じ値に加えて
fbclidを、Step 1 のPOST /api/v1/signupの本文に追加フィールドとして送信します。getTracking()は ランディングページの URL から値を読み取り、現在のアトリビューションとして使用し、30 日間localStorage(emap_tracking)に保存します。URL に新しいパラメーターがある場合は、保存済みの値を置き換えます(古い値は破棄されます)。URL にパラメーターがない場合は、保存済みの値を再利用するため、ページの再読み込みや後日の直接アクセスでも流入元が保持されます。 値がある場合にのみ送信してください。EMAP は値がない場合は無視します。保存されるのは Step 1 のみです。 EMAP は Step 1 のリクエストからアトリビューションをフラットなキーとして
utm_tracking(ユーザーと申請の両方)に保存します。Step 2 から 6 では送信されず、EMAP はそれらのエンドポイントでこれらのフィールドを無視するため、サインアップの途中で URL が変わっても、保存済みの情報は変更されません。HubSpot の取引とコンタクトのプロパティ(utm_*、gclid、gbraid、wbraid、fbclid)には、Step 1 の値が保持されます。 -
ファイルの
<script>ブロックの先頭にある 2 つの定数を設定します(「EDIT THESE」を検索してください):const EMAP_BASE_URL = 'https://emap.epd.dev'; // EMAP's TEST server const EMAP_PARTNER_KEY = ''; // optional — see Step 2 above環境変数も
.envファイルも、設定が必要なサーバープロセスもありません。公開前に、EMAP_BASE_URLを Easy Pay Direct から提供された本番 URL に切り替えてください。 -
個人識別情報の送信先を把握してください。 リダイレクト URL のクエリ文字列には、加盟店の名前、メールアドレス、電話番号が含まれます。ご自身のページに
Referrer-Policyを設定しても、その URL は保護されません。URL を読み込むのは EMAP のページであるため、そのページが送信するRefererヘッダーに情報が含まれないようにできるのは、EMAP 独自のReferrer-Policyと取り扱いだけです。これはリダイレクトモードの特性であることを開発者に伝えてください。これが許容できない場合は、データを POST 本文で送信する Integration 3 を推奨してください。 -
EMAP のテストサーバーでテストしてください。
- 自動送信フィールドを空欄にする:
phoneを空欄のままにします。EMAP ではフォームが入力済みの状態で表示され、加盟店が送信します。 - すべてのフィールドを入力する:すべてのフィールドに有効なデータを入力します。EMAP が加盟店に代わって送信し、加盟店はステップ 2 に進みます。EMAP のページで値(認識されないウェブサイトなど)が拒否された場合は、代わりに EMAP の入力済みフォームが加盟店に表示されます。詳細は
references/mode-2-redirect.mdを参照してください。 - ゲートを実行します。
python3 .emap/emap_gate.py(検証:スキーマへの準拠 を参照)。その後、フォームの作成が完了したと開発者に伝える前に、検証:セキュリティと網羅性 を実施してください。上記の手動テストは、どちらの検証の代わりにもなりません。ゲートにPASSと表示されるまで、ビルドは完了していません。
- 自動送信フィールドを空欄にする:
Integration 3 の構築:メールアドレスを使ったサインアップ
以下のファイルを作成する前に: このプロジェクトに .emap/emap_gate.py が存在することを確認してください。存在しない場合は、merchant-signup/ やその他のビルド出力がすでに存在していても、先にステップ 1 を実行してください。
続行する前に references/mode-3-api.md をお読みください。
必ず
signup-steps-schema.jsonに基づいて構築してください。 フィールドを作成する前に、signup-steps-schema.jsonでそのフィールドを確認し、ウィジェットのtype、API フィールド名、すべての検証ルール(国ごとのcountryVariantsを含む)、依存フィールドや条件付きフィールド(dependsOn/visibleIf)に厳密に従ってください。フィールド名、Markdown の表、説明文からこれらを推測しないでください。フォームを構築する際の唯一の信頼できる情報源は、この JSON ファイルです。
手順
-
テンプレートを使用します。
templates/integration-3/にあります。plain-html.html:ビルド手順不要のクライアントサイドフォームです。標準の参照実装です。 そのまま使用するか、埋め込んでください(ステップ 0 を参照)。SignupForm.tsx:React フレームワークで使える、同一フォームの React コンポーネントです。- それ以外のコンポーネントフレームワークでは、参照実装をマウントするか、1:1 で移植します
(
references/stack-guide.mdを参照)。 フォームはブラウザーから{EMAP_BASE_URL}/api/v1/signupに直接 POST し、JSON ボディにpartner_key(設定されている場合)とtrigger_email: trueを含めます。このフラグにより、 同じ呼び出しの一部として EMAP がウェルカムメール/確認メールを送信します。フラグがない場合、 アカウントは作成されますが、メールは送信されません。注:会社名の API フィールドはcompany_nameではなくnameです。テンプレートは送信時にすでにこの対応付けを行います (コメントを参照)。フォーム上でのみ使用するフィールド名で、ラベルをわかりやすくするためのものです。 その前に、名前、メールアドレス、電話番号が入力されると、Integration 1 のステップ 1 と同様に、 フォームがバックグラウンドで/api/v1/signup/auto-saveに自動保存します (references/mode-3-api.mdを参照)。
-
ファイルの
<script>ブロック冒頭にある 2 つの定数を設定します(「EDIT THESE」を検索してください)。const EMAP_BASE_URL = 'https://emap.epd.dev'; // EMAP's TEST server const EMAP_PARTNER_KEY = ''; // optional — see Step 2 above環境変数も
.envファイルも、設定が必要なサーバープロセスもありません。公開前にEMAP_BASE_URLを Easy Pay Direct から提供された本番 URL に切り替えてください。 -
クライアント側ですべてのレスポンス形式に対応します(全表は
references/api-errors.mdを参照)。フォームを使うのは加盟店なので、 すべてのメッセージは加盟店に向けた内容にします。{"status":true,"uuid":"..."}→ リンクの送信先アドレスを示して「メールを確認してください」と表示します。uuidは絶対に表示、ログ記録、保存しないでください。これを持つ人は誰でも申請を続行できます。{"verificationLink":true,"url":"..."}(既存ユーザー) → メールアドレスはすでに登録されていることを伝える注記を添えて、 同じ「メールを確認してください」パネルを表示します。urlには移動しないでください。{"status":false,"message":"Company already exists"}→ 申請がすでに存在することと、受信トレイを確認するか Easy Pay Direct に連絡するよう加盟店に伝えます。- HTTP 422 →
response.errorsからフィールドごとのエラーを表示します。 - HTTP 429 → 待ってから再試行するよう加盟店に伝えます。
-
ご自身が管理する一意のメールアドレスで送信し、EMAP のテストサーバーでテストします。 「メールを確認してください」が表示され、メールが届くことを確認します。本番環境では絶対にテストしないでください。
- ゲート
python3 .emap/emap_gate.pyを実行し( 確認:スキーマ準拠 を参照)、開発者にフォームの完成を伝える前に 確認:セキュリティとカバレッジ を行ってください。上記の手動テストは、 どちらの確認の代わりにもなりません。ゲートにPASSと表示されるまで、ビルドは完了ではありません。
- ゲート
確認:スキーマ準拠
プロジェクトのルートからゲートを実行します。追跡対象のフォームに対して、
LLM による読み取りを伴わない決定論的スクリプト
verify_form.py が実行されます。
python3 .emap/emap_gate.py
signup-steps-schema.json と照らし合わせて、フィールドを手作業で抜き取り確認しないでください。
どのエージェントであっても、ゲートに PASS と表示された場合にのみビルドは完了です。
verify/README.md では、修正 → 対象を絞った再実行 → ゲートの再実行(ゲート失敗は最大 5 回)という手順、
5 つのチェックカテゴリそれぞれの対象範囲、さらにスクリプトでは構造上検出できない内容について説明しています。
一部のバグは、スキーマとコードの差分確認だけでは不十分で、EMAP のテストサーバーでの実動テストが必要です。
検証:セキュリティと網羅性
公開前に、references/security-checklist.md に従って確認してください。
簡易セルフチェック — 上記の検証:スキーマへの準拠で確認するのはスキーマへの準拠のみです。デプロイや設定の配線は確認しないため、これらの項目は verify_form.py の実行で問題がないことを確認するだけでなく、手動で確認する必要があります:
- サイトで HTTPS を使用している。
-
EMAP_BASE_URLが、テストサーバーhttps://emap.epd.devではなく、Easy Pay Direct から指定された本番 URL を指している。 -
EMAP_PARTNER_KEYがデプロイされたファイルに直接設定されており、アトリビューションを意図的に省略する場合を除き、プレースホルダーの''のままになっていない。クライアント側で値が見えることを許容する旨を開発者に確認済みである(上記のステップ 2 を参照)。本人に断りなく判断しないでください。 - インテグレーション 3:成功パネルに申請の
uuidが表示されず、インテグレーション 1 独自の再開用localStorageキーを除き、どのフォームにも保存されていない。 - 二重送信を防ぐため、最初のクリック時に送信ボタンが無効になる。
- ユーザー向けのエラーメッセージが汎用的であり、EMAP の生のエラーメッセージをそのまま加盟店に表示していない。
- 埋め込みのドロップダウン用フォールバックデータが、デプロイされたファイルに実際に含まれている。ビルド処理で取り除かれていないことを確認してください。実際にデプロイされたファイルに
EMAP_DROPDOWN_FALLBACKS(またはテンプレート内の同等の定数)が残っていること、またフォールバックの処理経路が実際に利用可能であることを確認してください(たとえば、一時的にEMAP_BASE_URLを到達不能なホストに設定してテストします)。定義されているだけで一度も呼び出されない状態では不十分です。 - サーバー側のコードから EMAP を呼び出していない。ルートハンドラー、API ルート、Server Actions、ローダーとアクション、SSR のデータ取得、コントローラー、
curl/wp_remote_post、サーバーレス関数を確認してください(references/stack-guide.mdに、フレームワークごとの項目が記載されています)。該当するものがあれば、インテグレーションの構築方法が誤っています。EMAP の IP ベースのレート制限で加盟店の IP が認識されるよう、EMAP へのすべてのリクエストは加盟店のブラウザーから送信する必要があります。
ガードレール
どのスタックでもかまいませんが、設計上、EMAP の呼び出しはすべて加盟店のブラウザーから行います。 どのような形であっても、EMAP へのリクエストをパートナーのサーバー経由にしないでください。
- API ルート、Server Action、ローダーまたはアクション
- SSR のデータ取得
- コントローラー、
curl/wp_remote_post、サーバーレス関数
EMAP は呼び出し元の IP に基づいてサインアップをレート制限しているため、サーバー側のプロキシを使うと、すべての加盟店が同じ IP を経由することになります。EMAP の API は CORS を通じてブラウザーから呼び出せます。そのため、パートナーキーの定数はデプロイされたページのソースに表示されます。これは、開発者に確認済みの許容されるトレードオフ(上記のステップ 2 を参照)であり、バックエンドプロキシを追加して「修正」すべき見落としではありません。 ただし、パートナーキーをサードパーティの分析ツールやエラーツールに出力または記録してはならず、実際のキーの値を公開リポジトリにコミットしてもいけません。「デプロイされたページで見える」ことと「Git の履歴やログ集約ツールで見える」ことは、露出する場所が異なり、ここで許容されるのは前者のみです。 個人情報(氏名、メールアドレス、電話番号)は、どこにも保存しないでください。EMAP が記録管理の主体であり、このフォーム専用のバックエンドには保存先がありません。 ドキュメントのアップロード欄は追加しないでください。EMAP はステップ 6 の後、独自の UI でドキュメントを収集します。 フィールドの検証ルールは変更しないでください。このフォームで送信を許可した内容でも、EMAP 独自のサーバー側ルールを満たさなければ、EMAP は送信を拒否します。