Using the ONE Store In-App Library in Flutter

Overview

The ONE store payment plugin provides access to the latest features of the ONE Store payment library within applications developed in the Flutter environment.

This guide will explain how to use the plugin and implement the functions of the ONE store payment library.

Plugin Installation

How to add the plugin to your ‘pubspec.yaml’ file:

dependencids:
  ..
  flutter_onestore_inapp: ^0.1.0
  ..

Click on 'pub get' to download the package, or run 'flutter pub get' from the command line.

Adding dependencies to build.gradle

Add the maven address to your project's build.gradle.

dependencids:
  ..
  flutter_onestore_inapp: ^0.1.0
  ..

Add the configuration dependencies to your app's build.gradle.

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

Adding <queries> to AndroidManifest.xml

  • If you have set the Target SDK version to 30 (OS 11) or higher, you must add the following <queries> to ensure the in-app library functions correctly.

<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>

Requesting Login:

The ONE store in-app payment operates on a login basis. When the app starts for the first time, it prompts the user to log in before calling the API of the purchase library. This can prevent various issues, such as token expiration, before they occur when requesting the purchase library.

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
      }
    });
  }

Updating Purchase Data and Listening for Error Responses in the App

Implementing the ‘PurchasesUpdatedStream’ response:

This object should listen for purchase updates immediately when the app starts. Therefore, use 'lazy: false' to disable ‘Lazy Loading’ for this object.

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

Issuing a license key:

When you register your app through the ONE store developer center, a license key is generated.

Initialize the object for the first time once with PurchaseClientManager.initialize() call.

Prepare to receive purchase completion responses by 'listening' to the 'purchasesUpdatedStream'. Receive purchase data responses for successful transactions through _listenToPurchasesUpdated. For failed purchases, responses are conveyed through _updateStreamOnError.

Upon closing the app, you should call 'PurchaseClientManager.dispose()' to release memory and disconnect. Also, dispose () of the _purchaseUpdatedStream that you have been listening to in order to disconnect.

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();
  }
}

Searching for Detailed Product Information:

Utilize 'await _clientManager.queryProductDetails(List<String>, ProductType)' to request detailed information for in-app products registered through the One store developer center.

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();
  }
}

Initiating Purchase Request:

Use 'await _clientManager.launchPurchaseFlow(ProductDetail)' to request the purchase of a product.

Parameter

Type

Description

productDetail

ProductDetail

Detailed information you wish to purchase

quantity

Int

For multiple purchases

Default Value: 1

developerPayload

String

When making a purchase request, send the necessary data input by the developer along with the request.

This data will also be included in the purchase result (PurchaseData.developerPayload).

Limitation: 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);
  }

Post-purchase processing

If the purchase has been successfully completed, you can receive a response through '_listenToPurchasesUpdated' which you registered in 'Updating Purchase Data and Listening for Error Responses in the App'.

Of course, you can also receive responses for failed purchases through '_updateStreamOnError'.

Once you have received a purchase completion response from the store, it is crucial to perform a consume or acknowledge action.

If you do not acknowledge or consume the purchase within 3 days, it will be considered that the product has not been delivered to the user and will automatically be refunded.

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();
  }

Consumption:

Use '_clientManager.consumePurchase(PurchaseData)' to request the consumption of a managed product.

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]);
      }
    });
  }

Purchase Acknowledgement:

Use '_clientManager.acknowledgePurchase(PurchaseData)' to request confirmation for managed or subscription products.

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]);
      }
    });
  }

Retrieving Purchase History:

Use '_clientManager.queryPurchases(ProductType)' to request a list of unconsumed purchases.

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);
        }
      });
    }
  }

Upgrading and Downgrading Subscription:

Use '_clientManager.launchUpdateSubscription(ProductDetail, PurchaseData, ProrationMode)' to change the purchased subscription product to a new one.

Upgrade or Downgrade follows the same flow as the “Initiating Purchase Request,” so the response handling is the same as when requesting a purchase.

You can receive responses through '_listenToPurchasesUpdated'.

Parameter

Type

Description

productDetail

ProductDetail

Information on the subscription product to change

oldPurchseData

PurchaseData

Purchase information of the subscription product that is targeted to change

prorationMode

ProrationMode

The applicable mode when changing existing subscription purchase information (for detailed information, refer to the existing guide)

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);
  }

Opening the Subscription Management Screen:

Use '_clientManager.launchManageSubscription(PurchaseData?)' to navigate to the detailed page of the purchased subscription product.

Changing the subscription settings are up to the user, and the following can be managed in the subscription menu:

  • Change payment methods

  • Change subscription status (schedule cancellation, revoke cancellation)

  • Agreement to price changes for already subscribed products

If you request with the PurchaseData as ‘null,’ you will be directed to the subscription list screen instead of a specific subscription product's detail page.

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

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

Installing ONE Store Service:

If ONE store service is not installed or is not the minimum version required by the v21 SDK, a RESULT_NEED_UPDATE error will occur. If you receive this error, use '_clientManager.launchUpdateOrInstall()' to request the installation of the service.

import 'package:flutter_onestore_inapp/onestore_in_app_wrappers.dart';

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