iOS內(nèi)購代碼教程

繼上一篇iOS內(nèi)購圖文教程扬蕊,下面是代碼教程
創(chuàng)建一個單例類
.h文件

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

@protocol LMIAPManagerDelegate <NSObject>

@optional

- (void)receiveProduct:(SKProduct *)product;

- (void)successfulPurchaseOfId:(NSString *)productId andReceipt:(NSData *)transactionReceipt;

- (void)failedPurchaseWithError:(NSString *)errorDescripiton;


@end

@interface ApplePayManager : NSObject

@property (nonatomic, assign)id<LMIAPManagerDelegate> delegate;

//創(chuàng)建單例
+ (instancetype)sharedInstance;

- (BOOL)requestProductWithId:(NSString *)productId;
- (BOOL)purchaseProduct:(SKProduct *)skProduct;
- (BOOL)restorePurchase;

@property (strong, nonatomic)NSTimer *timer;

.m文件

#import "ApplePayManager.h"

@interface ApplePayManager()<SKProductsRequestDelegate, SKPaymentTransactionObserver>

{
    SKProduct *myProduct;
}

@end

@implementation ApplePayManager
//單例
+ (instancetype)sharedInstance{
    static ApplePayManager *payManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        payManager = [[ApplePayManager alloc] init];
    });
    
    return payManager;
}

#pragma mark - ****************  Public Methods

/** TODO:請求商品*/
- (BOOL)requestProductWithId:(NSString *)productId {
    
    if (productId.length > 0) {
        NSLog(@"請求商品: %@", productId);
        SKProductsRequest *productRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObject:productId]];
        productRequest.delegate = self;
        [productRequest start];
        return YES;
    } else {
        NSLog(@"商品ID為空");
    }
    return NO;
}

/** TODO:購買商品*/
- (BOOL)purchaseProduct:(SKProduct *)skProduct {
    
    if (skProduct != nil) {
        if ([SKPaymentQueue canMakePayments]) {
            SKPayment *payment = [SKPayment paymentWithProduct:skProduct];
            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
            [[SKPaymentQueue defaultQueue] addPayment:payment];
            return YES;
        } else {
            NSLog(@"失敗,用戶禁止應用內(nèi)付費購買.");
        }
    }
    return NO;
}

/** TODO:非消耗品恢復*/
- (BOOL)restorePurchase {
    
    if ([SKPaymentQueue canMakePayments]) {
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
        return YES;
    } else {
        NSLog(@"失敗,用戶禁止應用內(nèi)付費購買.");
    }
    return NO;
}
#pragma mark - ****************  SKProductsRequest Delegate

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    
    NSArray *myProductArray = response.products;
    if (myProductArray.count > 0) {
        myProduct = [myProductArray objectAtIndex:0];
        [_delegate receiveProduct:myProduct];
    }else {
        NSLog(@"無法獲取產(chǎn)品信息伸眶,購買失敗。");
        [_delegate receiveProduct:myProduct];
    }
}

#pragma mark - ****************  SKPaymentTransactionObserver Delegate

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
    
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing: //商品添加進列表
                NSLog(@"商品:%@被添加進購買列表",myProduct.localizedTitle);
                break;
            case SKPaymentTransactionStatePurchased://交易成功
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed://交易失敗
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored://已購買過該商品
                break;
            case SKPaymentTransactionStateDeferred://交易延遲
                break;
            default:
                break;
        }
    }
}



#pragma mark - ****************  Private Methods

- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    
    NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
    [_delegate successfulPurchaseOfId:transaction.payment.productIdentifier andReceipt:receiptData];
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    
    if (transaction.error.code != SKErrorPaymentCancelled && transaction.error.code != SKErrorUnknown) {
        [_delegate failedPurchaseWithError:transaction.error.localizedDescription];
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

-(void)dealloc
{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除監(jiān)聽
}

在禮物頁面,調(diào)起單例事哭,添加內(nèi)購代理方法

//appleProductId 就是添加商品時填寫的ID
[[ApplePayManager sharedInstance] requestProductWithId:[NSString stringWithFormat:@"%@",model.appleProductId]];
    self.hud = [MBProgressHUD showHUDAddedTo:self animated:YES];
    self.hud.delegate = self;
    self.hud.labelText = @"連接蘋果商店";

實現(xiàn)代理方法

#pragma mark - **************** MLIAPManager Delegate

- (void)receiveProduct:(SKProduct *)product {
    
    if (product != nil) {
        //購買商品
        if (![[ApplePayManager sharedInstance] purchaseProduct:product]) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"失敗" message:@"您禁止了應用內(nèi)購買權(quán)限,請到設置中開啟" delegate:self cancelButtonTitle:@"關(guān)閉" otherButtonTitles:nil, nil];
            [alert show];
        }
        [self.hud hide:YES];
    } else {
        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"失敗" message:@"無法連接App store!" delegate:self cancelButtonTitle:@"關(guān)閉" otherButtonTitles:nil, nil];
        [alert show];
        [self.hud hide:YES];
    }
    
}

- (void)successfulPurchaseOfId:(NSString *)productId andReceipt:(NSData *)transactionReceipt {
    
    NSString  *transactionReceiptString = [transactionReceipt base64EncodedStringWithOptions:0];

    if ([transactionReceiptString length] > 0) {
        // 向自己的服務器驗證購買憑證(此處應該考慮將憑證本地保存,對服務器有失敗重發(fā)機制)
        /**
         服務器要做的事情:
         接收ios端發(fā)過來的購買憑證。
         判斷憑證是否已經(jīng)存在或驗證過瓜富,然后存儲該憑證鳍咱。
         將該憑證發(fā)送到蘋果的服務器驗證,并將驗證結(jié)果返回給客戶端与柑。
         如果需要谤辜,修改用戶相應的會員權(quán)限
         */
//1.如果用戶購買成功之后,網(wǎng)絡突然不好价捧,無法上傳服務器丑念,會造成購買之后沒有金幣返回
//2.如果上傳服務器失敗,憑證保存在本地數(shù)組结蟋,向服務器發(fā)送請求渠欺,直至成功為止。
//3.如果購買成功之后椎眯,因某些原因APP突然閃退挠将,憑證保存在本地,應用啟動時编整,再次向服務器發(fā)送請求舔稀,直至成功為止
//4.出現(xiàn)以外情況,例如手機突然關(guān)機等掌测,提示用戶聯(lián)系客服
//5.我的做法:在mainRunLoop中添加定時器内贮,在出現(xiàn)以上狀況時,發(fā)起請求汞斧,直至數(shù)組中所有憑證上傳成功之后夜郁,移除定時器。
        NSLog(@"%@",transactionReceiptString);
    }
}

- (void)failedPurchaseWithError:(NSString *)errorDescripiton {
    NSLog(@"購買失敗");
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"失敗" message:errorDescripiton delegate:self cancelButtonTitle:@"關(guān)閉" otherButtonTitles:nil, nil];
    [alert show];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粘勒,一起剝皮案震驚了整個濱河市竞端,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庙睡,老刑警劉巖事富,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件技俐,死亡現(xiàn)場離奇詭異,居然都是意外死亡统台,警方通過查閱死者的電腦和手機雕擂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贱勃,“玉大人井赌,你說我怎么就攤上這事」笕牛” “怎么了仇穗?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拔鹰。 經(jīng)常有香客問我,道長贵涵,這世上最難降的妖魔是什么列肢? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮宾茂,結(jié)果婚禮上瓷马,老公的妹妹穿的比我還像新娘。我一直安慰自己跨晴,他們只是感情好欧聘,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著端盆,像睡著了一般怀骤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焕妙,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天蒋伦,我揣著相機與錄音,去河邊找鬼焚鹊。 笑死痕届,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的末患。 我是一名探鬼主播研叫,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼璧针!你這毒婦竟也來了嚷炉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤探橱,失蹤者是張志新(化名)和其女友劉穎渤昌,沒想到半個月后虽抄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡独柑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年迈窟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忌栅。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡车酣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出索绪,到底是詐尸還是另有隱情湖员,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布瑞驱,位于F島的核電站娘摔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唤反。R本人自食惡果不足惜凳寺,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望彤侍。 院中可真熱鬧肠缨,春花似錦、人聲如沸盏阶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽名斟。三九已至脑慧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砰盐,已是汗流浹背漾橙。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留楞卡,地道東北人霜运。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像蒋腮,于是被迫代替她去往敵國和親淘捡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,305評論 25 707
  • 藍圖雖好膘魄,可是要走心哦 職場折騰幾年乌逐,沒有成功的經(jīng)驗,失敗的教訓也不典型创葡,感悟倒是一大堆浙踢。 領(lǐng)導好像都一樣,無論在...
    心留痕閱讀 364評論 2 0
  • sunnyqz閱讀 181評論 0 3
  • 風灿渴, 吃著哨音洛波, 嘲笑著巷的無知 林, 挺拔了身軀骚露, 揶揄著此間的曾經(jīng) 火蹬挤, 露出了牙齒, 跳躍著棘幸, 還是蜷縮著身...
    無墨水閱讀 306評論 0 2