Guide InApp SDK
  • SDK Purchase v21
    • 원스토어 인앱결제 SDK를 사용해 구현하기 (SDK v21)
    • Unity에서 원스토어 인앱결제 사용하기 (SDK v21)
      • Migrating from v1.1.x to v1.2.0
    • Flutter에서 원스토어 인앱 결제 사용하기 (SDK v21)
  • SDK Purchase V19
    • 원스토어 인앱결제 적용하기 (SDK v19)
    • Unity에서 원스토어 인앱결제 사용하기 (SDK v19)
Powered by GitBook
On this page
  • 개요
  • 개발 버전
  • 플러그인 설정
  • pubspec.yaml 파일에 플러그인 추가하기
  • Android build.gradle에 종속성 추가하기
  • AndroidMainifest.xml 파일에 <queries> 추가하기
  • 글로벌 스토어 배포를 위한 테스트 옵션 설정
  • 앱에서 원스토어 인앱 결제 라이브러리 적용하기
  • 로그 레벨 설정
  • 로그인 요청하기
  • 구매 라이브러리 초기화
  • 구매 데이터 업데이트 및 오류 응답 청취하기
  • 상품 상세 정보 조회하기
  • 구매 요청하기
  • 정기 결제 업그레이드 또는 다운그레이드
  • 구매 후 처리
  • 구매 내역 조회하기
  • 정기 결제 관리 화면 열기
  • 원스토어 서비스 설치하기
  1. SDK Purchase v21

Flutter에서 원스토어 인앱 결제 사용하기 (SDK v21)

PreviousMigrating from v1.1.x to v1.2.0Next원스토어 인앱결제 적용하기 (SDK v19)

Last updated 6 months ago

개요

Flutter 환경에서 구현된 어플리케이션에서 원스토어 결제 라이브러리의 최신 기능 제공합니다. 이 가이드는 원스토어 결제 라이브러리 기능을 Flutter 환경에서 적용하는 방법을 설명합니다.

개발 버전

플러그인 설정

pubspec.yaml 파일에 플러그인 추가하기

flutter pub get 을 통하여 패키지를 다운로드합니다.

dependencids:
  flutter_onestore_inapp: ^0.3.0

Android build.gradle에 종속성 추가하기

Top-Level build.gradle 파일에 maven repository 주소 추가하기

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

AndroidMainifest.xml 파일에 <queries> 추가하기

<manifest> 태그의 직속 하위에 위치하며, 아래와 같은 요소를 추가해야 합니다.

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

글로벌 스토어 배포를 위한 테스트 옵션 설정

원스토어 인앱 SDK를 임의로 글로벌 스토어로 연동하는 옵션 이 기능은 Java SDK v21.01.00 버전부터 적용됩니다.

<application> 태그 직속 하위에 위치하며, 아래와 같은 요소를 추가합니다.

<manifest>
    <application>
        <activity>
        </activity>
        
        <!-- Options for in-app testing on your global store -->
        <meta-data android:name="onestore:dev_option" android:value="global" />
    </application>
</manifest>

배포 빌드 버전에서는 이 옵션을 반드시 삭제해야 합니다.

앱에서 원스토어 인앱 결제 라이브러리 적용하기

로그 레벨 설정

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class _HomePageState extends State<HomePage> {

  @override
  void initState() {
    super.initState();
    // 앱 개발 시 필요에 의해 SDK & Plugin의 로그 레벨을 변경하면 좀 더 자세한 정보를 얻을 수 있습니다.
    // WARNING! Release Build 시엔 로그 레벨 세팅을 제거 바랍니다. (default: Level.info)
    OneStoreLogger.setLogLevel(LogLevel.verbose);
  }
}
상수
값

VERBOSE

2

DEBUG

3

INFO (default)

4

WARN

5

ERROR

6

배포 빌드 버전에서는 보안에 취약할 수 있으니 이 옵션을 삭제해야 합니다.

로그인 요청하기

원스토어 인앱 결제는 로그인 기반으로 구동되는 서비스입니다. 앱 최초 시작 시 구매 라이브러리의 API 호출하기 전에 로그인을 유도합니다. 구매 라이브러리 요청 시 토큰 만료나 다른 여러 가지 사항을 미연에 방지할 수 있습니다.

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

final OneStoreAuthClient _authClient = OneStoreAuthClient();

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

구매 라이브러리 초기화

PurchaseClientManager 인스턴스 초기화를 요청합니다. 이때 필요한 값은 public license key입니다. 이 라이선스 키는 원스토어 개발자 센터에 앱 등록을 완료하면 발급받을 수 있습니다.

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
_clientManager.initialize("your license key");

public license key는 SDK 내에서 구매 응답에 대한 위변조 체크에 사용됩니다.

구매 데이터 업데이트 및 오류 응답 청취하기

PurchaseClientManager.purchasesUpdatedStream 을 통해 구매 완료 응답을 받을 준비를 합니다.

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
  late StreamSubscription<List<PurchaseData>> _purchaseDataStream;
  
  MyPurchaseManager() {
    _clientManager.initialize("your license key");
    
    // 구매 완료 후 Stream을 통해 데이터가 전달됩니다.
    _purchaseDataStream = _clientManager.purchasesUpdatedStream.listen(
        (List<PurchaseData> purchasesList) {
      _listenToPurchasesUpdated(purchasesList);
    }, onError: (error) {
      // 구매가 실패 되었거나 유저가 취소가 되었을 때 응답 됩니다.
      _logger.d('purchaseStream error: $error');
    }, onDone: () {
      _purchaseDataStream.cancel();
    });
  }
  

  void _listenToPurchasesUpdated(List<PurchaseData> purchasesList) {
    // do something
  }

}

상품 상세 정보 조회하기

PurchaseClientManager.queryProductDetails API를 통해 원스토어 개발자 센터에 등록된 인앱 상품의 상세 정보를 조회할 수 있습니다.

Parameter
Type
Description

productIds

List<String>

상품 아이디 리스트

productType

상품 타입

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
  
  static const consumableIds = ['product_1', 'product_2'];
  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);
    }
  }
  
}
class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
  
  static const consumableIds = ['product_1', 'product_2'];
  static const subscriptionIds = ['week', 'month', 'three_month'];
  
  final List<ProductDetail> _products = [];
  
  Future<void> fetchProductDetails() async {
    var productDetailsResponse = await _clientManager.queryProductDetails(
      productIds: (consumableIds + subscriptionIds),
      productType: ProductType.all
    );
     
    if (productDetailsResponse.iapResult.isSuccess()) {
      final List<ProductDetail> result = productDetailsResponse.productDetailsList;
      _products.clear();
      _products.addAll(result);

    } else {
      _handleError('fetchProductDetails', productDetailsResponse.iapResult);
    }
  }
  
}

등록된 인앱상품의 개수가 많은 경우 응답 지연이 발생할 수 있습니다. 이런 경우 안정성이나 속도 측면에서 400개 단위로 호출 하시는 것을 권장 드립니다.

구매 요청하기

PurchaseClientManager.launchPurchaseFlow() API를 사용하여 구매 요청합니다.

Parameter
Type
Description

productDetail

quantity

Int

수량 (default: 1) 복수 구매 시 max: 10

developerPayload

String

제약 사항 : max 200byte

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

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

정기 결제 업그레이드 또는 다운그레이드

정기 결제는 취소될 때까지 자동으로 갱신됩니다. 정기 결제는 다음 상태를 가질 수 있습니다.

  • 활성: 사용자가 콘텐츠 사용에 문제가 없는 양호한 상태이며 정기 결제에 접근할 수 있습니다.

  • 일시 중지 예약: 사용자가 정기 결제를 이용 중 일시 중지를 하고 싶을 때 선택할 수 있습니다.

    • 주간 정기 결제: 1~3주 단위로 일시 중지할 수 있습니다.

    • 월간 정기 결제: 1~3개월 단위로 일시 중지할 수 있습니다.

    • 연간 정기 결제: 일시 중지를 지원하지 않습니다.

  • 해지 예약: 사용자가 정기 결제를 이용 중이지만 취소하고 싶을 때 선택할 수 있습니다. 다음 결제일에 결제가 되지 않습니다.

  • 유예, 보류: 사용자에게 결제 문제가 발생하면 다음 결제일에 결제가 되지 않습니다. 취소 예약을 할 수 없으며 즉시 "구독 해지"를 할 수 있습니다.

정기 결제를 업데이트하기 위해서는 필수로 비례 배분 모드를 적용해야 합니다. 아래는 비례 배분 모드별 설명입니다.

Mode
Value
Description

IMMEDIATE_WITH_TIME_PRORATION

1

정기 결제의 교체가 즉시 이루어지며, 남은 시간은 가격 차이를 기반으로 조정되어 입금되거나 청구됩니다. (이것은 기본 동작입니다.)

IMMEDIATE_AND_CHARGE_PRORATED_PRICE

2

정기 결제의 교체가 즉시 이루어지며, 청구 주기는 동일하게 유지됩니다. 나머지 기간에 대한 가격이 청구됩니다. (이 옵션은 업그레이드 에서만 사용할 수 있습니다.)

IMMEDIATE_WITHOUT_PRORATION

3

정기 결제의 교체가 즉시 이루어지며, 다음 결제일에 새로운 가격이 청구됩니다. 청구 주기는 동일하게 적용됩니다.

DEFERRED

4

기존 요금제가 만료되면 교체가 적용되며 새 요금이 동시에 청구됩니다.

PurchaseClientManager.launchUpdateSubscription() API를 통해 요청할 수 있습니다.

Parameter
Type
Description

productDetail

oldPurchaseData

prorationMode

비례 배분 모드

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;

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

구매 후 처리

  • PurchaseClientManager.launchPurchaseFlow()

  • PurchaseClientManager.launchUpdateSubscription()

성공적으로 구매 완료되어 응답을 받았다면, 사용자는 소비(Consume) 또는 확인(Acknowledge) 작업을 하는 것이 매우 중요합니다.

3일 이내에 구매를 확인(acknowledge) 또는 소비(consume)를 하지 않으면 사용자에게 상품이 지급되지 않았다고 판단되어 자동으로 환불됩니다.

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
  
  final List<ProductDetail> _products = [];
  
  // 상품 상세 정보에서 ProductType.inapp인 것만 필터링 된 데이터
  List<ProductDetail> get consumableProducts => _products
      .where((element) => element.productType == ProductType.inapp)
      .toList();

  // 상품 상세 정보에서 ProductType.subs인 것만 필터링 된 데이터
  List<ProductDetail> get subscriptionProducts => _products
      .where((element) => element.productType == ProductType.subs)
      .toList();

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

소비하기 (Consume)

  • 소모성 상품: 구매 요청 → 응답 → 아이템 지급 → consumePurchase

  • 기간제 상품: 구매 요청 → 응답 → 아이템 지급 → acknowledgePurchase → 일정 기간이 지난 후 → consumePurchase

Parameter
Type
Description

purchaseData

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;

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

확인하기 (Acknowledge)

Parameter
Type
Description

purchaseData

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;

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

구매 내역 조회하기

PurchaseClientManager.queryPurchases() API를 사용하여 소비되지 않은 구매 내역을 요청합니다.

구매 완료 후 데이터를 처리하는 것만으로는 앱이 모든 구매를 처리하는 것을 보장하기에 충분하지 않습니다. 앱에서 사용자가 구매한 모든 항목을 인식하지 못할 수 있습니다. 앱에서 구매 추적을 놓치거나 구매를 인식하지 못할 수 있는 몇 가지 시나리오는 다음과 같습니다.

  • 구매 중 네트워크 문제: 사용자가 구매를 성공적으로 완료하고 원스토어에서 확인을 받았지만 기기가 구매 알림을 받기 전에 네트워크 연결이 끊어졌을 경우

  • 여러 기기: 사용자는 한 기기에서 항목을 구입한 후 기기를 전환할 때 이 항목이 표시되기를 기대합니다.

이러한 상황에 대처하려면 구매 내역 조회하기 API를 상황에 맞게 호출해야 합니다.

  • 어플리케이션 처음 구동시

  • 어플리케이션이 백그라운드에서 포그라운드로 재 진입했을 경우

  • 상점 진입 시

어플리케이션의 상황에 맞게 사용해 주세요.

Parameter
Type
Description

productType

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

class MyPurchaseManager {
  final PurchaseClientManager _clientManager = PurchaseClientManager.instance;
  
  Future<void> fetchPurchases(ProductType type) async {
    await _clientManager
        .queryPurchases(productType: type)
        .then((response) {
      if (response.iapResult.isSuccess()) {
        if (type == ProductType.inapp) {
          for (var purchaseData in response.purchasesList) {
            consumePurchase(purchaseData);
          }
        } else if (type == ProductType.subs) {
          for (var purchaseData in response.purchasesList) {
            if (!purchaseData.isAcknowledged) {
              acknowledgePurchase(purchaseData);
            }
          }
        }
      } else {
        _handleError('fetchPurchases($type)', response.iapResult);
      }
    });
  }
  
}

정기 결제 관리 화면 열기

PurchaseClientManager.launchManageSubscription() API를 사용하여 구독 상품의 상세 페이지로 이동합니다.

구독 상품의 설정 변경은 유저의 몫으로 관리 메뉴에서 할 수 있는 것들은 아래와 같습니다.

  • 결제 수단 변경

  • 구독 상태 변경 (해지 예약, 취소)

  • 기 구독한 상품의 가격 변경 동의

Parameter
Type
Description

purchaseData

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

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

원스토어 서비스 설치하기

PurchaseClientManager.launchUpdateOrInstall() API를 호출하여 '원스토어 서비스 앱'을 설치할 수 있습니다.

import 'package:flutter_onestore_inapp/flutter_onestore_inapp.dart';

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

원스토어 공지사항:

참고:

개발 단계에서 로그 레벨을 설정하여 SDK의 데이터 흐름을 좀 더 자세히 노출할 수 있습니다. 에 정의된 값을 기반으로 동작합니다.

상품 상세 정보는 객체를 담은 리스트로 전달됩니다.

요청하는 상품 리스트가 적다면 위의 예제처럼 상품 타입에 따라 개별 요청이 아닌 타입으로 요청할 수 있습니다.

타입은 상품 상세 조회하기에서만 사용할 수 있는 옵션입니다. 다른 API에서는 사용할 수 없습니다.

API를 통해 얻는 객체

개발사 입력 데이터로 필요한 데이터를 구매 요청 때같이 전송하면 구매 결과( )에도 포함되어 전송됨

정기결제는 와 동일한 API를 사용하여 사용자에게 업그레이드 또는 다운그레이드를 제공할 수 있습니다. 다만, 정기 결제의 업그레이드 다운그레이드를 적용하기 위해선 기존 정기 결제 구매 토큰과 비례 배분 모드 값이 필수로 필요합니다.

API를 통해 얻는 객체

API를 통해 얻는 객체

API를 사용하여 구매가 성공적으로 이루어졌다면 서 등록한 _listenToPurchasesUpdated() 를 통해 응답을 받을 수 있습니다.

PurchaseClientManager.consumePurchase() API를 사용하여 관리형 상품()을 소비합니다.

관리형 상품()은 소비를 하지 않으면 재 구매가 불가합니다.

관리형 상품()은 API 사용에 따라 두 가지로 사용될 수 있습니다.

또는 를 통해 얻는 PurchaseData

PurchaseClientManager.acknowledgePurchase() API를 사용하여 관리형 상품() 또는 구독형 상품()의 확인을 요청합니다.

구독형 상품()는 소비(Consume)는 할 수 없고 확인(Acknowledge)만 가능합니다.

또는 를 통해 얻는 PurchaseData

확인 작업이 완료되었다면 구독형 상품()의 경우 를 통해 업데이트해야 값이 변경된 것을 확인할 수 있습니다.

구매 내역 조회하기의 응답 데이터는 통해 받는 데이터와 동일합니다.

또는 를 통해 얻는 PurchaseData

값이 null일 경우 특정 구독 상품의 상세 페이지가 아닌 정기 결제 리스트 화면으로 이동합니다.

또는 를 통해 얻는 PurchaseData

PurchaseClientManager API를 사용하는 중에 에러 응답 중 코드가 발생했을 경우가 있습니다. 이는 원스토어 서비스 앱이 설치되지 않았거나 In-app SDK에서 요구하는 버전보다 낮을 경우 발생합니다.

Flutter

3.3.0

Java SDK (Java 11)

Purchase: v21.01.00

App License Checker: v2.1.0

Target SDK 버전이 30(OS 11) 이상으로 설정한 경우
https://developer.android.com/training/package-visibility
android.util.Log
ProductDetail
ProductType.all
ProductType.all
ProductType.inapp
ProductType.inapp
ProductType.inapp
ProductType.inapp
ProductType.subs
ProductType.subs
PurchaseData
RESULT_NEED_UPDATE
구매 요청하기
구매 데이터 업데이트 및 오류 응답 청취하기
ProductType.subs
PurchaseData
PurchaseData.isAcknowledged
구매 내역 조회하기
구매 데이터 업데이트 및 오류 응답 청취하기
ProductType
ProductDetail
PurchaseData.developerPayload
ProductDetail
PurchaseData
ProrationMode
PurchaseData
PurchaseData
ProductType
PurchaseData
상품 상세 정보 조회하기
상품 상세 정보 조회하기
구매 내역 조회하기
구매 완료
구매 내역 조회하기
구매 완료
구매 내역 조회하기
구매 완료
구매 내역 조회하기
구매 완료
구매 내역 조회하기