# 인앱 결제

마치면 앱 안에서 디지털 상품을 팔 수 있어요. 스토어에 인앱 상품이 생기고, 구매·복원·환불이 앱과 내 서버에 바로 반영돼요. 상품 심사는 첫 앱 버전과 함께 받아요.

두 스토어의 영수증 확인·구독 갱신·환불을 직접 다루기 어려워서 구독 관리 도구(예: RevenueCat, Adapty, Qonversion)를 많이 써요. 아래 순서는 RevenueCat 기준이에요. 다른 도구나 직접 만든 서버도 순서(스토어 앱 연결 → 상품 → 서버 알림 → 앱 코드 → 테스트 구매)는 같아요.

## 시작하기 전에

- **스토어에 앱** — App Store Connect·Play Console 에 앱이 있어야 해요([iOS 출시](/docs/journey-ios)·[Android 출시](/docs/journey-android)). Play 인앱 상품은 첫 AAB 를 올린 뒤에 만들 수 있어요.
- **App Store 유료 앱 계약이 활성** — [App Store 돈 받기](/docs/journey-apple-payouts).
- **Google 15% 수수료 등록** — [Google 돈 받기](/docs/journey-google-payouts).

allob 이 지금 바로 연결하는 구독 관리 도구는 RevenueCat 이에요. 다른 도구나 직접 만든 서버는 웹훅을 받는 서버에서 allob 서버 SDK 로 결제 이벤트를 보내면 매출과 퍼널을 같이 봐요. 스토어 판매 보고서는 App Store Connect 연결로도 가져와요.

:::prompt
이 allob 사업 가이드를 읽고, 내 사업에 해당하는 할 일만 골라 진행해 줘: https://allob.app/docs/journey-iap.md
1. allob business view 와 allob path view --business 로 내 사업 모양(사업자 형태·돈 버는 방식·파는 나라)을 먼저 확인해.
2. 해당하지 않는 할 일은 왜 아닌지 한 줄로 알려 주고 allob step skip <id> --reason "…" 으로 넘겨.
3. 해당하는 할 일은 allob step view <id> 의 안내대로 하고, 끝나면 allob step done <id> --note "끝났다는 증거" 를 불러.
4. 본인 인증·서명·결제·약관 동의처럼 사람만 하는 곳에서는 멈추고 무엇을 누르면 되는지 알려 줘.
5. 되돌리기 어려운 답(과세 유형·계정 유형 등)은 내 폰으로 확인 요청이 와 — 명령이 보여 준 승인 링크를 알려 주고, 기다리는 동안 다른 할 일로 넘어가.
:::

## RevenueCat 연결하기 {#iap.revenuecat}

RevenueCat 에 이 앱의 프로젝트를 만들고, 두 스토어 앱을 연결한 뒤 allob 에도 연결해요. 다른 도구를 쓰면 이 할 일은 건너뛰어요.

에이전트가 프로젝트를 만들고 두 스토어 앱(번들 id·패키지 이름)을 붙인 뒤, 꼭 필요한 권한만 가진 시크릿 키를 만들어 allob 에 연결해요. RevenueCat 은 앱마다 키가 달라서 제품마다 연결해요.

Play 앱 설정의 "credentials need attention" 경고는 첫 AAB 와 Play 인앱 상품이 생기면 사라져요. 그 전엔 그대로 둬도 돼요.

:::tip
Apple Small Business Program(15%)이 승인되면 구독 관리 도구(RevenueCat 은 앱 설정)에 실제 적용일을 넣어요. 그래야 매출 숫자가 맞아요.
:::

:::tip 끝났는지 확인
이 제품에 RevenueCat 연결이 있고 allob 이 키를 확인했으면 끝이에요. allob 이 자동으로 확인해요.
:::

RevenueCat 로그인만 해 주세요. 결과는 allob 의 이 제품 연결 목록에서 볼 수 있어요.
## 스토어 인앱 상품 만들기 {#iap.store-products}

App Store·Google Play 에 인앱 상품을 만들고, 구독 관리 도구의 상품·권한(RevenueCat 은 entitlement·offering)을 맞춰요.

| 조건 | 고를 상품 종류 | 그 길의 대가 |
|---|---|---|
| 광고 제거·기능 영구 해금 | 비소모품 | 복원이 돼요. 소모품으로 만들면 Android 에서 소비돼 복원이 안 돼요 |
| 코인·횟수권처럼 쓰면 없어지는 것 | 소모품 | 복원 대상이 아니에요. 받고 쓰고 환불하는 악용에 약해요 → 환불 알림으로 회수 |
| 계속 쓰는 서비스 | 자동 갱신 구독 | 첫 구독은 앱 버전과 함께 심사받아요. 상품 페이지에 약관(EULA) 링크가 꼭 있어야 해요. 가격 인상은 스토어 동의 흐름을 따라요 |

- App Store 상품엔 심사용 스크린샷이 있어야 해요. 없으면 MISSING_METADATA 예요.
- Play 구독은 첫 AAB 뒤에 기본 요금제를 활성화할 수 있어요.
- 앱은 상품 id 가 아니라 구독 관리 도구의 **권한이 켜져 있나**로 판단해요.

첫 인앱 상품은 앱 버전과 같은 제출로 심사받아요(출시 단계).

:::warning
⚠ 나중에 못 바꿔요 — 스토어 상품은 지울 수 없고(판매 중지·비활성화만), 한 번 쓴 상품 id 는 다시 못 써요. id 이름 규칙을 먼저 정해요(예: `<앱>_remove_ads`, `<앱>_pro_yearly`).
:::

:::tip 끝났는지 확인
App Store 인앱이 READY_TO_SUBMIT, Play 인앱이 ACTIVE 이고, 구독 관리 도구의 상품·권한이 두 스토어와 맞으면 끝이에요. 증거는 상품 종류·상태 메모예요.
:::

에이전트가 끝까지 해요. App Store Connect·Play Console 로그인만 해 주세요. 만든 상품은 두 콘솔의 인앱 상품 목록에서 볼 수 있어요.
## 스토어 서버 알림 연결하기 {#iap.notifications}

Apple·Google 서버 알림을 구독 관리 도구(또는 내 서버)에, 그 도구의 알림을 내 서버에 연결해요. 빠지면 환불·해지·갱신이 사용자가 앱을 다시 열 때까지 안 잡혀요.

- **Apple** — App Store 서버 알림(V2)을 운영·샌드박스 둘 다 받아요.
- **Google** — 실시간 알림은 Google Cloud 의 Pub/Sub 토픽을 거쳐 와요. 그래서 Google Cloud 프로젝트와 토픽이 먼저 있어야 해요(에이전트가 만들어요).
- **내 서버 웹훅** — 구독 관리 도구의 알림을 받아 확인하고, 운영 건만 기록해요. 이메일 같은 개인정보는 읽지 않아요. 여기서 allob 에 서버 결제 이벤트를 보내요.

환불 대응(Refund Control)을 켤지 골라요:

| 조건 | 고를 길 | 그 길의 대가 |
|---|---|---|
| 소모품이 있고 환불 악용이 걱정돼요 | 켬(나라별) | 약관에 "환불 요청 때 지급 여부를 스토어에 보낼 수 있음", 처리방침에 새 목적을 적어요. 이미 공개된 앱이면 불리한 약관 변경이라 한국은 30일 전에 게시해요. 유럽·스위스는 빼는 게 간단해요 |
| 출시 전(고객 없음) | 첫 버전에 넣고 바로 켬 | 공지 기간이 필요 없어요 |
| 비소모품·구독만 있어요 | 끔 | 약관·방침이 그대로예요 |

:::tip 끝났는지 확인
Apple·Google 테스트 알림이 구독 관리 도구(또는 내 서버)에 들어오고, 그 도구의 웹훅이 내 서버에 200 으로 들어오면 끝이에요. 증거는 메모예요.
:::

에이전트가 끝까지 해요. RevenueCat·App Store Connect·Play Console·Google Cloud 로그인만 해 주세요. 결과는 구독 관리 도구의 알림 기록에서 볼 수 있어요.
## 앱에 결제 코드 넣기 {#iap.code}

앱에서 상품을 불러와 구매·복원하고, entitlement 로 권한을 주는 코드를 넣어요.

- 구매 직전에 이 기기의 allob 기기 ID 를 결제 정보로 넘겨요. 서버 결제 이벤트가 이 기기의 퍼널과 연결돼요.
- 복원은 비소모품·구독만이에요. 복원 때 코인 같은 보상을 다시 주지 않아요.
- 구독이면 결제 화면에 이용약관·처리방침 링크가 있어야 해요.

:::tip 끝났는지 확인
개발 빌드에서 상품 목록이 뜨고, 구매·복원 흐름이 코드에 있으면 끝이에요. 증거는 고친 파일·흐름 메모예요.
:::

에이전트가 끝까지 해요. 고친 코드는 에이전트의 작업 내용에서 볼 수 있어요.
## 테스트 구매로 확인하기 {#iap.sandbox-purchase}

테스트 계정으로 한 번 사서, 스토어 → 구독 관리 도구 → 앱 → 내 서버까지 끝까지 도는지 확인해요.

- iOS 는 샌드박스 계정(TestFlight 빌드), Android 는 라이선스 테스트 계정(내부 테스트 빌드)으로 사요. 실제 돈은 나가지 않아요.
- 환불·해지 테스트까지 하면 알림 연결도 같이 확인돼요.

:::tip 끝났는지 확인
샌드박스 구매 한 번이 구독 관리 도구에 기록되고 앱에서 권한이 켜졌으면 끝이에요. 증거는 메모(플랫폼·상품·시각)예요.
:::

테스트 폰에서 샌드박스(또는 라이선스 테스트) 계정 로그인과 구매 확인만 해 주세요. 결제 창에 테스트 표시(예: [Sandbox])가 있는지 보고 눌러요.

## 자주 걸리는 함정

| 증상 | 원인·해결 |
|---|---|
| Android 에서 "광고 제거" 복원이 안 돼요 | 소모품으로 만들었어요 → 비소모품으로 |
| 인앱 상품이 MISSING_METADATA 예요 | 심사용 스크린샷이 없어요 |
| Play 인앱을 못 만들어요 | 결제 권한이 든 AAB 를 먼저 올려요 |
| Android 에서 구독을 못 찾아요 | (RevenueCat) Play 상품이 `<구독 id>:<기본 요금제 id>` 형식인지, 요금제가 ACTIVE 인지 봐요 |
| 환불이 며칠 뒤에야 잡혀요 | 서버 알림을 연결하지 않았어요 → 스토어 서버 알림 할 일 |
| 엉뚱한 사용자에게 구매가 붙어요 | (RevenueCat) 앱 사용자 id 가 UUID 가 아닌데 S2S 새 구매 추적을 켰어요 → 꺼요 |
| 자동 갱신 구독이 3.1.2 로 반려됐어요 | 상품 페이지에 약관(EULA) 링크가 없어요 |

한국에선 Google Play 대체 결제를 쓸 수 있어요(수수료 4%p 낮음). 대신 결제 흐름과 심사가 늘어요.

:::details 다른 나라에서 판다면
- **일본** — 앱 안 유료 화폐를 팔면 資金決済法 대상일 수 있어요. 6개월 안에 쓰게 하면 대상이 아니에요([금융청 안내](https://www.fsa.go.jp/policy/prepaid/index.html)).
- **미국** — 자동 갱신 구독의 고지·해지 규칙과 가격 인상 통지 기간을 지켜요.
- **나라별 가격** — Apple 가격 등급(자동 환산 또는 직접), Play 나라별 가격으로 정해요. 세금 포함 표시가 나라마다 달라서 약관엔 "결제 화면에 표시"만 적어요.
:::

## 출처

공식 안내와 마지막으로 맞춰 본 날: 2026-10-18

- [RevenueCat 문서](https://www.revenuecat.com/docs)
- [Apple 앱 내 구입](https://developer.apple.com/in-app-purchase/)
- [Apple App Store 서버 알림](https://developer.apple.com/documentation/appstoreservernotifications)
- [Google Play 실시간 개발자 알림](https://developer.android.com/google/play/billing/rtdn-reference)
- [Google Play 서비스 수수료 — 한국 대체 결제](https://support.google.com/googleplay/android-developer/answer/112622)
- [Apple App Review 가이드라인 3.1.2(구독)](https://developer.apple.com/app-store/review/guidelines/#subscriptions)
- [일본 금융청 前払式支払手段](https://www.fsa.go.jp/policy/prepaid/index.html)
- [allob 문서 — 결제 기록하기](https://allob.app/docs/payments)
