Additional features for working with payments in the Unity SDK🔗
Opening a Subscription Manager🔗
Some stores allow the user to manage their subscriptions (iOS, Google, Amazon, Huawei), and some stores require you to add this feature to your application (Google). To open the subscription manager, that is, redirect the user to the store in the desired section, it is enough to use the following function:
Support for promotional codes🔗
MRGS supports working with promotional codes for content creators - when using it, the creator receives a bonus. Systems will be added upon request.
To check for the presence of a promotional code entered by the user, you can use the following method:
MRGSBank.Instance.CheckPromocode("PromoTest", (valid, error) =>
{
if (valid)
{
// Apply for later purchases
}
});
Then, when starting the payment, you need to add a promotional code to the payment parameters:
After a successful purchase and payment verification, MRGS will report the use of the promotional code to the service (then the content creator who issued the promotional code receives a bonus).
iOS features🔗
[iOS] In-app purchases directly from the App Store on IOS 11 and later🔗
In iOS 11 Apple introduced a new way of purchasing - directly from the AppStore.
MRGS can work with such payments. If a user initiates the purchase in the AppStore, your application will be opened, and we will save the user's purchase in pendingPayment, and we will wait for your decision to continue the purchase. In order to work with deferred payments, we provide the API:
// Checks for a pending payment
public bool HasPendingAppStorePromotionPayment();
// Get the product the user wants to buy
public MRGSBankProduct GetPendingAppStorePromotionPaymentProduct();
// Start pending payment with optional setting of payload
public void ProceedPendingAppStorePromotionPayment(string developerPayload = null);
We can also work with the AppStore showcases (for example, if the user has bought a product, you should remove it from the AppStore showcase for this user) To work with the showcase, we suggest the following API:
// Get order of products
GetPromoProductsOrder(Action<List<String>, String>);
// Reset products order
ResetPromoProductsOrder(Action<String>);
// Set the order of products
SetPromoProductsOrder(List<String>, Action<String>);
// Checks if the in-app is shown on the store page
GetPromoProductVisibility(String, Action<Boolean, String>);
// Hides/Shows in-app on the store page
SetPromoProductVisibility(String, Boolean, Action<String>);
[iOS] Apple subscription offers and introductory price support🔗
To describe the discount object we have added the MRGSBankProductDiscount class. It describes all the necessary discount parameters (both Apple subscription offers, and introductory price), such as price, locale, localized price, type of the discount, duration of the discount, amount of periods, debiting funds method, symbol of the currency and identifier (only Apple subscription offers have identifiers).
In order to use discounts, you first need to create them in the appStoreConnect admin panel. Then, you should get a list of discounts for this product - use the fields MRGSBankProduct.iOS.Discounts (Apple subscription offers discounts) and MRGSBankProduct.IntroductoryPrice (initial discounts), each discount is of type MRGSBankProductDiscount and contains a description, price, and the period during which it operates. If the specified fields are not null, then the discount has been added to iTunes, and you can use it. Also, it is necessary to determine whether the user can get a discount, since the above fields only inform about the availability of a discount in iTunesConnect (AppStore Connect). To do this use the class MRGSBankReceiptProcessor. Then you can show the interface with the discount offer.
Apple subscription offers🔗
In iOS 12.2 Apple introduced subscription discounts. They can be received only by the user, who has previously been subscribed but canceled his subscription.
In order to start buying a subscription with a discount (meaning Apple subscription offers discounts, introductory discounts are applied automatically), use the class MRGSBankPurchaseRequest, in which you need to fill in the DiscountIdentifier fields, specifying the ID of the discount with which you want to buy a subscription, and the ApplicationUsername field, in which you must specify the hash from the user ID. The ApplicationUsername field is used so that Apple can determine within its system that the user is a fraud (for example, often makes refunds). If you do not fill in this field, then we will automatically substitute the user ID hash set via MRGSUsers.getInstance().SetUserId():
var purchaseRequest = new MRGSBankPurchaseRequest("games.my.mrgs.framework.subscription", "{\"item\": 123,\"store\": 546}");
purchaseRequest.iOS.DiscountIdentifier = "MRGSPromo_5";
purchaseRequest.iOS.ApplicationUsername = "userHash";
MRGSBank.Instance.PurchaseProduct(purchaseRequest);
After that we will process the purchases and inform you on the results through the delegate as usual.
We also added a special field MRGSBankTransaction.iOS.SelctedDiscount, which will contain the discount object if the purchase was made at a discount.
Important
In order for us to generate an application sign and process the payment, we need a key an its identifier. To receive the key you need to open AppStoreConnect-Users-Keys-Subscription, and create the key and the identifier. After that you need to send them to us (directly in the private messages or via e-mail).
Introductory price🔗
Also, we support the introductory price both on the client and on the server, and will be able to track normally not only the free trial periods, but also the usual initial discount of any format.
For your convenience, so that you can correctly provide the user with the data about the initial discount, the MRGSBankProduct object has a IntroductoryPrice field, which contains the MRGSBankProductDiscount object, which describes the price, and the period, and other parameters of the discount. Read more about MRGSBankProductDiscount in the description of this class.
[iOS] Working with the application receipt🔗
We support the work with the general application payment receipt. With this feature you can determine the eligibility of a discount for a user, learn about active subscriptions and receive information about subscriptions. Works only for iOS.
The main class for working with the receipt is MRGSBankReceiptProcessor.
The application receipt is a local file which keeps records of all completed purchases (except for consumable purchases) and renewals. It is the file we work with.
Important
To work with the receipt we need a payment verification key, therefore do not forget to add a special key needed to verify payments in the application settings in the MRGS admin panel. You can find the key in iTunesConnect-YourApp-InappPurcases-SharedSecret.
Usually, the receipt appears in the application right after downloading, but sometimes (for example, installing from iTunes, or restoring from a backup)the receipt can be missing. If there is no receipt, you need to make an update request (we will do it automatically), which will require the user to enter a password. A causeless pop-up can confuse the user, so we added the IsReceiptAvailable method, which allows to check the availability of the receipt, and, if it is missing, show the user a window that his password will be requested for certain purposes.
Example:
if (!MRGSBankReceiptProcessor.Instance.IsReceiptAvailable())
{
// Show warning window
}
else
{
// Call required methods
}
Important to know
The first request to any of the methods below (the ones with a callback) can be lingering and will require an Internet connection, since we need to send the receipt to our server and receive its decryption. But after that we will work with cached data (except for the special cases when the data needs to be updated), and requests will occur instantly, and upon the necessity to receive relevant data, we will download it again from the server. Even if you call all the methods simultaneously, only one request will still be sent to our server, and after its completion all other methods will receive a callback.
Possible error SSErrorDomain Code=100
If during testing you receive an error SSErrorDomain Code=100 (error updating receipt from Apple), then refer to this article:
The operation couldn’t be completed. (SSErrorDomain error 100.) If you’re trying to test iOS App Store receipt validation, and you perform a receipt refresh using SKReceiptRefreshRequest, you are almost certainly going to come across the mysterious and enigmatic SSErrorDomain Error 100. There’s not a lot of information on the googles, so here’s what I know/suspect. As far as I can tell, code 100 is the App Store’s way of telling you “Sorry, I have no receipt for that bundle ID for that user”. That’s unlikely to happen in production unless shenanigans are underway (a receipt is generated even for a free app ‘Get’), but it can happen often in development. The sandbox App Store appears to have the ability to generate fake receipts when requested, but all ducks need to be in a row for this to happen. In the sandbox (Development/Ad Hoc builds):
- If you don’t have an app record set up in iTunes Connect, you’ll get a Code 100
- If you’re signed in with your regular Apple ID instead of a sandbox account: Code 100
- If you’re signed in with a sandbox account associated with a different iTunes Connect account: Code 100
The story is a bit different for Apple Testflight builds – these are production builds with special handling for in-app purchases, and the App Store (currently) does NOT generate a fake original purchase receipt. I haven’t tested this myself, but from a developer report on the dev forums (login required):
- If you have a virgin install from TestFlight, you’ll get a Code 100
- If you’ve previously installed the App Store version of the app, you’ll get a receipt
- If you have a virgin install from TestFlight but have made an in-app purchase, you’ll get a receipt
[iOS] Receiving the list of purchased non-consumable products🔗
If you use non-consumable products, you need to restore them for the user in some situations (for example, changing a device/account). The list of purchased non-consumable products can be received by calling the restore method (according to the guides, it should be a separate button so that the user understands why is required to log into the account). Also, the list of purchased products can be received from the receipt. To do this, use the method:
public void GetAllPurchasedNonConsumableProducts(Action<List<MRGSBankReceiptItem>, string> completion);
The response will be a block, which is called after receiving the list. It contains an array of purchased non-consumable products, or an error, if there is any.
Note
There is a possibility, that this feature works only on production, and does not work in Sandbox/Testflight.
[iOS] Determine the eligibility for apple subscription offers and introductory prices🔗
If you use introductory prices or apple subscription offers, you need to determine the eligibility of these offers for the user, since he may have bought a subscription with a discount, and this offer is no longer relevant for him (technically impossible to buy).
The subscriptions are divided into groups (in iTunesConnect), and the possibility to offer a discount is determined for all the subscriptions within one group. It was for groups that we created verification methods. (For example, if the user bought a monthly subscription with an introductory discount from the one group, he cannot buy (change to) an annual subscription with a discount from the same group)
In order to check which of the groups have available discounts, use the methods:
public void GetUserIntroductoryEligibility(List<MRGSBankSubscriptionGroup> subscriptionGroups, Action<List<MRGSBankSubscriptionGroup>, string> completion);
public void GetUserDiscountEligibility(List<MRGSBankSubscriptionGroup> subscriptionGroups, Action<List<MRGSBankSubscriptionGroup>, string> completion);
Create the necessary subscription groups in advance. The group consists of a name (it is not obligatory to be filled) and an array of the subscription identifiers, included in this group.
Usage example:
var groups = new List<MRGSBankSubscriptionGroup>();
groups.Add(new MRGSBankSubscriptionGroup("group1", new List<string> { "games.my.mrgs.framework.AutoRenew1", "games.my.mrgs.framework.AutoRenew2" }));
groups.Add(new MRGSBankSubscriptionGroup(name: "group2", identifiers: new List<string> { "games.my.mrgs.framework.AutoRenew3", "games.my.mrgs.framework.AutoRenew4" }));
MRGSBankReceiptProcessor.Instance.GetUserIntroductoryEligibility(groups, (List<MRGSBankSubscriptionGroup> eligable, string error) => {
foreach (MRGSBankSubscriptionGroup eligableGroup in eligable)
{
//Eligable group process
}
});
MRGSBankReceiptProcessor.Instance.GetUserDiscountEligibility(groups, (List<MRGSBankSubscriptionGroup> eligable, string error) => {
foreach (MRGSBankSubscriptionGroup eligableGroup in eligable)
{
//Eligable group process
}
});
[iOS] Working with the subscription information🔗
When working with subscriptions, you may need to check whether there are active subscriptions, when they will end, or check the information of a specific subscription. Usually this information comes from the game server, but some non-server games initiate a restore upon every launch to find out the status of the subscription, which is not completely correct from the point of view of the user's experience. With our help, you can:
- Get the list of active subscriptions.
- Get the list of all subscriptions ever purchased.
- Get the necessary information about a specific subscription.
To describe the subscription object from the receipt, we created a MRGSBankReceiptItem class, which keeps the information about the product identifier (productIdentifier) and the expiration time (for subscriptions).
To receive the subscription information you the methods:
public void GetActiveSubscriptionsAndExpirations(Action<List<MRGSBankReceiptItem>, string> completion);
public void GetAllSubscriptionsExpirationDates(Action<List<MRGSBankReceiptItem>, string> completion);
public void GetExpirationDateForSubscription(string productIdentifier, Action<MRGSBankReceiptItem, string> completion);
Usage example:
// Get all active subscriptions, even those that have been renewed
MRGSBankReceiptProcessor.Instance.GetActiveSubscriptionsAndExpirations((List<MRGSBankReceiptItem> subscriptions, string errorIn) => {
foreach (MRGSBankReceiptItem subscriptionInfo in subscriptions)
{
//Process active subscription
}
});
// Get a list of all subscriptions ever purchased
MRGSBankReceiptProcessor.Instance.GetAllSubscriptionsExpirationDates((List<MRGSBankReceiptItem> subscriptions, string errorIn) => {
foreach (MRGSBankReceiptItem subscriptionInfo in subscriptions)
{
//Process subscription, that was bought any time ago
}
});
// Get information about a specific subscription
MRGSBankReceiptProcessor.Instance.GetExpirationDateForSubscription(groups[0].SubscriptionsIdentifiers[0], (MRGSBankReceiptItem subscription, string errorIn) => {
Debug.Log("MRGSBankReceiptProcessor subscription - " + subscription);
});
Important
We take subscription renewals into consideration. Thus, if the subscription has been renewed and the local receipt has not been renewed (which is common), we will still provide the up-to-date information about subscriptions.
Fraud detection🔗
To help Apple detect in-app purchase fraud, at the beginning of the purchase, you can set the parameter:
var purchaseRequest = new MRGSBankPurchaseRequest(<sku>, <developerPayload>);
purchaseRequest.iOS.ApplicationUsername = <user_id_hash>;
MRGSBank.Instance.PurchaseProduct(purchaseRequest);
In case you do not set this identifier, MRGS will automatically set its value as a hash from the user set to us at startup
Emulating parental purchase permission (Ask To Buy)🔗
Apple allows parents to confirm purchases of children. In this case, the status of pending will be received during the payment process. In order to test this functionality, you can set the parameter:
var purchaseRequest = new MRGSBankPurchaseRequest(<sku>, <developerPayload>);
purchaseRequest.iOS.SimulatesAskToBuyInSandbox = true;
MRGSBank.Instance.PurchaseProduct(purchaseRequest);
Features for Android (Google Play)🔗
All additional features are available only when using the Google Play application store
Subscription change🔗
The user can change one subscription to another. For example, from a monthly payment to an annual one. To do this, use the changeItem method. The first parameter must specify the new subscription, the second parameter - the old one.
var request = new MRGSBankPurchaseRequest("some.product.id", "optional payload");
request.Android.OldSubscriptionIdentifierToMoveFrom = "old.subscription.id";
MRGSBank.Instance.PurchaseProduct(request);
Read more about subscription change on the Google Billing website.
Subscription discounts🔗
In the Google Play console, you can set a discount on the first purchase of a subscription and the period, during which this initial price will be debited. For example, a subscription costs 100 rubles per month, and for the first three months you can specify a price of 50 rubles per month. In order to check whether a subscription discount is available to the user on the client, it is necessary to check the price in the IntroductoryPrice field of the MRGSBankProduct class.
public void OnReceiveProductsResponse(MRGSBankProductsResponse productsResponse)
{
foreach (var product in productsResponse.ValidProducts)
{
var discount = product.IntroductoryPrice;
Debug.Log("Product discount: " + discount);
}
}
Promo code redemption🔗
You can give out a specific product in the game by the promo code redemption. Promo codes can be created in the Google Play console, read more in the Google Billing documentation.
To redeem a promo code in the application, use the redeemPromoCode method:
Karaganda Fraud Prevention API🔗
In order to help Google detect fraud before it happens you can use the following properties: ObfuscatedAccountId and ObfuscatedProfileId
var request = new MRGSBankPurchaseRequest("some.product.id", "optional payload");
request.Android.ObfuscatedAccountId = <obfuscatedAccountId>;
request.Android.ObfuscatedProfileId = <obfuscatedProfileId>;
MRGSBank.Instance.PurchaseProduct(request);
In case you do not set this identifier, MRGS will automatically set its value as a hash from the user set to us at startup
Subscriptions are not currently supported
Payment support in external application stores on Android🔗
Apart from Google billing, MRGS supports working with the Samsung and Amazon application stores.
Adding Samsung payments🔗
- Download the latest version of the library from the website Unity.
- Extract the files from the archive.
- Add the IAP5Helper.aar library to your project.
- Upon the MRGS initialization set the "samsung" billing type.
using MRGS;
public class MasterController : MonoBehaviour
{
void Awake()
{
var serviceParams = new MRGServiceParams(APP_ID, APP_SECRET);
// Setting MRGS params
// ...
// Choosing a store to work on Android
// Available MRGSPlatform: Amazon, Android, Huawei, Samsung, and FacebookCloud
serviceParams.AndroidExtraOptions.Platform = MRGSPlatformAndroid.Samsung;
// Configuring External SDKs
MRGSExternalSDKParams sdkParams = new MRGSExternalSDKParams();
// Where mode:
// 1) "test" - is equivalent to OperationMode.OPERATION_MODE_TEST
// 2) "production" - is equivalent to OperationMode.OPERATION_MODE_PRODUCTION
sdkParams.SamsungBillingParams = new MRGSSamsungBillingParams(mode)
// ...
// Initializing MRGS
// ...
}
}
Adding Amazon payments🔗
- Download the latest version of the library from the website Unity.
- Extract the files from the archive.
- Add the in-app-purchasing-2.0.61.jar library to your project.
- Upon the MRGS initialization set the "amazon" billing type.
using MRGS;
public class MasterController : MonoBehaviour
{
void Awake()
{
var serviceParams = new MRGServiceParams(APP_ID, APP_SECRET);
// Setting MRGS params
// ...
// Choosing a store to work on Android
// Available MRGSPlatform: Amazon, Android, Huawei, Samsung, and FacebookCloud
serviceParams.AndroidExtraOptions.Platform = MRGSPlatformAndroid.Amazon;
// Configuring External SDKs and initializing MRGS
// ...
}
}
Adding Huawei/AppTouch payments🔗
- Add the library according to the documentation
- Upon the MRGS initialization set the "huawei" billing type.
using MRGS;
public class MasterController : MonoBehaviour
{
void Awake()
{
var serviceParams = new MRGServiceParams(APP_ID, APP_SECRET);
// Setting MRGS params
// ...
// Choosing a store to work on Android
// Available MRGSPlatform: Amazon, Android, Huawei, Samsung, and FacebookCloud
serviceParams.AndroidExtraOptions.Platform = MRGSPlatformAndroid.Huawei;
// Configuring External SDKs and initializing MRGS
// ...
}
}
Created: 2020-02-17