puma_pay

This package allows you app to manage in-app products and subscriptions.

Installation

  1. Add the following in you pubspec.yaml file under dependencies:

    puma_pay: ^2.0.1
    
  2. Add the Puma Pay middleware in your app (src/main.dart):

      final store = Store<MainState>(
        mainReducer,
        initialState: initialState,
        middleware: [
          PumaPayMiddleware<MainState>().createMiddleware(),
          mainMiddleware(),
        ],
      );
    
  3. Add the Puma Pay reducer in your app main reducer (src/redux/mainReducer.dart):

    final mainReducer = combineReducers<MainState>([
      kitReducers,
    ]);
    
    MainState kitReducers(MainState state, action) {
      return state.rebuild((b) => b
        ..pumaPayState.replace(pumaPayReducer(state.pumaPayState, action))
        ..accountKitState
            .replace(accountKitReducer(state.accountKitState, action)));
    }
    
  4. Add PumaPayState in your mainState (src/redux/mainState.dart):

     PumaPayState get pumaPayState;
    

    Don't forget to init pumaPayState in the mainState initial() function:

      static MainState initial() {
        final builder = MainStateBuilder();
        builder.pumaPayState = PumaPayState.initial().toBuilder();
    
        return new MainState((b) => b
          ..pumaPayState.replace(PumaPayState.initial())
          // other stuff
        );
      }
    

Getting Started

1. Init the package

On your src/app.dart you should init Puma Pay (using the StoreConnector onInitialBuild handler for example). You just need to dispatch the action InitPayAction.

The InitPayAction contains 4 parameters:

  1.  `productsID` List\<String\>: Id of your products (Make sure the IDS are the same on iOS and Android).
    
  2. verifyEndpoint String?: All restored products should be verified by Apple or Google because we cannot trust the restored product list provided by the package in-app-purchase. That is why your API should implement an endpoint that query /verifyReceipt for Apple or GET purchases.subscriptions for Google. Your endpoint must be a POST that accepts body like this:

    ```dart
    {
        "productID": STRING,
        "storeName": "android" || "ios"
        "data": STRING
      }
    ```
    
  3. appleAppStorePassword: String?: If you don't use an endpoint on your backend. You still could query the Apple /verifyReceipt even if it is not recommended. In that case, this field is required to perform the API call.

  4. appStoreSandbox: Bool?: If you don't use an endpoint on your backend and if you want to use the AppStore Sandbox.

As soon as you dispatch the action, the package will init and fetch all products provided on the productsID list on the different stores.

2. PumaPayState

Your MainState includes a PumaPayState that contains all you need:

4 Flags :

  • storeAvailable boolean: Whether the device store is available or not (AppStore or Google Play).
  • productsLoading boolean: Descibes if the app is still fetching the full products on the stores.
  • restoringPurchaseboolean: Descibes if the app is restoring all the purchases.
  • purchasePendingboolean: Descibes if a products is being purchased.

1 list :

  • productsProduct: list of fetched products.

A Product has the following shape:

  String get id;
  String get title;
  String get description;
  String get price;
  double get rawPrice;
  String get currencySymbol;
  String get currencyCode;
  BuiltList<PurchaseDetail> get purchaseDetails;

The PurchaseDetail is filled if you perform a restorePurchaseAction and will put all related items on this list. The object has this shape:

  String? get purchaseID;
  String get productID;
  String? get transactionDate;
  String get status;
  bool? get valid;

Note that the valid boolean is nullable. You should interpret its value like this:

  • true: Your backend has verified the integratity of the purchase. You can safely deliver the related content to the end user.
  • false: Your backend has rejected the integratity of the purchase (Could be expired or fraudulent). You should not deliver the content.
  • null: If you have a backend, your backend is verifying it (response expected soon). You should wait before deliver the content. If you DON'T have a backend, you can consider that the purchase is valid (the authenticity cannot be garanteed though).

3. Actions

  • GetProductsAction: Query the stores to get the Produc's object.
  • RestorePurchaseAction: Restore the products and query your backend if any to verify the integrity.
  • PurchaseProductAction: Purchase the product (productID needed).

4. Extensions

extensions/extProduct.dart: This extension allows you to know if a given product is valid right away:

import 'package:puma_pay/extensions/extProduct.dart';
...
final myProduct = store.state.pumaPayState.products.first;

if (myProduct.isValid()) {
	// do stuff
}

5. Debug Page

The package comes with a complete debug page: PumaPayDebugContainer. You can add this container to your app abd view the important part of PumaPayState.

Libraries

app_store_receipt
app_store_receipt_validation_result
appStore
appStoreReceiptTile
constants
extAppStoreReceipt
extProduct
extProductDetail
extPurchaseDetail
extPurchaseDetails
paymentService
product
puma_pay
puma_pay_state
pumaPayActions
PumaPayDebugTemplate
pumaPayMiddleware
pumaPayReducer
purchaseDetail
purchaseDetailTemplate
purchaseDetailTile
serializers
streamSubscriptionSingleton