Як додати покупку через додаток до програми iOS? Які всі деталі та чи є зразок коду?
Це означає, що це всілякі передумови щодо того, як додавати покупки через додаток до додатків iOS
Як додати покупку через додаток до програми iOS? Які всі деталі та чи є зразок коду?
Це означає, що це всілякі передумови щодо того, як додавати покупки через додаток до додатків iOS
Відповіді:
Користувачі Swift можуть переглянути мій відповідь Swift на це питання .
Або перегляньте відповідь Єдідії Рейс , яка переводить цей код Objective-C на Swift.
Решта цієї відповіді написана в Objective-C
My Apps
а потім натисніть додаток, до якого потрібно додати покупкуFeatures
заголовок, а потім виберіть In-App Purchases
зліва+
піктограму посерединіnon-consumable
. Якщо ви збиралися надіслати фізичний предмет користувачеві або подарувати йому щось, що вони можуть купувати не один раз, ви вибрали б consumable
.tld.websitename.appname.referencename
це буде працювати найкраще, так, наприклад, ви можете використовуватиcom.jojodmo.blix.removeads
cleared for sale
а потім виберіть рівень цін як 1 (99 ¢). 2-й рівень склав би 1,99 долара, а рівень 3 - 2,99 долара. Повний список доступний, якщо натиснути, view pricing matrix
я рекомендую вам використовувати рівень 1, оскільки це, як правило, найбільше хто-небудь заплатить за видалення реклами.add language
кнопку та введіть інформацію. Це ВСЕ буде показано замовнику, тому не вкладайте нічого, чого ви не хочете, щоб вони бачилиhosting content with Apple
вибору немаєscreenshot for review
НАЗАД , до всього, що ми пропустимо, ми повернемось.Реєстрація вашого ідентифікатора продукту може зайняти кілька годин App Store Connect
, тому будьте терплячі.
Тепер, коли ви налаштували інформацію про придбання через додаток у App Store Connect, перейдіть до свого проекту Xcode та перейдіть до менеджера програм (синій значок у формі сторінки вгорі, де знаходяться ваші методи та файли заголовків), натисніть на ваш додаток під цілями (має бути першим), а потім перейти до загального. Унизу ви повинні побачити linked frameworks and libraries
символ "плюс" та додати рамку. StoreKit.framework
Якщо цього не зробити, покупка через додаток НЕ буде працювати!
Якщо ви використовуєте Objective-C як мову свого додатка, вам слід пропустити ці п’ять кроків . В іншому випадку, якщо ви використовуєте Swift, ви можете відповісти на мій відповідь Swift для цього питання тут , або, якщо ви віддаєте перевагу використовувати Objective-C для коду покупки через додаток, але ви використовуєте Swift у своєму додатку, ви можете зробити наступне :
Створіть новий .h
файл (заголовок), перейшовши в File
> New
> File...
( Command ⌘+ N). Цей файл буде згадуватися як "Ваш .h
файл" в решті підручника
Коли буде запропоновано, натисніть кнопку Створити мостиковий заголовок . Це буде наш мостовий файл заголовка. Якщо вам не запропоновано, перейдіть до кроку 3. Якщо вам буде запропоновано, пропустіть крок 3 і перейдіть безпосередньо до кроку 4.
Створіть інший .h
файл, названий Bridge.h
у головній папці проекту, Потім перейдіть до диспетчера програм (синій значок, схожий на сторінку), потім виберіть додаток у Targets
розділі та натисніть Build Settings
. Знайдіть опцію, яка говорить про компілятор Swift - Генерація коду , а потім встановіть для параметра Об'єктивний заголовок Objective-C значенняBridge.h
У своєму мостовому файлі заголовка додайте рядок #import "MyObjectiveCHeaderFile.h"
, де MyObjectiveCHeaderFile
знаходиться назва файлу заголовка, який ви створили на першому кроці. Так, наприклад, якщо ви назвали свій файл заголовка InAppPurchase.h , ви додали б рядок #import "InAppPurchase.h"
до вашого файлу заголовка мосту.
Створити нову Objective-C методи ( .m
файл), перейшовши в File
> New
> File...
( Command ⌘+ N). Назвіть його так само, як файл заголовка, який ви створили на кроці 1. Наприклад, якщо ви назвали файл на кроці 1 InAppPurchase.h , ви б назвали цей новий файл InAppPurchase.m . Цей файл буде згадуватися як "Ваш .m
файл" в решті підручника.
Тепер ми перейдемо до фактичного кодування. Додайте у свій .h
файл наступний код :
BOOL areAdsRemoved;
- (IBAction)restore;
- (IBAction)tapsRemoveAds;
Далі вам потрібно імпортувати StoreKit
рамку у свій .m
файл, а також додати SKProductsRequestDelegate
та SKPaymentTransactionObserver
після @interface
декларації:
#import <StoreKit/StoreKit.h>
//put the name of your view controller in place of MyViewController
@interface MyViewController() <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@end
@implementation MyViewController //the name of your view controller (same as above)
//the code below will be added here
@end
і тепер додайте у свій .m
файл наступне , ця частина ускладнюється, тому пропоную прочитати коментарі в коді:
//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product
#define kRemoveAdsProductIdentifier @"put your product id (the one that we just made in App Store Connect) in here"
- (IBAction)tapsRemoveAds{
NSLog(@"User requests to remove ads");
if([SKPaymentQueue canMakePayments]){
NSLog(@"User can make payments");
//If you have more than one in-app purchase, and would like
//to have the user purchase a different product, simply define
//another function and replace kRemoveAdsProductIdentifier with
//the identifier for the other product
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kRemoveAdsProductIdentifier]];
productsRequest.delegate = self;
[productsRequest start];
}
else{
NSLog(@"User cannot make payments due to parental controls");
//this is called the user cannot make payments, most likely due to parental controls
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
SKProduct *validProduct = nil;
int count = [response.products count];
if(count > 0){
validProduct = [response.products objectAtIndex:0];
NSLog(@"Products Available!");
[self purchase:validProduct];
}
else if(!validProduct){
NSLog(@"No products available");
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}
- (void)purchase:(SKProduct *)product{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction) restore{
//this is called when the user restores purchases, you should hook this up to a button
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(@"received restored transactions: %i", queue.transactions.count);
for(SKPaymentTransaction *transaction in queue.transactions){
if(transaction.transactionState == SKPaymentTransactionStateRestored){
//called when the user successfully restores a purchase
NSLog(@"Transaction state -> Restored");
//if you have more than one in-app purchase product,
//you restore the correct product for the identifier.
//For example, you could use
//if(productID == kRemoveAdsProductIdentifier)
//to get the product identifier for the
//restored purchases, you can use
//
//NSString *productID = transaction.payment.productIdentifier;
[self doRemoveAds];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
//if you have multiple in app purchases in your app,
//you can get the product identifier of this transaction
//by using transaction.payment.productIdentifier
//
//then, check the identifier against the product IDs
//that you have defined to check which product the user
//just purchased
switch(transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[self doRemoveAds]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(@"Transaction state -> Purchased");
break;
case SKPaymentTransactionStateRestored:
NSLog(@"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
NSLog(@"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
Тепер ви хочете додати свій код для того, що станеться, коли користувач закінчить транзакцію. У цьому підручнику ми використовуємо видалення добавок, вам доведеться додати свій власний код для того, що станеться при завантаженні перегляду банера.
- (void)doRemoveAds{
ADBannerView *banner;
[banner setAlpha:0];
areAdsRemoved = YES;
removeAdsButton.hidden = YES;
removeAdsButton.enabled = NO;
[[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
//use NSUserDefaults so that you can load whether or not they bought it
//it would be better to use KeyChain access, or something more secure
//to store the user data, because NSUserDefaults can be changed.
//You're average downloader won't be able to change it very easily, but
//it's still best to use something more secure than NSUserDefaults.
//For the purpose of this tutorial, though, we're going to use NSUserDefaults
[[NSUserDefaults standardUserDefaults] synchronize];
}
Якщо у вашій програмі немає оголошень, ви можете використовувати будь-яку іншу річ, яку хочете. Наприклад, ми могли б зробити колір тла синім. Для цього ми хотіли б використовувати:
- (void)doRemoveAds{
[self.view setBackgroundColor:[UIColor blueColor]];
areAdsRemoved = YES
//set the bool for whether or not they purchased it to YES, you could use your own boolean here, but you would have to declare it in your .h file
[[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
//use NSUserDefaults so that you can load wether or not they bought it
[[NSUserDefaults standardUserDefaults] synchronize];
}
Тепер десь у вашому viewDidLoad
методі вам потрібно буде додати такий код:
areAdsRemoved = [[NSUserDefaults standardUserDefaults] boolForKey:@"areAdsRemoved"];
[[NSUserDefaults standardUserDefaults] synchronize];
//this will load wether or not they bought the in-app purchase
if(areAdsRemoved){
[self.view setBackgroundColor:[UIColor blueColor]];
//if they did buy it, set the background to blue, if your using the code above to set the background to blue, if your removing ads, your going to have to make your own code here
}
Тепер, коли ви додали весь код, увійдіть у свій файл .xib
чи storyboard
файл та додайте дві кнопки, одну приказка, а інша відновлення. Підключіть tapsRemoveAds
IBAction
щойно зроблену вами кнопку покупки та кнопку restore
IBAction
відновлення. restore
Дія буде перевіряти , якщо користувач раніше придбав покупку в додатку, і дати їм покупку в додаток безкоштовно, якщо вони вже не мають його.
Далі, перейдіть у App Store Connect і натисніть, а Users and Access
потім клацніть Sandbox Testers
заголовок, а потім натисніть +
символ ліворуч, де написано Testers
. Ви можете просто помістити випадкові речі на ім’я та прізвище, а електронна пошта не повинна бути справжньою - ви просто повинні мати можливість запам'ятати її. Введіть пароль (який вам доведеться запам’ятати) та заповніть решту інформації. Я рекомендую вам Date of Birth
вказати дату, яка зробить користувача 18 років і старше. App Store Territory
МАЄ бути у правильній країні. Далі вийдіть із свого наявного облікового запису iTunes (ви можете увійти в систему після цього підручника).
Тепер запустіть додаток на своєму пристрої iOS, якщо ви спробуєте запустити його на тренажері, покупка завжди буде помилкою, ви повинні запустити його на пристрої iOS. Після запуску програми натисніть кнопку придбання. Коли буде запропоновано увійти у свій акаунт iTunes, увійдіть як тестовий користувач, який ми щойно створили. Далі, коли він попросить підтвердити покупку в 99 ¢ або будь-який інший, який ви встановите ціновий рівень, ВІДКЛЮЧИТЬ ЕКРАН ЗНАЧЕННЯ З цього, це те, що ви збираєтеся використовувати для свого screenshot for review
App Store Connect. Тепер скасуйте платіж.
Тепер перейдіть в App Store Connect , а потім перейти до My Apps
> the app you have the In-app purchase on
> In-App Purchases
. Потім натисніть покупку через додаток та натисніть кнопку "Редагувати" під деталями покупки через додаток. Щойно ви зробите це, імпортуйте фотографію, яку ви тільки що взяли на iPhone, у свій комп’ютер, і завантажте її як скріншот для огляду, а потім у примітках з огляду введіть електронну пошту та пароль свого TEST USER . Це допоможе яблуку в процесі огляду.
Після цього поверніться до програми на пристрої iOS, яка все ще увійшла як тестовий обліковий запис користувача, і натисніть кнопку придбання. Цього разу підтвердьте платіж. Не хвилюйтесь, це НЕ буде стягувати рахунок з будь-яких грошей, тестуйте облікові записи користувачів, отримуйте всі покупки через додаток безкоштовно Після підтвердження платежу переконайтеся, що станеться, коли користувач фактично купує ваш продукт буває. Якщо це не так, то помилка з вашим doRemoveAds
методом стане помилкою . Знову ж таки, я рекомендую змінити фон на синій для тестування покупки через додаток, однак це не має бути фактичною покупкою через додаток. Якщо все працює, і ви добре підете! Просто переконайтеся, що включите покупку через додаток у свій новий бінарний файл, коли ви завантажуєте його в App Store Connect!
Зареєстровано: No Products Available
Це може означати чотири речі:
kRemoveAdsProductIdentifier
в наведеному вище кодіЯкщо це не спрацює з першого разу, не засмучуйтесь! Не здавайся! Мені знадобилося близько 5 годин прямо до того, як я змогла це працювати, і близько 10 годин на пошук потрібного коду! Якщо ви точно використовуєте код вище, він повинен працювати добре. Чи не соромтеся коментувати , якщо у вас є якісь - які питання на всіх .
Я сподіваюся, що це допомагає всім, хто сподівається додати покупку через додаток до своєї програми iOS. Ура!
Просто перекладіть код Jojodmo на Swift:
class InAppPurchaseManager: NSObject , SKProductsRequestDelegate, SKPaymentTransactionObserver{
//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product
let kRemoveAdsProductIdentifier = "put your product id (the one that we just made in iTunesConnect) in here"
@IBAction func tapsRemoveAds() {
NSLog("User requests to remove ads")
if SKPaymentQueue.canMakePayments() {
NSLog("User can make payments")
//If you have more than one in-app purchase, and would like
//to have the user purchase a different product, simply define
//another function and replace kRemoveAdsProductIdentifier with
//the identifier for the other product
let set : Set<String> = [kRemoveAdsProductIdentifier]
let productsRequest = SKProductsRequest(productIdentifiers: set)
productsRequest.delegate = self
productsRequest.start()
}
else {
NSLog("User cannot make payments due to parental controls")
//this is called the user cannot make payments, most likely due to parental controls
}
}
func purchase(product : SKProduct) {
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
func restore() {
//this is called when the user restores purchases, you should hook this up to a button
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func doRemoveAds() {
//TODO: implement
}
/////////////////////////////////////////////////
//////////////// store delegate /////////////////
/////////////////////////////////////////////////
// MARK: - store delegate -
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
if let validProduct = response.products.first {
NSLog("Products Available!")
self.purchase(validProduct)
}
else {
NSLog("No products available")
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
NSLog("received restored transactions: \(queue.transactions.count)")
for transaction in queue.transactions {
if transaction.transactionState == .Restored {
//called when the user successfully restores a purchase
NSLog("Transaction state -> Restored")
//if you have more than one in-app purchase product,
//you restore the correct product for the identifier.
//For example, you could use
//if(productID == kRemoveAdsProductIdentifier)
//to get the product identifier for the
//restored purchases, you can use
//
//NSString *productID = transaction.payment.productIdentifier;
self.doRemoveAds()
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
break;
}
}
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .Purchasing: NSLog("Transaction state -> Purchasing")
//called when the user is in the process of purchasing, do not add any of your own code here.
case .Purchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
self.doRemoveAds() //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
NSLog("Transaction state -> Purchased")
case .Restored:
NSLog("Transaction state -> Restored")
//add the same code as you did from SKPaymentTransactionStatePurchased here
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
case .Failed:
//called when the transaction does not finish
if transaction.error?.code == SKErrorPaymentCancelled {
NSLog("Transaction state -> Cancelled")
//the user cancelled the payment ;(
}
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
case .Deferred:
// The transaction is in the queue, but its final status is pending external action.
NSLog("Transaction state -> Deferred")
}
}
}
}
Це покликане доповнити мою відповідь Objective-C для користувачів Swift, щоб уникнути занадто великої відповіді від Objective-C.
Спочатку встановіть покупку через додаток на appstoreconnect.apple.com . Дотримуйтесь початкової частини моєї відповіді Objective-C (кроки 1-13, під заголовком App Store Connect ), щоб отримати інструкції щодо цього.
Реєстрація вашого ідентифікатора продукту в App Store Connect може зайняти кілька годин, тому будьте терплячі.
Тепер, коли ви налаштували інформацію про покупку через додаток у App Store Connect, нам потрібно додати в програму Apple для покупки StoreKit
через додаток.
Зайдіть у свій проект Xcode та перейдіть до менеджера програм (синій значок у вигляді сторінки у верхній частині лівої панелі, де знаходяться файли вашої програми). Клацніть на додаток під цілями зліва (це повинен бути перший варіант), а потім перейдіть до "Можливості" вгорі. У списку ви побачите опцію "Покупка в додатку". Увімкніть цю можливість, і Xcode додасть StoreKit
ваш проект.
Тепер ми почнемо кодування!
Спочатку створіть новий швидкий файл, який керуватиме всіма вашими покупками через додаток. Я буду називати цеIAPManager.swift
.
У цьому файлі ми збираємося створити новий клас, який називається IAPManager
a SKProductsRequestDelegate
і SKPaymentTransactionObserver
. Угорі переконайтесь, що ви імпортуєте Foundation
таStoreKit
import Foundation
import StoreKit
public class IAPManager: NSObject, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
}
Далі ми додамо змінну для визначення ідентифікатора для нашої покупки через додаток (ви також можете скористатися послугою enum
, яку було б легше підтримувати, якщо у вас є кілька IAP).
// This should the ID of the in-app-purchase you made on AppStore Connect.
// if you have multiple IAPs, you'll need to store their identifiers in
// other variables, too (or, preferably in an enum).
let removeAdsID = "com.skiplit.removeAds"
Давайте додамо наступний ініціалізатор для нашого класу:
// This is the initializer for your IAPManager class
//
// A better, and more scaleable way of doing this
// is to also accept a callback in the initializer, and call
// that callback in places like the paymentQueue function, and
// in all functions in this class, in place of calls to functions
// in RemoveAdsManager (you'll see those calls in the code below).
let productID: String
init(productID: String){
self.productID = productID
}
Тепер ми збираємося додати необхідні функції для SKProductsRequestDelegate
і SKPaymentTransactionObserver
для роботи:
RemoveAdsManager
Клас ми додамо пізніше
// This is called when a SKProductsRequest receives a response
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
// Let's try to get the first product from the response
// to the request
if let product = response.products.first{
// We were able to get the product! Make a new payment
// using this product
let payment = SKPayment(product: product)
// add the new payment to the queue
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
}
else{
// Something went wrong! It is likely that either
// the user doesn't have internet connection, or
// your product ID is wrong!
//
// Tell the user in requestFailed() by sending an alert,
// or something of the sort
RemoveAdsManager.removeAdsFailure()
}
}
// This is called when the user restores their IAP sucessfully
private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
// For every transaction in the transaction queue...
for transaction in queue.transactions{
// If that transaction was restored
if transaction.transactionState == .restored{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier
// In this case, we have only one IAP, so we don't need to check
// what IAP it is. However, this is useful if you have multiple IAPs!
// You'll need to figure out which one was restored
if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
// Restore the user's purchases
RemoveAdsManager.restoreRemoveAdsSuccess()
}
// finish the payment
SKPaymentQueue.default().finishTransaction(transaction)
}
}
}
// This is called when the state of the IAP changes -- from purchasing to purchased, for example.
// This is where the magic happens :)
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
for transaction in transactions{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier
// In this case, we have only one IAP, so we don't need to check
// what IAP it is.
// However, if you have multiple IAPs, you'll need to use productID
// to check what functions you should run here!
switch transaction.transactionState{
case .purchasing:
// if the user is currently purchasing the IAP,
// we don't need to do anything.
//
// You could use this to show the user
// an activity indicator, or something like that
break
case .purchased:
// the user successfully purchased the IAP!
RemoveAdsManager.removeAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
// the user restored their IAP!
IAPTestingHandler.restoreRemoveAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
// The transaction failed!
RemoveAdsManager.removeAdsFailure()
// finish the transaction
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred:
// This happens when the IAP needs an external action
// in order to proceeded, like Ask to Buy
RemoveAdsManager.removeAdsDeferred()
break
}
}
}
Тепер додамо кілька функцій, які можна використовувати для початку або відновлення покупок:
// Call this when you want to begin a purchase
// for the productID you gave to the initializer
public func beginPurchase(){
// If the user can make payments
if SKPaymentQueue.canMakePayments(){
// Create a new request
let request = SKProductsRequest(productIdentifiers: [productID])
// Set the request delegate to self, so we receive a response
request.delegate = self
// start the request
request.start()
}
else{
// Otherwise, tell the user that
// they are not authorized to make payments,
// due to parental controls, etc
}
}
// Call this when you want to restore all purchases
// regardless of the productID you gave to the initializer
public func beginRestorePurchases(){
// restore purchases, and give responses to self
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
Далі додамо новий клас утиліт для управління нашими IAP. Весь цей код може бути в одному класі, але наявність його декількох робить його трохи чистішим. Я збираюся зробити новий клас, який називається RemoveAdsManager
, і в ньому покласти кілька функцій
public class RemoveAdsManager{
class func removeAds()
class func restoreRemoveAds()
class func areAdsRemoved() -> Bool
class func removeAdsSuccess()
class func restoreRemoveAdsSuccess()
class func removeAdsDeferred()
class func removeAdsFailure()
}
Перші три функції, removeAds
, restoreRemoveAds
і areAdsRemoved
, є функціями , які ви будете називати робити певні дії. Останні чотири - це той, який буде викликанийIAPManager
.
Додамо трохи коду до перших двох функцій removeAds
і restoreRemoveAds
:
// Call this when the user wants
// to remove ads, like when they
// press a "remove ads" button
class func removeAds(){
// Before starting the purchase, you could tell the
// user that their purchase is happening, maybe with
// an activity indicator
let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginPurchase()
}
// Call this when the user wants
// to restore their IAP purchases,
// like when they press a "restore
// purchases" button.
class func restoreRemoveAds(){
// Before starting the purchase, you could tell the
// user that the restore action is happening, maybe with
// an activity indicator
let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginRestorePurchases()
}
І нарешті, давайте додамо якийсь код до останніх п’яти функцій.
// Call this to check whether or not
// ads are removed. You can use the
// result of this to hide or show
// ads
class func areAdsRemoved() -> Bool{
// This is the code that is run to check
// if the user has the IAP.
return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
}
// This will be called by IAPManager
// when the user sucessfully purchases
// the IAP
class func removeAdsSuccess(){
// This is the code that is run to actually
// give the IAP to the user!
//
// I'm using UserDefaults in this example,
// but you may want to use Keychain,
// or some other method, as UserDefaults
// can be modified by users using their
// computer, if they know how to, more
// easily than Keychain
UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
UserDefaults.standard.synchronize()
}
// This will be called by IAPManager
// when the user sucessfully restores
// their purchases
class func restoreRemoveAdsSuccess(){
// Give the user their IAP back! Likely all you'll need to
// do is call the same function you call when a user
// sucessfully completes their purchase. In this case, removeAdsSuccess()
removeAdsSuccess()
}
// This will be called by IAPManager
// when the IAP failed
class func removeAdsFailure(){
// Send the user a message explaining that the IAP
// failed for some reason, and to try again later
}
// This will be called by IAPManager
// when the IAP gets deferred.
class func removeAdsDeferred(){
// Send the user a message explaining that the IAP
// was deferred, and pending an external action, like
// Ask to Buy.
}
Збираючи все це разом, ми отримуємо щось подібне:
import Foundation
import StoreKit
public class RemoveAdsManager{
// Call this when the user wants
// to remove ads, like when they
// press a "remove ads" button
class func removeAds(){
// Before starting the purchase, you could tell the
// user that their purchase is happening, maybe with
// an activity indicator
let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginPurchase()
}
// Call this when the user wants
// to restore their IAP purchases,
// like when they press a "restore
// purchases" button.
class func restoreRemoveAds(){
// Before starting the purchase, you could tell the
// user that the restore action is happening, maybe with
// an activity indicator
let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginRestorePurchases()
}
// Call this to check whether or not
// ads are removed. You can use the
// result of this to hide or show
// ads
class func areAdsRemoved() -> Bool{
// This is the code that is run to check
// if the user has the IAP.
return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
}
// This will be called by IAPManager
// when the user sucessfully purchases
// the IAP
class func removeAdsSuccess(){
// This is the code that is run to actually
// give the IAP to the user!
//
// I'm using UserDefaults in this example,
// but you may want to use Keychain,
// or some other method, as UserDefaults
// can be modified by users using their
// computer, if they know how to, more
// easily than Keychain
UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
UserDefaults.standard.synchronize()
}
// This will be called by IAPManager
// when the user sucessfully restores
// their purchases
class func restoreRemoveAdsSuccess(){
// Give the user their IAP back! Likely all you'll need to
// do is call the same function you call when a user
// sucessfully completes their purchase. In this case, removeAdsSuccess()
removeAdsSuccess()
}
// This will be called by IAPManager
// when the IAP failed
class func removeAdsFailure(){
// Send the user a message explaining that the IAP
// failed for some reason, and to try again later
}
// This will be called by IAPManager
// when the IAP gets deferred.
class func removeAdsDeferred(){
// Send the user a message explaining that the IAP
// was deferred, and pending an external action, like
// Ask to Buy.
}
}
public class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver{
// This should the ID of the in-app-purchase you made on AppStore Connect.
// if you have multiple IAPs, you'll need to store their identifiers in
// other variables, too (or, preferably in an enum).
static let removeAdsID = "com.skiplit.removeAds"
// This is the initializer for your IAPManager class
//
// An alternative, and more scaleable way of doing this
// is to also accept a callback in the initializer, and call
// that callback in places like the paymentQueue function, and
// in all functions in this class, in place of calls to functions
// in RemoveAdsManager.
let productID: String
init(productID: String){
self.productID = productID
}
// Call this when you want to begin a purchase
// for the productID you gave to the initializer
public func beginPurchase(){
// If the user can make payments
if SKPaymentQueue.canMakePayments(){
// Create a new request
let request = SKProductsRequest(productIdentifiers: [productID])
request.delegate = self
request.start()
}
else{
// Otherwise, tell the user that
// they are not authorized to make payments,
// due to parental controls, etc
}
}
// Call this when you want to restore all purchases
// regardless of the productID you gave to the initializer
public func beginRestorePurchases(){
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
// This is called when a SKProductsRequest receives a response
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
// Let's try to get the first product from the response
// to the request
if let product = response.products.first{
// We were able to get the product! Make a new payment
// using this product
let payment = SKPayment(product: product)
// add the new payment to the queue
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
}
else{
// Something went wrong! It is likely that either
// the user doesn't have internet connection, or
// your product ID is wrong!
//
// Tell the user in requestFailed() by sending an alert,
// or something of the sort
RemoveAdsManager.removeAdsFailure()
}
}
// This is called when the user restores their IAP sucessfully
private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
// For every transaction in the transaction queue...
for transaction in queue.transactions{
// If that transaction was restored
if transaction.transactionState == .restored{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier
// In this case, we have only one IAP, so we don't need to check
// what IAP it is. However, this is useful if you have multiple IAPs!
// You'll need to figure out which one was restored
if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
// Restore the user's purchases
RemoveAdsManager.restoreRemoveAdsSuccess()
}
// finish the payment
SKPaymentQueue.default().finishTransaction(transaction)
}
}
}
// This is called when the state of the IAP changes -- from purchasing to purchased, for example.
// This is where the magic happens :)
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
for transaction in transactions{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier
// In this case, we have only one IAP, so we don't need to check
// what IAP it is.
// However, if you have multiple IAPs, you'll need to use productID
// to check what functions you should run here!
switch transaction.transactionState{
case .purchasing:
// if the user is currently purchasing the IAP,
// we don't need to do anything.
//
// You could use this to show the user
// an activity indicator, or something like that
break
case .purchased:
// the user sucessfully purchased the IAP!
RemoveAdsManager.removeAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
// the user restored their IAP!
RemoveAdsManager.restoreRemoveAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
// The transaction failed!
RemoveAdsManager.removeAdsFailure()
// finish the transaction
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred:
// This happens when the IAP needs an external action
// in order to proceeded, like Ask to Buy
RemoveAdsManager.removeAdsDeferred()
break
}
}
}
}
Нарешті, вам потрібно додати певний спосіб, щоб користувач розпочав покупку та дзвінок RemoveAdsManager.removeAds()
і почав відновлення та дзвінок RemoveAdsManager.restoreRemoveAds()
, як кнопка десь! Майте на увазі, що згідно з правилами магазину додатків вам потрібно надати кнопку, щоб десь відновити покупки.
Останнє, що потрібно зробити, - надіслати свій IAP на розгляд в App Store Connect! Щоб отримати докладні вказівки щодо цього, ви можете дотримуватися останньої частини моєї відповіді Objective-C під заголовком Надіслати на рецензію .
RMStore - це легка бібліотека iOS для покупок через додаток. Він завершує API StoreKit і надає вам зручні блоки для асинхронних запитів. Придбати товар так само просто, як викликати єдиний метод.
Для досвідчених користувачів ця бібліотека також забезпечує підтвердження отримання, завантаження вмісту та постійність транзакцій.
Я знаю, що вже запізнююся на це, але поділююсь подібним досвідом, коли дізнався мотузки моделі IAP.
Покупка через додаток - це один із найбільш всебічних робочих процесів в iOS, реалізований рамкою Storekit. Вся документація цілком зрозуміло , якщо ви терпіння прочитати його, але дещо просунулася в природі формальності.
Узагальнити:
1 - Запросіть продукти - використовуйте класи SKProductRequest & SKProductRequestDelegate, щоб надіслати запит на ідентифікатори продукту та отримати їх назад у власному магазині itunesconnect.
Ці SKProducts слід використовувати для заповнення інтерфейсу вашого магазину, який користувач може використовувати для придбання конкретного продукту.
2 - Запит на оплату - використовуйте SKPayment & SKPaymentQueue, щоб додати плату до черги транзакцій.
3 - Монітор черги транзакцій для оновлення статусу - використовуйте оновлений метод транзакцій протоколу SKPaymentTransactionObserver для оновлення стану:
SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction
4 - Відновлення потоку кнопок - використовуйте відновлення SKPaymentQueue відновленняCompletedTransaction для цього - крок 3 подбає про інше, а також наступні методи SKPaymentTransactionObserver:
paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError
Ось покроковий підручник (написаний мною в результаті моїх власних спроб зрозуміти це), який пояснює це. Зрештою, він також надає зразок коду, який ви можете безпосередньо використовувати.
Ось ще один, який я створив, щоб пояснити певні речі, які тільки текст міг би описати кращим чином.