Làm cách nào để thêm giao dịch mua trong ứng dụng vào ứng dụng iOS? Tất cả các chi tiết là gì và có mã mẫu nào không?
Điều này có nghĩa là tất cả các loại để biết cách thêm mua hàng trong ứng dụng vào ứng dụng iOS
Làm cách nào để thêm giao dịch mua trong ứng dụng vào ứng dụng iOS? Tất cả các chi tiết là gì và có mã mẫu nào không?
Điều này có nghĩa là tất cả các loại để biết cách thêm mua hàng trong ứng dụng vào ứng dụng iOS
Câu trả lời:
Người dùng Swift có thể kiểm tra Câu trả lời của tôi cho câu hỏi này .
Hoặc, xem Câu trả lời của Yedidya Reiss , bản dịch mã Objective-C này sang Swift.
Phần còn lại của câu trả lời này được viết bằng Objective-C
My Apps
sau đó nhấp vào ứng dụng bạn muốn thêm mua hàng vàoFeatures
tiêu đề, và sau đó chọn In-App Purchases
ở bên trái+
biểu tượng ở giữanon-consumable
. Nếu bạn định gửi một món đồ vật lý cho người dùng, hoặc đưa cho họ thứ gì đó họ có thể mua nhiều lần, bạn sẽ chọn consumable
.tld.websitename.appname.referencename
điều này sẽ hoạt động tốt nhất, vì vậy, ví dụ, bạn có thể sử dụngcom.jojodmo.blix.removeads
cleared for sale
và sau đó chọn tầng giá là 1 (99). Cấp 2 sẽ là $ 1,99 và cấp 3 sẽ là $ 2,99. Danh sách đầy đủ có sẵn nếu bạn nhấp vào view pricing matrix
Tôi khuyên bạn nên sử dụng cấp 1, vì đó thường là số tiền nhiều nhất mọi người sẽ trả để xóa quảng cáo.add language
nút màu xanh và nhập thông tin. Điều này sẽ TẤT CẢ được hiển thị cho khách hàng, vì vậy đừng đặt bất cứ thứ gì bạn không muốn họ nhìn thấyhosting content with Apple
chọn khôngscreenshot for review
NGAY BÂY GIỜ , mọi thứ chúng tôi bỏ qua chúng tôi sẽ quay lại.Có thể mất vài giờ để ID sản phẩm của bạn đăng ký App Store Connect
, vì vậy hãy kiên nhẫn.
Bây giờ bạn đã thiết lập thông tin mua trong ứng dụng của mình trên App Store Connect, hãy vào dự án Xcode của bạn và đi đến trình quản lý ứng dụng (biểu tượng giống như trang màu xanh ở đầu nơi chứa các phương thức và tệp tiêu đề của bạn) ứng dụng của bạn theo mục tiêu (nên là ứng dụng đầu tiên), sau đó đi đến chung. Ở phía dưới, bạn sẽ thấy linked frameworks and libraries
nhấp vào biểu tượng dấu cộng nhỏ và thêm khung StoreKit.framework
Nếu bạn không làm điều này, giao dịch mua trong ứng dụng sẽ KHÔNG hoạt động!
Nếu bạn đang sử dụng Objective-C làm ngôn ngữ cho ứng dụng của mình, bạn nên bỏ qua năm bước này . Mặt khác, nếu bạn đang sử dụng Swift, bạn có thể theo dõi Câu trả lời của Swift cho câu hỏi này, tại đây hoặc, nếu bạn muốn sử dụng Objective-C cho mã Mua trong ứng dụng nhưng đang sử dụng Swift trong ứng dụng của mình, bạn có thể làm như sau :
Tạo mới .h
(tiêu đề) tập tin bằng cách vào File
> New
> File...
( Command ⌘+ N). Tập tin này sẽ được gọi là " .h
Tập tin của bạn " trong phần còn lại của hướng dẫn
Khi được nhắc, bấm Tạo Tiêu đề Cầu nối . Đây sẽ là tập tin tiêu đề bắc cầu của chúng tôi. Nếu bạn không được nhắc, hãy đến bước 3. Nếu bạn được nhắc, hãy bỏ qua bước 3 và chuyển trực tiếp đến bước 4.
Tạo một .h
tệp khác có tên Bridge.h
trong thư mục dự án chính, sau đó chuyển đến Trình quản lý ứng dụng (biểu tượng giống trang màu xanh), sau đó chọn ứng dụng của bạn trong Targets
phần và nhấp vào Build Settings
. Tìm tùy chọn cho biết Trình biên dịch Swift - Tạo mã , sau đó đặt tùy chọn Tiêu đề cầu nối Objective-C thànhBridge.h
Trong tệp tiêu đề bắc cầu của bạn, hãy thêm dòng #import "MyObjectiveCHeaderFile.h"
, MyObjectiveCHeaderFile
tên của tệp tiêu đề mà bạn đã tạo ở bước một. Vì vậy, ví dụ, nếu bạn đặt tên tệp tiêu đề của mình là InAppPurchase.h , bạn sẽ thêm dòng #import "InAppPurchase.h"
vào tệp tiêu đề cầu của mình.
Tạo mới Objective-C Phương pháp ( .m
tập tin) bằng cách vào File
> New
> File...
( Command ⌘+ N). Đặt tên giống như tệp tiêu đề bạn đã tạo ở bước 1. Ví dụ: nếu bạn đã gọi tệp ở bước 1 InAppPurchase.h , bạn sẽ gọi tệp mới này là InAppPurchase.m . Tập tin này sẽ được gọi là " .m
Tập tin của bạn " trong phần còn lại của hướng dẫn.
Bây giờ chúng ta sẽ đi vào mã hóa thực tế. Thêm mã sau vào .h
tệp của bạn :
BOOL areAdsRemoved;
- (IBAction)restore;
- (IBAction)tapsRemoveAds;
Tiếp theo, bạn cần nhập StoreKit
khung vào .m
tệp của mình , cũng như thêm SKProductsRequestDelegate
và SKPaymentTransactionObserver
sau khi @interface
khai bá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
và bây giờ thêm phần sau vào .m
tệp của bạn , phần này trở nên phức tạp, vì vậy tôi khuyên bạn nên đọc các nhận xét trong 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;
}
}
}
Bây giờ bạn muốn thêm mã của mình cho những gì sẽ xảy ra khi người dùng kết thúc giao dịch, đối với hướng dẫn này, chúng tôi sử dụng loại bỏ thêm, bạn sẽ phải thêm mã của riêng mình cho những gì xảy ra khi tải banner xem.
- (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];
}
Nếu bạn không có quảng cáo trong ứng dụng của mình, bạn có thể sử dụng bất kỳ thứ gì khác mà bạn muốn. Ví dụ, chúng ta có thể làm cho màu của nền xanh. Để làm điều này, chúng tôi muốn sử dụng:
- (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];
}
Bây giờ, ở đâu đó trong viewDidLoad
phương thức của bạn , bạn sẽ muốn thêm mã sau đây:
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
}
Bây giờ bạn đã thêm tất cả mã, đi vào tệp .xib
hoặc storyboard
tệp của bạn và thêm hai nút, một nút nói mua và nút còn lại nói khôi phục. Kết nối tapsRemoveAds
IBAction
với nút mua hàng mà bạn vừa thực hiện và restore
IBAction
nút khôi phục. Các restore
hành động sẽ kiểm tra xem người dùng trước đây đã mua mua trong ứng dụng, và cung cấp cho họ mua trong ứng dụng miễn phí nếu họ chưa có nó.
Tiếp theo, đi vào App Store Connect và nhấp vào Users and Access
sau đó nhấp vào Sandbox Testers
tiêu đề và sau đó nhấp vào +
biểu tượng ở bên trái nơi nó nói Testers
. Bạn chỉ có thể đặt những thứ ngẫu nhiên cho tên và họ, và e-mail không phải là thật - bạn chỉ cần có thể nhớ nó. Đặt mật khẩu (mà bạn sẽ phải nhớ) và điền vào phần còn lại của thông tin. Tôi khuyên bạn nên thực hiện Date of Birth
một ngày sẽ khiến người dùng từ 18 tuổi trở lên. App Store Territory
ĐÃ ở đúng quốc gia Tiếp theo, đăng xuất khỏi tài khoản iTunes hiện tại của bạn (bạn có thể đăng nhập lại sau hướng dẫn này).
Bây giờ, hãy chạy ứng dụng của bạn trên thiết bị iOS của bạn, nếu bạn cố gắng chạy nó trên mô phỏng, việc mua sẽ luôn báo lỗi, bạn PHẢI chạy nó trên thiết bị iOS của bạn. Khi ứng dụng đang chạy, chạm vào nút mua hàng. Khi bạn được nhắc đăng nhập vào tài khoản iTunes của mình, hãy đăng nhập với tư cách là người dùng thử nghiệm mà chúng tôi vừa tạo. Tiếp theo, khi nó yêu cầu bạn xác nhận việc mua 99 hoặc bất cứ điều gì bạn đặt mức giá quá, HÃY NHANH TAY MỘT NỀN TẢNG Đây là những gì bạn sẽ sử dụng cho screenshot for review
Kết nối App Store của mình. Bây giờ hủy thanh toán.
Bây giờ, đi đến App Store Kết nối , sau đó đi đến My Apps
> the app you have the In-app purchase on
> In-App Purchases
. Sau đó nhấp vào mua trong ứng dụng của bạn và nhấp vào chỉnh sửa trong chi tiết mua trong ứng dụng. Khi bạn đã hoàn thành việc đó, hãy nhập ảnh mà bạn vừa chụp trên iPhone vào máy tính của mình và tải lên đó làm ảnh chụp màn hình để xem xét, sau đó, trong ghi chú đánh giá, hãy đặt e-mail và mật khẩu TEST USER của bạn . Điều này sẽ giúp táo trong quá trình xem xét.
Sau khi bạn đã hoàn thành việc này, hãy quay lại ứng dụng trên thiết bị iOS của bạn, vẫn đăng nhập với tư cách là tài khoản người dùng thử nghiệm và nhấp vào nút mua hàng. Lần này, hãy xác nhận thanh toán Đừng lo lắng, điều này sẽ KHÔNG tính phí tài khoản của bạn BẤT K money tiền nào, kiểm tra tài khoản người dùng được miễn phí tất cả các giao dịch mua trong ứng dụng Sau khi bạn đã xác nhận thanh toán, hãy chắc chắn rằng điều gì xảy ra khi người dùng thực sự mua sản phẩm của bạn xảy ra. Nếu không, thì đó sẽ là một lỗi với doRemoveAds
phương thức của bạn . Một lần nữa, tôi khuyên bạn nên sử dụng thay đổi nền thành màu xanh để kiểm tra mua trong ứng dụng, đây không phải là mua trong ứng dụng thực tế của bạn. Nếu mọi thứ hoạt động và bạn tốt để đi! Chỉ cần đảm bảo bao gồm giao dịch mua trong ứng dụng trong tệp nhị phân mới của bạn khi bạn tải nó lên App Store Connect!
Đã đăng nhập: No Products Available
Điều này có thể có nghĩa là bốn điều:
kRemoveAdsProductIdentifier
trong mã trênNếu lần đầu tiên nó không hoạt động, đừng nản lòng! Đừng bỏ cuộc! Tôi mất khoảng 5 giờ liên tục trước khi tôi có thể làm việc này và khoảng 10 giờ để tìm đúng mã! Nếu bạn sử dụng mã ở trên một cách chính xác, nó sẽ hoạt động tốt. Hãy bình luận nếu bạn có bất cứ thắc mắc gì cả .
Tôi hy vọng điều này sẽ giúp tất cả những người hy vọng thêm giao dịch mua trong ứng dụng vào ứng dụng iOS của họ. Chúc mừng!
Chỉ cần dịch mã Jojodmo sang 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")
}
}
}
}
Điều này có nghĩa là để bổ sung câu trả lời Objective-C của tôi cho người dùng Swift, để giữ cho câu trả lời Objective-C không quá lớn.
Đầu tiên, hãy thiết lập giao dịch mua trong ứng dụng trên appstoreconnect.apple.com . Thực hiện theo phần đầu của câu trả lời Objective-C của tôi (các bước 1-13, dưới tiêu đề Kết nối App Store ) để được hướng dẫn thực hiện điều đó.
Có thể mất vài giờ để ID sản phẩm của bạn đăng ký trong App Store Connect, vì vậy hãy kiên nhẫn.
Bây giờ bạn đã thiết lập thông tin mua trong ứng dụng của mình trên App Store Connect, chúng tôi cần thêm khung của Apple để mua trong ứng dụng StoreKit
, vào ứng dụng.
Đi vào dự án Xcode của bạn và đi đến trình quản lý ứng dụng (biểu tượng giống như trang màu xanh ở đầu thanh bên trái nơi chứa các tệp của ứng dụng của bạn). Nhấp vào ứng dụng của bạn dưới các mục tiêu ở bên trái (nó phải là tùy chọn đầu tiên), sau đó chuyển đến "Khả năng" ở trên cùng. Trong danh sách, bạn sẽ thấy tùy chọn "Mua trong ứng dụng". BẬT khả năng này, và Xcode sẽ thêm StoreKit
vào dự án của bạn.
Bây giờ, chúng ta sẽ bắt đầu viết mã!
Đầu tiên, tạo một tệp nhanh chóng mới sẽ quản lý tất cả các giao dịch mua trong ứng dụng của bạn. Tôi sẽ gọi nó IAPManager.swift
.
Trong tệp này, chúng tôi sẽ tạo một lớp mới, được gọi IAPManager
là một SKProductsRequestDelegate
và SKPaymentTransactionObserver
. Ở đầu trang, đảm bảo bạn nhập Foundation
vàStoreKit
import Foundation
import StoreKit
public class IAPManager: NSObject, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
}
Tiếp theo, chúng tôi sẽ thêm một biến để xác định mã định danh cho giao dịch mua trong ứng dụng của chúng tôi (bạn cũng có thể sử dụng một biến enum
, sẽ dễ duy trì hơn nếu bạn có nhiều 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"
Hãy thêm một trình khởi tạo cho lớp của chúng ta tiếp theo:
// 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
}
Bây giờ, chúng ta sẽ thêm các chức năng cần thiết cho SKProductsRequestDelegate
và SKPaymentTransactionObserver
để làm việc:
Chúng tôi sẽ thêm RemoveAdsManager
lớp sau
// 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
}
}
}
Bây giờ, hãy thêm một số chức năng có thể được sử dụng để bắt đầu mua hàng hoặc khôi phục mua hàng:
// 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()
}
Tiếp theo, hãy thêm một lớp tiện ích mới để quản lý IAP của chúng tôi. Tất cả các mã này có thể nằm trong một lớp, nhưng việc có nhiều mã này làm cho nó sạch hơn một chút. Tôi sẽ tạo một lớp mới được gọi RemoveAdsManager
, và trong đó, đặt một vài hàm
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()
}
Ba chức năng đầu tiên removeAds
,restoreRemoveAds
, vàareAdsRemoved
, là chức năng mà bạn sẽ gọi để làm một số hành động. Bốn người cuối cùng là một người sẽ được gọi bởi IAPManager
.
Hãy thêm một số mã vào hai chức năng đầu tiên removeAds
và 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()
}
Và cuối cùng, hãy thêm một số mã vào năm chức năng cuối cùng.
// 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.
}
Đặt tất cả lại với nhau, chúng ta có được một cái gì đó như thế này:
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
}
}
}
}
Cuối cùng, bạn cần thêm một số cách để người dùng bắt đầu mua hàng và gọi RemoveAdsManager.removeAds()
và bắt đầu khôi phục và gọiRemoveAdsManager.restoreRemoveAds()
, như một nút ở đâu đó! Hãy nhớ rằng, theo hướng dẫn của App Store, bạn cần cung cấp một nút để khôi phục mua hàng ở đâu đó.
Điều cuối cùng cần làm là gửi IAP của bạn để xem xét trên App Store Connect! Để được hướng dẫn chi tiết về cách thực hiện điều đó, bạn có thể làm theo phần cuối của câu trả lời Mục tiêu-C của tôi , trong phần Gửi bài cho tiêu đề đánh giá .
RMStore là một thư viện iOS nhẹ dành cho Mua trong ứng dụng. Nó bao bọc API StoreKit và cung cấp cho bạn các khối tiện dụng cho các yêu cầu không đồng bộ. Mua một sản phẩm dễ dàng như gọi một phương pháp duy nhất.
Đối với người dùng nâng cao, thư viện này cũng cung cấp xác minh biên nhận, tải xuống nội dung và kiên trì giao dịch.
Tôi biết tôi khá muộn để đăng bài này, nhưng tôi chia sẻ kinh nghiệm tương tự khi tôi học được các sợi dây của mô hình IAP.
Mua trong ứng dụng là một trong những quy trình làm việc toàn diện nhất trong iOS được triển khai bởi khung Storekit. Các toàn bộ tài liệu là khá rõ ràng nếu bạn kiên nhẫn để đọc nó, nhưng có phần tiên tiến trong bản chất của technicality.
Để tóm tắt:
1 - Yêu cầu các sản phẩm - sử dụng các lớp SK SẢNtRequest & SK SẢNtRequestDelegate để đưa ra yêu cầu cho ID sản phẩm và nhận lại chúng từ cửa hàng itunesconnect của riêng bạn.
Những SK Products này nên được sử dụng để điền vào UI cửa hàng của bạn mà người dùng có thể sử dụng để mua một sản phẩm cụ thể.
2 - Phát hành yêu cầu thanh toán - sử dụng SKPayment & SKPaymentQueue để thêm thanh toán vào hàng đợi giao dịch.
3 - Giám sát hàng đợi giao dịch để cập nhật trạng thái - sử dụng phương thức Cập nhật giao dịch của SKPaymentTransactionObserver Protocol để theo dõi trạng thái:
SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction
4 - Khôi phục luồng nút - sử dụng SKPaymentQueue restoreCompletedTransilities để thực hiện việc này - bước 3 sẽ xử lý phần còn lại, cùng với các phương pháp sau của SKPaymentTransactionObserver:
paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError
Đây là một hướng dẫn từng bước (do tác giả của tôi là kết quả của nỗ lực của tôi để hiểu nó) giải thích nó. Cuối cùng, nó cũng cung cấp mẫu mã mà bạn có thể trực tiếp sử dụng.
Đây là một cái khác tôi tạo ra để giải thích một số điều mà chỉ văn bản có thể mô tả theo cách tốt hơn.