ネイティブモバイルアプリ向けガイド: 複数ビジネスアカウントアクセスと OAuth フロー
レシートローラーには 1 人のユーザーが複数のビジネスアカウントに所属するケースが頻繁にあります。ネイティブモバイルアプリ (Android / iOS) で「ビジネスアカウント切替」UI を実現したい場合、本ガイドのパターンを使うとログインを 1 回だけで全ビジネスアカウントのデータにアクセスできます。
仕組み
3 つの仕組みを組み合わせます。
- user-scoped アクセストークン — トークンが特定のビジネスアカウントに紐付かない (
AuthorizedOrganizationId = null,AuthorizedUserIdのみ設定) GET /api/v1/me/organizations— ユーザーが所属するビジネスアカウント一覧を返す- 各 API に
?organizationId=を付与 — サーバー側でメンバーシップ検証を毎回実施
これにより、ユーザーは Web ブラウザベースの OAuth ログインを 1 回行うだけで、所属する全てのビジネスアカウントを切り替えながら参照・操作できます。
アプリ登録時の設定
このパターンを使うには、アプリ登録時に 「認可時に1つのビジネスアカウントへトークンを紐付ける」のチェックをオフにしてください。デベロッパーダッシュボードのアプリ作成画面で設定できます。
- オンのまま (デフォルト) → 認可画面でビジネスアカウントの選択ダイアログが表示され、トークンが選択した 1 つのアカウントに紐付く (通常の Web アプリ・サーバー連携向け)
- オフ → 認可画面でアカウント選択は出ず、user-scoped トークンが発行される (本ガイドの対象)
アプリ一覧の詳細画面では、フラグがオフのアプリには「複数アカウント対応」バッジが表示されます。
OAuth 認可フロー (PKCE + Chrome Custom Tabs / ASWebAuthenticationSession)
RFC 8252 (OAuth 2.0 for Native Apps) と OAuth 2.1 が推奨する形式に従います。アプリ内 WebView は避け、システムブラウザコンポーネントを利用してください。
- Android:
androidx.browser.customtabs.CustomTabsIntent - iOS:
AuthenticationServices.ASWebAuthenticationSession
手順
- OAuth クライアントを登録
- デベロッパーダッシュボードから手動作成: アプリを新規作成する
- 「認可時に1つのビジネスアカウントへトークンを紐付ける」のチェックをオフにする
redirect_uriはカスタムスキーマ (例:com.example.rrstore://oauth/callback) または HTTPS の App Links / Universal Links を指定
- PKCE パラメーターを生成
code_verifier: 43〜128 文字のランダム文字列code_challenge:BASE64URL(SHA256(code_verifier))state: CSRF 対策のランダム値
- システムブラウザで
/oauth/authorizeを開くhttps://receiptroller.io/oauth/authorize ?response_type=code &client_id={your_client_id} &redirect_uri={your_redirect_uri} &scope=sales.read sns.content.read sns.audience.read analytics.read store.customers.read store.flyers.read &state={state} &code_challenge={code_challenge} &code_challenge_method=S256ユーザーは未ログインなら
/Identity/Account/Loginに誘導され、ログイン後に同意画面 (要求されたスコープのみ表示・ビジネスアカウント選択はなし) を経由してアプリのredirect_uriに戻ります。注: 「認可時に1つのビジネスアカウントへトークンを紐付ける」をオフにしていない場合、ここで「どのビジネスアカウントにアクセスするか」を選ぶプルダウンが追加で表示されます。複数アカウント対応にするには必ずオフにしてください。
- 認可コードをアクセストークンに交換
POST https://receiptroller.io/api/v1/auth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code={code_from_redirect} &redirect_uri={your_redirect_uri} &client_id={your_client_id} &client_secret={your_client_secret} &code_verifier={code_verifier}レスポンスは RFC 6749 §5.1 準拠の snake_case JSON です。
{ "access_token": "...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 3600, "scope": "sales.read sns.content.read sns.audience.read analytics.read store.customers.read store.flyers.read" }
ユーザーが所属するビジネスアカウントを取得
アクセストークンを使ってビジネスアカウント一覧を取得します。
GET https://receiptroller.io/api/v1/me/organizations
Authorization: Bearer {access_token}
レスポンス:
{
"organizations": [
{ "id": "9f0dfdc3-77ac-4bf7-9c9e-a5d1285edd88", "name": "AB", "role": "SuperUser,StoreStaff" },
{ "id": "55a5b9bf-287c-4618-a973-66b67004f292", "name": "モスフードサービス", "role": "Admin" }
]
}
このレスポンスをアプリ内で「ビジネスアカウント切替」UI として表示します。ユーザーが選択したビジネスアカウントの id をローカルに保持し、以降の API 呼び出しで使います。
各 API に ?organizationId= を渡す
選択中のビジネスアカウント ID をクエリパラメーターとして毎回付与します。サーバー側で「呼び出し元ユーザーがそのビジネスアカウントのメンバーか」を毎回検証します (cross-org データリークの防止)。
GET https://receiptroller.io/api/v1/sales/kpis?organizationId={selected_org_id}&storeId={store_id}
Authorization: Bearer {access_token}
複数ビジネスアカウントアクセスに対応している API 一覧
2026 年 5 月時点で以下のエンドポイント群が user-scoped トークンと ?organizationId= パターンに対応しています。
共通
GET /api/v1/me/organizations— 所属するビジネスアカウント一覧
売上 / 分析
GET /api/v1/sales/kpisGET /api/v1/sales/trendGET /api/v1/sales/adviceGET /api/v1/sales/forecastGET /api/v1/sales/products— 商品 + ABC 分析GET /api/v1/sales/visitors— 来店客分析GET /api/v1/sales/churn— 離脱リスクスコアGET /api/v1/sales/recommendations— 商品レコメンデーションGET /api/v1/sales/multi-trend— 複数店舗比較
SNS
GET /api/v1/sns/accounts//accounts/{id}GET /api/v1/sns/posts//posts/{id}//posts/{id}/analyticsGET /api/v1/sns/comments
顧客 (CRM)
GET /api/v1/store/customers//customers/{id}POST /api/v1/store/customers(upsert)DELETE /api/v1/store/customers/{id}GET /api/v1/store/customers/{id}/purchases
チラシ
GET /api/v1/organizations/{orgId}/stores/{storeId}/flyersGET /api/v1/organizations/{orgId}/stores/{storeId}/flyers/{flyerId}POST/PUT/DELETE/ publish / unpublish / expire
チラシ系は URL パスに {orgId} が含まれるため ?organizationId= の付与は不要です。サーバー側で「URL パスの orgId にユーザーがアクセス可能か」を毎回検証します。
サンプル: Android で売上 KPI を取得
// 1. ログイン後 → /api/v1/me/organizations でビジネスアカウント一覧を取得
val orgs = api.getMyOrganizations() // Authorization: Bearer ... が付与される
showOrgSwitcher(orgs)
// 2. ユーザーがビジネスアカウントを選択
val selectedOrgId = "55a5b9bf-287c-4618-a973-66b67004f292"
// 3. 売上 KPI を取得
val kpis = api.getSalesKpis(organizationId = selectedOrgId, storeId = "abc123")
showDashboard(kpis)
// 4. ビジネスアカウント切替時はトークン再取得は不要 — selectedOrgId を変えて再リクエストするだけ
val newOrgId = "9f0dfdc3-77ac-4bf7-9c9e-a5d1285edd88"
val newKpis = api.getSalesKpis(organizationId = newOrgId, storeId = "xyz789")
セキュリティ考慮事項
- client_secret の取り扱い — Public client (モバイルアプリ単体) の場合、client_secret はアプリバイナリに含めず PKCE のみで認証する
token_endpoint_auth_method=none構成が推奨です。 - トークンの保存先 — Android Keystore / iOS Keychain など OS の安全領域を利用してください。
- 有効期限 — access_token は 1 時間、refresh_token は 30 日です。refresh_token は使い捨て (ローテーション方式) で発行されます。
- スコープ最小化 — 必要なスコープだけ要求してください。OAuth スコープ一覧
- サーバー側のメンバーシップ検証 — ユーザーが所属しないビジネスアカウントの
organizationIdを指定した場合、API は 403 Forbidden を返します。
関連記事
-
トークンが取得できない(認証エラー)アクセストークンが取得できないときの原因切り分け。invalid_client、invalid_grant、redirect_uri_mismatch などの代表的エラーと対処を解説します。
-
リダイレクトURLの設定レシートローラーの開発者ポータルで設定するリダイレクトURI(コールバックURL)の役割、登録ルール、開発・本番環境の使い分け、よくあるエラーと対処方法を解説します。
-
アクセストークンの取得と更新レシートローラーAPIで使うアクセストークンとリフレッシュトークンの取得方法、有効期限、更新手順、エラー時の対処を解説します。
-
開発者向けヘルプ目次レシートローラー開発者向けヘルプ目次です。開発者申請、アプリケーション登録、OAuth認証とスコープ、実装ガイド(ウォレットアプリ・店舗向けWebhook・Survey API)、データ領域別ガイド、運用とセキュリティ、コミュニティ、トラブルシューティングまでをまとめています。