Jak dodać zakup w aplikacji do aplikacji na iOS? Jakie są wszystkie szczegóły i czy jest jakiś przykładowy kod?
To ma być chwytliwy sposób dodawania zakupów w aplikacjach do aplikacji na iOS
Jak dodać zakup w aplikacji do aplikacji na iOS? Jakie są wszystkie szczegóły i czy jest jakiś przykładowy kod?
To ma być chwytliwy sposób dodawania zakupów w aplikacjach do aplikacji na iOS
Odpowiedzi:
Użytkownicy Swift mogą sprawdzić My Swift Answer na to pytanie .
Lub sprawdź odpowiedź Yedidya Reiss , która tłumaczy ten kod Celu C na Swift.
Reszta tej odpowiedzi jest napisana w Celu C
My Apps
a następnie kliknij aplikację, do której chcesz dodać zakupFeatures
nagłówek, a następnie wybierz In-App Purchases
po lewej stronie+
ikonę pośrodkunon-consumable
. Jeśli chcesz wysłać element fizyczny do użytkownika lub dać mu coś, co może kupić więcej niż jeden raz, wybierzesz consumable
.tld.websitename.appname.referencename
będzie to działać najlepiej, na przykład możesz użyćcom.jojodmo.blix.removeads
cleared for sale
a następnie wybierz warstwę ceny jako 1 (99 ¢). Poziom 2 wyniósłby 1,99 USD, a poziom 3 2,99 USD. Pełna lista jest dostępna po kliknięciu view pricing matrix
Polecam skorzystanie z poziomu 1, ponieważ zwykle jest to najwyższa kwota, jaką kiedykolwiek ktoś zapłaci za usunięcie reklam.add language
przycisk i wprowadź informacje. Zostanie to WSZYSTKIE pokazane klientowi, więc nie umieszczaj niczego, czego nie chciałbyś widziećhosting content with Apple
wybierz niescreenshot for review
TERAZ , wszystko, co pomijamy, wrócimy.Rejestracja identyfikatora produktu może potrwać kilka godzin App Store Connect
, więc bądź cierpliwy.
Po skonfigurowaniu informacji o zakupach w aplikacji w App Store Connect przejdź do projektu Xcode i przejdź do menedżera aplikacji (niebieska ikona przypominająca stronę u góry miejsca, w którym znajdują się twoje metody i pliki nagłówkowe), kliknij Twoja aplikacja pod celami (powinna być pierwsza), a następnie przejdź do ogólnych. Na dole powinieneś zobaczyć linked frameworks and libraries
mały symbol plus i dodać ramkę. StoreKit.framework
Jeśli tego nie zrobisz, zakup w aplikacji NIE zadziała!
Jeśli używasz Objective-C jako języka dla swojej aplikacji, powinieneś pominąć te pięć kroków . W przeciwnym razie, jeśli używasz Swift, możesz postępować zgodnie z My Swift Answer na to pytanie tutaj lub, jeśli wolisz używać Objective-C dla kodu zakupu w aplikacji, ale używasz Swift w swojej aplikacji, możesz wykonać następujące czynności :
Utwórz nowy .h
plik (nagłówek), przechodząc do File
> New
> File...
( Command ⌘+ N). Ten plik będzie nazywany „Twoim .h
plikiem” w dalszej części samouczka
Po wyświetleniu monitu kliknij opcję Utwórz nagłówek pomostowy . To będzie nasz mostkowy plik nagłówkowy. Jeśli nie zostanie wyświetlony monit, przejdź do kroku 3. Jeśli są poproszony, pomiń krok 3 i przejść bezpośrednio do kroku 4.
Utwórz inny .h
plik o nazwie Bridge.h
w głównym folderze projektu, a następnie przejdź do Menedżera aplikacji (niebieska ikona przypominająca stronę), a następnie wybierz aplikację w Targets
sekcji i kliknij Build Settings
. Znajdź opcję z napisem Swift Compiler - Generowanie kodu , a następnie ustaw opcję Header-C Bridging Header naBridge.h
W łączącym pliku nagłówka dodaj wiersz #import "MyObjectiveCHeaderFile.h"
, w którym MyObjectiveCHeaderFile
znajduje się nazwa pliku nagłówka utworzonego w kroku pierwszym. Na przykład, jeśli nazwiesz swój plik nagłówkowy InAppPurchase.h , dodasz linię #import "InAppPurchase.h"
do pliku nagłówkowego mostu.
Tworzenie Metody Objective-C (nowy .m
) plik, przechodząc do File
> New
> File...
( Command ⌘+ N). Nazwij go tak samo, jak plik nagłówka utworzony w kroku 1. Na przykład, jeśli plik został wywołany w kroku 1 InAppPurchase.h , nazwałbyś ten nowy plik InAppPurchase.m . Ten plik będzie nazywany „Twoim .m
plikiem” w dalszej części samouczka.
Teraz zajmiemy się kodowaniem. Dodaj następujący kod do swojego .h
pliku:
BOOL areAdsRemoved;
- (IBAction)restore;
- (IBAction)tapsRemoveAds;
Następnie musisz zaimportować StoreKit
framework do swojego .m
pliku, a także dodać SKProductsRequestDelegate
i SKPaymentTransactionObserver
po @interface
deklaracji:
#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
a teraz dodaj do .m
pliku następujące elementy, ta część komplikuje się, więc sugeruję przeczytanie komentarzy w kodzie:
//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;
}
}
}
Teraz chcesz dodać kod tego, co się stanie, gdy użytkownik zakończy transakcję, w tym samouczku używamy usuwania dodatków, musisz dodać własny kod, co się stanie, gdy załaduje się widok banera.
- (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];
}
Jeśli nie masz reklam w swojej aplikacji, możesz użyć dowolnej innej rzeczy. Na przykład możemy ustawić kolor tła na niebieski. Aby to zrobić, chcielibyśmy użyć:
- (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];
}
Teraz, gdzieś w swojej viewDidLoad
metodzie, będziesz chciał dodać następujący kod:
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
}
Po dodaniu całego kodu przejdź do pliku .xib
lub storyboard
pliku i dodaj dwa przyciski, jeden z napisem „kup”, a drugi „przywróć”. Podłącz utworzony przed tapsRemoveAds
IBAction
chwilą przycisk zakupu i restore
IBAction
przycisk przywracania. restore
Akcja będzie sprawdzać, czy użytkownik został wcześniej zakupiony zakupu w aplikacji, i dać im zakupu w aplikacji za darmo jeśli nie masz go.
Następnie przejdź do App Store Connect , kliknij, a Users and Access
następnie kliknij Sandbox Testers
nagłówek, a następnie kliknij +
symbol po lewej stronie, w którym jest napisane Testers
. Możesz po prostu wpisać losowe imię i nazwisko, a e-mail nie musi być prawdziwy - musisz tylko pamiętać. Wpisz hasło (które będziesz musiał zapamiętać) i wypełnij resztę informacji. Radziłbym ustalić Date of Birth
datę, która oznaczałaby, że użytkownik ma 18 lat lub więcej. App Store Territory
MUSI być we właściwym kraju. Następnie wyloguj się z istniejącego konta iTunes (możesz ponownie zalogować się po tym samouczku).
Teraz uruchom aplikację na urządzeniu z systemem iOS, jeśli spróbujesz uruchomić ją na symulatorze, zakup zawsze będzie błąd, MUSISZ uruchomić ją na urządzeniu z systemem iOS. Po uruchomieniu aplikacji dotknij przycisku zakupu. Po wyświetleniu monitu o zalogowanie się na konto iTunes zaloguj się jako użytkownik testowy, który właśnie utworzyliśmy. Następnie, gdy pojawi się prośba o potwierdzenie zakupu 99 centów lub cokolwiek innego, ustawisz także warstwę cenową, ZRÓB EKRAN SNAPSHOT TO, to będzie to, czego zamierzasz użyć screenshot for review
w App Store Connect. Teraz anuluj płatność.
Teraz przejdź do App Store Połącz , a następnie przejść do My Apps
> the app you have the In-app purchase on
> In-App Purchases
. Następnie kliknij zakup w aplikacji i edytuj pod szczegółami zakupu w aplikacji. Gdy to zrobisz, zaimportuj zdjęcie, które właśnie zrobiłeś iPhone'em na komputer, i prześlij je jako zrzut ekranu do recenzji, a następnie w notatkach z recenzji podaj swój adres e-mail i hasło TESTU UŻYTKOWNIKA . Pomoże to Apple w procesie przeglądu.
Po wykonaniu tej czynności wróć do aplikacji na urządzeniu z systemem iOS, nadal zalogowanego jako konto użytkownika testowego, i kliknij przycisk zakupu. Tym razem potwierdź płatność Nie martw się, to NIE obciąży Twojego konta ŻADNYMI pieniędzmi, przetestuj konta użytkowników otrzymuj wszystkie zakupy w aplikacji za darmo Po potwierdzeniu płatności upewnij się, że co się stanie, gdy użytkownik kupi Twój produkt dzieje się. Jeśli nie, oznacza to błąd w Twojej doRemoveAds
metodzie. Ponownie zalecam zmianę tła na niebieski w celu przetestowania zakupu w aplikacji, ale nie powinien to być faktyczny zakup w aplikacji. Jeśli wszystko działa i możesz już iść! Pamiętaj tylko, aby uwzględnić zakup w aplikacji w nowym pliku binarnym podczas przesyłania go do App Store Connect!
Zalogowany: No Products Available
Może to oznaczać cztery rzeczy:
kRemoveAdsProductIdentifier
w powyższym kodzieJeśli to nie zadziała za pierwszym razem, nie denerwuj się! Nie poddawaj się! Zajęło mi to około 5 godzin, zanim mogłem zacząć działać, i około 10 godzin na znalezienie odpowiedniego kodu! Jeśli dokładnie użyjesz powyższego kodu, powinien on działać poprawnie. Zapraszam do komentowania, jeśli masz jakiekolwiek pytania w ogóle .
Mam nadzieję, że to pomoże wszystkim tym, którzy chcą dodać zakup w aplikacji do swojej aplikacji na iOS. Twoje zdrowie!
Przetłumacz kod Jojodmo na 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")
}
}
}
}
Ma to na celu uzupełnienie mojej odpowiedzi Objective-C dla użytkowników Swift, aby nie dopuścić do zbyt dużej odpowiedzi Objective-C.
Najpierw skonfiguruj zakup w aplikacji na appstoreconnect.apple.com . Postępuj zgodnie z początkową częścią mojej odpowiedzi Cel C (kroki 1-13, pod nagłówkiem App Store Connect ), aby uzyskać instrukcje na ten temat.
Rejestracja identyfikatora produktu w App Store Connect może potrwać kilka godzin, więc bądź cierpliwy.
Po skonfigurowaniu informacji o zakupach w aplikacji w App Store Connect musimy dodać do aplikacji strukturę Apple do zakupów StoreKit
w aplikacji.
Przejdź do projektu Xcode i przejdź do menedżera aplikacji (niebieska ikona przypominająca stronę u góry lewego paska, w którym znajdują się pliki aplikacji). Kliknij aplikację pod celami po lewej stronie (powinna to być pierwsza opcja), a następnie przejdź do „Możliwości” u góry. Na liście powinna być widoczna opcja „Zakup w aplikacji”. Włącz tę funkcję, a Xcode doda StoreKit
do twojego projektu.
Teraz zaczniemy kodować!
Najpierw utwórz nowy szybki plik, który będzie zarządzał wszystkimi Twoimi zakupami w aplikacji. Nazwie to IAPManager.swift
.
W tym pliku utworzymy nową klasę o nazwie IAPManager
to a SKProductsRequestDelegate
i SKPaymentTransactionObserver
. U góry upewnij się, że importujesz Foundation
iStoreKit
import Foundation
import StoreKit
public class IAPManager: NSObject, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
}
Następnie dodamy zmienną, aby zdefiniować identyfikator naszego zakupu w aplikacji (możesz także użyć zmiennej enum
, która byłaby łatwiejsza do utrzymania, jeśli masz wiele 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"
Dodajmy teraz inicjalizator dla naszej klasy:
// 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
}
Teraz dodamy wymagane funkcje SKProductsRequestDelegate
i SKPaymentTransactionObserver
do pracy:
Dodamy RemoveAdsManager
klasę później
// 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
}
}
}
Dodajmy teraz funkcje, których można użyć do rozpoczęcia zakupu lub przywrócenia zakupów:
// 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()
}
Następnie dodajmy nową klasę narzędzi do zarządzania naszymi IAP. Cały ten kod może należeć do jednej klasy, ale jego wielokrotność sprawia, że jest trochę czystszy. Mam zamiar stworzyć nową klasę o nazwie RemoveAdsManager
, a w niej umieścić kilka funkcji
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()
}
Pierwsze trzy funkcje, removeAds
, restoreRemoveAds
, i areAdsRemoved
są funkcje, które musisz zadzwonić, aby wykonać pewne czynności. Cztery ostatnie to te, które będą wywoływane przez IAPManager
.
Dodajmy kod do pierwszych dwóch funkcji removeAds
i 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()
}
Na koniec dodajmy kod do pięciu ostatnich funkcji.
// 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.
}
Podsumowując, otrzymujemy coś takiego:
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
}
}
}
}
Na koniec musisz dodać sposób, w jaki użytkownik może rozpocząć zakup i zadzwonić RemoveAdsManager.removeAds()
oraz rozpocząć przywracanie i połączenie RemoveAdsManager.restoreRemoveAds()
, jak gdzieś przycisk! Pamiętaj, że zgodnie ze wskazówkami App Store musisz podać przycisk, aby przywrócić gdzieś zakupy.
Ostatnią rzeczą do zrobienia jest przesłanie IAP do sprawdzenia w App Store Connect! Aby uzyskać szczegółowe instrukcje na ten temat, możesz postępować zgodnie z ostatnią częścią mojej odpowiedzi na Cel C , pod nagłówkiem Przesyłanie do recenzji .
RMStore to lekka biblioteka iOS do zakupów w aplikacji. Zawiera API StoreKit i zapewnia przydatne bloki dla żądań asynchronicznych. Zakup produktu jest tak prosty, jak wywołanie jednej metody.
Dla zaawansowanych użytkowników ta biblioteka zapewnia również weryfikację pokwitowań, pobieranie treści i utrwalanie transakcji.
Wiem, że spóźniłem się z opublikowaniem tego, ale dzielę się podobnym doświadczeniem, kiedy poznałem liny modelu IAP.
Zakup w aplikacji to jeden z najbardziej kompleksowych przepływów pracy w systemie iOS realizowany przez platformę Storekit. Cała dokumentacja jest dość oczywiste, jeśli cierpliwość, aby ją przeczytać, ale jest nieco rozszerzone w naturze technicyzacji.
Podsumowując:
1 - Zamów produkty - skorzystaj z klas SKProductRequest & SKProductRequestDelegate, aby wysłać zapytanie o identyfikatory produktu i otrzymać je z powrotem z własnego sklepu itunesconnect.
Tych produktów SKProducts należy użyć do wypełnienia interfejsu użytkownika sklepu, którego użytkownik może użyć do zakupu określonego produktu.
2 - Wyślij prośbę o płatność - użyj SKPayment i SKPaymentQueue, aby dodać płatność do kolejki transakcji.
3 - Monitoruj kolejkę transakcji w celu aktualizacji statusu - użyj zaktualizowanej metody SKPaymentTransactionObserver protokołu do monitorowania statusu:
SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction
4 - Przywróć przepływ przycisków - użyj polecenia SKPaymentQueue restoreCompletedTransactions, aby to zrobić - krok 3 zajmie się resztą, wraz z następującymi metodami SKPaymentTransactionObserver:
paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError
Oto samouczek krok po kroku (autorstwa autora będący wynikiem moich własnych prób jego zrozumienia), który to wyjaśnia. Na koniec zawiera również przykładowy kod, którego można użyć bezpośrednio.
Oto kolejna, którą stworzyłem, aby wyjaśnić pewne rzeczy, które tylko tekst mógłby opisać w lepszy sposób.