본문 바로가기
개발/Flutter

In App 결제 기능 구축(3)

by dev_caleb 2023. 6. 4.
728x90

 

인앱결제가 이렇게 나온다! 

 

ios 랑 비슷하게 복사해서 설정해주면 android는 그렇게 어렵지 않다 .

 

 

 

이제 세팅은 끝났고 코드를 작성해야할 때가 온 것 같다!

 

 

 

https://velog.io/@soonmuu/Flutter-flutter-인앱결제-구현-2

 

[Flutter] flutter 인앱결제 구현 (2) - 코드구현

in_app_purchase 패키지를 사용한 인앱결제 코드 예시

velog.io

이제 이 페이지를 보면서 수행할 수 있을 것 같다!

 

 

https://pub.dev/packages/in_app_purchase

 

in_app_purchase | Flutter Package

A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play.

pub.dev

이 pub.dev를 보시고 아마 코드를 짜셨을 것 같으니 해당 페이지를 번역해본다 나의 친구 ChatGpt와 함께!

 

 

 

A storefront-independent API for purchases in Flutter apps.

This plugin supports in-app purchases (IAP) through an underlying store, which can be the App Store (on iOS and macOS) or Google Play (on Android).

플러터 앱에서 상점에 독립적인 인앱 구매를 위한 API입니다.

이 플러그인은 앱 스토어(iOS와 macOS에서는 App Store, Android에서는 Google Play)를 기반으로 인앱 구매(IAP)를 지원합니다.

 

 

Use this plugin in your Flutter app to:

  • Show in-app products that are available for sale from the underlying store. Products can include consumables, permanent upgrades, and subscriptions.
  • Load in-app products that the user owns.
  • Send the user to the underlying store to purchase products.
  • Present a UI for redeeming subscription offer codes. (iOS 14 only)

이 플러그인을 사용하여 플러터 앱에서 다음을 수행할 수 있습니다:

  • 기본 상점에서 판매 가능한 인앱 상품을 표시합니다. 이 상품들은 소비 가능한 상품, 영구적인 업그레이드, 구독 등을 포함할 수 있습니다.
  • 사용자가 소유한 인앱 상품을 로드합니다.
  • 사용자를 기본 상점으로 이동하여 상품을 구매하도록 안내합니다.
  • 구독 제안 코드를 교환하는 UI를 표시합니다. (iOS 14 전용)

Getting started 

This plugin relies on the App Store and Google Play for making in-app purchases. It exposes a unified surface, but you still need to understand and configure your app with each store. Both stores have extensive guides:

이 플러그인은 인앱 구매를 위해 App Store와 Google Play에 의존합니다. 통합된 플랫폼을 제공하지만, 여전히 각각의 상점에서 앱을 이해하고 구성해야 합니다. 두 상점 모두 상세한 가이드를 제공하고 있습니다:

  • App Store 문서
  • Google Play 문서

NOTE: Further in this document the App Store and Google Play will be referred to as "the store" or "the underlying store", except when a feature is specific to a particular store.

참고: 이 문서의 후반부에서는 특정 상점에 특화된 기능을 제외하고 "상점" 또는 "기본 상점"으로 언급됩니다.

 

For a list of steps for configuring in-app purchases in both stores, see the example app README.

Once you've configured your in-app purchases in their respective stores, you can start using the plugin. Two basic options are available:

  1. A generic, idiomatic Flutter API: in_app_purchase. This API supports most use cases for loading and making purchases.
  2. Platform-specific Dart APIs: store_kit_wrappers and billing_client_wrappers. These APIs expose platform-specific behavior and allow for more fine-tuned control when needed. However, if you use one of these APIs, your purchase-handling logic is significantly different for the different storefronts.

각 상점에서 인앱 구매를 구성하는 단계 목록은 예제 앱 README에서 확인할 수 있습니다.

각 상점에서 인앱 구매를 구성한 후, 플러그인을 사용할 수 있습니다. 두 가지 기본 옵션이 제공됩니다:

  • 일반적이고 플러터 다운 API: in_app_purchase. 이 API는 대부분의 로딩 및 구매 use case를 지원합니다.
  • 플랫폼별 Dart API: store_kit_wrappers와 billing_client_wrappers. 이 API는 플랫폼별 동작을 노출시키며, 필요한 경우 더 정교한 제어가 가능합니다. 그러나 이러한 API 중 하나를 사용하는 경우, 각각의 상점에서 구매 처리 로직이 크게 다를 수 있습니다.

 

See also the codelab for in-app purchases in Flutter for a detailed guide on adding in-app purchase support to a Flutter App.

 

플러터 앱에 인앱 구매 지원을 추가하는 상세 가이드는 플러터에서 인앱 구매를 위한 코드랩을 참조하세요.

 

 

Usage 

This section has examples of code for the following tasks:

Note: It is not necessary to depend on com.android.billingclient:billing in your own app's android/app/build.gradle file. If you choose to do so know that conflicts might occur.

 

이 섹션에는 다음 작업에 대한 코드 예제가 포함되어 있습니다:

  • 구매 업데이트 수신하기
  • 기본 상점에 연결하기
  • 판매용 제품 로드하기
  • 이전 구매 복원하기
  • 구매하기
  • 구매 완료하기
  • 기존 인앱 구독 업그레이드 또는 다운그레이드하기
  • 플랫폼별 제품 또는 구매 속성에 접근하기
  • 코드 교환 시트 표시하기 (iOS 14)

참고: 자체 앱의 android/app/build.gradle 파일에서 com.android.billingclient:billing에 의존할 필요는 없습니다. 이렇게 선택하는 경우 충돌이 발생할 수 있다는 점을 알고 계셔야 합니다.

 

Listening to purchase updates 

In your app's initState method, subscribe to any incoming purchases. These can propagate from either underlying store. You should always start listening to purchase update as early as possible to be able to catch all purchase updates, including the ones from the previous app session. To listen to the update:

 

구매 업데이트 수신하기

앱의 initState 메서드에서 수신되는 구매에 대해 구독(subscribe)합니다. 이는 기본 상점에서 전달될 수 있습니다. 가능한 한 빠르게 구매 업데이트를 수신할 수 있도록 항상 초기화(initState) 메서드에서 구매 업데이트 수신을 시작해야 합니다. 이전 앱 세션에서 발생한 구매 업데이트를 포함하여 모든 구매 업데이트를 포착하기 위해 초기화 단계에서 구매 업데이트를 수신할 수 있어야 합니다. 업데이트를 수신하기 위해 다음을 수행하세요:

 

class _MyAppState extends State<MyApp> {
  StreamSubscription<List<PurchaseDetails>> _subscription;

  @override
  void initState() {
    final Stream purchaseUpdated =
        InAppPurchase.instance.purchaseStream;
    _subscription = purchaseUpdated.listen((purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscription.cancel();
    }, onError: (error) {
      // handle error here.
    });
    super.initState();
  }

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

Here is an example of how to handle purchase updates:

void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
  purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
    if (purchaseDetails.status == PurchaseStatus.pending) {
      _showPendingUI();
    } else {
      if (purchaseDetails.status == PurchaseStatus.error) {
        _handleError(purchaseDetails.error!);
      } else if (purchaseDetails.status == PurchaseStatus.purchased ||
                 purchaseDetails.status == PurchaseStatus.restored) {
        bool valid = await _verifyPurchase(purchaseDetails);
        if (valid) {
          _deliverProduct(purchaseDetails);
        } else {
          _handleInvalidPurchase(purchaseDetails);
        }
      }
      if (purchaseDetails.pendingCompletePurchase) {
        await InAppPurchase.instance
            .completePurchase(purchaseDetails);
      }
    }
  });
}

 

 

 

 

---------------------------------------

 

Connecting to the underlying store 

 

final bool available = await InAppPurchase.instance.isAvailable();
if (!available) {
  // The store cannot be reached or accessed. Update the UI accordingly.
}

 

Loading products for sale

// Set literals require Dart 2.2. Alternatively, use
// `Set<String> _kIds = <String>['product1', 'product2'].toSet()`.
const Set<String> _kIds = <String>{'product1', 'product2'};
final ProductDetailsResponse response =
    await InAppPurchase.instance.queryProductDetails(_kIds);
if (response.notFoundIDs.isNotEmpty) {
  // Handle the error.
}
List<ProductDetails> products = response.productDetails;

 

Restoring previous purchases 

Restored purchases will be emitted on the InAppPurchase.purchaseStream, make sure to validate restored purchases following the best practices for each underlying store:

await InAppPurchase.instance.restorePurchases();

복원된 구매는 InAppPurchase.purchaseStream에서 발행됩니다. 각 하부 스토어에 대한 모범 사례를 따라 복원된 구매를 유효성 검사하도록 확인하십시오:

-App Store 구매 확인

-Google Play 구매 확인

 

 

Note that the App Store does not have any APIs for querying consumable products, and Google Play considers consumable products to no longer be owned once they're marked as consumed and fails to return them here. For restoring these across devices you'll need to persist them on your own server and query that as well.

 

App Store는 소비 가능한 제품을 쿼리하기 위한 API가 없으며, Google Play는 소비 가능한 제품을 "소비됨"으로 표시하면 더 이상 소유되지 않는 것으로 간주하고 이곳에서 반환하지 않습니다. 따라서 기기 간에 이러한 제품을 복원하기 위해서는 직접 서버에 저장하고 해당 서버를 쿼리해야 합니다.

 

Making a purchase 

Both underlying stores handle consumable and non-consumable products differently. If you're using InAppPurchase, you need to make a distinction here and call the right purchase method for each type.

 

기본 상점인 App Store와 Google Play는 소비 가능한 제품과 비소비 가능한 제품을 각각 다르게 처리합니다. InAppPurchase를 사용하는 경우, 여기에서 구매 유형에 따라 올바른 구매 메서드를 호출해야 합니다. 소비 가능한 제품과 비소비 가능한 제품을 구분하여 올바른 구매 메서드를 호출해야 합니다.

 

 

final ProductDetails productDetails = ... // Saved earlier from queryProductDetails().
final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetails);
if (_isConsumable(productDetails)) {
  InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
  InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
}
// From here the purchase flow will be handled by the underlying store.
// Updates will be delivered to the `InAppPurchase.instance.purchaseStream`.

 

Completing a purchase

The InAppPurchase.purchaseStream will send purchase updates after initiating the purchase flow using InAppPurchase.buyConsumable or InAppPurchase.buyNonConsumable. After verifying the purchase receipt and the delivering the content to the user it is important to call InAppPurchase.completePurchase to tell the underlying store that the purchase has been completed. Calling InAppPurchase.completePurchase will inform the underlying store that the app verified and processed the purchase and the store can proceed to finalize the transaction and bill the end user's payment account.

 

InAppPurchase.purchaseStream은 InAppPurchase.buyConsumable 또는 InAppPurchase.buyNonConsumable을 사용하여 구매 흐름을 시작한 후 구매 업데이트를 전송합니다. 구매 영수증을 확인하고 콘텐츠를 사용자에게 제공한 후에는 InAppPurchase.completePurchase를 호출하여 구매가 완료되었음을 기본 상점에 알려야 합니다. InAppPurchase.completePurchase를 호출하면 앱이 구매를 확인하고 처리했으며, 상점은 거래를 완료하고 최종 결제를 사용자의 결제 계정에 청구할 수 있도록 할 수 있습니다.

Warning: Failure to call InAppPurchase.completePurchase and get a successful response within 3 days of the purchase will result a refund.

경고: 구매 후 3일 이내에 InAppPurchase.completePurchase를 호출하고 성공적인 응답을 받지 않으면 환불이 이루어집니다.

Upgrading or downgrading an existing in-app subscription 

To upgrade/downgrade an existing in-app subscription in Google Play, you need to provide an instance of ChangeSubscriptionParam with the old PurchaseDetails that the user needs to migrate from, and an optional ProrationMode with the GooglePlayPurchaseParam object while callingInAppPurchase.buyNonConsumable.

The App Store does not require this because it provides a subscription grouping mechanism. Each subscription you offer must be assigned to a subscription group. Grouping related subscriptions together can help prevent users from accidentally purchasing multiple subscriptions. Refer to the Creating a Subscription Group section of Apple's subscription guide.

 

기존 인앱 구독을 업그레이드 또는 다운그레이드하기

Google Play에서 기존 인앱 구독을 업그레이드 또는 다운그레이드하려면 InAppPurchase.buyNonConsumable을 호출할 때 구매자가 마이그레이션해야 하는 이전 PurchaseDetails와 GooglePlayPurchaseParam 객체와 함께 ChangeSubscriptionParam의 인스턴스를 제공해야 합니다. 이와 달리 App Store에서는 이러한 작업이 필요하지 않으며 구독 그룹화 메커니즘이 제공됩니다. 제공하는 각 구독은 구독 그룹에 할당되어야 합니다. 관련된 구독을 그룹화하는 것은 사용자가 실수로 여러 구독을 구매하는 것을 방지하는 데 도움이 됩니다. Apple의 구독 가이드의 "구독 그룹 생성" 섹션을 참조하세요.

 

final PurchaseDetails oldPurchaseDetails = ...;
PurchaseParam purchaseParam = GooglePlayPurchaseParam(
    productDetails: productDetails,
    changeSubscriptionParam: ChangeSubscriptionParam(
        oldPurchaseDetails: oldPurchaseDetails,
        prorationMode: ProrationMode.immediateWithTimeProration));
InAppPurchase.instance
    .buyNonConsumable(purchaseParam: purchaseParam);

 

 

728x90