Consumable Products

Set up consumable products for Superwall paywalls on Android.

Use consumable products when a purchase should grant a quantity that can be used up, such as credits, coins, boosts, or tokens.

This guide assumes purchases are made from Superwall paywalls and that you are not using a PurchaseController.

Consumable products are one-time purchases that users can buy repeatedly, such as credits, tokens, boosts, or packs. Non-consumable products are also one-time purchases, but they grant permanent access, such as a lifetime unlock.

Superwall uses entitlements to decide whether a user has ongoing access. Because consumables are meant to be used up, they should usually not grant entitlements. Your app should listen for the purchase, grant the consumable benefit in your own system, and then consume the Google Play purchase token so the item can be purchased again.

Dashboard Setup

  1. Create the product as an in-app product in Google Play Console.
  2. Add the product in Superwall from Products.
  3. Use the Google Play product ID.
  4. Set Period to None (Lifetime / Consumable).
  5. Leave Entitlements empty.
  6. Add the product to any paywall that should sell it.

Do not attach an entitlement to a consumable unless the purchase should also unlock ongoing access. If a consumable has no entitlement, buying it does not make the user's subscription status active.

Consume The Purchase Token

Google Play in-app products must be consumed after you grant the benefit. If you do not consume the purchase token, the user may not be able to buy that same consumable again.

Use the purchaseToken from the TransactionComplete event, grant the benefit, then call Superwall.instance.consume(purchaseToken).

Superwall.instance.consume(purchaseToken) is available in Android SDK 2.6.2 and later.

import androidx.lifecycle.lifecycleScope
import com.superwall.sdk.Superwall
import com.superwall.sdk.analytics.superwall.SuperwallEvent
import com.superwall.sdk.analytics.superwall.SuperwallEventInfo
import com.superwall.sdk.delegate.SuperwallDelegate
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity(), SuperwallDelegate {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Superwall.instance.delegate = this
  }

  override fun handleSuperwallEvent(eventInfo: SuperwallEventInfo) {
    val event = eventInfo.event
    if (event !is SuperwallEvent.TransactionComplete) {
      return
    }

    if (event.product.productIdentifier != "coins_100") {
      return
    }

    val purchaseToken = event.transaction?.purchaseToken ?: return

    lifecycleScope.launch {
      ConsumablesService.grantCoins(
        count = 100,
        productId = event.product.productIdentifier,
        purchaseToken = purchaseToken,
      )

      Superwall.instance.consume(purchaseToken)
        .onFailure { error ->
          // Retry consumption after confirming the benefit was granted.
          println("Failed to consume purchase: ${error.message}")
        }
    }
  }
}
import com.superwall.sdk.Superwall;
import com.superwall.sdk.analytics.superwall.SuperwallEvent;
import com.superwall.sdk.analytics.superwall.SuperwallEventInfo;
import com.superwall.sdk.delegate.SuperwallDelegateJava;
import com.superwall.sdk.store.abstractions.transactions.StoreTransactionType;
import kotlin.Unit;

public class MainActivity extends AppCompatActivity implements SuperwallDelegateJava {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Superwall.getInstance().setJavaDelegate(this);
  }

  @Override
  public void handleSuperwallEvent(SuperwallEventInfo eventInfo) {
    if (!(eventInfo.getEvent() instanceof SuperwallEvent.TransactionComplete)) {
      return;
    }

    SuperwallEvent.TransactionComplete event =
      (SuperwallEvent.TransactionComplete) eventInfo.getEvent();

    if (!event.getProduct().getProductIdentifier().equals("coins_100")) {
      return;
    }

    StoreTransactionType transaction = event.getTransaction();
    if (transaction == null) {
      return;
    }

    String purchaseToken = transaction.getPurchaseToken();

    ConsumablesService.grantCoins(100, event.getProduct().getProductIdentifier(), purchaseToken);

    Superwall.getInstance().consume(purchaseToken, result -> {
      // Check the Result in your app and retry if consumption fails.
      return Unit.INSTANCE;
    });
  }
}

Grant the benefit before consuming the token. If your grant call fails, leave the purchase unconsumed and retry after your app confirms the user received the benefit.

Read Purchase History

Consumable and non-consumable purchases appear in customerInfo.nonSubscriptions. Use isConsumable to distinguish consumables from lifetime purchases.

val customerInfo = Superwall.instance.getCustomerInfo()

val consumables = customerInfo.nonSubscriptions.filter { it.isConsumable }
for (purchase in consumables) {
  println("Consumable purchased: ${purchase.productId}")
}
CustomerInfo customerInfo = Superwall.getInstance().getCustomerInfo();

for (NonSubscriptionTransaction purchase : customerInfo.getNonSubscriptions()) {
  if (purchase.isConsumable()) {
    System.out.println("Consumable purchased: " + purchase.getProductId());
  }
}

How is this guide?

On this page