在Flutter中使用ONE store In-App Library(内部應用程式庫)

概要

ONE store支付插件(plugin)通過在plotter環境中展現的應用程式(app), 為您提供ONE store 支付插件的最新功能。

本指南為你提供使用插件的方法和展現ONE store支付插件功能的方法。

安裝插件

在pubspec.yaml文件中加入插件的方法

dependencids:
  ..
  flutter_onestore_inapp: ^0.1.0
  ..

點擊'pub get'下載程式包或者在命令行運行'flutter pub get'。

加入build.gradle從屬性

在project的build.gradle中加入maven地址

allprojects {
    repositories {
        ..
        maven { url 'https://repo.onestore.co.kr/repository/onestore-sdk-public' }
    }
}

在app的build.gradle 中加入configuration從屬性

dependencies {
	..
    implementation "com.onestorecorp.sdk:sdk-configuration-kr:1.0.0"
    ..
}

在AndroidManifest.xml中加入<queries>

<manifest>
    ...
    <queries>
        <intent>
            <action android:name="com.onestore.ipc.iap.IapService.ACTION" />
        </intent>
        <intent>
            <action android:name="android.intent.action.VIEW" />

            <data android:scheme="onestore" />
        </intent>
    </queries>
    ...
    <application>
        ...
    </application>
</manifest>

請求登錄

ONE store内部app支付是需要登錄才能啟動的服務。首次開始啟動app時,在呼叫購買庫的API之前,先誘導登錄。

可以提前預防請求購買庫時出現token過期和其他各種狀況。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  final OneStoreAuthClient _authClient = OneStoreAuthClient();

  Future<void> launchSignInFlow() async {
    await _authClient.launchSignInFlow().then((signInResult) {
      if (signInResult.isSuccess()) {
        // success
      }
    });
  }

在App中更新購買資訊以及讀取出錯回覆

展現PurchasesUpdatedStream 回覆

本對象(object)需要在app啟動時直接更新購買,所以需要使用'lazy: false',讓針對本對象的'Lazy loading'處於未啟用狀態。

ChangeNotifierProvider<PurchaseViewModel>(
    create: (context) => PurchaseViewModel(),
    lazy: false,
)

颁发证书(license)密钥

在ONE store研发者中心登录app,就会生成证书密钥

通過呼叫PurchaseClientManager.initialize()在第一次建立對象。

在'purchasesUpdatedStream' 字符串中'listen',準備獲取購買结束的回覆。

以_listenToPurchasesUpdated成功購買,獲取購買資訊的回覆。

以_updateStreamOnError ,獲取購買失敗的回覆。

App结束時,呼叫'PurchaseClientManager.dispose()' 解除内存及解除連接,同時已經讀取的_purchaseUpdatedStream也需要dispose(),以此斷開連接。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

class PurchaseViewModel extends ChangeNotifier {

  final OneStoreAuthClient _authClient = OneStoreAuthClient();
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;

  late StreamSubscription<List<PurchaseData>> _purchaseUpdatedStream;

  PurchaseViewModel() {
    _clientManager.initialize('input your license key');
    
    _purchaseUpdatedStream = _clientManager.purchasesUpdatedStream.listen(
      _listenToPurchasesUpdated,
      onError: _updateStreamOnError,
      onDone: () => _purchaseDataStream.cancel(),
    );
  }

  @override
  void dispose() {
    _purchaseDataStream.cancel();
    _clientManager.dispose();
    super.dispose();
  }

  void _listenToPurchasesUpdated(List<PurchaseData> purchasesList) {
    // Handle purchases here
  }

  void _updateStreamOnError(dynamic error) {
    // Handle error here
  }

  Future<IapResult> launchPurchaseFlow(ProductDetail productDetail,
      int? quantity, String? developerPayload) async {
    return await _clientManager.launchPurchaseFlow(
        productDetail: productDetail,
        quantity: quantity,
        developerPayload: developerPayload
    );
  }

  Future<SignInResult> launchSignInFlow() async {
     return await _authClient.launchSignInFlow();
  }
}

查詢商品詳細資訊

使用'await _clientManager.queryProductDetails(List<String>, ProductType)'請求獲取ONE store研發者中心登錄的内部app商品的詳細資訊。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  static const consumableIds = ['p500', 'p510'];
  static const subscriptionIds = ['week', 'month', 'three_month'];
  
  final List<ProductDetail> _products = [];
  
  Future<void> fetchProductDetails() async {
    var responses = await Future.wait(<Future<ProductDetailsResponse>>[
      _clientManager.queryProductDetails(
          productIds: consumableIds, productType: ProductType.inapp),
      _clientManager.queryProductDetails(
          productIds: subscriptionIds, productType: ProductType.subs)
    ]);
  
    if (responses.first.iapResult.isSuccess()) {
      final List<ProductDetail> result =
          responses.expand((element) => element.productDetailsList).toList();
      _products.clear();
      _products.addAll(result);
      notifyListeners();
    } else {
      _handleError('fetchProductDetails', responses.first.iapResult);
    }
  
    /// WARNING! 请求信息太多时,若一次请求可能会产生回复延迟。
    /// 但是请求信息少时,一次性请求更有效率。
    // var allResponse = await _clientManager.queryProductDetails(
    //     productIds: (consumableIds + subscriptionIds),
    //     productType: ProductType.all);
    // 
    // if (allResponse.iapResult.isSuccess()) {
    //   final List<ProductDetail> result = allResponse.productDetailsList;
    //   _products.clear();
    //   _products.addAll(result);
    //   notifyListeners();
    // } else {
    //   _handleError('fetchProductDetails', allResponse.iapResult);
    // }
  }

請求購買

使用'await _clientManager.launchPurchaseFlow(ProductDetail)'請求商品購買。

Parameter

Type

Description

productDetail

ProductDetail

將購買商品的詳細資訊

quantity

Int

重複購買時使用

基本參數:1

developerPayload

String

作為研發公司輸入資訊的需要資訊,若在請求購買時被同時傳送,購買结果(PurchaseData.developerPayload )中(該資訊)也將被包含一起傳送。

限制事項:max 200byte

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<IapResult> launchPurchaseFlow(ProductDetail productDetail,
      int? quantity, String? developerPayload) async {
    return await _clientManager.launchPurchaseFlow(
        productDetail: productDetail,
        quantity: quantity,
        developerPayload: developerPayload);
  }

購買後處理

若購買成功,可以通過“在APP中更新購買資訊以及讀取出錯回覆”中登錄的'_listenToPurchasesUpdated' 獲取回覆。

當然購買失敗的回覆也可以通過'_updateStreamOnError'獲取。

若在store中獲取到購買结束的回覆,接下来使用(consume)或確認(acknowledge)的工作非常重要。

若3天内購買沒有確認(acknowledge)或使用(consume),會判斷為未向用戶支付商品,自動返款给用戶。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  List<ProductDetail> get consumableProducts;
  List<ProductDetail> get subscriptionProducts

  Future<void> _listenToPurchasesUpdated(List<PurchaseData> purchasesList) async {
    for (var element in purchasesList) {
      if (consumableProducts.any((p) => p.productId == element.productId)) {
        /// [ProductType.inapp] 상품은 [consumePurchase] 호출을 하여 소비 해야합니다.
        await consumePurchase(element);
      } else if (subscriptionProducts.any((p) => p.productId == element.productId)) {
        /// [ProductType.subs] 상품은 [acknowledgePurchase] 호출을 하여 확인 해야합니다.
        await acknowledgePurchase(element);
      }
    }
    notifyListeners();
  }

購買使用(consume)

使用'_clientManager.consumePurchase(PurchaseData)'請求管理型商品的使用。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<void> consumePurchase(PurchaseData purchaseData) async {
    await _clientManager
        .consumePurchase(purchaseData: purchaseData)
        .then((iapResult) {
      // iapResult 를 통해 해당 API의 성공 여부를 판단할 수 있습니다.
      if (iapResult.isSuccess()) {
        fetchPurchases([ProductType.inapp]);
      }
    });
  }

購買確認(acknowledge)

使用'_clientManager.acknowledgePurchase(PurchaseData)'請求管理型商品或訂閱商品的確認。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<void> acknowledgePurchase(PurchaseData purchaseData) async {
    await _clientManager
        .acknowledgePurchase(purchaseData: purchaseData)
        .then((iapResult) {
      // iapResult 를 통해 해당 API의 성공 여부를 판단할 수 있습니다.
      if (iapResult.isSuccess()) {
        fetchPurchases([ProductType.subs]);
      }
    });
  }

查詢購買明细

使用'_clientManager.queryPurchases(ProductType)',請求未使用的購買明细。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<void> fetchPurchases([List<ProductType>? types]) async {
    types ??= <ProductType>[ProductType.inapp, ProductType.subs];
    for (var element in types) {
      await _clientManager
          .queryPurchases(productType: element)
          .then((response) {
        if (response.iapResult.isSuccess()) {
          if (element == ProductType.inapp) {
            for (var purchaseData in response.purchasesList) {
              consumePurchase(purchaseData);
            }
          } else if (element == ProductType.subs) {
            for (var purchaseData in response.purchasesList) {
              if (!purchaseData.isAcknowledged) {
                acknowledgePurchase(purchaseData);
              }
            }
          }
          notifyListeners();
        } else {
          _handleError('fetchPurchases($element)', response.iapResult);
        }
      });
    }
  }

更新或下載更新

使用'_clientManager.launchUpdateSubscription(ProductDetail, PurchaseData, ProrationMode)' 將購買的定期支付商品變更為新商品。

變更定期支付商品和請求購買流程相同,所以和“請求購買”的回覆處理方式相同。

可以通過'_listenToPurchasesUpdated'獲取回覆。

Parameter

Type

Description

productDetail

ProductDetail

將變更的訂閱商品的資訊

oldPurchseData

PurchaseData

將成為變更對象的訂閱商品的購買資訊

prorationMode

ProrationMode

變更原有訂閱購買資訊時可以適用的模式

(詳細資訊參考原有指南)

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<IapResult> launchUpdateSubscription(ProductDetail productDetail,
      PurchaseData oldPurchaseData, ProrationMode prorationMode) async {
    return await _clientManager.launchUpdateSubscription(
        productDetail: productDetail,
        oldPurchaseData: oldPurchaseData,
        prorationMode: prorationMode);
  }

打開定期支付管理畫面

使用'_clientManager.launchManageSubscription(PurchaseData?)'可以移動到購買的訂閱商品的詳細内容網頁。

訂閱商品的設置變更由用戶處理,管理菜單中能做的如下。

  • 支付方式變更

  • 訂閱狀態變更(解除預約,取消)

  • 同意原有訂閱商品的價格變更

PurchaseDatanull時,將不出現購買商品的詳細内容網頁,而是移動到定期支付目錄畫面。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<void> launchManageSubscription(PurchaseData? purchaseData) async {
    await _clientManager.launchManageSubscription(purchaseData);
  }

安裝 ONE store服務

ONE store服務無法安裝或不是v21 SDK中支持的最低版本時,RESULT_NEED_UPDATE中會發生錯誤(error)。

收到相應錯誤(error)時,使用'_clientManager.launchUpdateOrInstall()',請求安裝ONE store服務。

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

  Future<void> launchUpdateOfInstall() async {
    await _clientManager.launchUpdateOrInstall().then((iapResult) {
      if (iapResult.isSuccess()) {
        fetchPurchases();
        fetchProductDetails();
      }
    });
  }

Last updated