Flutter iOS內(nèi)購(gòu)

工作中用flutter做過一個(gè)數(shù)字藏品的項(xiàng)目冻押,蘋果端需要加入內(nèi)購(gòu)才能上線發(fā)布杯拐,本文主要寫一下使用flutter插件in_app_purchase: ^3.0.7 實(shí)現(xiàn)蘋果內(nèi)購(gòu)功能以及遇到的問題旁舰。純屬技術(shù)交流,歡迎評(píng)論交流.
主要思路:
1.在蘋果開發(fā)賬號(hào)上面建好虛擬商品。
2.將在蘋果開發(fā)賬號(hào)上面建好的虛擬商品錄入后臺(tái)系統(tǒng),便于后端通過接口返回給前端對(duì)應(yīng)的商品id拨匆。
3.使用in_app_purchase插件,通過后端返回的商品id讯检,查詢到商品信息琐鲁,再將商品信息提交給蘋果服務(wù)器發(fā)起支付請(qǐng)求卫旱,再將蘋果服務(wù)器返回的支付結(jié)果傳給自己的服務(wù)器。
4.自己的服務(wù)器通過前端傳給的支付結(jié)果去進(jìn)行發(fā)貨以及其他操作围段。
詳細(xì)操作:

關(guān)于怎么在蘋果開發(fā)賬號(hào)上面建商品過程有些繁瑣顾翼,不是本文的重點(diǎn)內(nèi)容在這里就不詳細(xì)說明了,可以網(wǎng)上搜搜奈泪,有很多博主寫的很詳細(xì)适贸,貼一個(gè)圖示。
3A7EF337-BAAB-4076-8BD2-D82363973441.png
in_app_purchase的使用

in_app_purchase是谷歌官方出的插件涝桅,相對(duì)來說還是很堅(jiān)挺的拜姿,所以用的還是比較放心。我是用的in_app_purchase: ^3.0.7 #內(nèi)購(gòu)版本冯遂,直接導(dǎo)入項(xiàng)目使用即可蕊肥。
當(dāng)點(diǎn)到某個(gè)商品獲取到后臺(tái)返回的ProductId,調(diào)用下面的方法债蜜,仔細(xì)看注釋即可

/// 蘋果內(nèi)購(gòu) (購(gòu)買)
  Future<void> applePay(Map dataMap) async {
  /// _inAppPurchase是否有效
    final bool isAvailable = await _inAppPurchase.isAvailable();
    if (!isAvailable) {
      return;
    }
    /// 如果是iOS設(shè)備進(jìn)行設(shè)置代理晴埂,接口蘋果服務(wù)器的回調(diào)。
    if (Platform.isIOS) {
      final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
          _inAppPurchase
              .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
    }

    /// 獲取后臺(tái)返回的產(chǎn)品id
    if (dataMap['iosProductId'] == null) {
      Loading.dissmiss();
      AppDialog.showShucangText(title: '暫無產(chǎn)品');
      return;
    }
    _kProductIds.add(dataMap['ProductId']);

   /// 查詢后臺(tái)返回的ProductId是否在蘋果服務(wù)器上注冊(cè)了
    final ProductDetailsResponse productDetailResponse =
        await _inAppPurchase.queryProductDetails(_kProductIds.toSet());
    /// 查詢不到說明沒注冊(cè)
    if (productDetailResponse.error != null) {
      Loading.dissmiss();
      AppDialog.showShucangText(title: '獲取產(chǎn)品信息失敗');
      return;
    }
    /// 查詢不到商品詳情說明沒注冊(cè)
    if (productDetailResponse.productDetails.isEmpty) {
      Loading.dissmiss();
      AppDialog.showShucangText(title: '暫無產(chǎn)品');
      return;
    }
    _products = productDetailResponse.productDetails;
  /// 查詢成功
    ProductDetails productDetails = _products[0];
    FlutterKeychain.put(key: "iosPrice", value: productDetails.price);
    late PurchaseParam purchaseParam;
    purchaseParam = PurchaseParam(
        productDetails: productDetails,

        /// 添加自己服務(wù)器上生成的訂單
        applicationUserName:
            '${dataMap['orderNo11']}' + '${dataMap['orderNo22']}');
   /// 向蘋果服務(wù)器發(fā)起支付請(qǐng)求
    _inAppPurchase.buyConsumable(purchaseParam: purchaseParam);
  }

注意點(diǎn):

上面的 purchaseParam就是向蘋果服務(wù)器發(fā)起支付請(qǐng)求時(shí)傳遞的參數(shù)寻定,在purchaseParam這個(gè)對(duì)象里儒洛,可以設(shè)置自定義參數(shù),來方便判斷那筆訂單狼速。

監(jiān)聽蘋果服務(wù)器的回調(diào)

Future<void> _listenToPurchaseUpdated(
      List<PurchaseDetails> purchaseDetailsList) async {
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        /// 等待購(gòu)買中
      } else if (purchaseDetails.status == PurchaseStatus.canceled) {
        /// 取消訂單
        _inAppPurchase.completePurchase(purchaseDetails);
        Loading.dissmiss();
      } else {
        if (purchaseDetails.status == PurchaseStatus.error) {
          /// 購(gòu)買出錯(cuò)
          Loading.dissmiss();
          AppDialog.showShucangText(title: '購(gòu)買出錯(cuò)');
          _inAppPurchase.completePurchase(purchaseDetails);
        } else if (purchaseDetails.status == PurchaseStatus.purchased ||
            purchaseDetails.status == PurchaseStatus.restored) {
          /// 調(diào)用后臺(tái)接口,發(fā)放商品
          deliverProduct(purchaseDetails);
        }
      }
    }
  }

當(dāng) purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored說明在蘋果服務(wù)器上發(fā)起請(qǐng)求成功了琅锻,然后再通知一下自己的服務(wù)器去發(fā)貨然后一定記得要調(diào)用下這個(gè)方法_inAppPurchase.completePurchase(purchaseDetails)不然下一次就拉不起蘋果支付來了就萬事大吉了。但是你想多了向胡,測(cè)試可不這樣整恼蓬。

這張圖片相信大家很熟悉吧,當(dāng)在蘋果服務(wù)器上付款成功后變會(huì)彈出這個(gè)彈窗僵芹,只有當(dāng)用戶點(diǎn)擊“好”時(shí)才會(huì)觸發(fā)成功的回掉处硬,也就是才會(huì)代碼才會(huì)走到這個(gè)purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored判斷里面來,那如果這時(shí)候拇派,測(cè)試同學(xué)荷辕,不點(diǎn)擊“好”直接退到App后臺(tái),把App直接殺死或者把App卸載了件豌,那自己的服務(wù)器是不知道用戶在蘋果服務(wù)器上已經(jīng)完成付款了疮方,是不會(huì)給用戶發(fā)貨的,這也就是老生常談的丟單問題茧彤。

WeChatc5a772f243ebc90533be17c3af6bd25e.jpg

丟單問題處理

使用

_inAppPurchase.purchaseStream是用來監(jiān)聽消息隊(duì)列的回調(diào)的,也就是所有訂單的狀態(tài)以及信息回調(diào),in_app_purchase這個(gè)屬性的文檔中這么說到:

IMPORTANT! You must subscribe to this stream as soon as your app launches,
preferably before returning your main App Widget in main(). Otherwise you
will miss purchase updated made before this stream is subscribed to.
重要骡显!你必須在應(yīng)用程序啟動(dòng)后立即訂閱此流,
最好在main()中返回主應(yīng)用程序小部件之前。否則你
將錯(cuò)過訂閱此流之前更新的購(gòu)買惫谤。

也就是說當(dāng)我們的App在第一次啟動(dòng)的時(shí)候可以訂閱此流來完成補(bǔ)單的操作壁顶,但是如果用戶是之前丟單了,然后把App又卸載了石挂,再次下載打開App后并沒有進(jìn)行登錄操作博助,那用戶的登錄信息都拿不到怎么進(jìn)行補(bǔ)單操作呢?

補(bǔ)單解決方案

讓后端出一個(gè)補(bǔ)單的接口痹愚,在補(bǔ)單時(shí)只需要傳一個(gè)訂單號(hào)即可,那App都刪除了蛔糯,之前的訂單號(hào)客戶端怎么獲取呢拯腮?使用flutter_keychain來實(shí)現(xiàn),flutter_keychain就是使用的iOS的鑰匙串來實(shí)現(xiàn)的蚁飒,當(dāng)用戶在蘋果服務(wù)器下單時(shí)动壤,在鑰匙串中保存后端生成的訂單號(hào),然后再商品成功發(fā)貨后刪除鑰匙串里面的訂單號(hào)淮逻,完成一個(gè)完整的購(gòu)買過程琼懊,再購(gòu)買時(shí)任何一環(huán)出了問題鑰匙串里面緩存的訂單號(hào)都不會(huì)被清空,這樣在App下一次啟動(dòng)時(shí)爬早,在首頁或者main函數(shù)中使用_inAppPurchase.purchaseStream監(jiān)聽哼丈,在拿到flutter_keychain中保存的訂單號(hào)完成補(bǔ)單過程。

注意點(diǎn)

1.在完成蘋果服務(wù)器付款流程后通知到自己服務(wù)器接口也就是驗(yàn)單的接口返回的是成功或者不成功都要調(diào)用_inAppPurchase.completePurchase(purchaseDetails)這個(gè)方法筛严,不然下次就掉不起蘋果支付來了醉旦,當(dāng)然肯定會(huì)在失敗的判斷里面寫明白讓用戶自己去走蘋果退款流程的文案(概率較小,但是也得考慮)

2.商品類型如果是非消耗品的話桨啃,在下單完之后一定寫一個(gè)按鈕供點(diǎn)擊調(diào)用復(fù)原的方法车胡,要是不復(fù)原的話每次下的訂單,訂單號(hào)都是一樣的照瘾,我在開發(fā)時(shí)就遇到了這個(gè)問題匈棘,折騰了半天也發(fā)現(xiàn)是這個(gè)問題。

結(jié)尾

放上一張整個(gè)支付流程的圖示吧


5CE5E56A-63AC-4A95-A224-741D06DFAF80.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末析命,一起剝皮案震驚了整個(gè)濱河市主卫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碳却,老刑警劉巖队秩,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昼浦,居然都是意外死亡馍资,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸟蟹,“玉大人乌妙,你說我怎么就攤上這事〗ㄔ浚” “怎么了藤韵?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)熊经。 經(jīng)常有香客問我泽艘,道長(zhǎng),這世上最難降的妖魔是什么镐依? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任匹涮,我火速辦了婚禮,結(jié)果婚禮上槐壳,老公的妹妹穿的比我還像新娘然低。我一直安慰自己,他們只是感情好务唐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布雳攘。 她就那樣靜靜地躺著,像睡著了一般枫笛。 火紅的嫁衣襯著肌膚如雪吨灭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天崇堰,我揣著相機(jī)與錄音沃于,去河邊找鬼。 笑死海诲,一個(gè)胖子當(dāng)著我的面吹牛繁莹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播特幔,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼咨演,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蚯斯?” 一聲冷哼從身側(cè)響起薄风,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拍嵌,沒想到半個(gè)月后遭赂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡横辆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年撇他,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡困肩,死狀恐怖划纽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锌畸,我是刑警寧澤勇劣,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站潭枣,受9級(jí)特大地震影響比默,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盆犁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一退敦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚣抗,春花似錦、人聲如沸瓮下。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讽坏。三九已至锭魔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間路呜,已是汗流浹背迷捧。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胀葱,地道東北人漠秋。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像抵屿,于是被迫代替她去往敵國(guó)和親庆锦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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