유니티용 now.gg 결제 모듈과 함께 유니티 내에서 인앱 구매를 사용하실 수 있습니다.

방법:

  1. now.gg 결제 유니티 모듈을 다운로드하고 임포트합니다.
  2. 필수 종속성을 추가합니다.
  3. now.gg 결제 모듈을 적용합니다.

다운로드 및 임포트

now.gg 결제 유니티 모듈은 유니티 패키지 파일 nowgg-payments-login.unitypackage에 포함되어 있습니다.

방법:

  1. 최신 버전의 유니티용 now.gg 결제 모듈이 포함된 패키지를 다운로드합니다.
  2. 모듈을 다운로드한 후 프로젝트에 임포트합니다.
    • Assets > Import Package > Custom Package를 클릭하세요.
      • 다운로드한 nowgg-payments-login.unitypackage를 선택합니다.
    • 나열된 파일을 모두 선택하고 임포트를 클릭합니다.
  3. 필요한 종속성 추가
    • Assets > External Dependency Manager > Android Resolver > Resolve를 클릭하세요.
      이 작업은 Unity External Dependency Manager를 사용하여 필요한 모든 종속성을 프로젝트의 Assets/Plugins/Android 디렉토리에 다운로드 및 추가합니다.

중요 정보

  • 모든 모듈 파일이 가져오면, ‘NowGGSdk‘라는 폴더가 프로젝트에 추가됩니다. 이 폴더는 Assets 폴더의 루트에 있습니다.
  • NowGGSdk 폴더를 수정하지 마세요. 이 폴더에는 now.gg 결제와 관련된 모든 자산이 포함되어 있습니다.

now.gg 결제 모듈 적용

now.gg 결제 모듈을 임포트하고 종속성이 추가되면 이제 앱/게임에 now.gg 결제를 추가하실 수 있습니다.

IAP 플랫폼 체크 추가

now.gg IAP 서비스를 실행하려면 now.gg 플랫폼이 필요하며 유니티 네이티브 IAP(또는 기타 IAP 서비스)는 now.gg 플랫폼에서 실행되지 않을 때만 초기화 및 실행되어야 합니다.

플랫폼을 확인하고 앱이 now.gg 플랫폼에서 실행될 때 유니티 네이티브 IAP가 활성화되지 않도록 다음 코드를 추가합니다.

public void Start()
 {
 	 if (NowGG.Sdk.NowGGPaymentsSdkManager.IsNowGGIapAvailable())
            return;

	 // 여기에 코드를 작성합니다.
 }

유니티에 now.gg IAP 적용

아래는 유니티 내에서 호출할 수 있는 now.gg IAP 함수 예제입니다.

now.gg IAP가 포함된 클래스를 생성하고 인앱 구매를 초기화하는 데 사용할 게임 오브젝트에 추가합니다.

1. 상품 추가 및 SDK 초기화

이 단계에서는 앱/게임에 인앱 구매 제품을 추가하고 SDK를 초기화할 수 있습니다.예를 들어, 다음 코드에서는 productId ‘coin1’과 ‘coin2’라는 두 개의 IAP 소모품 제품과 productId ‘monthlyPack’이라는 구독을 추가했습니다.
마찬가지로, nowStudio에 추가한 해당 productId를 사용하여 다른 제품 및 구독을 추가할 수 있습니다. 아래 샘플 코드에 예시되어 있습니다:

public void Start()
  {
	 // Only initialize Now.gg IAP if running on Now.gg platform
	 if (!NowGG.Sdk.NowGGPaymentsSdkManager.IsNowGGIapAvailable())
		 return;

     // 소모품의 경우
	 NowGGProductBuilder.Instance.AddProduct("coin1", ProductType.Consumable);
	 NowGGProductBuilder.Instance.AddProduct("coin2", ProductType.Consumable);

     // 구독의 경우
     NowGGProductBuilder.Instance.AddProduct("monthlyPack", ProductType.Subscription);

     NowGGPaymentsSdkManager.Instance.OnInitSuccess += OnInitSuccess;
	 NowGGPaymentsSdkManager.Instance.OnInitFailed += OnInitFailed;
	 NowGGPaymentsSdkManager.Instance.OnPurchaseCompleted += OnPurchaseProduct;
     NowGGPaymentsSdkManager.Instance.OnPurchaseFailed += OnPurchaseFailed;

	 NowGGPaymentsSdkManager.Instance.InitializeIap(PAYMENT_ID);
 }

설명:

  • now.gg 플랫폼에서 now.gg 결제 서비스를 초기화하고 있는지 확인하기 위해 초기 점검이 수행됩니다.
  • 앱 내 구매 제품과 구독이 NowGGProductBuilder 클래스에 추가되었습니다.
  • PAYMENT_ID를 사용하여 결제 모듈을 초기화하기 위해 NowGGPaymentsSdkManager 클래스의 InitializeIap 함수가 호출됩니다.
  • SDK 콜백 메시지는 위와 같이 콜백 함수를 사용하여 처리할 수 있습니다.
  • 참조:NowGGPaymentsSdkManager

중요:

  • PAYMENT_ID는 앱의 고유 식별자입니다.
    • nowStudio에 추가되는 각 앱은 고유한 PAYMENT_ID를 생성합니다.
    • PAYMENT_ID는 nowStudio 자격 증명의 ‘결제용 APP_ID‘에서 확인하실 수 있습니다. (자세히)
  • 환불 처리 – now.gg 결제와 관련된 환불 절차는 여기에 설명되어 있습니다.

2. 상품 세부정보 호출

초기화가 성공적으로 완료되면 productId를 사용하여 상품의 세부정보를 얻을 수 있습니다.

아래는 예시입니다.

private void OnInitSuccess()
 {
    Debug.Log($"IAP init success");
    Product coin1 = NowGGPaymentsSdkManager.Instance.GetProductWithID("coin1");
    
    // now you can get product details using productId
    string price = coin1.price;
    string currencyCode = coin1.priceCurrencyCode;
    
    Product monthlyPack = NowGGPaymentsSdkManager.Instance.GetProductWithID("monthlyPack");
    string monthlyPackSubscriptionPeriod = monthlyPack.subscriptionPeriod;
 }

 private void OnInitFailed(string reason)
 {
   Debug.Log($"IAP init failed {reason}");
 }

참조:


3. 구매 프로세스 시작

상품의 세부정보를 확인한 후 구매를 시작하기 위해 아래와 같이 PurchaseProduct() 를 호출합니다.

public void PurchaseProduct(string productId, string developerPayload=null)
 {
     NowGGPaymentsSdkManager.Instance.PurchaseProduct(productId, developerPayload);
 }

참조:

중요

  • developerPayload는 선택 사항입니다.
  • ProductId는 특정 앱 (앱 ID)을 위해 nowStudio에서 정의한 앱 내 제품구독의 고유 식별자입니다.
  • 상품의 가격은 nowStudio에서 생성한 가격 템플릿에서 가져옵니다.

4. 구매 권한 할당

구매가 성공적으로 완료되면 유저에게 상품을 할당하실 수 있습니다.

방법:

1. 소비성 상품

방법:

2. 구독

방법:


Using PurchaseProcessingResult

private PurchaseProcessingResult OnPurchaseProduct(PurchasedProduct purchasedProduct)
 {
     string orderId = purchasedProduct.orderId;
     string purchaseToken = purchasedProduct.purchaseToken;
     string developerPayload = purchasedProduct.developerPayload;

     // 유저가 소모성 아이템을 구매했습니다.
     if (String.Equals(purchasedProduct.productId, "coin1", StringComparison.Ordinal))
     {
         Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId));
         // 성공적으로 구매한 소모성 아이템인 100코인을 유저의 게임 내 재화에 추가합니다.
         // 예: coin += 100;
     }
     else if (String.Equals(purchasedProduct.productId, "coin2", StringComparison.Ordinal))
     {
         Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId));
         // 성공적으로 구매한 소모성 아이템인 200코인을 유저의 게임 내 재화에 추가합니다.
         // 예: coin += 200;
     }
     else if (String.Equals(purchasedProduct.productId, "monthlyPack", StringComparison.Ordinal))
     {
         Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId));
         Debug.Log("Subscription: " + purchasedProduct.subscriptionStatus + " " + purchasedProduct.subscriptionPeriod + " " + purchasedProduct.expiryTimeMillis + " " + purchasedProduct.subscriptionPurchaseDateMillis);
     
         if (purchasedProduct.subscriptionStatus == "ACTIVE" && purchaseProduct.isAcknowledged == false)
         {
            // 구독이 성공적으로 구매되었습니다. 구독의 혜택을 플레이어에게 전달하십시오.
            // 구매한 구독의 상태, 청구 기간, 만료를 반영하도록 UI를 업데이트하십시오.
            // 구매가 확인되었음을 인정하기 위해 PurchaseProcessingResult.Complete을 반환합니다 (구독).
         }
     }
     else
     {
         Debug.Log(string.Format("OnPurchaseProduct: FAIL. Unrecognized product: '{0}'", purchasedProduct.productId));
     }

     // 해당 상품을 받았는지 또는 다음 앱 실행 시 애플리케이션이 해당 구매를 상기시켜야 하는지 여부를 나타내는 플래그를 반환합니다.
     // 다음에 앱/게임을 실행할 때 해당 구매에 대해 알림을 받기 위해 PurchaseProcessingResult.Pending을 반환합니다.
     // PurchaseProcessingResult.Complete를 반환하여 구매가 소비(소모품) 또는 승인(구독)되었으며 구매 혜택을 사용자에게 전달했음을 확인합니다.

     return PurchaseProcessingResult.Complete;
 }

참고:

  • 백엔드 서버에서 구매를 처리하거나 구현에 따라 다른 방법으로 구매를 완료해야 하는 경우, 앱은 OnPurchaseProduct에서 PurchaseProcessingResult.Pending을 반환해야 합니다.
    • 대기 중인 구매가 있는 경우 앱/게임을 다음에 실행할 때 다시 알림이 표시됩니다.
  • 대기 중인 구매를 확인하고 권한을 부여하려면 – 이 섹션을 참조하십시오.

참조:

중요

  • purchaseToken은 성공적인 구매 후에 생성되며 고유 식별자입니다.
  • 모든 주문 시 orderId도 생성됩니다.
    • 주문 ID 및 기타 관련 정보가 포함된 모든 구매 영수증도 유저에게 이메일로 전송됩니다.

Using consumePurchase API – 소비성 상품

백엔드 서버가 있는 앱의 서버측 구매 소비 시 consumePurchase API를 사용하시기 바랍니다.

구매를 인증한 후에는 소비됨으로 표시해야 합니다.

구매 소비에는 다음이 포함됩니다.

  • 구매한 제품을 유저에게 할당합니다.
  • 제품이 소비되었음을 now.gg에 알립니다.
샘플 코드

다음 샘플 코드는 consumePurchase API를 통한 관련 요청 방법을 보여드립니다

import requests

 url = "https://payments-api.now.gg/v2/order/consumePurchase"

 headers = {
     "Authorization": "<payment_api_key_here>",
     "Content-Type": "application/x-www-form-urlencoded"
 }

 data = {
     "purchaseToken": "<purchase_token_here>",
     "productId": "<product_id_here>", // 멀티 스토어에만 필요
     "developerPayload": "developerPayload",  // 선택 사항 멀티 스토어에만 필요
     "currency": "<currency>", // 멀티 스토어에만 필요
     "type": "<store>"  // xiaomi, onestore. 존재하지 않을 경우 기본값은 nowgg로 설정됩니다.
 }

 response = requests.post(url, headers=headers, data=data)

 print("응답 상태 코드:", response.status_code)
 print("응답 본문:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/order/consumePurchase' \
 --header 'Authorization: <payment_api_key_here>' \
 --header 'Content-Type': 'application/x-www-form-urlencoded' \
 --data '{
     "purchaseToken": "<purchase_token_here>",
     "productId": "<product_id_here>", //멀티 스토어에만 필요
     "developerPayload" : developerPayload, // 선택 사항, 멀티 스토어에만 필요
     "currency": "<currency>", // 멀티 스토어에만 필요
     "type": "<store>"  // xiaomi | onestore 존재하지 않을 경우 기본값은 nowgg로 설정됩니다.
 }'

참조:

중요

  • 구매한 제품을 consumePurchase API를 호출하기 전에 사용자에게 할당해야 합니다.
  • API 키Payments API Key는 nowStudio의 자격 증명에 있습니다. (자세히)
  • 구매가 성공적으로 완료되면 purchaseToken 가 반환됩니다. 더 자세한 내용은 다음 문서를 참조하시기 바랍니다.
  • 구매일로부터 3일 이내에 권한이 부여되지 않으면 거래가 취소되고 사용자에게 환불이 시작됩니다.

Using acknowledgePurchase API – 구독

acknowledgePurchase API는 백엔드 서버가 있는 앱의 서버 측 구매 승인을 위해 사용되어야 합니다.

구매를 확인한 후에는 이를 승인된 것으로 표시해야 합니다.

Sample Acknowledge Purchase Code

다음 샘플 코드는 acknowledgePurchase API를 통한 관련 요청 방법을 보여드립니다

import requests

  url = "https://payments-api.now.gg/v2/seller/order/acknowledgepurchase"

  payload = 'purchaseToken=nowgg-_purchase_token'
  headers = {
    'Authorization': 'payment_api_key_here',
    'Content-Type': 'application/x-www-form-urlencoded'
  }

  response = requests.request("POST", url, headers=headers, data=payload)

  print(response.text)

참조:

Important Information

  • API 키Payments API Key는 nowStudio의 자격 증명에 있습니다. (자세히)
  • 구매가 성공적으로 완료되면 purchaseToken 가 반환됩니다. 더 자세한 내용은 다음 문서를 참조하시기 바랍니다.
  • 구매일로부터 3일 이내에 권한이 부여되지 않으면 거래가 취소되고 사용자에게 환불이 시작됩니다.

5. 대기 중인 구매 확인

백엔드 서버에서 구매를 처리하거나 구현에 따라 다른 방법을 사용하여 구매를 완료해야 하는 경우 OnPurchaseProduct에서 PurchaseProcessingResult.Pending을 반환합니다.

클라이언트 측

구매 흐름을 완료하려면 ConfirmPendingPurchase 메서드를 사용하여 now.gg IAP 서비스에 앱이 구매를 기록했음을 알릴 수 있습니다.

public void ConfirmPendingPurchase(string purchasetoken, ProductType productType) {
   if (productType == ProductType.Consumable) {
       ConsumeProduct(purchasetoken);
   } else {
       AcknowledgePurchase(purchasetoken);
   }
 }

참조:

서버 측

앱 백엔드 서버를 사용하여 구매 흐름을 완료하고 구매 권한을 부여하려면 다음을 호출하십시오:

중요 정보

  • 대기 중인 구매가 확인되면 now.gg IAP 서비스는 해당 구매에 대해 앱에 알리지 않습니다.
    • 대기 중인 구매가 있는 경우 앱/게임을 다음에 실행할 때 다시 알림이 표시됩니다.
  • 구독 기간은 인식된 날짜부터 시작됩니다.
  • 구매일로부터 3일 이내에 권한이 부여되지 않으면 거래가 취소되고 사용자에게 환불이 시작됩니다.

6. 대기 중인 구매 호출

대기 중인 구매를 위해 NowGGPaymentsSdkManager 클래스의 QueryPendingPurchases를 호출하실 수 있으며 이때 OnPurchaseProduct 콜백을 통해 처리하실 수 있습니다.

NowGGPaymentsSdkManager.Instance.QueryPendingPurchases();

참조: OnPurchaseProduct.


7. 추가 상품 호출

구매할 추가 상품을 가져오거나 기존 상품의 세부 정보/메타데이터를 갱신하려면 아래와 같이 FetchAdditionalProducts를 사용하실 수 있습니다.

var newProducts = new System.Collections.Generic.Dictionary<string, ProductType>();
 newProducts.Add("coin2", ProductType.Consumable);
 newProducts.Add("coin3", ProductType.Consumable);

 NowGGPaymentsSdkManager.Instance.FetchAdditionalProducts(newProducts,
       () =>
       {
           // 가져온 추가 상품을 이제 구매할 수 있습니다.
           Debug.Log($"Successfully fetched additional products");
           Product coin1 = NowGGPaymentsSdkManager.Instance.GetProductWithID("coin1");
           string price = coin1.price;
           string currencyCode = coin1.priceCurrencyCode;
       },
      reason =>
      {
          Debug.Log($"Fetching additional products failed: {reason}");
      });

8. 실패한 구매 처리

다음을 포함한 여러 가지 이유로 인해 구매가 실패할 수도 있습니다.

      • 네트워크/연결 문제
      • 결제 실패
      • 설정 관련 문제

아래와 같이 실패한 구매를 확인하고 처리하실 수 있습니다.

public void OnPurchaseFailed(int errorCode, string errorMessage)
 {
    Debug.Log($"OnPurchaseFailed: errorCode: {errorCode} and msg: {errorMessage}");
 }

중요:

  • OnPurchaseFailed 관련 응답 코드는 여기에서 참조하실 수 있습니다.

9. 구독 상태 콜백 API

(개발자 제공)

구독 상태 업데이트에 대한 알림을 보내기 위해 SubscriptionStatusCallback API를 제공하는 데 필요한 API입니다.
다음은 관련 워크플로입니다:

  • nowStudio는 이 API를 사용하여 요청 데이터를 게임 백엔드로 보냅니다.
  • 귀하의 게임 백엔드는 당사가 제공한 API 키를 사용하여 요청을 인증할 수 있습니다. (웹훅 API 키).
  • 구독의 현재 상태에 따라 사용자의 구매 권한을 할당할 수 있습니다. (여기에서 정의됨).
  • 제공한 콜백 URL을 사용하여 요청 데이터를 보낸 후, 귀하의 API에서 응답을 기다립니다.
    • 상태 코드 200과 성공 여부가 true인 응답을 받지 못하면 일정 시간 동안 재시도합니다.

중요: Subscription Status Callback API.



구매 인증

구매 인증을 통해 구매의 진위 여부를 확인할 수 있으며 이때 사기를 줄이는 가장 좋은 방법은 상품을 유저에게 할당하기 전 모든 구매를 인증하는 것입니다.

다음 두 가지 방법 중 하나를 사용하여 구매를 인증하실 수 있습니다.

  • verifyPurchase API
  • 공개 키

1. VerifyPurchase API

구매를 인증하는 첫 번째 방법은 verifyPurchase API를 사용하는 것으로 해당 API를 사용하여 백엔드 서버를 통해 구매를 인증하실 수 있습니다.

먼저 아래와 같이 앱 백엔드 서버에서 purchaseToken 와 함께 verifyPurchase API를 호출합니다.

import requests

 url = "https://payments-api.now.gg/v2/sellers/order/verifyPurchase"

 headers = {
     "Authorization": "<payment_api_key_here>",
     "Content-Type": "application/x-www-form-urlencoded"
 }

 data = {
     "purchaseToken": "<purchase_token_here>", 
     "productId": "<product_id_here>", // 멀티 스토어에만 필요
     "currency": "<currency>", // 멀티 스토어에만 필요
     "type": "<store>"  // 选项: xiaomi, onestore, amazon, huawei. 존재하지 않을 경우 기본값은 nowgg로 설정됩니다.
 }

 response = requests.post(url, headers=headers, data=data)

 print("响应状态码:", response.status_code)
 print("响应正文:", response.text)
curl --location --request POST 'https://payments-api.now.gg/v2/sellers/order/verifyPurchase' \
 --header 'Authorization: <payment_api_key_here>' \
 --header 'Content-Type': 'application/x-www-form-urlencoded' \
 --data '{
     "purchaseToken": "<purchase_token_here>",
     "productId": "<product_id_here>", // 멀티 스토어에만 필요
     "currency": "<currency>", // 멀티 스토어에만 필요
     "type": "<store>"  // xiaomi | onestore | amazon | huawei 존재하지 않을 경우 기본값은 nowgg로 설정됩니다.
 }'

중요

  • verifyPurchase API는 앱 백엔드 서버에서 호출되어야 합니다.
  • 구매가 성공적으로 완료되면 purchaseToken 가 반환됩니다. 더 자세한 내용은 다음 문서를 참조하시기 바랍니다.

2. 공개 키로 구매 인증

구매는 로컬 또는 백엔드 서버를 통해 인증하실 수 있으며, 이때 백엔드 서버를 사용하여 구매를 인증하시는 것을 권장해 드립니다.

  • 여기에 나열된 단계에 따라 공개 키를 생성할 수 있습니다.

아래와 같이 로컬 또는 백엔드 서버를 통해 인증하실 수 있으며 자세한 내용은 데모 프로젝트를 참조하시기 바랍니다.

private PurchaseProcessingResult OnPurchaseProduct(PurchasedProduct purchasedProduct) {
    // 로컬 인증을 하고 싶다면 해당 샘플 코드를 사용하실 수 있습니다.
    bool isValidPurchase = PurchaseVerification.Instance.VerifyPurchaseLocally(
        BASE_64_ENCODED_PUBLIC_KEY,
        purchasedProduct.originalJson,
        purchasedProduct.signature); 

    // 백엔드 서버에서 구매를 인증하시는 것을 권장해 드립니다.
    // 인증을 위해 서명과 원본 데이터를 백엔드 서버로 보냅니다.
    PurchaseVerification.Instance.VerifyValidSignatureOnBackend(
        purchasedProduct.originalJson,
        purchasedProduct.signature);

    // 유저가 소모성 아이템을 구매했습니다.
    if (String.Equals(purchasedProduct.productId, "coin1", StringComparison.Ordinal))
    {
        Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId));
        // 성공적으로 구매한 소모성 아이템인 100코인을 유저의 게임 내 재화에 추가합니다.
        // 예: coin += 100;
    } 
    else if (String.Equals(purchasedProduct.productId, "coin2", StringComparison.Ordinal)) 
    {
        Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId));
        // 성공적으로 구매한 소모성 아이템인 200코인을 유저의 게임 내 재화에 추가합니다
        // 예: coin += 200;
    } 
    else 
    {
        Debug.Log(string.Format("OnPurchaseProduct: FAIL. Unrecognized product: '{0}'", purchasedProduct.productId));
    }

    return PurchaseProcessingResult.Complete;
}

서버 인증

다운로드 패키지에는 백엔드 서버를 사용하여 구매를 인증하는 샘플 코드가 포함되어 있습니다.

샘플 코드

  • PurchaseVerification 클래스: 데모 프로젝트에 포함된 클래스로 구매 데이터와 서명을 백엔드 서버로 보내는 유니티 코드가 포함되어 있습니다.
  • Server.py: 다운로드 패키지 내 별도로 위치하며, 로컬 서버를 실행하고 백엔드 인증을 수행하는 샘플 파이썬 코드가 포함되어 있습니다.

로컬 인증

구매 로컬 인증 관련 샘플 코드는 다운로드 패키지 내에서 확인해 보실 수 있습니다.

샘플 코드

  • PurchaseVerification클래스의 VerifyPurchaseLocally를 참조해 보시기 바랍니다.

중요

  • 모듈 테스트 시 여기를 참조해 보실 수 있습니다.
  • now.gg 결제 모듈 데모 프로젝트는 여기에서 확인해 보실 수 있습니다.
×
클립보드에 복사된 텍스트
copyLinkText
질문이 있으신가요? 다음 주소로 문의하세요. dev-support@now.gg