原生行動應用程式指南:多商業帳戶存取與 OAuth 流程
ReceiptRoller 常有 1 位使用者所屬於多個商業帳戶的情況。若想在原生行動應用程式(Android / iOS)實作「商業帳戶切換」UI,使用本指南的模式,只需登入 1 次即可存取所有商業帳戶的資料。
運作機制
組合 3 個機制。
- user-scoped 存取權杖 — 權杖不關聯至特定的商業帳戶(
AuthorizedOrganizationId = null,僅設定AuthorizedUserId) GET /api/v1/me/organizations— 回傳使用者所屬的商業帳戶一覽- 在各 API 加上
?organizationId=— 伺服器端每次執行成員資格驗證
藉此,使用者只需進行 1 次 Web 瀏覽器基礎的 OAuth 登入,即可切換參照、操作所屬的所有商業帳戶。
應用程式登錄時的設定
要使用此模式,請在應用程式登錄時將 「授權時將權杖關聯至 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指定自訂 scheme(例:com.example.rrstore://oauth/callback)或 HTTPS 的 App Links / Universal Links
- 產生 PKCE 參數
code_verifier: 43~128 字元的隨機字串code_challenge:BASE64URL(SHA256(code_verifier))state: CSRF 對策的隨機值
- 以系統瀏覽器開啟
/oauth/authorizehttps://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。
相關文章
-
店鋪資訊 API(Store Information API)的使用方式說明如何以 REST API 取得、更新店鋪的基本資訊(店鋪名稱、店鋪類別、聯絡方式、地址)。可從員工應用程式等以權杖驗證的用戶端實作店鋪資訊的編輯畫面。
-
交易一覽 API(Transactions API:POS+OMS 整合摘要)的使用方式ReceiptRoller 的 Transactions API 是將收銀營業額(PosTransactions)與銷售管理訂單(OmsOrders)整合為單一摘要的唯讀 API。在 Android/iOS 應用程式中一覽顯示「整個商業帳戶的交易」時,即為入口。
-
無法取得權杖(驗證錯誤)無法取得存取權杖時的原因釐清。解說 invalid_client、invalid_grant、redirect_uri_mismatch 等代表性錯誤與因應。
-
營業額實績 API(Sales API)的使用方式彙整 ReceiptRoller 營業額實績 API(/api/v1/sales/*)的概要,以及依商業帳戶、店鋪、POS 端末、期間的篩選方法。這是一份涵蓋實際回應結構(KpiValue 巢狀型)與 averageTicket、權杖有效期限(8 小時)在內,為在 Android/iOS 行動應用程式或伺服器串接實作營業額儀表板的入門指南。
-
銷售管理 API(Orders / OMS API)的使用方式這是使用 ReceiptRoller 銷售管理 API(/api/v1/orders)對商業帳戶底下的訂單進行 CRUD 操作的指南。彙整訂單的建立、更新、狀態轉移(確認、處理中、取消)、刪除,以及適用於 Android/iOS 應用程式或伺服器串接的流程。