1. 程式人生 > >【支付】Cocos2d-x IOS內購(IAP支付)

【支付】Cocos2d-x IOS內購(IAP支付)

【說明】

遊戲開發中,整合支付是一個重要的環節,AppStore稽核指南規定,App內虛擬物品必須使用IAP支付。這篇文章主要記錄我在整合IAP的過程,我參考了泰然網的一篇文章,對其進行了封裝和擴充套件,並對結構和使用流程進行了簡單的介紹,僅供自己學習使用。

關於iTunes Connect上的商品配置,可以參考泰然網的文章,這裡只對程式碼進行討論。

更新:加入內購恢復介面,一鍵恢復購買過的非消耗型商品。(蘋果規定費消耗型商品必須要有恢復購買)

【參考】

【封裝】

1. 結構介紹:

    結構和核心程式碼還是參照泰然網的那篇文章,我主要是在其基礎上封裝做了一些封裝和強化,更加方便使用。

    > IOSiAP

:支付核心類,實現各種請求,直接呼叫API的介面。(在mm檔案中會實現一些API的回撥函式)

    > IOSiAPDelegate:代理類(抽象類),方法會在API返回結果時呼叫,在IOSiAP_Birdge中實現,以達到將結果傳遞出去的目的。 

    > IOSiAP_Bridge:起到橋樑作用的中間類,主要功能是呼叫IOAiAP中的各種請求介面,並且IOSiAPDelegate中的結果會返回到此類中(通過代理)。

    > GamePayment:單例類,純粹是為了方便使用,因為我把所有支付方式介面都統一到此類管理。(目前只有支付寶和IAP)

2. 支付流程:

    如果完全按照我使用的流程說,會比較糾結,因為封裝層數太多容易混淆,這裡拋開單例那一層,直接從IOSiAP_Birdge這層開始。以下是一次正常支付的流程,都是從IOSiAP_Bridge開始請求直到其收到返回結果為止。

    > 請求商品資訊:(主動)呼叫IOSiAP_Bridge的requestProducts方法,其會呼叫IOSiAP的requestProducts方法,呼叫API介面處理請求。

    > 返回商品資訊:返回在iAPProductsRequestDelegate(.mm檔案中)的對應回撥函式,如果成功,進入requestDidFinish,如果失敗進入didFailWithEror,在這裡會分別呼叫IOSiAP_Bridge的onRequestProductsFinsih或者onRequestProductsError函式,即通過代理將結果傳遞到IOSiAP_Birdge中。

    > 請求支付商品:(主動)呼叫IOSiAP_Birdge的requestPayment方法,其會呼叫IOSiAP的paymentWithProduct方法,呼叫API介面處理請求。

    > 返回支付結果:方式與商品資訊返回一樣,但是會返回在iAPTransactionObserver(.mm檔案中)中的updatedTransactions中,注意,這裡會接收各種支付請求相關的事件,包括後面要說的回覆購買的事件。此處直接通過代理將引數傳到IOSiAP_Bridge中的onPaymentEvent進行分析處理。

    到這裡,一次完整的請求支付並回調就完成了,下面再說下回復購買的流程。

    > 請求恢復購買:(主動)呼叫IOSiAP_Birdge的requestRestore方法,其會呼叫IOSiAP的restorePayment方法,呼叫API介面處理請求。

    > 恢復購買結果:此時updateTransactions會受到已經購買的商品資訊(返回支付結果那裡),如果有多個就會返回多次,與支付結果一樣會呼叫IOSiAP_Birdge的onPaymentEvent處理。此時我們可以處理這些資訊,完成恢復操作。

    > 恢復購買完成:當上一步資訊全部都返回完畢,在paymentQueueRestoreCompletedTransactionsFinsished會收到返回結果,如果上一步沒有內購,即沒有上一步的返回,會直接在restoreComletedTransactionsFailedWithError中返回。這兩個返回都會傳到IOSiAP_Birdge的onRestoreFinished中處理。

    好了所有流程就這些了,看起來有點亂,不明白可以直接看程式碼,下面有完整版下載地址。

3. 核心程式碼:

這裡只貼出了最核心的部分,完整版見文章後面下載地址。

> IOSiAP

class IOSiAP
{
public:
    IOSiAP();
    ~IOSiAP();
    void requestProducts(std::vector <std::string> &productIdentifiers); // 請求商品
    IOSProduct *iOSProductByIdentifier(std::string &identifier);
    void paymentWithProduct(IOSProduct *iosProduct, int quantity = 1); // 請求支付
    void restorePayment(); // 恢復購買
    
    IOSiAPDelegate *delegate;
    // ===  internal use for object-c class ===
    void *skProducts;// object-c SKProduct
    void *skTransactionObserver;// object-c TransactionObserver
    std::vector<IOSProduct *> iOSProducts;
};
> IOSiAPDelegate
class IOSiAPDelegate
{
public:
    virtual ~IOSiAPDelegate() {}
    // for request product
    virtual void onRequestProductsFinish(void) = 0;
    virtual void onRequestProductsError(int code) = 0;
    // for payment event (also for restore event)
    virtual void onPaymentEvent(std::string &identifier, IOSiAPPaymentEvent event, int quantity) = 0;
    // for restore finished
    virtual void onRestoreFinished(bool succeed) = 0;
};
>> iAPProductsRequestDelegate
@interface iAPProductsRequestDelegate : NSObject<SKProductsRequestDelegate>
@property (nonatomic, assign) IOSiAP *iosiap;
@end

@implementation iAPProductsRequestDelegate

- (void)productsRequest:(SKProductsRequest *)request
     didReceiveResponse:(SKProductsResponse *)response
{
     ......
}

- (void)requestDidFinish:(SKRequest *)request
{
    _iosiap->delegate->onRequestProductsFinish();
    [request.delegate release];
    [request release];
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
    _iosiap->delegate->onRequestProductsError([error code]);
}

@end
> iAPTransactionObserver
@interface iAPTransactionObserver : NSObject<SKPaymentTransactionObserver>
@property (nonatomic, assign) IOSiAP *iosiap;
@end

@implementation iAPTransactionObserver

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
     ......
     _iosiap->delegate->onPaymentEvent(identifier, event, transaction.payment.quantity);
}

- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
    _iosiap->delegate->onRestoreFinished(false);
}

- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    _iosiap->delegate->onRestoreFinished(true);
}

@end
> IOSiAP_Bridge
class IOSiAP_Bridge : public IOSiAPDelegate
{
public:
    // [請求]獲取商品資訊
    void requestProducts(std::string &identifier, iapProductCallback callback);
   
    // [請求]付款請求
    void requestPayment(int quantity, iapPaymentCallback callback);

    // [請求]恢復購買
    void requestRestore(iapRestoreCallback restoreCallback, iapRestoreFinishCallback finishCallback);

public:
    IOSiAP_Bridge();
    ~IOSiAP_Bridge();
    
    // [回撥]請求商品資訊回撥
    virtual void onRequestProductsFinish(void);
    virtual void onRequestProductsError(int code);
    // [回撥]付款結果回撥(恢復流程也走這裡)
    virtual void onPaymentEvent(std::string &identifier, IOSiAPPaymentEvent event, int quantity);
    // [回撥]恢復購買完成回撥
    void onRestoreFinished(bool succeed);
    
private:
    IOSiAP             *iap;             // IOSiAp例項
    
    std::string        _identifier;      // 商品編號(獲取請求)
    int                _quantity;        // 商品數量(購買請求)
    IOSProduct         *_product;        // 商品資訊(返回資訊)
    
    iapProductCallback _productCallback; // 外部回撥(商品資訊)
    iapPaymentCallback _paymentCallback; // 外部回撥(購買結果)
    iapRestoreCallback _restoreCallback; // 外部回撥(恢復購買);
    iapRestoreFinishCallback _restoreFinishCallback; // 外部回撥(恢復完成)
};
> GamePayment
class GamePayment
{
public:
    static GamePayment *getInstance();
    
protected:
    GamePayment();
    
    // IAP內購(IOS)
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
public:
    // 請求商品資訊
    void req_iap(std::string &identifier, iapProductCallback callback);
    
    // 購買請求
    void pay_iap(int quantity, iapPaymentCallback callback);
    
    // 恢復購買
    void restore_iap(iapRestoreCallback restoreCallback, iapRestoreFinishCallback finishCallback);
    
private:
    IOSiAP_Bridge _iap;
#endif
}

【附錄】

相關推薦

支付Cocos2d-x IOSIAP支付

【說明】 遊戲開發中,整合支付是一個重要的環節,AppStore稽核指南規定,App內虛擬物品必須使用IAP支付。這篇文章主要記錄我在整合IAP的過程,我參考了泰然網的一篇文章,對其進行了封裝和擴充套件,並對結構和使用流程進行了簡單的介紹,僅供自己學習使用。 關於iTune

儲存Cocos2d-x將資源目錄Assets檔案拷貝到可寫目錄

【說明】 將安卓的資源目錄(Assets)下得指定檔案,拷貝到可寫目錄指定位置,以便對檔案進行讀寫。 【正文】 1. 首先得在可寫目錄建立指定的資料夾,當然也可以不用,如果建立目錄,則需包含標頭檔案。 #include <sys/stat.h> #include

Spring Boot + IOSIAP

分享一個關於IAP(IOS內購)的專案 IAP驗證工具類 /** * @program: learningapi * @description: IOS驗證工具 * @author: Irving Wei * @create: 2018-09-10 17:20 **/ publ

專欄 - Cocos2d-x 3.x 圖形學渲染

Cocos2d-x 3.x 圖形學渲染 該專欄主要是介紹Cocos2d-x 3.x圖形學渲染技術講解,針對的是3D模型的材質渲染,幫助喜歡3D的開發者快速掌握該技術。。。。。

加密Cocos2d-x PNG圖片資源加密

實現原理     如果你對實現原理並不感興趣,只是想直接使用的話可以跳過這節。首先我假設讀者已經清楚PNG影象檔案結構了,如果沒有的話這裡建議可以看看《揭祕資料解密的關鍵技術》第5章,這章裡面專門對PNG影象格式進行了介紹。或者看看《PNG檔案格式詳解》這篇

Cocos2dx3.2從零開始Cocos2d-x 3.2專案建立和編譯,移植到Android

最近決定入手Cocos2d-x 3.2。目前最新版。 起初糾結於Cocos2d 還是Unity3d,後來糾結於Cocos2d-x 2.x還是Cocos2d-x 3.x。 遇到問題記錄。 一、建立Coc

動作Cocos2d-x 動作的分類與使用總結

本文蒐集了幾篇關於動作的文章,作為收藏。 //////////////////這一段轉自Cocos2d-x官方中文文件 v3.x//////////////////////////// 動作類(Action)是所有動作的基類,它建立的一個物件代

專欄 - cocos2d-x遊戲開發

cocos2d-x遊戲開發 使用cocos2d-x開源引擎開發Iphone遊戲。 Cocos2D是iPhone開發中一個非常有用的庫,它可以讓你在建立自己的iPhone遊戲時節省很多的時間。它具有很多的功能,比如sprite(精靈

Python正則表達式1未完

pes mmu get regular rop 則表達式 line out github 1、正則表達式唯一的用途就是在文本中匹配和尋找模式,模式可以簡單,也可以復雜。 2、Regexr 這個網站很個性的就是,有一個community標簽,打開後可以看到評分由高到低

BZOJ4942[Noi2017]整數 線段樹+DFS卡過

push 正常的 int 描述 printf turn n-1 如果 bzoj 【BZOJ4942】[Noi2017]整數 題目描述去uoj 題解:如果只有加法,那麽直接暴力即可。。。(因為1的數量最多nlogn個) 先考慮加法,比較顯然的做法就是將A二進制分解成lo

洛谷P3374 模板樹狀數組 1CDQ分治

size 結果 pri amp fine open sum turn 二維 題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某一個數加上x 2.求出某區間每一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數列數字的個

模板可持久化線段樹 1主席樹

base math 一次 數據 mar 指定 das min 第k小 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間

MongoDBNoSQL Manager for MongoDB 教程基礎篇

好的 log 很難 gpo ssi next 破解 情況 eight 前段時間,學習了一下mongodb,在客戶端工具方面,個人認為 NoSQL Manager for MongoDB 是體驗比較好的一個,功能也較齊全。可惜在找教程的時候,發現很難找到比較詳細的

BZOJ1260 [CQOI2007]塗色paint區間dp

c++ ide hid event pri display pro == spl 題目 傳送門:QWQ 分析 區間dp, 詳見代碼 代碼 /*****************************************

刷題洛谷 P3834 模板可持久化線段樹 1主席樹

!= tchar 這樣的 信息 reg har mem hair define 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間查詢其區間內的第K小值。

題解 bzoj1207: [HNOI2004]打鼴鼠 動態規劃

def math clas amp online code 直接 tro ID bzoj1207,懶得復制,戳我戳我 Solution: 挺傻逼的一個\(dp\),直接推就好了 這題在bzoj上的數據有點問題,題目保證每個時間點不會出現在同一位置兩個地鼠,然而他有= =(

P3834 模板可持久化線段樹 1主席樹

lose printf TE article 發現 AC 但是 || amp 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於

題解 bzoj3036: 綠豆蛙的歸宿 期望dp

www. cpp can bit push oid rom .cn 概率 題面戳我 Solution 反向建圖跑拓撲排序,順便處理\(dp\) 假設某條邊是\(u \rightarrow v (dis)\) ,那麽轉移方程就是\(dp[v]+=(dp[u]+dis)/in

luogu P4779 模板單源最短路徑標準版

-o2 struct call 哈哈 poi fun fin hole char 線段樹優化dij 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 我可能是個智障 // luogu-judger-enable-o2 #pragma GCC diagnostic error "-

BZOJ3191 [JLOI2013]卡牌遊戲概率dp

如果 style ++ bzoj mem color con size oid 題目 傳送門:QWQ 分析 算是概率dp不錯的題。 $ dp[i][j] $表示有i個人時,這i個人中的第j個獲勝的概率。 我們把i從1推到n,那麽答案就是$ dp[n