Дополнительные возможности для работы с платежами в SDK на iOS🔗
Открытие Менеджера по управлению подписками.🔗
Для открытия менеджера по управлению подписками, то есть перенаправить пользователя в магазин в нужный раздел (а на iOS 15+ открыть окно прямо внутри приложения), достаточно воспользоваться следующей функцией:
Поддержка промокодов🔗
MRGS поддерживает работу с промокодами создателей контента - при его использовании создатель получает бонус. Системы добавляются по запросу.
Для проверки наличия промокода, введенного пользователем, можно воспользоваться методом:
[[MRGSBank sharedInstance] checkPromocode:@"PromoTest" completionHandler:^(BOOL valid, NSError* error) {
if (valid) {
// Apply for later purchases
}
}];
Затем, при начале платежа, необходимо добавить промокод в параметры платежа:
MRGSBankPurchaseRequest* request = [MRGSBankPurchaseRequest requestWithProductIdentifier:@"test.product.id"];
request.promocode = @"PromoTest";
После успешной покупки и проверки платежа, MRGS сообщит об использовании промокода сервису (тогда создатель контента, выпустивший промокод, получает бонус).
In-app покупки прямо из the App Store на IOS 11 и выше🔗
В IOS 11 Apple представила новый способ покупок - прямо из AppStore.
MRGS умеет работать с такими платежами. Если пользователь начал покупку в AppStore, то будет открыто Ваше приложение, и мы сохраним покупку пользователя в pendingPayment, и будем ожидать вашего решения о продолжении покупки. Для того, чтобы работать с отложенными платежами, мы предоставляем API:
// Проверяет наличие отложенного платежа
- (BOOL)hasPendingAppStorePromotionPayment;
// Получить продукт, который хочет купить пользователь
- (MRGSBankProduct* _Nullable)getPendingAppStorePromotionPaymentProduct;
// Начать проведение отложенного платежа
- (void)proceedPendingAppStorePromotionPayment;
// Начать проведение отложенного платежа с опциональным указанием пейлоада
- (void)proceedPendingAppStorePromotionPaymentWithDeveloperPayload:(NSString* _Nullable)payload;
Также, мы умеем работать с витринами в AppStore (например, если пользователь купил продукт, то стоит убрать его с витрины AppStore для этого пользователя) Для того, чтобы работать с витриной, мы предоставляем следующий API:
//Прячет in-app на странице магазина
- (void)hideProductInStore:(NSString*)paymentIdentifier completionHandler:(void (^)(NSError *error))handler;
//Отображает in-app на странице магазина
- (void)showProductInStore:(NSString*)paymentIdentifier completionHandler:(void (^)(NSError *error))handler;
//Проверяет отображение in-app на странице магазина
- (void)isProductVisibleInStore:(NSString*)paymentIdentifier completionHandler:(void (^)(BOOL visible, NSError *error))handler;
Поддержка Apple subscription offers и introductory price🔗
В iOS 12.2 были представлены скидки для подписки. Получить их может только пользователь, который ранее был подписан, но отменил подписку.
Для того, чтобы использовать скидки, Вам сначала нужно завести их в админке appStoreConnect. Затем, Вы должны получить список скидок для данного продукта - воспользуйтесь полями MRGSBankProduct.skProduct.discounts (скидки Apple subscription offers) и MRGSBankProduct.skProduct.introductoryPrice (первоначальные скидки), каждая скидка содержит описание, цену, и период, в течении которого она действует. Если указанные поля не nil, то скидка заведена в iTunes, и ей можно воспользоваться. Также, необходимо определить, может ли пользователь получить скидку, так как вышеуказанные поля сообщают только лишь о наличии скидки в iTunesConnect (AppStore Connect). Для этого воспользуйтесь классом MRGSBankReceiptProcessor. Затем можно показывать интерфейс с предложением скидки.
Для того, чтобы начать покупку подписки со скидкой (имеются в виду скидки Apple subscription offers, introductory скидки применяются автоматически), воспользуйтесь классом MRGSBankPurchaseRequest, в котором необходимо заполнить поля discountIdentifier, указав идентификатор скидки, с которой вы хотите купить подписку, а также поле applicationUsername, в котором необходимо указать хеш от идентификатора пользователя. Поле applicationUsername служит для того, чтобы Apple могла внутри своей системы определить, что пользователь мошенник (например, часто делает рефанды). Если вы не заполните данное поле, то мы автоматически подставим хеш идентификатора пользователя, установленный через [MRGSUsers setUserId]:
MRGSBankPurchaseRequest* request = [MRGSBankPurchaseRequest requestWithProductIdentifier:@"games.my.mrgs.framework.subscription" developerPayload:@"{\"item\": 123,\"store\": 546}"];
request.discountIdentifier = @"MRGSPromo_5";
request.applicationUsername = @"userHash";
[[MRGSBank sharedInstance] purchaseProductWithRequest:request];
Далее мы проведем покупки и сообщим Вам о результате через делегат, как обычно.
Также мы добавили специальное поле MRGSBankTransaction.selctedDiscount, в котором будет находиться объект скидки, если покупка была произведена со скидкой.
Важно
Для того, чтобы мы могли сгенерировать подпись и провести платеж, нам необходим ключ и его идентификатор. Для получения ключа необходимо перейти в AppStoreConnect-Users-Keys-Subscription, и сгенерировать ключ и идентификатор. Затем их необходимо отправить нам (напрямую в личных сообщениях или по почте).
Также, мы поддерживаем introductory price как на клиенте, так и на сервере, и сможем нормально отслеживать не только бесплатные пробные периоды, но и обычную первоначальную скидку любого формата.
Работа с чеком приложения🔗
Мы поддерживаем работу с общим чеком покупок приложения. С помощью данной функциональности Вы можете определить доступность(eligability) скидки для пользователя, узнать про активные подписки, а также получить информацию о подписках.
Основной класс для работы с чеком - MRGSBankReceiptProcessor. Для получения объекта класса, воспользуйтесь методом [MRGSBankReceiptProcessor sharedInstance].
Чек приложения - локальный файл, который содержит в себе записи обо всех сделанных покупках (за исключением consumable покупок) и продлениях. Именно с ним мы и работаем.
Важно
Для работы с чеком нам необходим ключ проверки платежей, поэтому не забудьте в админке MRGS в настройках приложения добавить специальный ключ, необходимый для проверки платежей. Найти его можно в iTunesConnect-YourApp-InappPurcases-SharedSecret.
Чаще всего чек находится в приложении сразу после скачивания, но бывают ситуации (например, установка из iTunes, или восстановление из резервной копии), когда чека может не оказаться. Если чека нет, то необходимо сделать запрос обновления(мы его сделаем автоматически), который потребует от пользователя ввод пароля. Беспричинный попап может насторожить пользователя, поэтому мы сделали метод isReceiptAvailable, который позволит Вам узнать наличие чека, и, если он отсутствует, показать пользователю окно о том, что сейчас будет запрашивать пароль для таких-то целей.
Пример:
if(![[MRGSBankReceiptProcessor sharedInstance] isReceiptAvailable]){
//Показать окно с предупреждением
}else{
//Вызывать необходимые методы
}
Необходимо знать
Первый запрос к любому из методов ниже(тем, которые с колбеком) может быть долгим и потребует интернет-соединение, так как нам необходимо отправить чек на наш сервер, и получить его расшифровку. Но после него мы будем работать с закешированными данными (за исключением особых случаев, когда данные нужно обновить), и запросы будут происходить мгновенно, а при необходимости в актуальных данных, мы снова загрузим их с сервера. Даже если Вы одновременно вызовете все методы, то на наш сервер все равно будет отправлен всего один запрос, и после его окончания все методы получат callback.
Возможная ошибка SSErrorDomain Code=100
Если при тестировании Вы получаете ошибку SSErrorDomain Code=100 (ошибка обновления чека со стороны Apple), то обратитесь к данной статье:
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
Получение списка купленных non-consumable товаров🔗
Если Вы используете непотребляемые товары, то Вам необходимо их восстанавливать для пользователя в некоторых ситуациях (например, переезд на новый девайс/аккаунт). Получить список купленных непотребляемых товаров можно вызвав метод restore (по гайдам это должна быть отдельная кнопка, чтобы пользователь понимал зачем ему нужно будет входить в аккаунт). Также, получить список купленных товаров можно из чека. Для этого воспользуйтесь методом:
-(void)getAllPurchasedNonConsumableProductsWithCompletionHandler: (void (^_Nonnull)(NSArray<MRGSBankReceiptItem*>* purchasedNonConsumables, NSError * _Nullable error)) completion;
В ответ придет блок, который будет вызван после получения списка. Содержит в себе массив купленных non-consumable товаров, а также ошибку, если таковая произошла.
Note
Есть вероятность, что данный функционал работает только в production, а в Sandbox/Testflight нет.
Определение доступности скидок и начальных скидок (Determine eligability for apple subscription offers and introductory prices)🔗
Если вы используете introductory prices или apple subscription offers, то Вам необходимо определить доступность (eligability) данных предложений для пользователя, так как он мог уже покупать подписку по скидке, и больше ему данное предложение не актуально (технически невозможно купить).
Подписки разбиты по группам (в iTunesConnect), и возможность предоставления скидки определяется именно для всех подписок одной группы. Именно для групп мы и создали методы проверки. (Например, если он купил подписку на месяц из одной группы с начальной скидкой, то он не может купить(перейти) со скидкой подписку на год из той же группы)
Для того, чтобы проверить, для каких групп доступна скидка, воспользуйтесь методами:
-(void)getUserIntroductoryEligabilityForSubscriptions: (NSArray<MRGSBankSubscriptionGroup*>*) subscriptionGroups withCompletionHandler: (void (^_Nonnull)(NSArray<MRGSBankSubscriptionGroup*>* _Nonnull eligableGroups, NSError * _Nullable error)) completion;
-(void)getUserDiscountEligabilityForSubscriptions: (NSArray<MRGSBankSubscriptionGroup*>*) subscriptionGroups withCompletionHandler: (void (^_Nonnull)(NSArray<MRGSBankSubscriptionGroup*>* _Nonnull eligableGroups, NSError * _Nullable error)) completion;
Предварительно создав необходимые группы подписок. Группа состоит из названия (его не обязательно заполнять), и массива идентификаторов подписок, входящих в эту группу.
Пример использования:
MRGSBankSubscriptionGroup* group1 = [MRGSBankSubscriptionGroup createGroupWithName:@"Subscription_Group_1" andIdentifiers:@[@"games.my.mrgs.framework.AutoRenew1",@"games.my.mrgs.framework.AutoRenew2"]];
MRGSBankSubscriptionGroup* group2 = [MRGSBankSubscriptionGroup createGroupWithName:@"Subscription_Group_2" andIdentifiers:@[@"games.my.mrgs.framework.AutoRenew3",@"games.my.mrgs.framework.AutoRenew4"]];
NSArray<MRGSBankSubscriptionGroup*>* subscriptionGroups = @[group1,group2];
[[MRGSBankReceiptProcessor sharedInstance] getUserDiscountEligabilityForSubscriptions:subscriptionGroups withCompletionHandler:^(NSArray<MRGSBankSubscriptionGroup *> * _Nonnull eligableGroups, NSError * _Nullable error) {
for(MRGSBankSubscriptionGroup* group in eligableGroups){
//Eligable group process
}
}];
[[MRGSBankReceiptProcessor sharedInstance] getUserIntroductoryEligabilityForSubscriptions:subscriptionGroups withCompletionHandler:^(NSArray<MRGSBankSubscriptionGroup *> * _Nonnull eligableGroups, NSError * _Nullable error) {
for(MRGSBankSubscriptionGroup* group in eligableGroups){
//Eligable group process
}
}];
Работа с информацией о подписках🔗
При работе с подписками возникает необходимость проверить, есть ли сейчас активные (действующие) подписки, когда они заканчиваются, или проверить информацию по конкретной подписке. Обычно такая информация приходит с сервера игры, но некоторые игры без сервера делают восстановление платежей на каждом старте, чтобы узнать состояние подписки, что не очень верно с точки зрения user experience. С помощью нас Вы сможете:
- Получить список активных подписок
- Получить список всех когда-либо купленных подписок
- Получить интересующую информацию о конкретной подписке
Для описания объекта подписки из чека мы сделали класс MRGSBankReceiptItem, который содержит в себе информацию о идентификаторе продукта (productIdentifier), а также времени истечения(для подписок).
Для получения информации о подписках воспользуйтесь методами:
-(void)getActiveSubscriptionsAndExpirationsWithCompletionHandler: (void (^_Nonnull)(NSArray<MRGSBankReceiptItem*>* activeSubscriptions, NSError * _Nullable error)) completion;
-(void)getAllSubscriptionsExpirationDatesWithCompletionHandler: (void (^_Nonnull)(NSArray<MRGSBankReceiptItem*>* subscriptions, NSError * _Nullable error)) completion;
-(void)getExpirationDateForSubscription: (NSString*)productIdentifier withCompletionHandler: (void (^_Nonnull)(MRGSBankReceiptItem* _Nullable subscription, NSError * _Nullable error)) completion;
Примеры использования:
//Получение всех активных подписок, даже тех, которые были продлены
[[MRGSBankReceiptProcessor sharedInstance] getActiveSubscriptionsAndExpirationsWithCompletionHandler:^(NSArray<MRGSBankReceiptItem *> * _Nonnull activeSubscriptions, NSError * _Nullable error)
{
for(MRGSBankReceiptItem* subscription in activeSubscriptions){
//Process active subscription
}
}];
//Получение списка всех когда-либо купленных подписок
[[MRGSBankReceiptProcessor sharedInstance] getAllSubscriptionsExpirationDatesWithCompletionHandler:^(NSArray<MRGSBankReceiptItem *> * _Nonnull subscriptions, NSError * _Nullable error)
{
for(MRGSBankReceiptItem* subscription in subscriptions){
//Process subscription, that was bought any time ago
}
}];
//Получение информации по конкретной подписке
[[MRGSBankReceiptProcessor sharedInstance] getExpirationDateForSubscription:@"games.my.mrgs.framework.AutoRenew3" withCompletionHandler:^(MRGSBankReceiptItem * _Nullable subscription, NSError * _Nullable error) {
NSDate* weekAgo = [NSDate dateWithTimeIntervalSinceNow:-604800];
if(subscription && subscription.expirationDate > weekAgo && subscription.expirationDate < [NSDate date]){
//Подписка не была продлена (пользователь отказался, или не хватило денег) меньше, чем неделю назад
}
}];
Важно
Мы учитываем продления подписок. Таким образом, если подписка была продлена, а локальный чек не был обновлен (обычно так и происходит), мы все равно предоставим актуальную, самую последнюю информацию о подписках.
Обнаружение мошенничества🔗
Для того чтобы помочь Apple обнаружить мошенничество в покупках внутри приложения, при начале покупки, вы можете выставить параметр:
MRGSBankPurchaseRequest* request = [MRGSBankPurchaseRequest requestWithProductIdentifier:<sku> developerPayload:<developerPayload>];
request.applicationUsername = <user_id_hash>;
[[MRGSBank sharedInstance] purchaseProductWithRequest:request];
В случае, если вы не установите данный идентификатор, MRGS автоматически установит его значение как хеш от пользователя, выставленного нам при старте
Эмулирование разрешения на покупку от родителей🔗
Apple дает возможность родителям подтверждать покупки детей. В таком случае при проведении платежа придет статус pending. Для того, чтобы протестировать данный функционал, вы можете выставить параметр:
MRGSBankPurchaseRequest* request = [MRGSBankPurchaseRequest requestWithProductIdentifier:<sku> developerPayload:<developerPayload>];
request.simulatesAskToBuyInSandbox = true;
[[MRGSBank sharedInstance] purchaseProductWithRequest:request];
Дата создания: 2020-01-20