콘텐츠로 이동

구독형 결제 시스템

자동 갱신 구독 (Auto-renewable Subscriptions)

Hive SDK v4.11.6부터 제공하는 자동 갱신 구독은 정기적으로 결제를 반복하는 상품입니다. 일반적인 형태는 원간 구독, 연간 구독 또는 무료 체험 기간 설정 등 입니다. Google과 Apple에서 제공하는 구독 상품의 형태가 궁금하시다면 아래 표를 확인해보세요. 유저는 사용하는 OS에서 정기 결제 기간을 선택한 후 자동 갱신 구독 상품을 이요할 수 있습니다.

OS Google Apple
상품 형태 주간
월간
3개월
6개월
연간
주간
월간
2개월
3개월
6개월
연간

유저가 특정 상품을 구독 결제하는 상태라면 기기 종류에 상관 없이 어디서든 자동 갱신 구독에 접근할 수 있어야 합니다. 사용자의 필요에 따라 구독 상태 복구 역시 가능해야 합니다.

상품 등급 변경

자동 갱신 구독 상품은 높은 등급이나 낮은 등급으로 상품 변경이 자유롭습니다. 구독 상품은 소모되지 않으며, 복구를 시도할 때는 항상 영수증을 제공합니다.
Google에서는 구독 상품을 다른 등급으로 업그레이드나 다운그레이드, 또는 이전의 구독 상품과 관계 없이 새로운 구독 상품으로 변경할 수 있습니다. 이 때 이전 구독 상품의 잔여 기간 가치를 새로운 구독 상품의 만료 기간에 일할 계산해 부여합니다.
Apple은 iTunes 콘솔에 구독형 상품의 그룹을 정의하고 있으며, 이를 기준으로 동일한 구독 그룹 내 하나의 상품을 유지하는 범위에서 변경을 허용합니다. 상품을 업그레이드하면 새로운 구독 상품의 만료 기간에 이전 구독 상품의 잔여 기간 가치만큼 일할 계산해 부여하고, 다운그레이드할 때는 이전 구독 상품의 기간이 만료된 후 새 상품으로 변경한 구독을 갱신합니다.

Warning

  • Apple의 구독 상품 주기를 Google과 동일하게 주간, 월간, 3개월, 6개월, 연간 형태만 사용하도록 권장합니다.
  • 구독 상품은 자동으로 복구되지 않습니다.
  • Hive SDK v4.11.6에서 제공하는 자동 갱신 구독 상품은 게임당 하나로 권장합니다.
    • 모든 상품은 단일 구독 그룹으로 기능을 제공합니다.
  • 발생한 매출 정보를 클라이언트 내 마케팅 어트리뷰션 툴(예: Singular, AdjustP)에 전달하지 안습니다. IAP 서버에서 구독 갱신 주기에 따라 매출 정보를 전달합니다.
  • Android 구독 기능: Google Play Store, Amazon, One Store, Samsung-GalaxyStore, Huawei만 지원합니다. Facebook Cloud와 Hive 러비는 지원하지 않습니다.
  • iOS 구독 기능: Apple iTunes Store만 지원합니다.

Note

운영체제/마켓별로 지원하는 기능에 차이가 있을 수 있습니다. 자세한 내용은 기능 지원 현황을 확인하세요.

구독형 상품

구매

  • 무료 체험
    구독 시작 시점에 무료 체험 기간이 있다면 결제하더라도 매출로 산정되지 않습니다. 무료 체험 기간이 끝나면 자동으로 결제됩니다.
  • 최초 구독 할인
    최초로 구독 상품을 이용할 때 진행하는 프로모션 형태입니다. 기존 구독 상품과 다르게 설정된 기간에 할인된 가격을 제공할 수 있습니다.

취소

유저는 구독을 갱신하기 전에 취소할 수 있습니다. 각 스토어의 구독 관리 메뉴에서 취소 방법을 확인할 수 있으며, 구독을 취소하면 자동 갱신이 종료됩니다.

환불

구독 취소 후 잔여 구독 시간의 환불을 욫어하는 경우 남은 기간과 부여된 가치에 비례해 환불합니다. (Google 콘솔)

복구

구독 중인 상품이 지급되지 않았을 때 복구 기능흘 수행할 수 있습니다. 복구는 해당 구독 상품의 소유권이 있는 경우 소유권 정책에 따라 지급합니다.

유예

구독 갱신 시점에 결제가 정상 처리되지 않을 경우 제공하는 결제 유예 기능이 있습니다. 결제 유예 기간은 구독 중인 상태와 동일한 가치를 부여해야 합니다.

계정 보류 (Google)

구독 결제 시점에 발생하는 문제로 결제 갱신에 실패하고, 유예 기간 내 문제를 해결하지 않은 채 종료되는 경우를 계정 보류 상태라고 합니다. 최대 30일까지 구독 혜택을 받을 수 없도록 처리해야 합니다.

일시정지 (Google)

유저는 정기 결제를 최대 3개월까지 일시중지할 수 있습니다. 일시중지 기간이 만료된 후에는 자동 결제가 재개됩니다.

Google 알림 팝업 노출하기

Google 구독 결제가 유예 기간이거나 계정 보류 상태일 때는 유저에게 알림 팝업을 노출해야 합니다. 알림 팝업에는 현재 구독 상태와 문제 해결 방안, 그리고 마켓 설정으로 이동할 수 있는 버튼이 포함되어야 합니다. 이 버튼이 유예 기간에는 필수 구성 요소가 아니지만, 유저 편의를 고려해 포함하도록 권장합니다. 계정 상태가 유예나 보류인 상황에서 유저가 결제 정보를 변경하면, 유예일 경우 설정된 결제 시점에 자동 결제되지만 보류일 경우에는 유저가 직접 구독을 재시작해야 합니다. 재시작으로 유저의 구독 상태가 갱신되면 수동 복원 기능으로 구독 혜택을 받을 수 있도록 구독 상품 구매 페이지, 또는 적절한 위치에 구매 복원 버튼을 배치해 주세요.

알림 팝업의 내용은 아래에 제공하는 예시 문구를 사용하거나 게임이 맞게 새로 구성할 수 있습니다.

  • 유예 기간일 때 알림 팝업 문구 예시
언어 문구
한국어 구독 자동 결제가 정상적으로 이뤄지지 않아 구독 서비스가 만료되었습니다.
Google Play의 구독 설정으로 이동하여 결제 방법을 확인하고 다시 결제를 해주세요.
결제 후엔 반드시 복원(restore)버튼을 클릭하여 구독 상태를 확인해주세요.
영어 The subscription service has expired because the auto subscription payment has failed.
Go to your Google Play subscription settings, check your payment method and try again.
Be sure to click Restore after purchase to check your subscription status.
일본어 購読自動決済が行われなかったため、購読サービスが終了しました。
Google Playの購読設定に移動して決済方法を確認し、もう一度お試しください。
決済した後、必ず【復元】ボタンをタップして購読状態を確認してください。
중국어 간체 由于自动付款失败,订阅服务已到期。
前往谷歌商店的订阅设置,查看付款方式后重新进行付款吧。
付款后请务必点击复原(restore)按键查看订阅状态。
중국어 번체 自動續訂扣款失敗,已取消訂閱服務。
請至Google Play的[訂閱]設定中,確認付款方式後,再次付款。
付款完畢後,請務必點選[回復]按鈕,並確認訂閱狀態。
프랑스어 Le service d’abonnement est interrompu : le paiement automatique ne s’est pas déroulé normalement.
Veuillez procéder de nouveau au paiement en vous rendant dans les paramètres d’abonnement de Google Play.
Après le paiement, confirmez bien le statut de l’abonnement en appuyant sur le bouton “Reprendre”.
독일어 Der Abonnement ist abgelaufen, weil die automatische Zahlung für das Abonnement fehlgeschlagen ist.
Gehe zu den Einstellungen deiner Google Play-Abonnements, überprüfe deine Zahlungsmethode und versuche es erneut.
Bitte stelle sicher, dass du nach dem Kauf auf Wiederherstellen klickst, um den Status deines Abonnements zu überprüfen.
러시아어 Срок действия подписки истек, поскольку не удалось произвести автоматическую оплату за подписку.
Перейдите в настройки подписки Google Play, проверьте метод оплаты и повторите вновь.
После оплаты подтвердите статус подписки, нажав на кнопку «Возобновить».
스페인어 La subscripción del servicio ha expirado porque ha fallado el método de pago automático de la subscripción.
Ve a los ajustes de subscripción de tu Google Play, comprueba tu método de pago e inténtalo de nuevo.
Asegúrate de pulsar Restaurar después de comprar para comprobar el estado de tu subscripción.
포르투갈어 O serviço de assinatura expirou porque houve uma falha no pagamento automático da assinatura.
Acesse as suas configurações de assinatura da Google Play, verifique o seu método de pagamento e tente novamente.
Certifique-se de clicar em Recuperar após a compra, para conferir o status da sua assinatura.
인도네시아어 Layanan berlangganan dihentikan karena pembayaran otomatis telah gagal.
Silakan buka pengaturan langganan Google Play, periksa metode pembayaranmu dan coba lagi.
Pastikan kamu klik Pulihkan setelah membeli untuk melihat status berlangganan.
베트남어 Dịch vụ đăng ký đã hết hạn do thanh toán đăng ký tự động không thành công.
Vui lòng vào thiết lập đăng ký của Google Play để kiểm tra phương thức thanh toán của bạn và thanh toán lại.
Đừng quên nhấn vào nút Khôi Phục (Restore) sau khi thanh toán để kiểm tra tình trạng đăng ký của bạn.
태국어 บริการจากการสมัครบริการหมดอายุแล้ว เนื่องจากชำระเงินอัตโนมัติไม่สำเร็จ
กรุณาไปที่หน้าตั้งค่า Subscription ใน Google Play แล้วตรวจสอบวิธีการชำระเงินอีกครั้ง
อย่าลืมกด Restore หลังซื้อ เพื่อตรวจสอบสถานะการสมัครของคุณ
이탈리아어 Il servizio di abbonamento è scaduto perché il pagamento dell’abbonamento automatico è fallito.
Vai alle impostazioni degli abbonamenti di Google Play, controlla il metodo di pagamento e riprova.
Assicurati di fare clic su Ripristina dopo l’acquisto per controllare lo stato dell’abbonamento.
터키어 Abone otomatik ödeme düzgün gerçekleşmediği için abonelik hizmeti sona erdi.
Google Play Abonelik ayarına gidip ödeme yöntemini kontrol edip tekrar ödeme yapınız.
Ödemeyi yaptıktan sonra geri yükle (restore) butonuna tıklayıp abonelik durumunuzu kontrol ediniz.
아랍어 فشل في إجراء عملية الدفع الآلي للاشتراك.
انتقل إلى إعداداتك في غوغل بلاي وتحقق من طريقة الدفع.
  • 보류 상태일 때 알림 팝업 문구 예시
언어 문구
한국어 구독 자동 결제가 정상적으로 이뤄지지 않아 구독 서비스가 만료되었습니다.
Google Play의 구독 설정으로 이동하여 결제 방법을 확인하고 다시 결제를 해주세요.
결제 후엔 반드시 복원(restore)버튼을 클릭하여 구독 상태를 확인해주세요.
영어 The subscription service has expired because the auto subscription payment has failed.
Go to your Google Play subscription settings, check your payment method and try again.
Be sure to click Restore after purchase to check your subscription status.
일본어 購読自動決済が行われなかったため、購読サービスが終了しました。
Google Playの購読設定に移動して決済方法を確認し、もう一度お試しください。
決済した後、必ず【復元】ボタンをタップして購読状態を確認してください。
중국어 간체 由于自动付款失败,订阅服务已到期。
前往谷歌商店的订阅设置,查看付款方式后重新进行付款吧。
付款后请务必点击复原(restore)按键查看订阅状态。
중국어 번체 自動續訂扣款失敗,已取消訂閱服務。
請至Google Play的[訂閱]設定中,確認付款方式後,再次付款。
付款完畢後,請務必點選[回復]按鈕,並確認訂閱狀態。
프랑스어 Le service d’abonnement est interrompu : le paiement automatique ne s’est pas déroulé normalement.
Veuillez procéder de nouveau au paiement en vous rendant dans les paramètres d’abonnement de Google Play.
Après le paiement, confirmez bien le statut de l’abonnement en appuyant sur le bouton “Reprendre”.
독일어 Der Abonnement ist abgelaufen, weil die automatische Zahlung für das Abonnement fehlgeschlagen ist.
Gehe zu den Einstellungen deiner Google Play-Abonnements, überprüfe deine Zahlungsmethode und versuche es erneut.
Bitte stelle sicher, dass du nach dem Kauf auf Wiederherstellen klickst, um den Status deines Abonnements zu überprüfen.
러시아어 Срок действия подписки истек, поскольку не удалось произвести автоматическую оплату за подписку.
Перейдите в настройки подписки Google Play, проверьте метод оплаты и повторите вновь.
После оплаты подтвердите статус подписки, нажав на кнопку «Возобновить».
스페인어 La subscripción del servicio ha expirado porque ha fallado el método de pago automático de la subscripción.
Ve a los ajustes de subscripción de tu Google Play, comprueba tu método de pago e inténtalo de nuevo.
Asegúrate de pulsar Restaurar después de comprar para comprobar el estado de tu subscripción.
포르투갈어 O serviço de assinatura expirou porque houve uma falha no pagamento automático da assinatura.
Acesse as suas configurações de assinatura da Google Play, verifique o seu método de pagamento e tente novamente.
Certifique-se de clicar em Recuperar após a compra, para conferir o status da sua assinatura.
인도네시아어 Layanan berlangganan dihentikan karena pembayaran otomatis telah gagal.
Silakan buka pengaturan langganan Google Play, periksa metode pembayaranmu dan coba lagi.
Pastikan kamu klik Pulihkan setelah membeli untuk melihat status berlangganan.
베트남어 Dịch vụ đăng ký đã hết hạn do thanh toán đăng ký tự động không thành công.
Vui lòng vào thiết lập đăng ký của Google Play để kiểm tra phương thức thanh toán của bạn và thanh toán lại.
Đừng quên nhấn vào nút Khôi Phục (Restore) sau khi thanh toán để kiểm tra tình trạng đăng ký của bạn.
태국어 บริการจากการสมัครบริการหมดอายุแล้ว เนื่องจากชำระเงินอัตโนมัติไม่สำเร็จ
กรุณาไปที่หน้าตั้งค่า Subscription ใน Google Play แล้วตรวจสอบวิธีการชำระเงินอีกครั้ง
อย่าลืมกด Restore หลังซื้อ เพื่อตรวจสอบสถานะการสมัครของคุณ
이탈리아어 Il servizio di abbonamento è scaduto perché il pagamento dell’abbonamento automatico è fallito.
Vai alle impostazioni degli abbonamenti di Google Play, controlla il metodo di pagamento e riprova.
Assicurati di fare clic su Ripristina dopo l’acquisto per controllare lo stato dell’abbonamento.
터키어 Abone otomatik ödeme düzgün gerçekleşmediği için abonelik hizmeti sona erdi.
Google Play Abonelik ayarına gidip ödeme yöntemini kontrol edip tekrar ödeme yapınız.
Ödemeyi yaptıktan sonra geri yükle (restore) butonuna tıklayıp abonelik durumunuzu kontrol ediniz.
아랍어 انتهت صلاحية خدمة الاشتراك بسبب فشل دفع الاشتراك الآلي.
انتقل إلى إعداداتك في غوغل بلاي وتحقق من طريقة الدفع و حاول مرة أخرى.
تأكد من النقر فوق زر استعادة الأشتراك بعد الشراء للتحقق من حالة اشتراكك.
Note

운영체제/마켓별로 지원하는 기능에 차이가 있을 수 있습니다. 자세한 내용은 기능 지원 현황을 확인하세요.

구독형 상품 구매 과정

도식도

연동 플로우

구글 구독 V2 지원

구글 구독 V2를 사용하면 새로운 정기 결제 방식을 사용해 여러 혜택을 만들고 관리할 수 있습니다. 혜택 예시로 첫 구독 시 혜택, 기존에 다른 구독이 있을 때 이 건을 추가 구독함으로 인해 받는 할인 등이 있습니다. 구글 정기 결제에 관한 자세한 내용은 이 곳에서 확인하세요.

구글 구독 V2를 사용하면, 구독 기간과 횟수 등 구독 상태에 따라 결제 가격에 차등적인 혜택을 부여할 수 있습니다. 구글 구독 V2를 사용하려면 다음 과정을 따르세요.

Note

구글 결제 라이브러리 변화에 관한 자세한 내용은 다음을 확인하세요.

Google Play Console에 제품 등록

구글 구독 V2를 사용해 판매할 제품을 Google Play Console에 등록합니다. 자세한 내용은 Play Console Guide를 참고하세요.

getSubscriptionProductInfo로 등록한 제품 정보 확인

IAPV4.getSubscriptionProductInfo 응답값인 IAPV4Product 객체의 originalMarketJson에서 Google Play Console에 등록한 상품 정보를 확인할 수 있습니다.

  • productId: 제품
  • basePlanId: 요금제
  • offerIdToken: 혜택

아래는 "sub003"이라는 상품이 있을 때, 매주와 매월 결제를 하나씩 등록한 후, 매월 결제에 2가지 혜택을 정의했을 때 originalMarketJson에서 확인할 수 있는 정보입니다.

{
    "productId": "sub003",
    "type": "subs",
    "title": "sub003-name (HIVE SDK)",
    "name": "sub003-name",
    "localizedIn": [
        "ko-KR"
    ],
    "skuDetailsToken": "AEuhp4KfxD0MTrwzJV_jl0exSz9NuCZzipMe8mo45lR7pi3y_bWsvXdeKNM04CMxxNY=",
    "subscriptionOfferDetails": [
        {
            "offerIdToken": "AUj\/Yhj7Zx5lt7B0G4qWDtmPFDioPkobZB7zw0xi1A8+B4iD0AbZbwQJKitQLCjR9CMLssbH+g43p4wf3tLREE8H41NgdDpOaCsla0xoTQKPkCsH6+U4XszzPUtOGNwuskbzNjEDfTJoF50j1fSd+IXCkQ==",
            "basePlanId": "baseprice-p1m",
            "offerId": "offer-freetrial",
            "pricingPhases": [
                {
                    "priceAmountMicros": 0,
                    "priceCurrencyCode": "KRW",
                    "formattedPrice": "무료",
                    "billingPeriod": "P1M",
                    "recurrenceMode": 2,
                    "billingCycleCount": 1
                },
                {
                    "priceAmountMicros": 1650000000,
                    "priceCurrencyCode": "KRW",
                    "formattedPrice": "₩1,650",
                    "billingPeriod": "P1M",
                    "recurrenceMode": 2,
                    "billingCycleCount": 2
                },
                {
                    "priceAmountMicros": 3300000000,
                    "priceCurrencyCode": "KRW",
                    "formattedPrice": "₩3,300",
                    "billingPeriod": "P1M",
                    "recurrenceMode": 1
                }
            ],
            "offerTags": [
                "offer-free",
                "offer-freetrial"
            ]
        },
        {
            "offerIdToken": "AUj\/YhjUXnT6K8XbdZd5lUtCSef0HA+qikjGfNPEAXvLeLKWTZ0RqwcFIvR5GhECXTRD1SdUrn0LcaB6oquBo5QeUTEZJGTstWGzfmrwL3oSsvBs1EERERSdJ6NDq54=",
            "basePlanId": "baseprice-p1m",
            "pricingPhases": [
                {
                    "priceAmountMicros": 3300000000,
                    "priceCurrencyCode": "KRW",
                    "formattedPrice": "₩3,300",
                    "billingPeriod": "P1M",
                    "recurrenceMode": 1
                }
            ],
            "offerTags": []
        },
        {
            "offerIdToken": "AUj\/YhhlJDIsjfnvcc5TfxlBjSA+o3QcNsCAjU+DfXBJn2JF\/zLgew468AvchceNBynmB3CvnPLhqlr8be8dJlIvAEkKgdas1DGoiu3p52jSvwsxh8jQLN7QReXUdrY=",
            "basePlanId": "baseprice-p1w",
            "pricingPhases": [
                {
                    "priceAmountMicros": 500000000,
                    "priceCurrencyCode": "KRW",
                    "formattedPrice": "₩500",
                    "billingPeriod": "P1W",
                    "recurrenceMode": 1
                }
            ],
            "offerTags": []
        }
    ]
}

위 예시에서 offerIdToken로 나타나는 혜택 키 값은 총 3개로, 매월 결제 시 혜택 종류 2개와 매주 결제 시 기본 혜택 1개가 있습니다. 매월 결제 시 유저에게 실제 적용되는 혜택은 2개 중 1개입니다.

구독 요청하기

위에서 얻은 IAPV4Product 객체 originalMarketJson값을 사용해 아래와 같이 구독을 요청합니다. 유저에게 해당하는 혜택을 적용하여 정기 결제를 요청할 수 있습니다.

필드명 설명 필수 여부
YOUR_PRODUCT_MARKET_PID originalMarketJsonproductId입니다. Y
YOUR_PRODUCT_OLD_MARKET_PID 유저가 기존에 이미 구독하던 상품이 있을 때 기존 구독 상품의 productId입니다. 현재 요청하는 구독으로 구독을 업그레이드할 떄 필요합니다. N
YOUR_PAYLOAD 게임 스튜디오에서 사전에 정의한 구매 메타 데이터입니다. N
YOUR_OFFER_ID_TOKEN originalMarketJsonofferIdToken입니다. 이 필드에 값을 입력하지 않으면, Hive SDK는 originalMarketJson subscriptionOfferDetails에 있는 첫 번째 offerIdToken을 사용합니다. N

API Reference: IAPV4 .purchaseSubscription

using hive;    
    IAPV4PurchaseParam.Builder builder = new IAPV4.IAPV4PurchaseParam.Builder();    
    IAPV4PurchaseParam param = builder.setMarketPid("YOUR_PRODUCT_MARKET_PID")    
    .setOldMarketPid("YOUR_PRODUCT_OLD_MARKET_PID")    
    .setIapPayload("YOUR_PAYLOAD")    
    .setOfferToken("YOUR_OFFER_ID_TOKEN")    
    .build();    

    IAPV4.purchaseSubscription (param, (ResultAPI result, IAPV4Receipt receipt) => {    
        if (result.isSuccess()) {    
        // TODO: Request verification of receipt with received receipt    
        }    
});
#include "HiveIAPV4.h"

FHiveIAPV4PurchaseParam Param;
Param.MarketPid = TEXT("YOUR_PRODUCT_MARKET_PID");
Param.OldMarketPid = TEXT("YOUR_PRODUCT_OLD_MARKET_PID");
Param.IapPayload = TEXT("YOUR_PAYLOAD");
Param.OfferToken = TEXT("YOUR_OFFER_ID_TOKEN");

FHiveIAPV4::PurchaseSubscription(Param, FHiveIAPV4OnPurchaseDelegate::CreateLambda([this](const FHiveResultAPI& Result, const FHiveIAPV4Receipt& Receipt) {
        if (Result.IsSuccess()) {
                // TODO: 전달받은 receipt로 영수증 검증 요청
        }
}));

API Reference: IAPV4 ::purchaseSubscription

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
    using namespace std;    
    using namespace hive;    
    auto* param = new IAPV4PurchaseParam();    
    param->marketPid = "YOUR_PRODUCT_MARKET_PID";    
    param->oldMarketPid = "YOUR_PRODUCT_OLD_MARKET_PID";    
    param->iapPayload = "YOUR_PAYLOAD";    
    param->offerToken = "YOUT_OFFER_ID_TOKEN";    

    IAPV4::purchaseSubscription(*param, [=](ResultAPI const & result, IAPV4Receipt const & receipt) {    
         if (result.isSuccess()) {    
             // TODO: Request verification of receipt with received receipt    
         }    
});

API Reference: IAPV4.purchaseSubscription

import com.hive.IAPV4    
    import com.hive.ResultAPI    
    val param = IAPV4.IAPV4PurchaseParam.Builder()    
    .setMarketPid("YOUR_PRODUCT_MARKET_PID")    
    .setOldMarketPid("YOUR_PRODUCT_OLD_MARKET_PID")    
    .setIapPayload("YOUR_PAYLOAD")    
    .setOfferToken("YOUR_OFFER_ID_TOKEN")    
    .build()    
    IAPV4.purchaseSubscription(param, object : IAPV4.IAPV4PurchaseListener {    
         override fun onIAPV4Purchase(result: ResultAPI, receipt: IAPV4.IAPV4Receipt?) {    
             if (result.isSuccess) {    
                 // TODO: Request verification of receipt with received receipt    
             }    
         }    
})

API Reference: IAPV4.INSTANCE .purchaseSubscription

import com.hive.IAPV4;    
    import com.hive.ResultAPI;    
    IAPV4.IAPV4PurchaseParam param = new IAPV4.IAPV4PurchaseParam.Builder()    
             .setMarketPid("YOUR_PRODUCT_MARKET_PID")    
             .setOldMarketPid("YOUR_PRODUCT_OLD_MARKET_PID")    
             .setIapPayload("YOUR_PAYLOAD")    
             .setOfferToken("YOUR_OFFER_ID_TOKEN")    
             .build();    
    IAPV4.INSTANCE.purchaseSubscription(param, (result, iapV4Receipt) -> {    
         if (result.isSuccess()) {    
             // TODO: Request verification of receipt with received receipt    
         }    
});

API Reference: IAPV4Interface.purchaseSubscription

import HIVEService    
    let param = IAPV4PurchaseParam.Builder()    
    .setMarketPid("YOUR_PRODUCT_MARKET_PID")    
    .setOldMarketPid("YOUR_PRODUCT_OLD_MARKET_PID")    
    .setIapPayload("YOUR_PAYLOAD")    
    .setOfferToken("YOUR_OFFER_ID_TOKEN")    
    .build()    

    IAPV4Interface.purchaseSubscription(param) { result, receipt in    
        if result.isSuccess() {    
        // TODO: Request verification of receipt with received receipt    
        }    
}

API Reference: HIVEIAPV4 purchaseSubscription

#import <HIVEService/HIVEService-Swift.h>    
    HIVEIAPV4PurchaseParamBuilder *builder = [[HIVEIAPV4PurchaseParamBuilder alloc] init];    
    HIVEIAPV4PurchaseParam *param = [[[[[builder setMarketPid: @"YOUR_PRODUCT_MARKET_PID"]    
    setIapPayload: @"YOUR_PRODUCT_OLD_MARKET_PID"]    
    setOldMarketPid: @"YOUR_PAYLOAD"]    
    setOfferToken: @"YOUR_OFFER_ID_TOKEN"]    
    build];    

    [HIVEIAPV4 purchaseSubscription: param handler: ^(HIVEResultAPI *result, HIVEIAPV4Receipt *receipt) {    
        if ([result isSuccess]) {    
        // TODO: Request verification of receipt with received receipt    
        }    
}];
Note

첫 구독 시 혜택을 부여할 때, 결제하는 Google PlayStore 계정 기준으로 첫 구독이어야만 혜택을 부여할 수 있습니다.

 

구독형 상품 API

Warning

구독형 결제 시스쳄을 적용하려면 먼저 컴투스플랫폼 솔루션아키텍트팀에 문의해 주세요.

구독형 상품 목록 조회

게임 내 구독형 상품 상점에 진입할 때 구독형 상품 목록 조회 API인 IAPV4.getSubscriptionProductInfo()메서드를 호출해야 합니다. API를 호출하면 상품 목록과 상품 정보가 들어있는 IAPV4Product 객체를 결과값으로 받습니다. IAPV4Product 객체는 상품 목록 조회에서 전달하는 객체와 거의 동일합니다.

IAPV4Product 객체

필드명 설명
marketPid 마켓에 등록된 고유 상품 ID
currency 통화 코드 (예. KRW / USD)
price 상품 가격
title 상품의 타이틀
displayPrice 상품 가격 문자열 (예. $100.00) 자세히
productDescription 상품 설명
originalMarketJson 마켓에서 받은 상품 정보 원문
Google 구독 V2 사용 시 Google Play Console에 정의한 제품 정보를 확인할 수 있음
displayOriginalPrice 할인 전 가격 문자열
Google 전용이며 Hive SDK v4.12.0 이후부터 사용 가능
originalPrice 할인 전 가격
iconURL 상품 아이콘 (512*512)
Google 전용이며 Hive SDK v4.12.0 이후부터 사용 가능

displayPrice

  • 통화 기호와 가격을 조합한 형태(예: $0.99)로 displayPrice를 전달합니다. Android와 iOS의 노출 방식이 다르기 때문에 일부 국가에서는 OS에 따라 통화 기호와 가격 순서가 다를 수 있습니다.
  • 각 나라의 통화기호는 통화기호표와 동일하게 출력합니다. 통화기호는 Hive 식별자 정책을 참고하세요.
  • 다른 나라에서 부가세를 추가하거나 변동하는 경우에 서버에서 계산하여 displayPrice, price, displayOriginalPrice, originalPrice에 반영하므로 추가 작업이 필요 없습니다.
Note

Google은 price 값과 originalPrice 값이 다르면 할인 전 원가를 표기할 수 있습니다. 두 값이 같다면 할인 상태가 아님을 의미합니다.
Apple에서는 iconURL 값을 제공하지 않습니다.
originalPrice, displayOriginalPrice는 Google(1.12.0.0+), Apple(1.24.0.0+) 전용이며, price 값과 originalPrice 값이 같다면 할인 상태가 아님을 의미합니다. 반대로 두 값이 다르다면 할인 중이므로 할인가와 원가를 따로 표기하는 등의 활용이 가능합니다.

다음은 getSubscriptionProductInfo()메서드 구현 예제입니다.

API Reference: IAPV4::getSubscriptionProductInfo

```None linenums="1"
// Request to search subscription lists
IAPV4::getSubscriptionProductInfo([=](ResultAPI const & result, std::vector const & productInfoList, unsigned int balance) {
        if (result.isSuccess()) {
                for( auto productInfo : productInfoList) {
                        // Implementation of the desired features by the game studio, such as shop UI.
                }
        }
        else {
                // Error Handling
        }
});
```
#include "HiveIAPV4.h"

FHiveIAPV4::GetSubscriptionProductInfo(FHiveIAPV4OnProductInfoDelegate::CreateLambda([this](const FHiveResultAPI& Result, const TArray<FHiveIAPV4Product>& ProductInfoList, uint32 Balance) {

        if (Result.IsSuccess()) {
                for (const auto& ProductInfo : ProductInfoList) {
                        // 상점 UI 구성 등 게임 스튜디오에서 원하는 기능 구현
                }
        } else {
                // Error Handling
        }
}));

API Reference: com.hive.IAPV4.getSubscriptionProductInfo

// Request to search subscription lists    
    IAPV4.getSubscriptionProductInfo(new IAPV4.IAPV4ProductInfoListener() {    
        @Override    
        public void onIAPV4ProductInfo(ResultAPI result, final List iapV4ProductList, final int balance) {    
            if (result.isSuccess()) {    
                for (IAPV4Product productInfo : iapV4ProductList) {    
                // Implementation of the desired features by the game studio, such as shop UI.    
                }    
            } else {    
                // Error Handling    
            }    
        }    
});

API Reference: HIVEIAPV4::getSubscriptionProductInfo:

// Request to search subscription lists
    IAPV4.getSubscriptionProductInfo(new IAPV4.IAPV4ProductInfoListener() {
            @Override
            public void onIAPV4ProductInfo(ResultAPI result, final List iapV4ProductList, final int balance) {
                    if (result.isSuccess()) {
                            for (IAPV4Product productInfo : iapV4ProductList) {
                            // Implementation of the desired features by the game studio, such as shop UI.
                            }
                    } else {
                            // Error Handling
                    }
            }
    });

API Reference: HIVEIAPV4::getSubscriptionProductInfo:

    // Request to search subscription lists
    [HIVEIAPV4 getSubscriptionProductInfo:^(HIVEResultAPI *result, NSArray *productInfoList, NSUInteger balance) {
            if( [result isSuccess]) {
                    for( HIVEIAPV4Product* productInfo in productInfoList) {
                            // Implementation of the desired features by the game studio, such as shop UI.
                    }
            } else {
                    // Error Handling
            }

    }];
Warning

getSubscriptionProductInfo() 메서드 호출 실패 시 상품 페이지 구현에 구독형 상품 정보를 활용할 수 없습니다.
실패 Result API 코드를 받았다면, 게임 클라이언트에서 성공 콜백을 받을 때까지 재시도하거나 에러 상황(마켓 사용 불가)을 팝업으로 사용자에게 노출하는 등을 자체적으로 구현하여 대응해야 합니다. IAP v4 Result API 코드 가이드에서 관련 Result API 코드를 확인하세요.

상품 구매

Apple App Store, Google Play Store에 등록된 marketPid를 매개변수로 IAPV4 클래스의 purchaseSubscriptionUpdate() 메서드를 호출하여 구독형 상품 구매를 요청합니다.

Note

기존에 구독 중인 상품이 있어 구독 변경이 필요한 경우에는 oldMarketPid에 구독 중인 상품의 marketPid를 입력하세요. 이 기능은 Android에서만 동작합니다.

다음은 purchaseSubscriptionUpdate() 메서드 구현 예제입니다.

API Reference: IAPV4::purchaseSubscriptionUpdate

```unity linenums="1"
std::string marketPid = "{YOUR_PRODUCT_MARKET_PID}";
std::string oldMarketPid = "{YOUR_PRODUCT_OLD_MARKET_PID}";
std::string iapPayload = "{YOUR_PRODUCT_IAP_PAYLOAD}";

// Request to purchase subscription products
IAPV4::purchaseSubscriptionUpdate(marketPid, oldMarketPid, iapPayload, [=](ResultAPI const & result, IAPV4Receipt const & receipt) {
        if (result.isSuccess()) {
                                // Request to verify the receipt with the received receipt
        }
        else {
                // Error Handling
        }
});
```

API Reference: IAPV4::purchaseSubscriptionUpdate

```c++ linenums="1"
std::string marketPid = "{YOUR_PRODUCT_MARKET_PID}";
std::string oldMarketPid = "{YOUR_PRODUCT_OLD_MARKET_PID}";
std::string iapPayload = "{YOUR_PRODUCT_IAP_PAYLOAD}";

// Request to purchase subscription products
IAPV4::purchaseSubscriptionUpdate(marketPid, oldMarketPid, iapPayload, [=](ResultAPI const & result, IAPV4Receipt const & receipt) {
        if (result.isSuccess()) {
                                // Request to verify the receipt with the received receipt
        }
        else {
                // Error Handling
        }
});
```

API Reference: com.hive.IAPV4.purchaseSubscriptionUpdate

val marketPid = "{YOUR_PRODUCT_MARKET_PID}"    
    val oldMarketPid = "{YOUR_PRODUCT_OLD_MARKET_PID}"    
    val iapPayload = "{YOUR_PRODUCT_IAP_PAYLOAD}"    


    // Request to purchase subscription products    
    IAPV4.purchaseSubscriptionUpdate(pid, oldMarketPid, iapPayload, object : IAPV4.IAPV4PurchaseListener {    
        override fun onIAPV4Purchase(result: ResultAPI, iapV4Receipt: IAPV4.IAPV4Receipt?) {    
            if (result.isSuccess()) {    
                    // Request to verify the receipt with the received receipt    
            } else {    
                   // Error Handling    
            }    
        }    
})

API Reference: com.hive.IAPV4.purchaseSubscriptionUpdate

String marketPid = "{YOUR_PRODUCT_MARKET_PID}";    
    String oldMarketPid = "{YOUR_PRODUCT_OLD_MARKET_PID}";    
    String iapPayload = "{YOUR_PRODUCT_IAP_PAYLOAD}";    


    // Request to purchase subscription products    
    IAPV4.purchaseSubscriptionUpdate(pid, oldMarketPid, iapPayload, new IAPV4.IAPV4PurchaseListener() {    
        @Override    
        public void onIAPV4Purchase(ResultAPI result, IAPV4.IAPV4Receipt iapV4Receipt) {    
            if (result.isSuccess()) {    
                    // Request to verify the receipt with the received receipt    
            } else {    
                   // Error Handling    
            }    
        }    
});

API Reference: HIVEIAPV4::purchaseSubscriptionUpdate:oldMarketPid:additionalInfo:handler:

let marketPid = "{YOUR_PRODUCT_MARKET_PID}"    
    let oldMarketPid = "{YOUR_PRODUCT_OLD_MARKET_PID}"    
    let iapPayload = "{YOUR_PRODUCT_IAP_PAYLOAD}"    

    // Request to purchase subscription products    
    HIVEIAPV4.purchaseSubscriptionUpdate(marketPid, oldMarketPid: oldMarketPid, iapPayload: iapPayload) { result, receipt in    
        if result.isSuccess() {    
            // Request to verify the receipt with the received receipt    
        } else {    
            // Error Handling    
        }    
}

API Reference: HIVEIAPV4::purchaseSubscriptionUpdate:oldMarketPid:additionalInfo:handler:

NSString* marketPid = @"{YOUR_PRODUCT_MARKET_PID}";    
    NSString* oldMarketPid = @"{YOUR_PRODUCT_OLD_MARKET_PID}";    
    NSString* iapPayload = @"{YOUR_PRODUCT_IAP_PAYLOAD}";    

    // Request to purchase subscription products    
    [HIVEIAPV4 purchaseSubscriptionUpdate:marketPid oldMarketPid:oldMarketPid iapPayload:iapPayload handler:^(HIVEResultAPI *result, HIVEIAPV4Receipt *receipt) {    
        if ([result isSuccess]) {    
                    // Request to verify the receipt with the received receipt    
        } else {    
                   // Error Handling    
        }    
}];

구독 상태 복구

구독 중인 상품의 지급이 이루어지지 않았다면 구독 상태를 복구하는 restoreSubscription() 메서드를 호출해 이전에 구매한 구독 상품의 영수증을 요청해야 합니다.

다음은 restoreSubscription() 메서드 구현 예제입니다.

API Reference: hive .IAPV4.restoreSubscription

using hive;    
    IAPV4.restoreSubscription((ResultAPI result, List<IAPV4Receipt> receiptList) => {    
    if (result.isSuccess()) {    
    // TODO: Request receipt verification using the received receiptList    
         }    
});
#include "HiveIAPV4.h"

FHiveIAPV4::RestoreSubscription(FHiveIAPV4OnRestoreDelegate::CreateLambda([this](const FHiveResultAPI& Result, const TArray<FHiveIAPV4Receipt>& IAPV4ReceiptList) {
        if (Result.IsSuccess()) {
                // TODO: 전달받은 IAPV4ReceiptList 로 영수증 검증 요청
        }
}));

API Reference: IAPV4 ::restoreSubscription

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
    using namespace std;    
    using namespace hive;    
    IAPV4::restoreSubscription([=](ResultAPI const & result, vector<reference_wrapper<IAPV4Receipt>> receiptList) {    
         if (result.isSuccess()) {    
    // TODO: Request receipt verification using the received receiptList    
         }    
});

API Reference: IAPV4.restoreSubscription

import com.hive.IAPV4    
    import com.hive.ResultAPI    
    IAPV4.restoreSubscription(object : IAPV4.IAPV4RestoreListener {    
         override fun onIAPV4Restore(result: ResultAPI, iapv4ReceiptList: ArrayList<IAPV4.IAPV4Receipt>?) {    
             if (result.isSuccess) {    
                 // TODO: Request receipt verification using the received iapv4ReceiptList    
             }    
         }    
})

API Reference: com .hive.IAPV4.restoreSubscription

import com.hive.IAPV4;    
    import com.hive.ResultAPI;    
    IAPV4.INSTANCE.restoreSubscription((result, iapv4ReceiptList) -> {    
         if (result.isSuccess()) {    
             // TODO: Request receipt verification using the received iapv4ReceiptList    
         }    
});

API Reference: IAPV4Interface .restoreSubscription

import HIVEService    
    IAPV4Interface.restoreSubscription() { result, receiptList in    
    if result.isSuccess() {    
    // TODO: Request receipt verification using the received receiptList    
    }    
}

API Reference: HIVEIAPV4 ::restoreSubscription:

#import <HIVEService/HIVEService-Swift.h>    
    [HIVEIAPV4 restoreSubscription: ^(HIVEResultAPI *result, NSArray<HIVEIAPV4Receipt *> *receiptList) {    
         if ([result isSuccess]) {    
    // TODO: Request receipt verification using the received receiptList    
         }    
}];