메뉴 이름은 영어 App Store Connect 기준입니다. 한국어로 쓰면 번역된 이름으로 보입니다.

Apple이 보내는 것과 보내지 않는 것

Apple은 App Store Server Notifications를 “인앱 구매 이벤트를 실시간으로 알려 주는 서버 간(server-to-server) 서비스”라고 설명합니다. 이벤트가 생기면 App Store가 내가 정한 URL로 HTTP POST를 보냅니다. JSON 본문에는 signedPayload 필드 하나가 있고, 디코딩하면 notificationType, 대개 subtype, 그리고 서명된 거래 정보가 들어 있습니다.

돈이 들어오거나 나가는 유형:

notificationType보내는 때
SUBSCRIBED처음 구독(INITIAL_BUY)하거나 다시 구독(RESUBSCRIBE)할 때. 오퍼 코드로 구독해도 이 유형입니다
DID_RENEW구독이 갱신될 때. 실패했던 갱신이 나중에 성공하면 subtype이 BILLING_RECOVERY
ONE_TIME_CHARGE소모성·비소모성·비갱신 구독 상품을 구매(또는 오퍼 코드로 사용)할 때
OFFER_REDEEMED구독 중인 고객이 오퍼를 사용할 때
REFUNDApple이 거래를 환불했을 때
REFUND_REVERSED분쟁 뒤 Apple이 환불을 취소했을 때

금액은 서명된 거래(JWSTransactionDecodedPayload)에 있습니다. price는 1/1000 단위(milliunits)라서 $1.99 구매는 1990으로 옵니다. 그 밖에 currency, storefront, productId, transactionId, purchaseDate, 오퍼가 적용됐다면 offerType이 있습니다.

만들기 전에 알아 둘 한계 네 가지:

1단계: HTTPS 엔드포인트

엔드포인트 조건:

성공 응답을 받지 못하면 Apple은 V2 알림을 다섯 번 다시 보냅니다. 직전 시도에서 1, 12, 24, 48, 72시간 뒤입니다. 재시도는 프로덕션에서만 하고, 샌드박스는 한 번만 보냅니다. 오래 멈췄다면 App Store Server API의 Get Notification History로 놓친 V2 알림을 받을 수 있습니다.

서버리스 함수(Cloudflare Workers, AWS Lambda, Google Cloud Run 등)면 충분합니다. 알림 하나가 작은 POST 한 번입니다.

2단계: App Store Connect에 URL 입력

필요한 역할: Account Holder, Admin, App Manager, Marketing 중 하나.

  1. Apps에서 앱을 고릅니다.
  2. 사이드바의 General 아래에서 App Information을 누릅니다.
  3. App Store Server Notifications까지 내려갑니다.
  4. Production Server URL에서 Set Up URL을 누르고, URL을 입력하고, Version 2를 고른 뒤 Save를 누릅니다.
  5. Sandbox Server URL도 같은 방법으로 입력합니다. 두 환경에 같은 URL을 써도 됩니다.

Apple 도움말의 두 가지 주의점: 샌드박스 URL을 넣지 않으면 두 환경의 알림이 모두 프로덕션 URL로 가고, 샌드박스 URL만 넣으면 프로덕션 알림은 오지 않습니다.

이미 구독 관리 서비스를 쓰고 있다면? RevenueCat 같은 서비스나 직접 만든 백엔드가 이미 URL을 쓰고 있다면 덮어쓰지 마세요. 환경마다 URL은 딱 하나입니다. 대신 그 URL을 가진 서버가 내 서버로 알림을 전달하게 하세요.

같은 설정은 App Store Connect API의 App 리소스에도 있습니다(subscriptionStatusUrl, subscriptionStatusUrlVersion과 …ForSandbox). 도구들이 API 키로 이 값을 설정하는 방식입니다.

3단계: 서명 검증과 디코딩

알림 안의 모든 것은 JWS(JSON Web Signature)입니다. 바깥의 signedPayload, 그 안의 data.signedTransactionInfo, 구독이라면 data.signedRenewalInfo까지 각각 서명을 확인한 뒤에 믿어야 합니다. JWS 헤더의 x5c에는 Apple이 서명에 쓴 인증서 체인(서명 인증서, Apple 중간 인증서, Apple PKI의 루트 인증서)이 들어 있습니다.

직접 구현할 필요는 없습니다. Apple의 오픈소스 App Store Server Library(Swift, Java, Python, Node)가 알림·거래·갱신 정보를 검증하고 디코딩해 줍니다. Node 예시:

import { SignedDataVerifier, Environment } from "@apple/app-store-server-library";

// Apple 루트 인증서: https://www.apple.com/certificateauthority/
// 환경마다 verifier를 하나씩. Production이면 appAppleId가 필요합니다.
const verifier = new SignedDataVerifier(appleRootCAs, true, Environment.PRODUCTION, "com.example.app", 1234567890);

export async function onNotification(request) {
  const { signedPayload } = await request.json();
  const notification = await verifier.verifyAndDecodeNotification(signedPayload);
  const signedTransaction = notification.data?.signedTransactionInfo;
  const transaction = signedTransaction ? await verifier.verifyAndDecodeTransaction(signedTransaction) : null;
  // notification.notificationType, notification.subtype, notification.notificationUUID
  // transaction.price / 1000, transaction.currency, transaction.storefront, transaction.productId
  return new Response(null, { status: 200 });
}

4단계: 테스트 알림 보내기

App Store Server API의 Request a Test Notification 엔드포인트를 부릅니다(In-App Purchase 키로 만든 JWT로 인증). 프로덕션 엔드포인트를 부르면 프로덕션 URL로, 샌드박스 엔드포인트를 부르면 샌드박스 URL로 TEST 알림이 옵니다. Get Test Notification Status로 내 서버가 어떻게 응답했는지 확인할 수 있습니다.

실제 구매 유형을 보려면 Sandbox Apple Account로 샌드박스에서 구매해 보세요. 구독을 사면 SUBSCRIBED(subtype INITIAL_BUY)가 옵니다.

5단계: 알림을 판매 알림으로

그다음은 원하는 곳으로 보내면 됩니다. Slack·Discord 웹훅, 푸시 알림, 메뉴바 앱 등.

서버를 운영하고 싶지 않다면

RevBell은 Mac에서 1–5단계를 대신합니다.

자주 묻는 질문

App Store Connect 판매 보고서를 대신하나요?

아니요. 알림은 빨리 알려 주는 용도이고, 수익금·세금·환율이 반영된 재무 기록은 보고서입니다.

유료 앱 다운로드도 알림이 오나요?

아니요. 인앱 구매 이벤트만 옵니다.

서버가 멈추면요?

프로덕션 알림은 약 6.5일(1+12+24+48+72시간)에 걸쳐 다섯 번 재시도됩니다. 그 뒤에는 Get Notification History를 쓰세요.

두 서비스가 같은 알림을 받을 수 있나요?

직접은 안 됩니다. 앱마다 프로덕션 URL과 샌드박스 URL이 하나씩입니다. URL을 가진 서버에서 전달하세요.

얼마나 빠른가요?

Apple은 실시간 서비스라고 설명하지만 전달 시간을 공개하지는 않습니다. RevBell 서버를 거쳐 Mac까지 오는 판매 알림은 보통 구매 후 1–2분 안에 도착합니다.