> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 独自の連携ポップアップの構築

> ダイレクトモード(Direct Mode)— 貴社自身のボタンから、貴社が開いて監視するポップアップで、ネットワークを 1 つずつ連携します。

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["business"]} maxPackRequired={true} />

ダイレクトモード(Direct Mode)は、貴社自身のダッシュボードのボタンから、**ソーシャルネットワークを
一度に 1 つ**連携します。そのネットワーク用のリンクセッションを作成し、返された URL をポップアップで
開くと、何が起きたかを貴社のページが受け取ります。ユーザーがすべてのネットワークを列挙したページを
目にすることはなく、貴社のアプリを離れる時間はネットワーク自身のログインにかかる時間だけです。

## どのサーフェスを使うべきか

形は 3 つあり、最初の 2 つは同じ統合です。このページは、貴社が自分で構築するものです。

| サーフェス                                                                                                  | ユーザーに見えるもの                                                  | ホワイトラベル度                                    | 選ぶべき場面                                                          |
| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------- |
| **ウィジェット — 埋め込みフレーム**([mount](/docs/multiple-users/connect-widget#mount-a-slot))                            | 貴社レイアウト内にインラインで表示される当社のボタン。ネットワーク自身のポップアップまで、ポップアップは表示されません | **最も強力。** 貴社のページ、貴社のフォントと配色で、ユーザーはページを離れません | ネットワークごとの行を持つダッシュボードがあり、その場で連携を完結させたい場合。Max Pack が必要です。         |
| **ウィジェット — 独自のボタン**([popup](/docs/multiple-users/connect-widget#your-own-button))                           | 貴社のボタン、その後ネットワーク用のポップアップが 1 つ                               | **強力。** ポップアップは当社のものですが、短時間で、貴社の外観を継承します    | 独自のボタンとスタイルを使い、レイアウトにフレームを置きたくない場合。Max Pack が必要です。              |
| **ホスト型リンクページ**([使い方](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | 当社がホストするページ。貴社のロゴ、カラー、カスタム CSS が反映されます                      | **最も弱い。** 当社のページであり、ユーザーは貴社のページを離れて使用します    | 配布するリンクを 1 つ用意したい場合や、メールでオンボーディングする場合。構築するものはなく、Max Pack も不要です。 |

<Note>
  **ダイレクトモードは、当社のスクリプトなしで実現する 3 行目のポップアップです。** ページが
  スクリプトを読み込めるなら、[ウィジェット](/docs/multiple-users/connect-widget)がこのページの内容を
  すべて代わりに行います — ポップアップを自分で開いて監視し、さらにフレームの埋め込みもできます。
  ダイレクトモードは、ページがサードパーティのスクリプトを読み込めない場合や、サーフェスがブラウザ
  ではなくネイティブアプリの場合に選ぶものです。
</Note>

<Frame caption="貴社のボタン、貴社のページ。ユーザーが目にする当社のものはポップアップだけで、それもネットワークが必要とする間だけです。">
  <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=b68021d477703d97a5994ee85d299be6" alt="A customer dashboard with its own Connect buttons and an Ayrshare popup showing the Facebook hand-off screen" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
</Frame>

## 構築するもの

ステップは 4 つです。最初の 1 つはサーバー上で、残りはページ内で行います。

<Steps>
  <Step title="1 つのネットワーク用のセッションを作成する">
    バックエンドから、`mode: "connect"`、`network`、そして貴社のページが動作する `origin` を指定して
    [Link Session の作成](/docs/apis/profiles/create-link-session)を呼び出します。

    ```javascript Your backend theme={"system"}
    const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`,
        "Profile-Key": profileKey,
      },
      body: JSON.stringify({
        mode: "connect",
        network: "reddit",
        origin: "https://app.example.com",
      }),
    });

    const { url } = await response.json();
    ```

    単一ネットワークの connect ページを指す `url` が返され、`token` は返されません — トークンは
    URL の中にあります。URL 全体をパスワードと同様に扱ってください: ユーザーを User Profile に
    サインインさせるものです。

    <Warning>
      セッションは必ずサーバー上で作成し、ブラウザ内では作成しないでください。この呼び出しには
      API Key が必要です。
    </Warning>
  </Step>

  <Step title="クリックハンドラー内で同期的に開く">
    ポップアップは、**クリックハンドラーそのものの中で** `window.open` によって開く必要があります。
    ブラウザは、ユーザーのクリックを処理している間しかポップアップを許可せず、その許可は `await` を
    越えて存続しません — そのため、URL を先に fetch してコールバックで開く方法は、確実にポップアップ
    ブロックに引っかかります。

    URL はボタンをレンダリングするとき、またはユーザーがホバーしたときに fetch してください。クリック
    の時点では、すでに手元にあるはずです。

    ```javascript Your page theme={"system"}
    // `url` was fetched earlier. Nothing async between the click and window.open.
    button.addEventListener("click", () => {
      const popup = window.open(url, "ayrshare-connect", "width=600,height=800");
      if (!popup) {
        showError("Allow popups for this site to connect an account.");
        return;
      }
      listenForOutcome(popup, url);
    });
    ```
  </Step>

  <Step title="結果をリッスンする">
    `origin` を渡したため、ポップアップは中で起きたことをイベントとして貴社のページに post します:
    `connect:success`、`connect:error`、`connect:cancelled`、およびその間の進捗イベントです。
    接続ごとに、この 3 つのうちちょうど 1 つが届きます。

    [リンク完了イベント](/docs/multiple-users/link-completion-events)に完全なイベント一覧とコピー&ペースト
    できるリスナーがあります — 上記の `listenForOutcome` はそのスニペットです。抜けやすい部分が
    2 つあり、どちらも実際のバグの原因になります:

    * **`event.origin` をチェックする** — 開いた URL のオリジンと照合します。どのページでも貴社の
      ウィンドウにメッセージを post でき、偽装できないのはメッセージのうちオリジンだけです。
    * **`popup.closed` をポーリングする** — 何かを結論づける前に短い猶予時間を置いてください。
      ユーザーが手動で閉じたポップアップは何も送信せず、猶予時間がないと成功した接続が
      キャンセルとして報告されることがあります。
  </Step>

  <Step title="それぞれの結末を処理する">
    | 結末                  | 意味                                          | 対処                                                                            |
    | ------------------- | ------------------------------------------- | ----------------------------------------------------------------------------- |
    | `connect:success`   | アカウントが接続され、**保存された**                        | その行を更新します。直後の [`GET /user`](/docs/apis/user/profile-details) にはすでに反映されています。        |
    | `connect:error`     | 接続が失敗した                                     | `message` を表示します。`code` は失敗にカタログ化されたコードがある場合に存在し、ない場合は省略されます。                 |
    | `connect:cancelled` | ユーザーが途中でやめた、またはウィンドウを閉じた                    | 行はそのままにします。`reason` は `popupClosed`、`scopesDenied`、`userCancelled` のいずれかです。   |
    | 何も届かない              | リンクが期限切れ、失効済み、または不明で、ページがイベントの送信先を知る前に拒否された | それらの状態を確定的に報告する [Link Session の取得](/docs/apis/profiles/get-link-session)をポーリングします。 |
  </Step>
</Steps>

<Tip>
  構築中は URL に `&autoClose=false` を追加してください。ポップアップはどの結末の後も自動的に
  閉じずに開いたままになるので、表示内容を読むことができます。
</Tip>

## ネットワーク別の注意点

ほとんどのネットワークは、ポップアップ 1 つでそれ以外に何もありません: ユーザーがクリックし、
ネットワーク側で認可すると、ポップアップが閉じます。構築前に知っておく価値がある例外は以下のとおり
です。

<AccordionGroup>
  <Accordion title="X には貴社自身の API キーが必要">
    ダイレクトモードの X は、**貴社の** X Developer App の認証情報を使用します。セッション作成時に
    [Link Session の作成](/docs/apis/profiles/create-link-session)の `X-Twitter-OAuth1-Api-Key` および
    `X-Twitter-OAuth1-Api-Secret` ヘッダーとして指定します。

    これらのヘッダー**なし**で `twitter` または `x` 向けに作成されたセッションは、ポップアップが
    開いた時点で拒否されます: ユーザーには接続を利用できない旨が表示されてフォームは表示されず、
    貴社のページは `message` 付きで **`code` のない** `connect:error` を受け取ります。これは意図的な
    ものです。欠けている認証情報はユーザーのものではなく貴社のものであり、エンドユーザーに貴社の
    API キーの入力を求めてはならないからです。

    対照的なのが Bluesky で、アプリパスワードはエンドユーザー自身の認証情報です — こちらは connect
    ページがポップアップ内のフォームで収集します。
  </Accordion>

  <Accordion title="Facebook は Meta のログインの前にボタンを 1 つ表示する">
    `network: "facebook"` はポップアップ内にボタンを 1 つ表示し、そのクリックから Meta 自身の
    ログインが開きます — Meta は、その SDK をホストするページ内のクリックからログインが開始される
    ことを要求しています。ユーザーのクリックは 1 回ではなく 2 回になりますが、それ以外に違いは
    ありません。

    Instagram を **Facebook Page 経由**でリンクする場合 — つまりセッションが
    `instagramLinkMethod: "facebook"` を持つ場合や、アカウントの
    [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) 設定がそのフローを選択
    している場合 — も同じ挙動になります。直接の Instagram Login では追加のボタンはありません。
  </Accordion>

  <Accordion title="Bluesky と Telegram はリダイレクトではなくページコンテンツを表示する">
    どちらもユーザーをネットワークのログインに送りません。代わりにポップアップがコンテンツを
    レンダリングします: Bluesky はハンドルとアプリパスワードのフォーム、Telegram は使用するコード
    です。結果のイベントはどちらの場合も同じです。

    X はこのグループには入りません。セッションに貴社のキーがあればユーザーに何も求めずに完了し、
    なければ拒否されます — 上記を参照してください。
  </Accordion>

  <Accordion title="Telegram は帯域外で完了する">
    Telegram はどこにもリダイレクトせずにコードを表示し、接続はユーザーがそのコードを使用した時点 —
    ポップアップが消えた後 — で完了します。待つべきブラウザイベントがないため、
    [Link Session の取得](/docs/apis/profiles/get-link-session)をポーリングして `completedNetworks` を
    監視してください。
  </Accordion>

  <Accordion title="Facebook Groups はこの方法では接続できない">
    Facebook Groups はリンク対象ではないため、`network: "fbg"` はセッション作成時に `code: 508` を
    返します。

    WhatsApp はダイレクトモードで**利用できます**。ポップアップ内で Meta の Embedded Signup が
    開き、結果のイベントは他のネットワークと同じです。
  </Accordion>
</AccordionGroup>

<h2 id="native-apps">
  ネイティブアプリ
</h2>

ネイティブアプリは同じ `url` を**システムブラウザ**で開き、
[Link Session の取得](/docs/apis/profiles/get-link-session)をポーリングして結果を知ります。`origin` には
カスタムスキーム(`myapp://connected`)を設定し、ページがアプリに戻る手段を確保してください。カスタム
スキームはイベントを受信できません。post する先のブラウザウィンドウが存在しないためです。

* **iOS** — `ASWebAuthenticationSession`、または `SFSafariViewController`。
* **Android** — Chrome Custom Tabs。

<Warning>
  **リンク用 URL を埋め込み webview(`WKWebView`、`UIWebView`、Android の `WebView`)で開いては
  いけません。** ソーシャルネットワークは webview 内での認証を拒否します: Google はサインインを
  `disallowed_useragent` で拒否し、Meta は完全にブロックします。ユーザーには当社のものではなく
  ネットワーク自身のエラーページが表示され、貴社側で変更できるものでは解決できません。上記の
  システムブラウザコンポーネントはまさにこの理由のために存在し、ユーザーをアプリ内に留めます。
</Warning>

## ダイレクトモードに必要なもの

<ul className="custom-bullets">
  <li>
    **[Max Pack](/docs/additional/maxpack)**。Max Pack なしで connect モードのセッションを作成すると、
    リクエストの他の内容にかかわらず `code: 504` が返されます。
  </li>

  <li>
    すべてのセッションに **`origin`**。ホワイトリストも登録手順もありません — 呼び出しごとに送信
    します。省略すると `code: 505` が返されます。`https` オリジン、カスタムスキーム、
    `http://localhost` のいずれでもない値は `code: 506` を返します。
  </li>

  <li>
    アカウントで有効になっている **`network`**。認識されない名前は `code: 508` を返します。認識
    されるがアカウントで有効になっていないものは `code: 509` を返し、これは
    [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) ページで
    修正できます。
  </li>

  <li>
    `allowedSocial` は**指定しないこと**。`network` と組み合わせることはできません(`code: 507`)—
    単一ネットワークのセッションは、それ自体がすでにホワイトリストだからです。
  </li>
</ul>

これらはすべて、API が返すメッセージとともに
[Link Session エラー](/docs/errors/errors-ayrshare#link-session-errors)リファレンスに記載されています。

## 次のステップ

<Card title="リンク完了イベント" icon="tower-broadcast" href="/docs/multiple-users/link-completion-events" horizontal>
  ポップアップが送信するすべてのイベントと、それらを受信するリスナー。
</Card>

## 関連情報

<Card title="Link Session の作成" icon="link" href="/docs/apis/profiles/create-link-session#connect-mode" horizontal>
  `mode`、`origin`、`network` パラメータとレスポンスの形。
</Card>

<Card title="Link Session の取得" icon="magnifying-glass" href="/docs/apis/profiles/get-link-session" horizontal>
  ポップアップを使えない場合の、完了のポーリング。
</Card>
