iOS內置付費開發筆記四(解鎖產品)

上一篇筆記

沃爾瑪賣一支牙膏的流程是: 1.把商家的牙膏放到貨柜上 2. 讓用戶自由選擇 3.用戶去收銀臺刷信用卡 4. 刷卡器交給用戶,等待銀行確認刷卡信息,如果返回付款確認信息,讓用戶拿走牙膏。

內置付費已經走完了前面的三步,用戶要一手交錢了,我們也要準備一手交貨嘍。(只收錢不辦事兒在App Store是行不通的,寫軟件易,建國家難,且寫且珍惜。)

這一步的流程圖如下:


Delivering Products
Delivering Products

處理支付信息 (Processes payment)

再回到沃爾瑪購買牙膏的場景,當刷信用卡的時候,整個操作流程大體如下:

對于銀聯的直聯商戶,流程如下:
1、刷卡信息(包括磁道和密碼)由POS機具受理后通過收單機構送往銀聯的收單系統。
2、銀聯收單系統將報文通過銀聯核心交換平臺送到信用卡的發卡銀行,根據交易指令,在發卡銀行的對應的卡片賬戶進行扣款。
3、銀聯核心交換系統收到扣款成功的返回后,將交易結果原路返回到POS終端上。
4、當天晚上11點,清算信息開始批量處理。
5、T+1日,各行在人行的頭寸賬戶根據銀聯的清算文件(指令)將資金進行劃撥,即交易資金從信用卡的發卡銀行轉移到商戶的收單銀行。
6、收單銀行將資金轉入商戶的具體清算賬戶(也可以由銀聯直接轉入)。
就扮演的角色而言,有持卡人、商戶、收單機構(為商戶提供服務的銀行或機構)、轉接清算機構(銀聯、VISA等卡組織)、發卡機構(信用卡銀行)
(以上答案為知乎網友周宇的解答,鏈接在此)

在內置付費購買環節中,App Store在此處也扮演了銀聯收單系統的角色,App Store會把扣款成功的信息返回給“售貨員”, 這里的“售貨員”是我們的一段代碼,名字叫做transaction queue observer。這個“售貨員”放在哪里有程序員自己來決定,大體上有兩個地方比較好:

  1. 對于非常小型的App, 可以放在 app delegate中
    2.對大部分的Apps, 單獨弄一個類,和其它與Store有關的代碼放在一起就很不錯
    這個名叫observer的"售貨員"必須要"簽署"SKPaymentTransactionObserver協議才能完成工作。

    • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
      {
      /
      放一個“售貨員” */
      [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
      }

簽署了"SKPaymentTransactionObserver協議的“售貨員”必須遵從協議中的要求——執行paymentQueue:updatedTransactions:這個函數。工作的職責是: 當交易狀態(The Status of a Transaction)有任何的變化, 都要調用這個操作。操作的具體細節需要我們來完成。

交易的四種主要狀態以及采取相應的行動:

  1. SKPaymentTransactionStatePurchasing: 購買中,此時可更新UI來展現購買的過程
  1. SKPaymentTransactionStateFailed: 購買錯誤,此時要根據錯誤的代碼給用戶相應的提示

  2. SKPaymentTransactionStatePurchased: 購買成功,此時要提供給用戶相應的內容

  3. SKPaymentTransactionStateRestored: 恢復已購產品,此時需要將已經購買的商品恢復給用戶

    • (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: // 恢復已購
      [self restoreTransaction:transaction];
      default:
      break;
      }
      }
      }

    • (void)completeTransaction:(SKPaymentTransaction *)transaction
      {
      NSString * productIdentifier = transaction.payment.productIdentifier;
      NSString * receipt = [transaction.transactionReceipt base64EncodedString];
      if ([productIdentifier length] > 0) {
      // 向自己的服務器驗證購買憑證
      }
      // 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 {
      // 恢復已經購買的產品
      [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
      }

保存好購物憑證(Persisting the Purchase)

現實中,購物以后要給個發票或者購物小票。在這里,也需要這么做,永久存儲交易記錄。這樣做至少有兩個用處:

  • 程序啟動以后,檢查購買記錄,讓已購的功能生效。
  • 當用戶需要恢復已購功能的時候, 可以讀取這個記錄。
    保存購物憑證的方法有如下幾種:
  1. 對于非消耗(non-consumable) 品, 并且iOS 7以上,可以使用app receipt來記錄
  2. 對于非消耗(non-consumable)品,但是是iOS7以下,可以使用User Defaults system 或者 iCloud來記錄
  3. 對于消耗品(consumable), 因為不能在不同設備上同步,因此不需要做永久記錄(有種強拆的感覺啊!)

將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

當用戶購買成功以后,就需要對相應的產品功能進行解鎖, 當使用Receipt的時候,代碼應該類似于下面的樣子

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"];

當使用Key:Value來存儲的時候, 代碼應該類似于下面的樣子:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];

在程序中寫下如下相應的代碼,判斷是否可以使用高級一點的功能 :)

if (rocketCarEnabled) {
    // Use the rocket car.
} else {
    // Use the regular car.
}

解鎖資源Delivering Associated Content

如果購買是有關資源的,比如更多的聲音,更多的圖片,更多的素材等等,可以有三種方式來處理這種情況:

  1. (Local Content) 內置一些熱門資源(預期會大賣的資源),不要太大,頂多幾M左右即可。
  2. (Apple-hosted Content) 使用Apple提供的Apple-hosted服務,這樣可以保證App的尺寸較為精簡。支持iOS 6以上。
  3. 使用自己的服務器。

結束交易 Finishing the Transaction

這里沒什么好講的,就是結束交易了。

SKPaymentTransaction *transaction = <# The current payment #>;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

需要注意的一點是,在交易結束之前,不要調用這個函數,會讓Apple-hosted Content沒法下載,因為在下載Apple-hosted內容之前,返回的transaction有一個SKDownload屬性,如果貿然調用了此函數,有可能會導致下載中斷,以及潛在的其它問題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容