iOS內(nèi)購-從客戶端到服務(wù)器分析

一、財務(wù)配置

image.png

登錄到蘋果哦itunsconnect后臺后圾另,可以到協(xié)議集乔、稅務(wù)和銀行業(yè)務(wù)這里交給財務(wù)配置就行

二坡椒、itunes后臺商品配置

進入到我們的iTunes后臺后尤溜,在App Store 下面 有一個子欄目App 內(nèi)購買項目這里點擊管理我們就能看到我們進行商品配置的入口了
我們可以添加的商品類型

  • Consumable 消耗品: 可以多次購買汗唱,適合游戲內(nèi)貨幣
  • Non-Consumable 非消耗品: 購買一次哩罪,永久有效,適合解鎖永久功能;
  • Non-Renewing Subscription 非續(xù)訂訂閱: 在固定時間段內(nèi)可用的內(nèi)容深碱,如vip
  • Auto-Renewing Subscription 自動續(xù)費訂閱:到期會自動扣款的訂閱,適用按月的vip功咒;
    詳細內(nèi)容請查看:https://developer.apple.com/support/app-store-connect/#//apple_ref/doc/uid/TP40013727-CH3-SW1
image.png

三力奋、客戶端集成(integration)

1幽七、支付流程

image.png

2、iOS- swift代碼

import StoreKit

class InAppPurchaseManager: NSObject,SKPaymentTransactionObserver, SKProductsRequestDelegate {
    //登錄的時候調(diào)用
    func addIAPObserver() -> Void {
        SKPaymentQueue.default().remove(self)
        SKPaymentQueue.default().add(self)
    }
    //登出的時候調(diào)用,  防止發(fā)貨到未知用戶
    func removeIAPObserver() -> Void {
        SKPaymentQueue.default().remove(self)
    }
    
    func addPayment(_ productId:String) {
        let product = NSSet(array: [productId] as [AnyObject])
        let request = SKProductsRequest(productIdentifiers: product as! Set<String>)
        request.delegate = self
        request.start()
    }
    
    
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let avaliableProducts = response.products
        guard let product = avaliableProducts.first else {
            return
        }
        let mutablePayment = SKMutablePayment.init(product: product)
        //發(fā)起購買請求
        SKPaymentQueue.default().add(mutablePayment)
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                self.complete(transaction)
                break
            case .restored:
                self.restored(transaction)
                break
            case .failed:
                self.fail(transaction)
                break
            default:
                //其他情況
                break
            }
        }
    }
    
    //交易完成
    func complete(_ transaction: SKPaymentTransaction) -> Void {
        guard let receiptUrl = Bundle.main.appStoreReceiptURL, let receiveData:NSData = NSData(contentsOf: receiptUrl) else {
            return
        }
        let receiptString = receiveData.base64EncodedString(options: .endLineWithLineFeed)
        let transactionIdentifier = transaction.transactionIdentifier
        //將transactionIdentifier & receiptString發(fā)送給服務(wù)器,去驗證票據(jù)的正確性
        //todo
    }
    //交易失敗
    func fail(_ transaction: SKPaymentTransaction) {
        //todo
    }
    //交易restored
    func restored(_ transaction:SKPaymentTransaction) {
        //todo
    }
    
    //交易完成記得finish掉绩蜻,不然會出現(xiàn)卡單,無法進行下次支付
    func finishTransactionWith(transactionId transctionId:String) {
        let transactions = SKPaymentQueue.default().transactions
        for transaction in transactions {
            if transaction.transactionIdentifier == transctionId {
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            }
        }
    }
}

四办绝、簡單的服務(wù)器驗票邏輯(nodejs)

整個服務(wù)邏輯牽扯太多姚淆,我用nodejs整理出單純的驗票部分,簡單的校驗和借鑒是沒有問題的昔驱。

const https = require("https")
var postData = "客戶端傳過來receiptString";

var IAPVerifier = function(){};

IAPVerifier.verifyWithRetry = function(receipt, isBase64, cb) {
    var encoded = null, receiptData = {};
    if (isBase64) {
        encoded = receipt;
    } else {
        encoded = new Buffer(receipt).toString('base64');
    }
    receiptData['receipt-data'] = encoded;
    var options = this.requestOptions();
    return this.verify(receiptData, options, (function(_this) {
        return function(error, data) {
            if (error) return cb(error);
            if ((21007 === (data != null ? data.status : void 0)) && (_this.productionHost == _this.host)) {
                // 指向沙盒測試環(huán)境再次驗證
                options.host = 'sandbox.itunes.apple.com';
                return _this.verify(receiptData, options, function(err, data) {
                    return cb(err, data);
                });
            } else {
                return cb(error, data);
            }
        };
    })(this));
};


/*
  verify the receipt data
 */

IAPVerifier.verify = function(data, options, cb) {
    var post_data = JSON.stringify(data);
    var request = https.request(options, (function(_this) {
        return function(response) {
            var response_chunk = [];
            response.on('data', function(data) {
                if (response.statusCode !== 200) {
                    return cb(new Error("response.statusCode != 200"));
                }
                response_chunk.push(data);
            });
            return response.on('end', function() {
                var responseData, totalData;
                totalData = response_chunk.join('');
                try {
                    responseData = JSON.parse(totalData);
                } catch (_error) {
                    return cb(_error);
                }
                return cb(null, responseData);
            });
        };
    })(this));
    request.write(post_data);
    request.end();
    request.on('error', function (exp) {
        console.log('problem with request: ' + exp.message);
    });
};


IAPVerifier.requestOptions = function() {
    return options = {
        host: 'buy.itunes.apple.com',
        port: 443,
        path: '/verifyReceipt',
        method: "POST",
        rejectUnauthorized: false/*不加:返回證書不受信任CERT_UNTRUSTED*/
    };
};


IAPVerifier.verifyWithRetry(postData, true, function (e, responseData) {
    console.log("responseData:",JSON.stringify(responseData,null,'\t'));
    //todo 驗證客戶端傳過來的transactionIdentifier纳本,是否存在于"in_app"中
});

參考鏈接:
http://www.reibang.com/p/2f98b7937b6f

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末繁成,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子巾腕,更是在濱河造成了極大的恐慌絮蒿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佛寿,死亡現(xiàn)場離奇詭異但壮,居然都是意外死亡,警方通過查閱死者的電腦和手機弹渔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門肢专,熙熙樓的掌柜王于貴愁眉苦臉地迎上來您没,“玉大人,你說我怎么就攤上這事欧募∑偷郑” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵舔糖,是天一觀的道長金吗。 經(jīng)常有香客問我,道長摇庙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任宵呛,我火速辦了婚禮夕凝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逮矛。我一直安慰自己转砖,他們只是感情好橱鹏,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著堪藐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挑围。 梳的紋絲不亂的頭發(fā)上礁竞,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音杉辙,去河邊找鬼模捂。 笑死,一個胖子當著我的面吹牛蜘矢,可吹牛的內(nèi)容都是我干的狂男。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼品腹,長吁一口氣:“原來是場噩夢啊……” “哼岖食!你這毒婦竟也來了泡垃?” 一聲冷哼從身側(cè)響起蔑穴,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤存和,失蹤者是張志新(化名)和其女友劉穎纵朋,沒想到半個月后叙量,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绞佩,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡胆建,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凉驻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片复罐。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡胀滚,死狀恐怖咽笼,靈堂內(nèi)的尸體忽然破棺而出剑刑,到底是詐尸還是另有隱情叛甫,我是刑警寧澤其监,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布毁菱,位于F島的核電站贮庞,受9級特大地震影響窗慎,放射性物質(zhì)發(fā)生泄漏卤材。R本人自食惡果不足惜扇丛,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一较屿、第九天 我趴在偏房一處隱蔽的房頂上張望隘蝎。 院中可真熱鬧襟企,春花似錦整吆、人聲如沸表蝙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妆距。三九已至蚪黑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抒寂,已是汗流浹背屈芜。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毅糟,地道東北人姆另。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓迹辐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親殷费。 傳聞我的和親對象是個殘疾皇子详羡,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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