IOS內(nèi)購開發(fā)(Objc和Swift)及漏單處理

Demo

demo是使用FMDB保存到本地(也可以使用歸檔等方法保存)
支付測試必須要使用真機
注意:在調(diào)用支付的過程比較慢,需要加入動畫提示,不然可能被拒。

內(nèi)購申請

進入https://appstoreconnect.apple.com

1.png

這個是填寫銀行卡等信息的(這里我已經(jīng)填寫了)、具體就不展示了


2.png

添加上面的信息了、需要等待蘋果審核(最晚一天、如果一天還沒有通過。請檢查個人信息)
進入我的App添加內(nèi)購項目


3.png

這里我選擇消耗型的項目作為Demo


4.png

進入用戶和訪問、添加沙盒測試員


5.png

沙盒測試


5.jpeg

內(nèi)購流程

內(nèi)購流程

根據(jù)上圖可以知道蘋果內(nèi)購結(jié)果回調(diào)是APP告訴應(yīng)用服務(wù)器的。不是蘋果服務(wù)器直接告訴應(yīng)用服務(wù)器。這樣可能出現(xiàn)漏單的情況。

比如說
網(wǎng)絡(luò)不好的說話、沒有告訴應(yīng)用服務(wù)器結(jié)果
還沒有應(yīng)用服務(wù)器結(jié)果、用戶關(guān)閉了APP等等情況。

處理方法

1.當支付成功的時候、把訂單數(shù)據(jù)保存到本地。重新進入App的時候請求支付回調(diào)
//實現(xiàn)第一部的時候可以處理大部分漏單情況、但是還是沒有辦法避免漏單(用戶刪除了APP沒有本地數(shù)據(jù)的情況等)
2.在創(chuàng)建預(yù)訂單的時候保存下單時間等數(shù)據(jù)、可以讓客服查詢

Objc

#import "ViewController.h"
#import "SqlManager.h"
#import "ApplePayModel.h"

#import <StoreKit/StoreKit.h>

@interface ViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
@property (nonatomic, strong) SKProductsRequest * request;
@end

@implementation ViewController

- (void)dealloc {

    NSLog(@"釋放充值");
    if (self.request)
    {
        [self.request cancel];
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    NSArray *array = [[SqlManager sharedManager] getAllApplePayModel];
    NSLog(@"數(shù)據(jù)庫里面的數(shù)據(jù) = %@",array);
    if (array.count > 0) {
        for (ApplePayModel *model in array) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:0];
            [dict setObject:model.payData forKey:@"receiptData"];
            /**
             * 內(nèi)購校驗的Api(你的應(yīng)用的api)
             */
        }
    }
}

# pragma mark 購買
- (IBAction)buyBtn:(id)sender {
    
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    if([SKPaymentQueue canMakePayments]){
        // 你的itunesConnect的商品ID
        [self getProductInfowithprotectId:@""];
    }else{
        NSLog(@"不允許程序內(nèi)付費");
    }
    

}


#pragma mark -- 蘋果內(nèi)購服務(wù),下面的ProductId應(yīng)該是事先在itunesConnect中添加好的,已存在的付費項目。否則查詢會失敗。
- (void)getProductInfowithprotectId:(NSString *)proId
{
    NSMutableArray *proArr = [NSMutableArray new];
    [proArr addObject:proId];
    NSSet * set = [NSSet setWithArray:proArr];
    
    self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    self.request.delegate = self;
    [self.request start];
    
    NSLog(@"%@",set);
    NSLog(@"請求開始請等待...");
}




//收到產(chǎn)品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    
    NSLog(@"--------------收到產(chǎn)品反饋消息---------------------");
    
    NSArray *product = response.products;
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    if(product.count==0){
        NSLog(@"查找不到商品信息");
        return;
    }

    SKProduct *p = nil;
    for(SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        
        if([pro.productIdentifier isEqualToString: [pro productIdentifier]]){
            p = pro;
        }
    }
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    NSLog(@"發(fā)送購買請求");
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

//請求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"支付失敗");
}

- (void)requestDidFinish:(SKRequest *)request{
    NSLog(@"支付調(diào)用完成");
}

//監(jiān)聽購買結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
    for(SKPaymentTransaction *tran in transaction){

        switch(tran.transactionState) {
            case SKPaymentTransactionStatePurchased:{ // 購買成功,此時要提供給用戶相應(yīng)的內(nèi)容

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                
                NSLog(@"購買成功 = %@", tran);
                
                NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
                NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
                NSString *receiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
                
                // 保存訂單信息到數(shù)據(jù)庫
                ApplePayModel *model = [ApplePayModel new];
                // 這里把訂單ID寫死了
                model.orderId = @"12345";
                model.payData = receiptString;
                
                [[SqlManager sharedManager] saveApplePayModel:model];

                    
                /**
                 * 請求校驗API 請求完成刪除數(shù)據(jù)庫對應(yīng)數(shù)據(jù)
                 */
//              [[SqlManager sharedManager] removeMotionModel:model];

                
                
                
            }
                break;
            case SKPaymentTransactionStatePurchasing: // 購買中,此時可更新UI來展現(xiàn)購買的過程
                break;
            case SKPaymentTransactionStateRestored:{ //恢復(fù)已購產(chǎn)品,此時需要將已經(jīng)購買的商品恢復(fù)給用戶
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                
            }
                break;
            case SKPaymentTransactionStateFailed:{ // 購買錯誤,此時要根據(jù)錯誤的代碼給用戶相應(yīng)的提示
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                NSLog(@"購買失敗");
            }
                break;
            default:
                break;
        }
    }
}

@end

Swift

import UIKit
import StoreKit

class ViewController: UIViewController {

    var request:SKProductsRequest! = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()

        
        let array = SqlManager.shared().getAllApplePayModel()
        if array.count > 0 {
            for model in array {
                let mo:ApplePayModel = model as! ApplePayModel
                print(mo.orderId as Any)
                /**
                 * 內(nèi)購校驗的Api(你的應(yīng)用的api)
                 */
            }
        }
    }
    
    deinit {
        print("釋放充值")
        if (self.request != nil) {
            self.request.cancel()
        }
        
        NotificationCenter.default.removeObserver(self)
        SKPaymentQueue.default().remove(self)
    }

    // MARK:-購買
    @IBAction func buyBtn(_ sender: Any) {
        SKPaymentQueue.default().add(self)
        if SKPaymentQueue.canMakePayments() {
            // 你的itunesConnect的商品ID
            self.getProductInfow(proId: "")
        }else{
            print("不允許程序內(nèi)付費")
        }
    }
    
}

extension ViewController: SKProductsRequestDelegate, SKPaymentTransactionObserver{
    
    // 蘋果內(nèi)購服務(wù),下面的ProductId應(yīng)該是事先在itunesConnect中添加好的,已存在的付費項目。否則查詢會失敗。
    func getProductInfow(proId:String){
        let proArr = NSMutableArray()
        proArr.add(proId)
        let set = NSSet(array: proArr as! [Any])
        
        self.request = SKProductsRequest(productIdentifiers: set as! Set<String>)
        self.request.delegate = self
        self.request.start()
        
        print("set")
        print("請求開始請等待...")
    }
    
    // 收到產(chǎn)品返回信息
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("--------------收到產(chǎn)品反饋消息---------------------")
        let product = response.products;
        print("productID:\(response.invalidProductIdentifiers)")
        if product.count == 0 {
            print("查找不到商品信息")
            return
        }
        
        var p = SKProduct()
        for pro in product {
            print(pro.description)
            print(pro.localizedTitle)
            print(pro.localizedDescription)
            print(pro.price)
            print(pro.productIdentifier)
            
            if pro.productIdentifier == pro.productIdentifier {
                p = pro
            }
            
            let payment = SKPayment(product: p)
            print("發(fā)送購買請求")
            SKPaymentQueue.default().add(payment)
        }
        
    }
    
    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("請求失敗")
    }
    
    func requestDidFinish(_ request: SKRequest) {
        print("支付調(diào)用完成")
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for tran in transactions {
            switch tran.transactionState {
            case .purchased: // 購買成功,此時要提供給用戶相應(yīng)的內(nèi)容
                SKPaymentQueue.default().finishTransaction(tran)
                
                let model = ApplePayModel()
                model.orderId = "1234"
                model.payData = "1234"
                SqlManager.shared().saveApplePayModel(model: model)
                
                
                /**
                 * 請求校驗API 請求完成刪除數(shù)據(jù)庫對應(yīng)數(shù)據(jù)
                 */
//                SqlManager.shared().removeMotionModel(model: model)
                
                break
                
            case .purchasing: // 購買中,此時可更新UI來展現(xiàn)購買的過程
                break
            
            case .restored: // 恢復(fù)已購產(chǎn)品,此時需要將已經(jīng)購買的商品恢復(fù)給用戶
                SKPaymentQueue.default().finishTransaction(tran)
                break
            
            case .failed: // 購買錯誤,此時要根據(jù)錯誤的代碼給用戶相應(yīng)的提示
                SKPaymentQueue.default().finishTransaction(tran)
                print("購買失敗")
                break
                
            default:
                break
            }
        }
    }
    

    
}

Demo

demo是使用FMDB保存到本地(也可以使用歸檔等方法保存)

如果要保存蘋果內(nèi)購數(shù)據(jù)還是使用keychain保存的好
keyChain是ios中唯一可以存儲安全可靠敏感數(shù)據(jù)的地方。而且應(yīng)用被卸載,數(shù)據(jù)也不會被刪除,所以非常可靠。
keychain文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容