In-app Purchase: In-app Purchase. Is Apple’s customized system for trading virtual goods and services within apps.

In-app purchases are not allowed to use third-party payment methods such as wechat/Alipay /Apple Pay when operating virtual goods, and those who avoid using alternative methods will be removed from the shelves.

Configure internal purchase information

Please refer to the previous article, “iOS Packaged New (including in-app Purchases) 2019”.

Creating a commodity type

Consumable: Used once and used up, can be re-purchased. It’s like a chest, a weapon skin

Non-consumable: purchase only once and will not expire. It’s like a map, an e-book

Automatic renewal subscription: The service or content will be automatically renewed after a purchase until the user decides to cancel. Similar to Apple Music

Non-renewal subscription: once purchased, will not automatically renew, and can be purchased again, equivalent to the purchase of a period of service can be superimposed. Similar to membership, a certain number of diamonds

StoreKit

Connect your application to the App Store through the StoreKit framework to prompt and securely process payments.



To follow the SKProductsRequestDelegate, SKPaymentTransactionObserver agreement.

// Add payment listener skPaymentQueue.default ().add(self) // Remove listener skPaymentQueue.default ().remove(self)Copy the code

Click to buy

// Whether to allow payment (limit the number of people who buy by permission)if SKPaymentQueue.canMakePayments() {pid: select the id corresponding to the package, which is generated from the apple background package configuration and obtained from the App serverlet set= NSSet(array: [pid])let request = SKProductsRequest(productIdentifiers: set as! Set<String>)  
	request.delegate = self
	request.start()
}else {
    print("No purchase")}Copy the code

Restore purchase, which is used to persist our order information and will be called again the next time we listen to the payment queue

Restore before buying SKPaymentQueue. Default () restoreCompletedTransactions () / / non renew subscription and consumption cannot recover purchase history records of transactions, Add to queue error correction func paymentQueue (_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: error) {print("Error recovery: \(error)")} restore purchase, successful add queue callback func paymentQueueRestoreCompletedTransactionsFinished (_ queue: SKPaymentQueue) {}Copy the code

Product requirements agent < SKProductsRequestDelegate >

Func productsRequest(_ Request: SKProductsRequest, didReceive Response: Func Request (_ request: SKRequest, didFailWithError error: Func requestDidFinish(_ request: SKRequest) {}Copy the code

Payment transaction observation agent < SKPaymentTransactionObserver >

Func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {for tran in transactions {
            switch tran.transactionState {
            case.purchased: // purchased to verify //1. Get the persistent order information from the sandbox //2. Upload the receipt to the App server and close the transaction after success //3. Delete the saved order informationcase.purchasing: // in tradecase.restored: // already boughtcase. Failed: // The transaction failedbreak
            default:
                break}}} deal ends private func payFinish (transaction: SKPaymentTransaction) {}Copy the code

code

Import UIKit import StoreKit // MARK: -swift title: In-app Purchase // MARK: -click to buy class UserIAPBuy: NSObject,SKProductsRequestDelegate,SKPaymentTransactionObserver { func startBuyInnerProduct(_type: String) -> () {​        
        SKPaymentQueue.default().add(self as SKPaymentTransactionObserver)​        ​        
        localRequest(type)​    
    }​    ​    
    func localRequest(_ type: String) -> () {​        ​        
        if SKPaymentQueue.canMakePayments() {​            
            let set = NSSet(array: [type]) // Retrieve localized product informationlet request = SKProductsRequest(productIdentifiers: set as! Set<String>)​            
            request.delegate = self​            
            request.start()​        
        }else {​            
            print("No purchase"}}} // MARK: - Resume purchasing extension UserIAPBuy {func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {print("Error recovery code: (error)")} func paymentQueueRestoreCompletedTransactionsFinished (_ queue: SKPaymentQueue) {/ / recovery buy success callback var array = Stringprint("received restored transactions: (queue.transactions.count)")​        
        for pay:SKPaymentTransaction in queue.transactions {​            
            let productID = pay.payment.productIdentifier​            
            array.append(productID)​            
            print("message: (array)"}} // Restore before buying @objc funcreplyToBuy() { SKPaymentQueue.default().restoreCompletedTransactions() } } // MARK: - agent: commodity SKProductsRequestDelegate extension UserIAPBuy {func productsRequest (_ the request: SKProductsRequest, didReceive Response: SKProductsResponse) {// Received App Store product information responselet pro = response.products​        ​        
        if pro.count > 0 {​            
            var p: SKProduct?​            
            for pid in pro {​                
                print("Description: (pD.description)")​                
                print(Product title: (PID. LocalizedTitle), Product description: (PID. LocalizedDescription))​                
                print("Price: (PD.price)")​                
                p = pid​            
            }​            
            guard let pro = p else {​                
                return​            
            }​            
            SKPaymentQueue.default().add(SKPayment(product: p))​            ​        
        }else{// Handle the operation where the product information cannot be obtainedprint("Can't get commodity information."}} func requestDidFinish(_ request: SKRequest) {// handle the request complete} func request(_ request: SKRequest) SKRequest, didFailWithError error: error) {// Failed to handle the request}} // MARK: - agent: payment transactions to observe SKPaymentTransactionObserver extension UserIAPBuy {func paymentQueue (_ the queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])for tran in transactions {​            
            switch tran.transactionState {​            
            case.purchased: // purchased to verify //1. Get the persistent order information from the sandbox //2. Upload the receipt to the App server and close the transaction after success //3. Delete saved order information paymentTransactionPurchased SKPaymentQueue (). The default () finishTransaction (tran)case. Purchasing: / / trading paymentTransactionPurchasing ()caseRestored: / / has been buying paymentTransactionRestored SKPaymentQueue (). The default () finishTransaction (tran)casePaymentTransactionFailed (TRAN) default:break​            
            }​        
        }​    
    }​    ​    
    func paymentTransactionPurchased() {// Verify purchases to avoid jailbreak software simulating Apple requests to reach illegal purchaseslet receiptUrl = Bundle.main.appStoreReceiptURL,let receiptData = try? Data(contentsOf: receiptUrl) else{// get the transaction certificate from the sandbox and splice b into request data 0return​        
        }​        
        letReceiptStr = receiptData. Base64EncodedString (options: Data. Base64EncodingOptions. EndLineWithLineFeed) / / processing order informationprint("Order Information: (receiptStr)") // Delete the saved order} funcpaymentTransactionPurchasing() {​        
        print("In trade.")​    
    }​    ​    
    func paymentTransactionRestored() {​        
        print("Already purchased.")​    
    }​    ​    
    func paymentTransactionFailed(_ transaction: SKPaymentTransaction) {​        
        guard let err = transaction.error else {​            
            SKPaymentQueue.default().finishTransaction(transaction)​            
            return​        
        }​        
        if(err as NSError).code ! = SKError.paymentCancelled.rawValue {print("Deal failed.")}else{​            
            print("Deal off.")}}}Copy the code

The sandbox test

The sandbox account is an Apple ID for test purposes. Since in-app purchase involves actual money transactions, we can complete in-app purchase payment by registering an Apple sandbox account in the test phase.

Email can be customized.


The choice of App Store region is related to the settlement price.



App IAP permissions open




The last

Technical summary:

1. To configure the internal purchase project in the App Store background, it is necessary to complete the agreement and tax bank project first. This part can refer to the previous article

2. Configure the App ID with the in-app Purchase function and enable THE IAP permission.

3. Implement and tweak your in-app purchase code where appropriate, using the StoreKit framework.

Points to note:

1. As for the server verification receipt, the server also has two environments, sandbox and official. There are also many mature open source frameworks available for specific implementation, and the application package information needs to be configured for the server.

2. Apple’s built-in in-purchase experience is really not very good, because the server itself is in the United States, the connection will be slow, add gaza box App ID, it is recommended not to bind in the App, you can choose from the Settings -iTunesStore to bind AppleID test account, binding success, the bottom will add a sandbox account.

3. The application package is also need to review, to fill the place to fill in clear, no big problem, the other application package in auditing is along with the main App review, if the main App not, then the state of the original package approval will be change, in the next time to resubmit the App need to reset the state of the package, otherwise, can’t find the corresponding information package.

Verify purchase (secondary verification) :

1.App sends the order receipt to the server

2. The server sends the receipt to the App Store for verification and returns the verification result

This step should be performed as far as possible on the server side. Placing all the validation transaction data on the client side is not only unsafe, but also unstable.

Server-side Ruby Integrated In-app Purchase Framework (Monza)

OC code DemoGithub.com/marst123/In…



This is a personal share and I hope I can contribute more to the iOS community.