in_app_purchase_tizen 0.1.1 copy "in_app_purchase_tizen: ^0.1.1" to clipboard
in_app_purchase_tizen: ^0.1.1 copied to clipboard

Tizen implementation of the in_app_purchase plugin for Samsung Smart TV.

example/lib/main.dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_tizen/in_app_purchase_tizen.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(_MyApp());
}

// To try without auto-consume, change `true` to `false` here.
const bool _kAutoConsume = true;

const String _kAppId = '3201504002021';
const int _kPageSize = 20;
const int _kPageNum = 1;
// Do not expose your DPI security key. You can use a key management server to retrieve it for greater security.
const String _kSecurityKey = 'YxE757K+aDWHJXa0QMnL5AJmItefoEizvv8L7WPJAMs=';

class _MyApp extends StatefulWidget {
  @override
  State<_MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<_MyApp> {
  final InAppPurchase _inAppPurchase = InAppPurchase.instance;
  late StreamSubscription<List<PurchaseDetails>> _subscription;
  List<String> _notFoundIds = <String>[];
  List<ProductDetails> _products = <ProductDetails>[];
  List<PurchaseDetails> _purchases = <PurchaseDetails>[];
  bool _isAvailable = false;
  bool _purchasePending = false;
  bool _loading = true;
  String? _queryProductError;

  @override
  void initState() {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subscription = purchaseUpdated.listen(
      (List<PurchaseDetails> purchaseDetailsList) {
        _listenToPurchaseUpdated(purchaseDetailsList);
      },
      onDone: () {
        _subscription.cancel();
      },
      onError: (Object error) {
        // handle error here.
      },
    );
    initStoreInfo();
    super.initState();
  }

  Future<void> initStoreInfo() async {
    // Tizen specific API:
    // You need to set necessary parameters before calling any plugin API.
    final InAppPurchaseTizenPlatformAddition platformAddition =
        _inAppPurchase
            .getPlatformAddition<InAppPurchaseTizenPlatformAddition>();
    platformAddition.setRequestParameters(
      appId: _kAppId,
      pageSize: _kPageSize,
      pageNum: _kPageNum,
      securityKey: _kSecurityKey,
    );

    final bool isAvailable = await _inAppPurchase.isAvailable();
    if (!isAvailable) {
      setState(() {
        _isAvailable = isAvailable;
        _products = <ProductDetails>[];
        _purchases = <PurchaseDetails>[];
        _notFoundIds = <String>[];
        _purchasePending = false;
        _loading = false;
      });
      return;
    }

    // The `identifiers` argument is not used on Tizen.
    // Use `InAppPurchaseTizenPlatformAddition.setRequestParameters` instead.
    final ProductDetailsResponse productDetailResponse = await _inAppPurchase
        .queryProductDetails(<String>{});
    if (productDetailResponse.error != null) {
      setState(() {
        _queryProductError = productDetailResponse.error!.message;
        _isAvailable = isAvailable;
        _products = productDetailResponse.productDetails;
        _purchases = <PurchaseDetails>[];
        _notFoundIds = productDetailResponse.notFoundIDs;
        _purchasePending = false;
        _loading = false;
      });
      return;
    }

    if (productDetailResponse.productDetails.isEmpty) {
      setState(() {
        _queryProductError = null;
        _isAvailable = isAvailable;
        _products = productDetailResponse.productDetails;
        _purchases = <PurchaseDetails>[];
        _notFoundIds = productDetailResponse.notFoundIDs;
        _purchasePending = false;
        _loading = false;
      });
      return;
    }

    setState(() {
      _isAvailable = isAvailable;
      _products = productDetailResponse.productDetails;
      _notFoundIds = productDetailResponse.notFoundIDs;
      _purchasePending = false;
      _loading = false;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    final List<Widget> stack = <Widget>[];
    if (_queryProductError == null) {
      stack.add(
        ListView(
          children: <Widget>[
            _buildConnectionCheckTile(),
            _buildProductList(),
            _buildRestoreButton(),
          ],
        ),
      );
    } else {
      stack.add(Center(child: Text(_queryProductError!)));
    }
    if (_purchasePending) {
      stack.add(
        const Stack(
          children: <Widget>[
            Opacity(
              opacity: 0.3,
              child: ModalBarrier(dismissible: false, color: Colors.grey),
            ),
            Center(child: CircularProgressIndicator()),
          ],
        ),
      );
    }

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('IAP Example')),
        body: Stack(children: stack),
      ),
    );
  }

  Card _buildConnectionCheckTile() {
    if (_loading) {
      return const Card(child: ListTile(title: Text('Trying to connect...')));
    }
    final Widget storeHeader = ListTile(
      leading: Icon(
        _isAvailable ? Icons.check : Icons.block,
        color:
            _isAvailable ? Colors.green : ThemeData.light().colorScheme.error,
      ),
      title: Text(
        'The store is ${_isAvailable ? 'available' : 'unavailable'}.',
      ),
    );
    final List<Widget> children = <Widget>[storeHeader];

    if (!_isAvailable) {
      children.addAll(<Widget>[
        const Divider(),
        ListTile(
          title: Text(
            'Not connected',
            style: TextStyle(color: ThemeData.light().colorScheme.error),
          ),
          subtitle: const Text(
            'Unable to connect to the payments processor. Are you signed in with your Samsung account on this device?',
          ),
        ),
      ]);
    }
    return Card(child: Column(children: children));
  }

  Card _buildProductList() {
    if (_loading) {
      return const Card(
        child: ListTile(
          leading: CircularProgressIndicator(),
          title: Text('Fetching products...'),
        ),
      );
    }
    if (!_isAvailable) {
      return const Card();
    }
    const ListTile productHeader = ListTile(title: Text('Products for Sale'));
    final List<ListTile> productList = <ListTile>[];
    if (_notFoundIds.isNotEmpty) {
      productList.add(
        ListTile(
          title: Text(
            '[${_notFoundIds.join(", ")}] not found',
            style: TextStyle(color: ThemeData.light().colorScheme.error),
          ),
          subtitle: const Text(
            'This app needs special configuration to run. Please see README.md for instructions.',
          ),
        ),
      );
    }

    productList.addAll(
      _products.map((ProductDetails productDetails) {
        return ListTile(
          title: Text(productDetails.title),
          subtitle: Text(productDetails.description),
          trailing: TextButton(
            style: TextButton.styleFrom(
              backgroundColor: Colors.green[800],
              foregroundColor: Colors.white,
            ),
            onPressed: () {
              final PurchaseParam purchaseParam = PurchaseParam(
                productDetails: productDetails,
              );

              if (productDetails is SamsungCheckoutProductDetails) {
                if (productDetails.itemDetails.itemType ==
                    ItemType.consumable) {
                  _inAppPurchase.buyConsumable(
                    purchaseParam: purchaseParam,
                    // ignore: avoid_redundant_argument_values
                    autoConsume: _kAutoConsume,
                  );
                } else {
                  _inAppPurchase.buyNonConsumable(purchaseParam: purchaseParam);
                }
              }
            },
            child: Text(productDetails.price),
          ),
        );
      }),
    );

    return Card(
      child: Column(
        children: <Widget>[productHeader, const Divider()] + productList,
      ),
    );
  }

  Widget _buildRestoreButton() {
    if (_loading) {
      return Container();
    }

    return Padding(
      padding: const EdgeInsets.all(4.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          TextButton(
            style: TextButton.styleFrom(
              backgroundColor: Theme.of(context).colorScheme.primary,
              foregroundColor: Colors.white,
            ),
            onPressed: () => _inAppPurchase.restorePurchases(),
            child: const Text('Restore purchases'),
          ),
        ],
      ),
    );
  }

  void showPendingUI() {
    setState(() {
      _purchasePending = true;
    });
  }

  Future<void> deliverProduct(PurchaseDetails purchaseDetails) async {
    // IMPORTANT!! Always verify purchase details before delivering the product.
    setState(() {
      _purchases.add(purchaseDetails);
      _purchasePending = false;
    });
  }

  void handleError(IAPError error) {
    setState(() {
      _purchasePending = false;
    });
  }

  Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) {
    // IMPORTANT!! Always verify a purchase before delivering the product.

    // Tizen specific verify purchase:
    // If `PurchaseDetails.status` is `purchased`, need to verify purchase.
    final InAppPurchaseTizenPlatformAddition platformAddition =
        _inAppPurchase
            .getPlatformAddition<InAppPurchaseTizenPlatformAddition>();
    return platformAddition.verifyPurchase(purchaseDetails: purchaseDetails);
  }

  void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
    // handle invalid purchase here if  _verifyPurchase` failed.
  }

  Future<void> _listenToPurchaseUpdated(
    List<PurchaseDetails> purchaseDetailsList,
  ) async {
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      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) {
          final bool valid = await _verifyPurchase(purchaseDetails);
          if (valid) {
            unawaited(deliverProduct(purchaseDetails));
          } else {
            _handleInvalidPurchase(purchaseDetails);
            return;
          }
        }
      }
    }
  }
}
copied to clipboard
1
likes
120
points
126
downloads

Publisher

verified publishertizen.org

Weekly Downloads

2024.09.13 - 2025.03.28

Tizen implementation of the in_app_purchase plugin for Samsung Smart TV.

Homepage
Repository (GitHub)

Documentation

API reference

License

unknown (license)

Dependencies

crypto, flutter, in_app_purchase_platform_interface

More

Packages that depend on in_app_purchase_tizen