Repository navigation
Getting Started
처음 붙이는 사람을 위한 최단 경로입니다. 로그인 → 이벤트 한 건 전송까지가 목표입니다.
앱은 SDK 하나만 알면 됩니다. SDK가 역할별 서비스로 나눠 보내고, 마켓·AdMob·백오피스는 서버 쪽에서 붙습니다.
flowchart LR
subgraph Client["앱"]
SDK["Platform SDK<br/>TypeScript 또는 GDScript"]
end
subgraph Roles["Cloud Run — asia-northeast3"]
API["platform-api<br/>세션 · 설정 · 계정 연결"]
IAP["platform-iap<br/>결제 검증 · 마켓 자격증명 격리"]
INGEST["platform-ingest<br/>이벤트 · presence 토큰"]
ADS["platform-ads<br/>광고 정책 · reward claim"]
ADMIN["platform-admin<br/>운영 API — Google OIDC 전용"]
WORKER["platform-worker<br/>마켓 완료 재시도"]
end
FS[("Firestore<br/>사용자 매핑 · IAP 원장 · 레지스트리")]
MARKET["Google Play · App Store · AppsInToss"]
ADMOB["AdMob SSV"]
EDGE["presence edge — RPI"]
BO["Backoffice"]
SDK --> API
SDK --> IAP
SDK --> INGEST
SDK --> ADS
SDK -.->|opt-in heartbeat| EDGE
API --> FS
IAP --> FS
INGEST --> FS
ADS --> FS
WORKER --> FS
ADMIN --> FS
IAP -->|구매 확인| MARKET
MARKET -->|환불 웹훅| IAP
ADMOB -->|SSV 콜백| ADS
BO -->|OIDC| ADMIN
| 항목 | 필요한 것 |
|---|---|
appId |
레지스트리에 등록된 앱 ID. 없으면 등록 PR부터 |
| 인증 수단 | 앱별 Firebase Auth의 ID 토큰, 또는 AppsInToss appLogin() 결과, 또는 익명 키 |
| 런타임 | TypeScript: Node 20+ / React Native / Web (fetch 전역) · GDScript: Godot 4.3 |
appId가 서버에 없으면 모든 호출이 app_unknown으로 거부됩니다. 자격증명은
X-Seori-App 헤더가 아니라 토큰의 aud 검증이 담당하므로, 다른 앱의 토큰으로는
이 앱의 세션을 열 수 없습니다.
역할별 Cloud Run 서비스입니다. 전부 asia-northeast3이고 Secret이 아닙니다.
| 역할 | URL | 담당 |
|---|---|---|
api |
https://platform-api-306278488979.asia-northeast3.run.app |
세션, 설정, 계정 연결, 콘텐츠 |
iap |
https://platform-iap-306278488979.asia-northeast3.run.app |
결제 검증, entitlement, AppsInToss 세션 |
ingest |
https://platform-ingest-306278488979.asia-northeast3.run.app |
이벤트 수집, presence 토큰 |
ads |
https://platform-ads-306278488979.asia-northeast3.run.app |
광고 정책, reward claim |
admin |
— | 백오피스 전용. Google OIDC 없이는 403 |
역할별 URL을 생략하면 SDK가 baseUrl로 대신 보냅니다. 동작은 하지만 결제
트래픽이 api 서비스로 가므로 production 앱은 넷 다 지정하세요.
세션 발급은 기본 api입니다. AppsInToss 로그인(ait-login)은 Toss mTLS 자격증명이
격리된 iap 또는 ads 역할에서만 됩니다 — TypeScript는 adsBaseUrl을 지정하면
세션 요청이 그리로 가고, GDScript는 auth_base_url로 따로 지정합니다.
살아 있는지 확인:
curl -s https://platform-api-306278488979.asia-northeast3.run.app/health/livepnpm add https://github.com/seorilabs/platform/releases/download/v0.6.7/seorilabs-platform-sdk-0.4.0.tgzimport { createPlatform } from "@seorilabs/platform-sdk";
const platform = createPlatform({
baseUrl: "https://platform-api-306278488979.asia-northeast3.run.app",
iapBaseUrl: "https://platform-iap-306278488979.asia-northeast3.run.app",
ingestBaseUrl: "https://platform-ingest-306278488979.asia-northeast3.run.app",
adsBaseUrl: "https://platform-ads-306278488979.asia-northeast3.run.app",
appId: "my-app",
eventContext: { platform: "android", appVersion: "1.0.0", locale: "ko-KR" },
});
await platform.signIn({ kind: "firebase-id-token", value: firebaseIdToken });
platform.events.track({ name: "onboarding_complete" });
await platform.events.flush();세션이 열리면 platform.session.current()에 platformUserId와 CS용
supportCode가 들어 있습니다. 나머지는 TypeScript SDK.
V=0.6.7; B=https://github.com/seorilabs/platform/releases/download/v$V
curl -fsSLO "$B/seorilabs-platform-gdscript-$V.tar.gz"
curl -fsSLO "$B/seorilabs-platform-gdscript-$V.tar.gz.sha256"
sha256sum -c "seorilabs-platform-gdscript-$V.tar.gz.sha256"
tar -xzf "seorilabs-platform-gdscript-$V.tar.gz" -C addons/var platform := SeoriPlatformClient.new()
platform.configure({
"base_url": "https://platform-api-306278488979.asia-northeast3.run.app",
"iap_base_url": "https://platform-iap-306278488979.asia-northeast3.run.app",
"ingest_base_url": "https://platform-ingest-306278488979.asia-northeast3.run.app",
"ads_base_url": "https://platform-ads-306278488979.asia-northeast3.run.app",
"app_id": "my-app",
"event_context": {"platform": "android", "appVersion": "1.0.0", "locale": "ko-KR"},
})
add_child(platform) # HTTPRequest가 동작하려면 트리에 있어야 한다
platform.sign_in({"kind": "firebase-id-token", "value": firebase_id_token},
func(res): if res["ok"]: platform.track("onboarding_complete"))콜백은 항상 {ok, result, code, message, local} Dictionary 하나를 받습니다.
나머지는 GDScript SDK.
세션이 열렸는지, 이벤트가 나갔는지는 클라이언트에서 바로 볼 수 있습니다.
| 확인 | TypeScript | GDScript |
|---|---|---|
| 세션 | await platform.session.current() |
platform.current_session() |
| 남은 이벤트 |
await platform.events.flush() 후 outbox 크기 |
platform.pending_event_count() |
| 설정 | await platform.config.fetch({...}) |
platform.fetch_config({...}, cb) |
서버 쪽 반영(원장, 백오피스 화면)은 메인테이너가 확인합니다.
- 결제를 붙이려면 레지스트리에
features.iap와entitlement_ids가 있어야 합니다. → App Registration - 오류 코드로 UI를 분기합니다.
message는 언제든 바뀝니다. → Error Codes - 이벤트 이름은
spec/events.md의 3층 구조를 따릅니다.seori_*는 SDK 예약이라 앱이 보낼 수 없습니다. - 레퍼런스 배선: React Native · Godot
구현과 API 계약의 정본은 seorilabs/platform 저장소입니다.