接上一篇筆記
沃爾瑪賣一支牙膏的流程是: 1.把商家的牙膏放到貨柜上 2. 讓用戶自由選擇 3.用戶去收銀臺刷信用卡 4. 刷卡器交給用戶己肮,等待銀行確認(rèn)刷卡信息给猾,如果返回付款確認(rèn)信息占锯,讓用戶拿走牙膏。
內(nèi)置付費已經(jīng)走完了前面的三步透乾,用戶要一手交錢了侠仇,我們也要準(zhǔn)備一手交貨嘍。(只收錢不辦事兒在App Store是行不通的筑累,寫軟件易袱蜡,建國家難,且寫且珍惜慢宗。)
這一步的流程圖如下:

處理支付信息 (Processes payment)
再回到沃爾瑪購買牙膏的場景坪蚁,當(dāng)刷信用卡的時候,整個操作流程大體如下:
對于銀聯(lián)的直聯(lián)商戶镜沽,流程如下:
1敏晤、刷卡信息(包括磁道和密碼)由POS機(jī)具受理后通過收單機(jī)構(gòu)送往銀聯(lián)的收單系統(tǒng)。
2淘邻、銀聯(lián)收單系統(tǒng)將報文通過銀聯(lián)核心交換平臺送到信用卡的發(fā)卡銀行茵典,根據(jù)交易指令,在發(fā)卡銀行的對應(yīng)的卡片賬戶進(jìn)行扣款宾舅。
3统阿、銀聯(lián)核心交換系統(tǒng)收到扣款成功的返回后,將交易結(jié)果原路返回到POS終端上筹我。
4扶平、當(dāng)天晚上11點,清算信息開始批量處理蔬蕊。
5结澄、T+1日,各行在人行的頭寸賬戶根據(jù)銀聯(lián)的清算文件(指令)將資金進(jìn)行劃撥岸夯,即交易資金從信用卡的發(fā)卡銀行轉(zhuǎn)移到商戶的收單銀行麻献。
6、收單銀行將資金轉(zhuǎn)入商戶的具體清算賬戶(也可以由銀聯(lián)直接轉(zhuǎn)入)猜扮。
就扮演的角色而言勉吻,有持卡人、商戶旅赢、收單機(jī)構(gòu)(為商戶提供服務(wù)的銀行或機(jī)構(gòu))齿桃、轉(zhuǎn)接清算機(jī)構(gòu)(銀聯(lián)惑惶、VISA等卡組織)、發(fā)卡機(jī)構(gòu)(信用卡銀行)
(以上答案為知乎網(wǎng)友周宇的解答短纵,鏈接在此)
在內(nèi)置付費購買環(huán)節(jié)中带污,App Store在此處也扮演了銀聯(lián)收單系統(tǒng)的角色,App Store會把扣款成功的信息返回給“售貨員”, 這里的“售貨員”是我們的一段代碼香到,名字叫做transaction queue observer鱼冀。這個“售貨員”放在哪里有程序員自己來決定,大體上有兩個地方比較好:
-
對于非常小型的App, 可以放在 app delegate中
2.對大部分的Apps, 單獨弄一個類养渴,和其它與Store有關(guān)的代碼放在一起就很不錯
這個名叫observer的"售貨員"必須要"簽署"SKPaymentTransactionObserver協(xié)議才能完成工作雷绢。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
{
/ 放一個“售貨員” */
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
簽署了"SKPaymentTransactionObserver協(xié)議的“售貨員”必須遵從協(xié)議中的要求——執(zhí)行paymentQueue:updatedTransactions:這個函數(shù)泛烙。工作的職責(zé)是: 當(dāng)交易狀態(tài)(The Status of a Transaction)有任何的變化, 都要調(diào)用這個操作理卑。操作的具體細(xì)節(jié)需要我們來完成。
交易的四種主要狀態(tài)以及采取相應(yīng)的行動:
- SKPaymentTransactionStatePurchasing: 購買中蔽氨,此時可更新UI來展現(xiàn)購買的過程
SKPaymentTransactionStateFailed: 購買錯誤藐唠,此時要根據(jù)錯誤的代碼給用戶相應(yīng)的提示
SKPaymentTransactionStatePurchased: 購買成功,此時要提供給用戶相應(yīng)的內(nèi)容
-
SKPaymentTransactionStateRestored: 恢復(fù)已購產(chǎn)品鹉究,此時需要將已經(jīng)購買的商品恢復(fù)給用戶
(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
// Call the appropriate custom method.
case SKPaymentTransactionStatePurchased: // 購買成功
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed: // 購買失敗
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored: // 恢復(fù)已購
[self restoreTransaction:transaction];
default:
break;
}
}
}(void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSString * productIdentifier = transaction.payment.productIdentifier;
NSString * receipt = [transaction.transactionReceipt base64EncodedString];
if ([productIdentifier length] > 0) {
// 向自己的服務(wù)器驗證購買憑證
}
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}(void)failedTransaction:(SKPaymentTransaction *)transaction {
if(transaction.error.code != SKErrorPaymentCancelled) {
NSLog(@"購買失敗");
} else {
NSLog(@"用戶取消交易");
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}(void)restoreTransaction:(SKPaymentTransaction *)transaction {
// 恢復(fù)已經(jīng)購買的產(chǎn)品
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
保存好購物憑證(Persisting the Purchase)
現(xiàn)實中宇立,購物以后要給個發(fā)票或者購物小票。在這里自赔,也需要這么做妈嘹,永久存儲交易記錄。這樣做至少有兩個用處:
- 程序啟動以后绍妨,檢查購買記錄润脸,讓已購的功能生效。
- 當(dāng)用戶需要恢復(fù)已購功能的時候, 可以讀取這個記錄他去。
保存購物憑證的方法有如下幾種:
- 對于非消耗(non-consumable) 品毙驯, 并且iOS 7以上,可以使用app receipt來記錄
- 對于非消耗(non-consumable)品灾测,但是是iOS7以下爆价,可以使用User Defaults system 或者 iCloud來記錄
- 對于消耗品(consumable), 因為不能在不同設(shè)備上同步,因此不需要做永久記錄(有種強(qiáng)拆的感覺啊!)
將Value/Key保存在User Defaults 或者 iCloud中
#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
[storage setBool:YES forKey:@"enable_rocket_car"];
[storage setObject:@15 forKey:@"highest_unlocked_level"];
[storage synchronize];
將Receipt保存在User Defaults 或者 iCloud中
#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
NSData *newReceipt = transaction.transactionReceipt;
NSArray *savedReceipts = [storage arrayForKey:@"receipts"];
if (!receipts) {
// Storing the first receipt
[storage setObject:@[newReceipt] forKey:@"receipts"];
} else {
// Adding another receipt
NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt];
[storage setObject:updatedReceipts forKey:@"receipts"];
}
[storage synchronize];
解鎖功能 Unlocking App Functionality
當(dāng)用戶購買成功以后媳搪,就需要對相應(yīng)的產(chǎn)品功能進(jìn)行解鎖, 當(dāng)使用Receipt的時候铭段,代碼應(yīng)該類似于下面的樣子
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// Custom method to work with receipts
BOOL rocketCarEnabled = [self receipt:receiptData includesProductID:@"com.example.rocketCar"];
當(dāng)使用Key:Value來存儲的時候, 代碼應(yīng)該類似于下面的樣子:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];
在程序中寫下如下相應(yīng)的代碼,判斷是否可以使用高級一點的功能 :)
if (rocketCarEnabled) {
// Use the rocket car.
} else {
// Use the regular car.
}
解鎖資源Delivering Associated Content
如果購買是有關(guān)資源的秦爆,比如更多的聲音序愚,更多的圖片,更多的素材等等鲜结,可以有三種方式來處理這種情況:
- (Local Content) 內(nèi)置一些熱門資源(預(yù)期會大賣的資源)展运,不要太大活逆,頂多幾M左右即可。
- (Apple-hosted Content) 使用Apple提供的Apple-hosted服務(wù)拗胜,這樣可以保證App的尺寸較為精簡蔗候。支持iOS 6以上。
- 使用自己的服務(wù)器埂软。
結(jié)束交易 Finishing the Transaction
這里沒什么好講的锈遥,就是結(jié)束交易了。
SKPaymentTransaction *transaction = <# The current payment #>;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
需要注意的一點是勘畔,在交易結(jié)束之前所灸,不要調(diào)用這個函數(shù),會讓Apple-hosted Content沒法下載炫七,因為在下載Apple-hosted內(nèi)容之前爬立,返回的transaction有一個SKDownload屬性,如果貿(mào)然調(diào)用了此函數(shù)万哪,有可能會導(dǎo)致下載中斷侠驯,以及潛在的其它問題。