Membership
유료 구독 준비와 서명된 청구 결과 전달.
Membership
명시된 호출자 환경에서만 사용하세요. 먼저 자격 증명을 설정하세요. 패키지 surface는 SDK README가 소유합니다.
유료 Membership 구독
이 흐름에는 customers와 membership을 활성화하세요. 상품과 주문도 사용하려면
ecommerce를 별도로 선택하세요.
server.memberships.subscribe()는 유료 Membership을 준비하며 provider 결제
식별자를 받거나 Membership을 활성화하지 않습니다. 서버 보관 Secret Key로
호출한 뒤 응답 union을 분기하세요. 유료 응답은 incomplete이며 tenant PSP에
전달할 정확한 금액, 통화, 기간 시작, 만료 시각, 그리고
firstCharge.idempotencyKey를 포함합니다. 이 키로 PSP를 결제한 다음 기존의
서명된 charge-result webhook으로 결과를 전송합니다. Trial 응답은 즉시
trialing이고 first-charge 지시가 없습니다.
유료 subscribe 전에는 tenant의 활성 delegated billing connection을 구성해야 하며,
없으면 Console은 lifecycle write 없이 409 conflict를 반환합니다.
import { createServerClient } from '@01.software/sdk/server'
type Server = ReturnType<typeof createServerClient>
type Prepared = Awaited<ReturnType<Server['memberships']['subscribe']>>
type FirstCharge = Extract<Prepared, { status: 'incomplete' }>['firstCharge']
export async function prepareSubscription(
server: Server,
input: Parameters<Server['memberships']['subscribe']>[0],
tenantPsp: { charge: (charge: FirstCharge) => Promise<void> },
) {
const prepared = await server.memberships.subscribe(input)
if (prepared.status === 'incomplete') {
const expiresAt = Date.parse(prepared.firstCharge.expiresAt)
if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) {
throw new Error('First charge instruction expired or invalid')
}
await tenantPsp.charge(prepared.firstCharge)
}
return prepared
}활성화는 서명된 결과 webhook 또는 기존 reconciliation query만 결정합니다. 첫 결제가
pending인 동안에는 같은 subscribe idempotency key로 재시도할 수 있고, 결제 완료나
expiresAt 이후 재시도는 새 결제 지시 대신 409를 반환합니다. expiresAt이 유효하지 않거나 해당 시각에 도달했으면 PSP 결제를 시작하지 마세요. tenant PSP는
firstCharge.idempotencyKey로 중복 결제를 막아야 합니다. 늦은 paid 결과는
증빙으로 보존되지만 Membership을 활성화하지 않으므로 tenant는 이 API 밖에서
환불을 처리해야 합니다.