This document illustrates the now.gg Payments Standard Integration flow.
The now.gg Payments Unity module is included as a Unity package file nowgg-payments-login.unitypackage
.
Add the module to your Unity project:
nowgg-payments-login.unitypackage
that you previously downloaded.Assets/Plugins/Android
directory of your project using the Unity External Dependency Manager.Once the now.gg Payments module has been imported, and the dependencies have been added; you can follow this section to implement now.gg Payments with your app/game.
To run the now.gg IAP service, the now.gg platform is required. The Unity IAP (or any other IAP services) should only be initialized when you are not running on the now.gg platform.
To check the platform, you should add the following code to your implementation so that Unity IAP is not activated when your app runs on the now.gg platform:
public void Start() { if (NowGG.Sdk.NowGGPaymentsSdkManager.IsNowGGIapAvailable()) return; // Your Unity IAP code here... }
The following sections illustrate the implementation of the now.gg IAP functions, which you can call from your app/game in Unity.
Start by creating a class that contains the now.gg IAP implementation and attach it to a game object that you can use to initialize the in-app purchases.
This step will allow you to add the in-app purchase products in your app/game and initialize the SDK. For example, we have added two IAP consumable products with productId
‘coin1’ and ‘coin2’ and a subscription with productId
‘monthlyPack’ in the following code.
Similarly, you can add other products and subscriptions with the corresponding productId
that you have added within nowStudio, as illustrated in the following sample code:
public void Start() { // Only initialize Now.gg IAP if running on Now.gg platform if (!NowGG.Sdk.NowGGPaymentsSdkManager.IsNowGGIapAvailable()) return; // for consumable products NowGGProductBuilder.Instance.AddProduct("coin1", ProductType.Consumable); NowGGProductBuilder.Instance.AddProduct("coin2", ProductType.Consumable); // for subscriptions 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); }
Explanation:
NowGGProductBuilder
class.InitializeIap
function of the NowGGPaymentsSdkManager
Class is called to initialize the Payments module using the PAYMENT_ID
.PAYMENT_ID
is a unique identifier for your app.
PAYMENT_ID
.PAYMENT_ID
can be found within the App Details section of nowStudio – more information.After successful initialization, you can get the product details in your app using the productId
.
To get product details, refer to the following implementation:
private void OnInitSuccess() { Debug.Log($"IAP init success"); Product coin1 = NowGGPaymentsSdkManager.Instance.GetProductWithID("coin1"); // now you can get product details using productId // consumable string price = coin1.price; string currencyCode = coin1.priceCurrencyCode; // subscription Product monthlyPack = NowGGPaymentsSdkManager.Instance.GetProductWithID("monthlyPack"); string monthlyPackSubscriptionPeriod = monthlyPack.subscriptionPeriod; } private void OnInitFailed(string reason) { Debug.Log($"IAP init failed {reason}"); }
Reference:
After you have the product details, you can initiate a product purchase. To do so, you have to call the PurchaseProduct()
function as illustrated below:
public void PurchaseProduct(string productId, string developerPayload=null) { NowGGPaymentsSdkManager.Instance.PurchaseProduct(productId, developerPayload); }
Reference:
developerPayload
is an optional param.ProductId
is a unique identifier for the in-app product(s) and Subscriptions you have defined within nowStudio for your specific App (App Id).The following methods illustrate how to assign purchase entitlement to the user:
We have provided two methods that you can use to consume a purchase:
We have provided two methods that you can use to acknowledge a purchase:
private PurchaseProcessingResult OnPurchaseProduct(PurchasedProduct purchasedProduct) { string orderId = purchasedProduct.orderId; string purchaseToken = purchasedProduct.purchaseToken; string developerPayload = purchasedProduct.developerPayload; // A consumable product has been purchased by this user. if (String.Equals(purchasedProduct.productId, "coin1", StringComparison.Ordinal)) { Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId)); // The consumable item has been successfully purchased, add 100 coins to the player's in-game currency. // coin += 100; } else if (String.Equals(purchasedProduct.productId, "coin2", StringComparison.Ordinal)) { Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId)); // The consumable item has been successfully purchased, add 200 coins to the player's in-game currency. // 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) { // The subscription has been successfully purchased. Please pass the benefits of the subscription to the player. // Update the UI to reflect the status of the purchased subscription, billing period, and expiry. // Return PurchaseProcessingResult.Complete to acknowledge that the purchase has been acknowledged (subscriptions) } } else { Debug.Log(string.Format("OnPurchaseProduct: FAIL. Unrecognized product: '{0}'", purchasedProduct.productId)); } // Return a flag indicating whether this product has been received or if the application needs to be reminded of this purchase at the next app launch. // Return PurchaseProcessingResult.Pending if you want to be reminded of this purchase on the next launch of your app/game. // Return PurchaseProcessingResult.Complete to acknowledge that the purchase has been consumed (consumable products) or acknowledged (subscriptions) // Make sure that you have passed the benefits of the purchase to the user. return PurchaseProcessingResult.Complete; }
Note:
PurchaseProcessingResult.Pending
from OnPurchaseProduct
.
Reference:
purchaseToken
is generated after a successful purchase and is a unique identifier.orderId
is also generated for every order.
The consumePurchase
API should be used for server-side purchase consumption for apps with a backend server.
After you have verified the purchase, you should mark it as consumed.
Purchase consumption involves:
Assigning the purchased product to the user.
Notifying now.gg that the product has been consumed.
The following sample code illustrates the associated request using the 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>", // Required only for multi-store "developerPayload": "developerPayload", // Optional // Required only for multi-store "currency": "<currency>", // Required only for multi-store "type": "<store>" // xiaomi, onestore. If not present, the fallback will be nowgg } response = requests.post(url, headers=headers, data=data) print("Response Status Code:", response.status_code) print("Response Body:", 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>", // Required only for multi-store "developerPayload" : developerPayload, //Optional, // Required only for multi-store "currency": "<currency>", // Required only for multi-store "type": "<store>" // xiaomi | onestore // If not present, fallback value will be nowgg }'
Reference
consumePurchase
API.Payments API Key
can be found within the credentials section of nowStudio. More information.The acknowledgePurchase
API should be used for server-side purchase acknowledgement for apps with a backend server.
After you have verified the purchase, you should mark it as acknowledged.
The following sample code illustrates the associated request using the 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)
Reference:
Payments API Key
can be found within the credentials section of nowStudio. More information.If you wish to process the purchase on your backend server or need to complete the purchase using another method based on your implementation, your app should return PurchaseProcessingResult.Pending
from OnPurchaseProduct
.
To complete the purchase flow, you can use the ConfirmPendingPurchase
method to inform the now.gg IAP service, the app has made a record of the purchase.
public void ConfirmPendingPurchase(string purchasetoken, ProductType productType) { if (productType == ProductType.Consumable) { ConsumeProduct(purchasetoken); } else { AcknowledgePurchase(purchasetoken); } }
Reference:
To complete the purchase flow and grant the entitlement of a purchase using your app backend server, call the:
If you need to query for pending purchases, you can call the QueryPendingPurchases
function of the NowGGPaymentsSdkManager
class. This function will query all the pending purchases and call the OnPurchaseProduct
callback along with the pending purchases.
NowGGPaymentsSdkManager.Instance.QueryPendingPurchases();
Reference: OnPurchaseProduct.
If you wish to fetch additional products for purchase or refresh the details/metadata associated with the existing products, you can use the FetchAdditionalProducts
function as illustrated below.
var newProducts = new System.Collections.Generic.Dictionary<string, ProductType>(); newProducts.Add("coin2", ProductType.Consumable); newProducts.Add("coin3", ProductType.Consumable); NowGGPaymentsSdkManager.Instance.FetchAdditionalProducts(newProducts, () => { //Additional products fetched can now be purchased. 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}"); });
Purchases may fail due to any number of reasons, including:
You may investigate and handle failed purchases by referring to the following implementation:
public void OnPurchaseFailed(int errorCode, string errorMessage) { Debug.Log($"OnPurchaseFailed: errorCode: {errorCode} and msg: {errorMessage}"); }
Note:
OnPurchaseFailed
function are referenced here.This section illustrates the API specs for you to provide us with a SubscriptionStatusCallback
API to send subscription status updates.
The following is the associated workflow:
Refer: Subscription Status Callback API section.
You can verify the purchases using either of the following two methods:
The first method to verify the purchase is using verifyPurchase API. You can use this API to verify the purchases with our backend server.
To verify the purchase, call the verifyPurchase API from your app backend server with purchaseToken
, as illustrated in the following sample request code:
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>", // Required only for multi-store "currency": "<currency>", // Required only for multi-store "type": "<store>" // xiaomi, onestore, amazon, huawei. If not present, the fallback value will be nowgg. } response = requests.post(url, headers=headers, data=data) print("Response Status Code:", response.status_code) print("Response Body:", 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>", // Required only for multi-store "currency": "<currency>", // Required only for multi-store "type": "<store>" // xiaomi | onestore | amazon | huawei //If not present, the fallback value will be nowgg. }'
You can either verify purchase(s) locally or use a backend server. However, we recommend using your backend server to verify a purchase.
You can generate your public key by following the steps listed here.
The following code segment illustrates the verification of purchase(s) locally and using a backend server. You can refer to the Demo project for more details.
private PurchaseProcessingResult OnPurchaseProduct(PurchasedProduct purchasedProduct) { // If you want to do local verification, use this sample code bool isValidPurchase = PurchaseVerification.Instance.VerifyPurchaseLocally( BASE_64_ENCODED_PUBLIC_KEY, purchasedProduct.originalJson, purchasedProduct.signature); // We recommend you to verify purchases on your backend server. // You need to send signature and original data to your backend server for verification. PurchaseVerification.Instance.VerifyValidSignatureOnBackend( purchasedProduct.originalJson, purchasedProduct.signature); // A consumable product has been purchased by this user. if (String.Equals(purchasedProduct.productId, "coin1", StringComparison.Ordinal)) { Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId)); // The consumable item has been successfully purchased, add 100 coins to the player's in-game currency. // coin += 100; } else if (String.Equals(purchasedProduct.productId, "coin2", StringComparison.Ordinal)) { Debug.Log(string.Format("OnPurchaseProduct: PASS. Product: '{0}'", purchasedProduct.productId)); // The consumable item has been successfully purchased, add 200 coins to the player's in-game currency. // coin += 200; } else { Debug.Log(string.Format("OnPurchaseProduct: FAIL. Unrecognized product: '{0}'", purchasedProduct.productId)); } return PurchaseProcessingResult.Complete; }
The download package contains a sample implementation to verify a purchase using a backend server which you can use to write your own implementation.
The sample implementation includes:
We have provided a sample implementation within the download package for local verification of purchase. You can use the provided sample to write your own implementation.
In the sample implementation:
VerifyPurchaseLocally
function of the PurchaseVerification
class.Document Rev. 1.0