Como você adiciona uma compra no aplicativo a um aplicativo iOS? Quais são todos os detalhes e existe algum código de exemplo?
Isso pretende ser uma espécie de captura geral de como adicionar compras no aplicativo a aplicativos iOS
Como você adiciona uma compra no aplicativo a um aplicativo iOS? Quais são todos os detalhes e existe algum código de exemplo?
Isso pretende ser uma espécie de captura geral de como adicionar compras no aplicativo a aplicativos iOS
Respostas:
Os usuários do Swift podem conferir Minha resposta rápida para esta pergunta .
Ou confira a resposta de Yedidya Reiss , que traduz esse código de Objective-C para Swift.
O restante desta resposta está escrito em Objective-C
My Apps
e clique no aplicativo ao qual você deseja adicionar a compraFeatures
cabeçalho e selecione In-App Purchases
à esquerda+
ícone no meionon-consumable
. Se você iria enviar um item físico para o usuário ou fornecer algo que ele possa comprar mais de uma vez, você escolheria consumable
.tld.websitename.appname.referencename
isso funcionará melhor; por exemplo, você pode usarcom.jojodmo.blix.removeads
cleared for sale
e escolha o nível de preço como 1 (99 ¢). A camada 2 seria de US $ 1,99 e a camada 3 seria de US $ 2,99. A lista completa está disponível se você clicar em view pricing matrix
Eu recomendo que você use a camada 1, porque geralmente é o máximo que alguém pagará para remover anúncios.add language
botão azul e insira as informações. Tudo isso será mostrado ao cliente, portanto, não coloque nada que você não queira que ele vejahosting content with Apple
escolher nãoscreenshot for review
FOR NOW , tudo o que pularmos, voltaremos.Pode levar algumas horas para que o seu ID do produto seja registrado App Store Connect
, portanto seja paciente.
Agora que você configurou suas informações de compra no aplicativo na App Store Connect, entre no seu projeto Xcode e vá para o gerenciador de aplicativos (ícone azul semelhante a uma página na parte superior de onde estão seus métodos e arquivos de cabeçalho) clique em seu aplicativo em metas (deve ser o primeiro) e vá para geral. Na parte inferior, você deve linked frameworks and libraries
clicar no pequeno símbolo de adição e adicionar a estrutura. StoreKit.framework
Se você não fizer isso, a compra no aplicativo NÃO funcionará!
Se você estiver usando o Objective-C como o idioma do seu aplicativo, pule estas cinco etapas . Caso contrário, se você estiver usando o Swift, poderá seguir Minha resposta rápida para esta pergunta, aqui ou, se preferir usar o Objective-C para o código de compra no aplicativo, mas estiver usando o Swift no seu aplicativo, poderá fazer o seguinte :
Criar um novo .h
arquivo (cabeçalho), indo para File
> New
> File...
( Command ⌘+ N). Este arquivo será referido como "Seu .h
arquivo" no restante do tutorial
Quando solicitado, clique em Criar Bridging Header . Este será o nosso arquivo de cabeçalho de ponte. Se você não for solicitado, vá para a etapa 3. Se for solicitado, pule a etapa 3 e vá diretamente para a etapa 4.
Crie outro .h
arquivo nomeado Bridge.h
na pasta principal do projeto. Em seguida, vá para o Application Manager (o ícone azul semelhante a uma página), selecione o aplicativo na Targets
seção e clique em Build Settings
. Localize a opção que diz Swift Compiler - Geração de código e defina a opção Objective-C Bridging Header comoBridge.h
No seu arquivo de cabeçalho de ponte, adicione a linha #import "MyObjectiveCHeaderFile.h"
, onde MyObjectiveCHeaderFile
está o nome do arquivo de cabeçalho que você criou na etapa um. Portanto, por exemplo, se você nomear seu arquivo de cabeçalho InAppPurchase.h , você adicionará a linha #import "InAppPurchase.h"
ao arquivo de cabeçalho da ponte.
Criar um novo Objective-C Métodos ( .m
arquivo), indo para File
> New
> File...
( Command ⌘+ N). Nomeie o mesmo que o arquivo de cabeçalho que você criou na etapa 1. Por exemplo, se você chamou o arquivo na etapa 1 InAppPurchase.h , você chamaria esse novo arquivo de InAppPurchase.m . Este arquivo será referido como "Seu .m
arquivo" no restante do tutorial.
Agora vamos entrar na codificação real. Adicione o seguinte código ao seu .h
arquivo:
BOOL areAdsRemoved;
- (IBAction)restore;
- (IBAction)tapsRemoveAds;
Em seguida, você precisa importar a StoreKit
estrutura para o seu .m
arquivo, além de adicionar SKProductsRequestDelegate
e SKPaymentTransactionObserver
após a sua @interface
declaração:
#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
e agora adicione o seguinte ao seu .m
arquivo, esta parte fica complicada, por isso sugiro que você leia os comentários no código:
//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;
}
}
}
Agora você deseja adicionar seu código para o que acontecerá quando o usuário concluir a transação. Para este tutorial, usamos a remoção de adições. Você precisará adicionar seu próprio código para o que acontece quando a exibição do banner for carregada.
- (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];
}
Se você não tiver anúncios em seu aplicativo, poderá usar qualquer outra coisa que desejar. Por exemplo, podemos tornar a cor do fundo azul. Para fazer isso, gostaríamos de usar:
- (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];
}
Agora, em algum lugar do seu viewDidLoad
método, você desejará adicionar o seguinte código:
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
}
Agora que você adicionou todo o código, entre no seu arquivo .xib
ou storyboard
e adicione dois botões, um dizendo compra e outro dizendo restauração. Conecte o tapsRemoveAds
IBAction
botão de compra que você acabou de fazer e o restore
IBAction
botão de restauração. A restore
ação verificará se o usuário comprou anteriormente a compra no aplicativo e oferecerá a compra no aplicativo gratuitamente, caso ainda não a tenha.
Em seguida, entre no App Store Connect , Users and Access
clique no Sandbox Testers
cabeçalho e clique no +
símbolo à esquerda onde está escrito Testers
. Você pode simplesmente colocar coisas aleatórias para o nome e o sobrenome, e o e-mail não precisa ser real - você só precisa se lembrar dele. Coloque uma senha (da qual você precisará se lembrar) e preencha o restante das informações. Eu recomendo que você marque a Date of Birth
data que tornaria o usuário 18 anos ou mais. App Store Territory
TEM que estar no país correto. Em seguida, efetue logout da sua conta existente do iTunes (você poderá efetuar login novamente após este tutorial).
Agora, execute o aplicativo no seu dispositivo iOS, se você tentar executá-lo no simulador, a compra será sempre erro, você TEM QUE executá-lo em seu dispositivo iOS. Quando o aplicativo estiver em execução, toque no botão de compra. Quando você for solicitado a fazer login na sua conta do iTunes, faça o login como o usuário de teste que acabamos de criar. Em seguida, quando solicitar que você confirme a compra de 99 ¢ ou o que quer que você defina também o preço, FAÇA UM INSTANTÂNEO DE TELA, isto é o que você usará para o seu screenshot for review
na App Store Connect. Agora cancele o pagamento.
Agora, vá para loja App Ligue , em seguida, ir para a My Apps
> the app you have the In-app purchase on
> In-App Purchases
. Em seguida, clique na sua compra no aplicativo e clique em editar nos detalhes da compra no aplicativo. Depois de fazer isso, importe a foto que você acabou de tirar no seu iPhone para o seu computador e carregue-a como captura de tela para revisão e, em notas de revisão, coloque seu e-mail e senha de USUÁRIO DE TESTE . Isso ajudará a apple no processo de revisão.
Depois de fazer isso, volte para o aplicativo no seu dispositivo iOS, ainda conectado como a conta de usuário de teste e clique no botão comprar. Desta vez, confirme o pagamento Não se preocupe, isso NÃO cobrará QUALQUER dinheiro em sua conta, teste as contas de usuário e receba todas as compras no aplicativo gratuitamente Depois de confirmar o pagamento, verifique se o que acontece quando o usuário compra seu produto realmente acontece. Caso contrário, será um erro no seu doRemoveAds
método. Novamente, eu recomendo usar a alteração do plano de fundo para azul para testar a compra no aplicativo. Porém, essa não deve ser sua compra real no aplicativo. Se tudo funcionar e você estiver pronto! Apenas inclua a compra no aplicativo em seu novo binário ao enviá-lo para o App Store Connect!
Registrado: No Products Available
Isso pode significar quatro coisas:
kRemoveAdsProductIdentifier
no código acimaSe não funcionar pela primeira vez, não fique frustrado! Não desista! Demorei cerca de 5 horas seguidas até que eu conseguisse fazer isso funcionar, e cerca de 10 horas procurando o código certo! Se você usar o código acima exatamente, ele deverá funcionar bem. Sinta-se livre para comentar, se você tiver quaisquer perguntas em tudo .
Espero que isso ajude todos aqueles que desejam adicionar uma compra no aplicativo ao aplicativo iOS. Felicidades!
Basta traduzir o código Jojodmo para 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")
}
}
}
}
Isso visa complementar minha resposta Objective-C para usuários Swift, para impedir que a resposta Objective-C fique muito grande.
Primeiro, configure a compra no aplicativo em appstoreconnect.apple.com . Siga a parte inicial da minha resposta Objective-C (etapas 1 a 13, abaixo do cabeçalho da App Store Connect ) para obter instruções sobre como fazer isso.
Pode demorar algumas horas para o seu ID do produto se registrar no App Store Connect, por isso seja paciente.
Agora que você configurou suas informações de compra no aplicativo no App Store Connect, precisamos adicionar a estrutura da Apple para compras StoreKit
no aplicativo.
Entre no seu projeto do Xcode e vá para o gerenciador de aplicativos (ícone azul semelhante a uma página na parte superior da barra esquerda, onde estão os arquivos do seu aplicativo). Clique no seu aplicativo em destinos à esquerda (deve ser a primeira opção) e vá para "Recursos" na parte superior. Na lista, você verá a opção "Compra no aplicativo". Ative esse recurso e o Xcode será adicionado StoreKit
ao seu projeto.
Agora, vamos começar a codificar!
Primeiro, crie um novo arquivo rápido que gerencie todas as suas compras no aplicativo. Eu vou ligar IAPManager.swift
.
Neste arquivo, vamos criar uma nova classe, chamada IAPManager
is SKProductsRequestDelegate
e SKPaymentTransactionObserver
. No topo, certifique-se de importar Foundation
eStoreKit
import Foundation
import StoreKit
public class IAPManager: NSObject, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
}
Em seguida, adicionaremos uma variável para definir o identificador de nossa compra no aplicativo (você também pode usar um enum
, que seria mais fácil de manter se você tiver vários IAPs).
// 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"
Vamos adicionar um inicializador para a nossa classe a seguir:
// 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
}
Agora, vamos adicionar as funções necessárias para SKProductsRequestDelegate
e SKPaymentTransactionObserver
de trabalho:
Adicionaremos a RemoveAdsManager
turma mais tarde
// 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
}
}
}
Agora, vamos adicionar algumas funções que podem ser usadas para iniciar uma compra ou restaurar compras:
// 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()
}
Em seguida, vamos adicionar uma nova classe de utilitários para gerenciar nossos IAPs. Todo esse código pode estar em uma classe, mas ter vários torna um pouco mais limpo. Vou criar uma nova classe chamada RemoveAdsManager
, e nela, colocar algumas funções
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()
}
As três primeiras funções, removeAds
, restoreRemoveAds
e areAdsRemoved
, são funções que você vai chamar para fazer certas ações. Os quatro últimos são aqueles que serão chamados por IAPManager
.
Vamos adicionar algum código às duas primeiras funções removeAds
e 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()
}
E, finalmente, vamos adicionar um código às cinco últimas funções.
// 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.
}
Juntando tudo, temos algo parecido com isto:
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
}
}
}
}
Por fim, você precisa adicionar uma maneira de o usuário iniciar a compra e ligar RemoveAdsManager.removeAds()
e iniciar uma restauração e ligar RemoveAdsManager.restoreRemoveAds()
, como um botão em algum lugar! Lembre-se de que, de acordo com as diretrizes da App Store, você precisa fornecer um botão para restaurar as compras em algum lugar.
A última coisa a fazer é enviar seu IAP para análise na App Store Connect! Para obter instruções detalhadas sobre como fazer isso, você pode seguir a última parte da minha resposta Objective-C , no cabeçalho Submetendo para revisão .
O RMStore é uma biblioteca iOS leve para compras no aplicativo. Ele envolve a API StoreKit e fornece blocos úteis para solicitações assíncronas. Comprar um produto é tão fácil quanto chamar um único método.
Para usuários avançados, essa biblioteca também fornece verificação de recebimento, downloads de conteúdo e persistência de transação.
Sei que estou atrasado para postar isso, mas compartilho experiências semelhantes quando aprendi as regras do modelo IAP.
A compra no aplicativo é um dos fluxos de trabalho mais abrangentes no iOS implementados pela estrutura do Storekit. A documentação toda está muito claro se você paciência para lê-lo, mas é um pouco avançado na natureza de tecnicidade.
Para resumir:
1 - Solicite os produtos - use as classes SKProductRequest e SKProductRequestDelegate para emitir a solicitação de IDs do produto e recebê-las de volta na sua própria loja itunesconnect.
Esses SKProducts devem ser usados para preencher a interface da loja, que o usuário pode usar para comprar um produto específico.
2 - Emitir solicitação de pagamento - use SKPayment & SKPaymentQueue para adicionar pagamento à fila de transações.
3 - Monitorar a fila de transações para atualização de status - use o método updatedTransactions do protocolo SKPaymentTransactionObserver para monitorar o status:
SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction
4 - Restaurar o fluxo do botão - use restoreCompletedTransactions do SKPaymentQueue para fazer isso - a etapa 3 cuidará do resto, juntamente com os seguintes métodos do SKPaymentTransactionObserver:
paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError
Aqui está um tutorial passo a passo (criado por mim como resultado de minhas próprias tentativas de entendê-lo) que explica isso. No final, ele também fornece um exemplo de código que você pode usar diretamente.
Aqui está outro que eu criei para explicar certas coisas que somente o texto poderia descrever de uma maneira melhor.