Flutter iOS內購

工作中用flutter做過一個數(shù)字藏品的項目,蘋果端需要加入內購才能上線發(fā)布,本文主要寫一下使用flutter插件in_app_purchase: ^3.0.7 實現(xiàn)蘋果內購功能以及遇到的問題。純屬技術交流,歡迎評論交流.
主要思路:
1.在蘋果開發(fā)賬號上面建好虛擬商品。
2.將在蘋果開發(fā)賬號上面建好的虛擬商品錄入后臺系統(tǒng),便于后端通過接口返回給前端對應的商品id。
3.使用in_app_purchase插件,通過后端返回的商品id,查詢到商品信息,再將商品信息提交給蘋果服務器發(fā)起支付請求,再將蘋果服務器返回的支付結果傳給自己的服務器。
4.自己的服務器通過前端傳給的支付結果去進行發(fā)貨以及其他操作。
詳細操作:

關于怎么在蘋果開發(fā)賬號上面建商品過程有些繁瑣,不是本文的重點內容在這里就不詳細說明了,可以網上搜搜,有很多博主寫的很詳細,貼一個圖示。
3A7EF337-BAAB-4076-8BD2-D82363973441.png
in_app_purchase的使用

in_app_purchase是谷歌官方出的插件,相對來說還是很堅挺的,所以用的還是比較放心。我是用的in_app_purchase: ^3.0.7 #內購版本,直接導入項目使用即可。
當點到某個商品獲取到后臺返回的ProductId,調用下面的方法,仔細看注釋即可

/// 蘋果內購 (購買)
  Future<void> applePay(Map dataMap) async {
  /// _inAppPurchase是否有效
    final bool isAvailable = await _inAppPurchase.isAvailable();
    if (!isAvailable) {
      return;
    }
    /// 如果是iOS設備進行設置代理,接口蘋果服務器的回調。
    if (Platform.isIOS) {
      final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
          _inAppPurchase
              .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
    }

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

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

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

注意點:

上面的 purchaseParam就是向蘋果服務器發(fā)起支付請求時傳遞的參數(shù),在purchaseParam這個對象里,可以設置自定義參數(shù),來方便判斷那筆訂單。

監(jiān)聽蘋果服務器的回調

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

purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored說明在蘋果服務器上發(fā)起請求成功了,然后再通知一下自己的服務器去發(fā)貨然后一定記得要調用下這個方法_inAppPurchase.completePurchase(purchaseDetails)不然下一次就拉不起蘋果支付來了就萬事大吉了。但是你想多了,測試可不這樣整。

這張圖片相信大家很熟悉吧,當在蘋果服務器上付款成功后變會彈出這個彈窗,只有當用戶點擊“好”時才會觸發(fā)成功的回掉,也就是才會代碼才會走到這個purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored判斷里面來,那如果這時候,測試同學,不點擊“好”直接退到App后臺,把App直接殺死或者把App卸載了,那自己的服務器是不知道用戶在蘋果服務器上已經完成付款了,是不會給用戶發(fā)貨的,這也就是老生常談的丟單問題。

WeChatc5a772f243ebc90533be17c3af6bd25e.jpg

丟單問題處理

使用

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

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.
重要!你必須在應用程序啟動后立即訂閱此流,
最好在main()中返回主應用程序小部件之前。否則你
將錯過訂閱此流之前更新的購買。

也就是說當我們的App在第一次啟動的時候可以訂閱此流來完成補單的操作,但是如果用戶是之前丟單了,然后把App又卸載了,再次下載打開App后并沒有進行登錄操作,那用戶的登錄信息都拿不到怎么進行補單操作呢?

補單解決方案

讓后端出一個補單的接口,在補單時只需要傳一個訂單號即可,那App都刪除了,之前的訂單號客戶端怎么獲取呢?使用flutter_keychain來實現(xiàn),flutter_keychain就是使用的iOS的鑰匙串來實現(xiàn)的,當用戶在蘋果服務器下單時,在鑰匙串中保存后端生成的訂單號,然后再商品成功發(fā)貨后刪除鑰匙串里面的訂單號,完成一個完整的購買過程,再購買時任何一環(huán)出了問題鑰匙串里面緩存的訂單號都不會被清空,這樣在App下一次啟動時,在首頁或者main函數(shù)中使用_inAppPurchase.purchaseStream監(jiān)聽,在拿到flutter_keychain中保存的訂單號完成補單過程。

注意點

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

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

結尾

放上一張整個支付流程的圖示吧


5CE5E56A-63AC-4A95-A224-741D06DFAF80.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容