트랜잭션
이 가이드는 트랜잭션 통합을 다룹니다. 부모가 볼 수 있도록 구매를 보고하는 방법, 청구 전에 부모에게 구매 승인을 요청하는 방법, 그리고 완료된 카드 결제가 부모 동의를 대신하도록 하는 방법을 설명합니다.
트랜잭션이 무엇이고 k-ID와 제품 사이의 경계가 어디인지는 먼저 트랜잭션 개념을 읽어보세요. 결제 기반 확인은 자체 개념 페이지가 있습니다.
시작하기 전에
- 조직에서 트랜잭션을 사용할 수 있는지 확인하세요. 이 기능을 조직에서 활성화하려면 k-ID에 문의하세요.
- 사용하는 부분을 활성화하세요. 트랜잭션은 기본적으로 꺼져 있으며 Compliance Studio의 Transactions 섹션에서 제품별로 구성하며, Transactions는 나머지 두 스위치가 의존하는 스위치입니다. 특정 엔드포인트가 필요로 하는 스위치가 켜지기 전까지 그 엔드포인트는
FEATURE_DISABLED를 반환합니다. 세 개의 스위치와 각각이 무엇을 제어하는지는 기능 플래그를 참조하세요. - 웹훅 엔드포인트를 구성하세요. 구매 승인을 사용하는 경우입니다. 승인 결과는 웹훅으로 도착합니다. 제품의 Developer Settings에서 URL과 시크릿을 설정하고, 웹훅 개요의 설명에 따라 서명을 검증하세요.
- 처리할 이벤트를 구독하세요. 엔드포인트는 구독한 이벤트 유형만 수신하며, k-ID는 나머지를 전달 시도나 오류 없이 버립니다. 제품의 Developer Settings 페이지에서 엔드포인트에 대해
Transaction.PurchaseApprovalResult를 선택하세요.
구매 보고
/transaction/push는 세션에 대해 완료된 구매를 기록합니다. 구매가 완료된 후에 호출하고, 반복 구매가 갱신될 때마다 호출하세요.
각 이벤트는 멱등성을 위한 자체 id, 부모에게 그대로 표시되는 title과 선택적 description, 통화의 최소 단위로 표현된 amount, currency, 완료된 시점의 timestamp, 부모가 구매를 관리할 수 있는 선택적 url, 그리고 status를 가집니다.
| 필드 | 설명 |
|---|---|
id | 제품이 생성하는 UUID입니다. sessionId와 함께 멱등성 키가 됩니다. 이미 수락된 id를 다시 제출하면 중복 기록 없이 다시 accepted로 집계됩니다. |
type | purchase. |
title, description | 부모에게 그대로 표시됩니다. title은 필수이고 description은 선택입니다. |
amount | 통화의 ISO 4217 최소 단위 정수입니다. 따라서 499는 USD에서 4.99, JPY에서 499입니다. 무료 항목의 경우 0도 허용됩니다. |
currency | ISO 4217 코드입니다. 대문자가 표준이며, 소문자도 허용되어 정규화됩니다. |
timestamp | RFC 3339 UTC이며, 지난 7일 이내이고 미래가 아니어야 합니다. |
url | 부모가 구매를 관리하기 위해(예: 구독 취소) 열 수 있는 선택적 HTTPS 링크입니다. |
status | successful, 또는 시도되었지만 완료되지 않은 청구의 경우 failed입니다. failed를 보고하면 부모가 실제로 처리된 구매와 그렇지 않은 구매를 구분할 수 있습니다. |
한 번의 호출은 최대 100개의 이벤트를 받습니다.
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"events": [
{
"id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e",
"type": "purchase",
"title": "Starter Pack",
"description": "500 coins and a cosmetic item",
"amount": 499,
"currency": "USD",
"timestamp": "2026-06-24T09:00:00Z",
"status": "successful",
"url": "https://your-webstore.example.com/manage?ref=order-4455667"
}
]
}
이벤트는 각각 독립적으로 처리되므로 배치 전체가 성공했다고 가정하지 말고 이벤트별 상태를 읽으세요.
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 0,
"events": [
{ "id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e", "status": "accepted" }
]
}
거부된 이벤트의 reason이 invalid_input이면 검증 규칙을 위반한 것이므로 그대로 재시도해도 다시 실패합니다. reason이 internal_error이면 일시적인 문제이므로 재시도해도 안전합니다. 세션이 존재하지 않거나 취소된 경우에는 이벤트별이 아니라 배치 전체가 거부됩니다.
보고된 구매는 Family Connect에서 연결된 부모에게 자녀별 구매 화면으로 표시되며, 그 금액은 신뢰할 수 있는 성인이 그곳과 정기 다이제스트 이메일에서 읽는 활동에도 반영됩니다.
승인 요청하기
청구 전에 부모의 결정을 원할 때, /transaction/request-purchase가 승인 요청을 엽니다. 청구하기 전에 요청하고, approved 결과가 도착한 뒤에만 구매를 완료하세요.
{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e",
"title": "Starter Pack",
"description": "500 coins and a cosmetic item",
"amount": 499,
"currency": "USD"
}
응답에는 여러분이 보낸 id와 expiresAt이 담깁니다. id를 저장하세요. 결과 웹훅과 연결됩니다.
{
"id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e",
"expiresAt": "2026-06-25T16:00:00Z"
}
k-ID는 세션에 등록된 부모에게 요청을 이메일로 보내며, 부모는 Family Connect에서 승인하거나 거부합니다. 결과는 data.id로 연결되는, approved, denied, expired를 담은 Transaction.PurchaseApprovalResult 웹훅으로 도착합니다.
이 흐름의 다음 성질을 고려하여 설계하세요.
- 청구하기 전에 승인받으세요. 요청은 구매 뒤에 남는 기록이 아니라 구매 앞에 두는 게이트입니다.
approved일 때 청구하고,denied와expired는 "청구하지 않음"으로 처리하세요. - 요청은 24시간 후에 만료됩니다. 만료는
status: "expired"로 웹훅을 발생시키며 실패가 아니라 정상적인 결과입니다. 플레이어에게 구매를 다시 제안할 수 있습니다. 웹훅을 무한정 기다리는 대신expiresAt을 기한으로 취급하세요. - 요청은
id별로 멱등합니다. 아직 결정을 기다리는id를 다시 제출하면 두 번째 요청을 열거나 부모에게 두 번 알리지 않고 대기 중인 요청을 그대로 반환합니다. - 플레이어는 대기 중인 요청을 최대 20개까지 보유합니다. 그 이상이 되면 하나가 해결될 때까지 호출이
INVALID_INPUT으로 거부됩니다. - 플레이어에게 알림이 가려면 연결된 부모가 있어야 합니다. 요청은 세션에 등록된 승인자에게 전송됩니다. 부모가 연결되지 않은 플레이어에 대해 요청을 열면 알릴 대상이 없으므로, 먼저 부모 링크를 설정하세요. Verified parent linking을 참조하세요.
페이로드와 모든 상태는 Transaction.PurchaseApprovalResult를 참조하세요.
결제 기반 확인
신용카드 확인 방법이 세션의 관할권에 대해 활성화된 곳에서는, 완료된 신용카드 결제가 별도의 확인 대신 부모 동의 단계를 충족할 수 있습니다. 무엇이 적격이고 그 이유인지는 결제 기반 확인 개념을 읽어보세요. 이 섹션은 통합입니다.
결제 기반 확인은 표준 부모 동의 Challenge에 연결됩니다. /challenge/send-email로 동의 이메일을 보낼 곳에서 대신 /challenge/send-email-with-payment-verification로 보내며 paymentVerification 증명을 추가합니다. Challenge에 관한 나머지는 모두 그대로입니다.
증명은 어떤 제공업체를 사용하든 k-ID의 필드로 결제를 설명합니다.
| 필드 | 설명 |
|---|---|
provider | 결제를 처리한 등록 판매자(merchant of record)입니다. invoiceId의 범위를 지정합니다. |
invoiceId | 결제에 대한 등록 판매자의 식별자입니다. k-ID는 이를 불투명한 값으로 취급합니다. |
payerEmail | 결제한 이메일 주소입니다. |
instrument | 결제에 사용된 수단입니다. card, wallet 또는 other. |
funding | 카드의 자금 유형입니다. credit, debit, prepaid 또는 unknown. credit만 적격입니다. 제공업체가 자금 유형을 보고하지 않으면 unknown을 보내세요. |
다른 동의 이메일을 보내는 것과 같은 방식으로, 증명을 paymentVerification에 담아 Challenge에서 보내세요.
{
"challengeId": "ae6d4729-af32-42ea-8ef2-ff46c7664802",
"email": "parent@example.com",
"paymentVerification": { "...": "the normalized attestation from your merchant of record" }
}
k-ID는 결제를 처리하지도 목격하지도 않으므로, 실제로 완료된 결제에 대해서만 증명하세요.
제공업체의 결제 정규화하기
등록 판매자는 저마다 결제를 다르게 보고합니다. 선택적으로 /transaction/lookup-provider를 사용하여 등록 판매자의 원시 결제 신호를 표준 증명 형태로 정규화할 수 있습니다.
예를 들어 Xsolla의 경우 카드 BIN을 반드시 포함해야 하는 원시 payment 웹훅 본문을 보냅니다.
{
"provider": "xsolla",
"data": { "...": "the raw Xsolla payment webhook body" }
}
이것은 동의 이메일의 paymentVerification에 넣을 정규화된 증명을 반환합니다.
{
"provider": "xsolla",
"invoiceId": "2110445753",
"payerEmail": "parent@example.com",
"instrument": "card",
"funding": "credit"
}
기능 플래그
트랜잭션에는 세 개의 스위치가 있으며, Transactions는 나머지 두 스위치가 필요로 하는 스위치입니다.
| 스위치 | 활성화하는 기능 | 적용 엔드포인트 |
|---|---|---|
| Transactions | 구매 보고 및 제공업체의 결제 정규화 | /transaction/push, /transaction/lookup-provider |
| Purchase Controls | 부모에게 구매 승인 요청 | /transaction/request-purchase |
| Payment-as-verification | 적격 카드 결제가 동의를 대신함 | /challenge/send-email-with-payment-verification |
스위치가 꺼진 엔드포인트는 FEATURE_DISABLED를 반환합니다. Purchase Controls와 Payment-as-verification은 각각 Transactions도 필요로 하므로, Transactions만 켠 제품은 구매를 보고하고 결제를 정규화할 수 있지만 승인을 요청할 수는 없으며, 아무것도 켜지 않은 제품은 어느 것도 호출할 수 없습니다.
출시 전 점검 목록
- 모든
/transaction/push가 배치가 성공했다고 가정하지 않고 이벤트별 상태를 읽으며,internal_error이벤트만 재시도합니다. - 구매는
failed청구를 포함한 실제status로 보고되어 부모가 실제로 일어난 일을 봅니다. - 구매 승인은 청구하기 전에 요청하며,
denied와expired를 모두 "청구하지 않음"으로 처리합니다. - 제품의 웹훅이
Transaction.PurchaseApprovalResult를 구독하고 있으며, 핸들러는data.id에 대해 멱등하고 서명을 검증합니다. - 제품이 플레이어에 대해 승인 요청을 열기 전에 그 플레이어에게 연결된 부모가 있습니다.