billing
billing 카테고리의 API 작업 6개를 다룹니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /v1/hooks/polar | Polar 결제 웹훅 (docs/billing.md) |
| GET | /v1/orgs/{orgSlug}/billing | 조직의 요금제와 결제 상태 (docs/billing.md) |
| POST | /v1/orgs/{orgSlug}/billing/checkout | Pro 요금제용 체크아웃 세션을 생성합니다 |
| POST | /v1/orgs/{orgSlug}/billing/portal | 고객 포털 세션을 생성합니다(결제 수단, 영수증, 해지) |
| POST | /v1/orgs/{orgSlug}/billing/session | 고객 세션 토큰(결제 수단 임베드를 인증합니다) |
| POST | /v1/orgs/{orgSlug}/billing/cancel | 기간 종료 시점의 해지를 예약하거나 취소합니다 |
POST /v1/hooks/polar
이 경로는 사용자가 호출하는 경로가 아닙니다. Polar가 이곳으로 구독 이벤트를 전달합니다. 인증은 토큰이 아니라 Standard Webhooks 서명입니다 — webhook-id, webhook-timestamp, webhook-signature 세 헤더와 서버의 RUNLOT_POLAR_WEBHOOK_SECRET을 함께 사용해 검증합니다(그렇지 않으면 403, 타임스탬프는 ±5분 이내여야 합니다). 같은 webhook-id는 한 번만 적용됩니다(재전송은 200을 반환하고 무시됩니다).
이곳이 orgs.plan을 바꾸는 유일한 자리입니다: subscription.active와 updated의 경우 상태 active/trialing/past_due는 pro로, 그 외(canceled, unpaid 등)는 전부 free로 매핑됩니다. 서버에 토큰이 없으면 501을 반환합니다. 클라이언트 라이브러리는 이를 호출하지 않습니다.
operationId polarHook
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 수신됨(applied가 true이면 요금제가 변경된 것입니다) | — |
| 400 | — | — |
| 403 | 서명이 일치하지 않습니다 | — |
| 501 | 빌링이 꺼져 있습니다 | — |
GET /v1/orgs/{orgSlug}/billing
요금제(plan)를 반환하며, Pro의 경우 상태(status: active · past_due · canceled)와 현재 기간의 종료 시점(periodEnd)도 함께 반환합니다. enabled가 false이면 이 배포에는 결제가 설정되어 있지 않으며, 화면에는 연락처 주소만 표시됩니다. sandbox가 true이면 Polar의 테스트 환경이므로 실제 결제는 이루어지지 않습니다.
limits는 두 요금제의 한도 표입니다(cp/internal/plan). 화면에 숫자가 나타나지 않는 이유는 OrgMembership.projectLimit와 같습니다. Viewer 이상.
operationId getBilling
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 결제 상태 | BillingStatus |
| 403 | — | — |
| 404 | — | — |
POST /v1/orgs/{orgSlug}/billing/checkout
Polar 체크아웃 세션을 하나 생성하고 그 url을 반환합니다. Dashboard는 그 URL을 자체 페이지의 iframe에 표시합니다(@polar-sh/checkout; 세션의 embed_origin은 returnTo의 origin입니다). 결제가 완료되어도 요금제는 바뀌지 않습니다: 요금제는 웹훅(/v1/hooks/polar)이 바꿉니다. 화면은 성공 이벤트 이후 상태를 다시 읽어옵니다.
returnTo는 서버의 RUNLOT_PUBLIC_URL 아래여야 합니다(그렇지 않으면 400). 이미 Pro이면 409 already_pro이며, 대신 포털을 여세요. admin.
operationId createBillingCheckout
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 체크아웃 세션 | object |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 409 | already_pro | Error |
| 501 | billing_off — 빌링이 설정되어 있지 않습니다 | — |
| 502 | billing_unavailable — Polar가 응답하지 않습니다 | — |
POST /v1/orgs/{orgSlug}/billing/portal
Polar 고객 포털의 일회용 url을 반환합니다. 결제 수단 변경, 영수증, 해지가 모두 그곳에서 이루어집니다 — 그 화면을 자체 화면에 두지 않습니다(카드 번호를 서버에 두지 않는 것이 MoR의 가치입니다). 결제한 적이 없는 조직은 404를 반환합니다. admin이 필요합니다.
operationId createBillingPortal
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 포털 세션 | object |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |
POST /v1/orgs/{orgSlug}/billing/session
@polar-sh/checkout의 결제 수단 임베드에 필요한 고객 세션 토큰입니다. 해당 고객에 대해 1시간 동안 유효합니다. 화면이 열릴 때마다 새로 가져옵니다. 결제 이력이 없는 조직은 404를 받습니다. admin.
operationId createBillingSession
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 고객 세션 | object |
| 403 | — | — |
| 404 | — | — |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |
POST /v1/orgs/{orgSlug}/billing/cancel
cancel: true인 경우, Pro는 현재 주기가 끝날 때 종료됩니다(그때까지는 Pro) — Polar의 cancel_at_period_end입니다. false는 예약된 취소를 되돌립니다. 즉시 회수는 없습니다: 이미 결제한 기간은 끝까지 그대로 사용됩니다. 응답은 즉시 우리 행의 status를 갱신하며, 웹훅도 곧 같은 결론에 도달합니다. Pro가 아니면 409 not_pro입니다. admin.
operationId cancelBilling
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 변경된 상태 | object |
| 403 | — | — |
| 404 | — | — |
| 409 | not_pro | Error |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |