npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-cafebazaar-billing

v1.0.3

Published

CafeBazaar In-App Billing for Expo (Android). Purchase products and subscriptions via Poolakey SDK.

Downloads

92

Readme

expo-cafebazaar-billing

Expo module for Cafe Bazaar In-App Billing on Android. Sell one-time products and subscriptions using the official Poolakey SDK.

References


Table of contents


Requirements

  • Android only — Cafe Bazaar is an Android app store; this module does nothing on iOS.
  • Cafe Bazaar app installed on the device (required for testing and production).
    Download from Cafe Bazaar
  • Expo project with expo-modules-core (Expo SDK 49+).

Installation

pnpm add expo-cafebazaar-billing
# or
npm install expo-cafebazaar-billing

No extra native configuration is required. The module registers the Poolakey dependency and the billing activity automatically.


Usage overview

You can use either:

  1. HooksuseBillingProduct and useBillingSubscription handle connection, status checks, and purchase/subscribe flows with a single call. Best for screens that show one product or subscription.
  2. Imperative API — Call connect(), then methods on the default export, then disconnect(). Use when you need full control (e.g. multiple products, custom flows).

Both rely on the same native module and Cafe Bazaar billing concepts.


Hooks (recommended)

The hooks connect to Bazaar, query purchase/subscription status on mount, and disconnect. They expose loading state, errors, and functions to run the purchase or subscription flow.

useBillingProduct

Use for one-time in-app products.

Signature

function useBillingProduct(
  productId: string,
  connectOptions?: UseBillingProductOptions
): UseBillingProductResult

Parameters

| Parameter | Type | Description | | ----------------- | ----------------------- | --------------------------------------------------------------------------- | | productId | string | Your in-app product SKU (e.g. from Cafe Bazaar developer panel). | | connectOptions | ConnectOptions (opt.) | Optional. { rsaPublicKey?: string }. Use RSA public key for verification. |

Return value

| Property | Type | Description | | ------------ | ----------------------------------------- | ---------------------------------------------------------- | | purchased | PurchaseResult \| null | Current purchase for this product, or null if not owned. | | loading | boolean | true while checking status on mount or after refresh(). | | purchase | (options?) => Promise<PurchaseResult \| null> | Starts the purchase flow. Resolves with result or null on failure. | | purchasing | boolean | true while the purchase flow is in progress. | | error | string \| null | Last error message from purchase or refresh. | | refresh | () => Promise<void> | Re-queries purchase status from Bazaar. | | isAvailable| boolean | false when not Android or when the native module is not linked. |

Flowcharts: HOOK_FLOWS.md.

Example

import { useMemo } from "react";
import { useBillingProduct } from "expo-cafebazaar-billing";

function ProductScreen() {
  const connectOptions = useMemo(
    () => ({ rsaPublicKey: process.env.EXPO_PUBLIC_CAFEBAZAAR_RSA_KEY }),
    []
  );
  const product = useBillingProduct("your_product_sku", connectOptions);

  if (!product.isAvailable) {
    return <Text>Billing is not available on this device.</Text>;
  }

  if (product.loading) {
    return <Text>Checking purchase status…</Text>;
  }

  return (
    <>
      {product.purchased ? (
        <Text>Already purchased</Text>
      ) : (
        <Button
          title={product.purchasing ? "Processing…" : "Buy"}
          onPress={() => product.purchase()}
          disabled={product.purchasing}
        />
      )}
      {product.error && <Text style={{ color: "red" }}>{product.error}</Text>}
    </>
  );
}

Behavior

  • On mount, the hook connects, calls queryPurchaseProduct(productId), then disconnects. It sets purchased and turns off loading when done.
  • Call purchase() to run the purchase flow; on success, purchased is updated.
  • Call refresh() to re-query status (e.g. after a purchase made elsewhere).

useBillingSubscription

Use for subscriptions.

Signature

function useBillingSubscription(
  productId: string,
  connectOptions?: UseBillingSubscriptionOptions
): UseBillingSubscriptionResult

Parameters

| Parameter | Type | Description | | ----------------- | --------------------------------- | --------------------------------------------------------------------------- | | productId | string | Your subscription product SKU. See Cafe Bazaar subscriptions. | | connectOptions | ConnectOptions (opt.) | Optional. { rsaPublicKey?: string }. |

Return value

| Property | Type | Description | | -------------------- | ----------------------------------------- | --------------------------------------------------------------------------- | | activeSubscription | PurchaseResult \| null | Active subscription for this product, or null if none or expired. | | loading | boolean | true while checking status on mount or after refresh(). | | subscribe | (options?) => Promise<PurchaseResult \| null> | Starts the subscription flow. Resolves with result or null on failure. | | subscribing | boolean | true while the subscription flow is in progress. | | error | string \| null | Last error message from subscribe or refresh. | | refresh | () => Promise<void> | Re-queries subscription status from Bazaar. | | isAvailable | boolean | Same as in useBillingProduct. |

Flowcharts: HOOK_FLOWS.md.

Example

import { useMemo } from "react";
import { useBillingSubscription } from "expo-cafebazaar-billing";

function SubscriptionScreen() {
  const connectOptions = useMemo(
    () => ({ rsaPublicKey: process.env.EXPO_PUBLIC_CAFEBAZAAR_RSA_KEY }),
    []
  );
  const subscription = useBillingSubscription("your_subscription_sku", connectOptions);

  if (!subscription.isAvailable || subscription.loading) {
    return <Text>Loading…</Text>;
  }

  return (
    <>
      {subscription.activeSubscription ? (
        <Text>Subscription active</Text>
      ) : (
        <Button
          title={subscription.subscribing ? "Processing…" : "Subscribe"}
          onPress={() => subscription.subscribe()}
          disabled={subscription.subscribing}
        />
      )}
      {subscription.error && (
        <Text style={{ color: "red" }}>{subscription.error}</Text>
      )}
    </>
  );
}

Behavior

  • On mount, connects, calls querySubscribeProduct(productId), then disconnects. Sets activeSubscription and clears loading when done.
  • Call subscribe() to run the subscription flow; on success, activeSubscription is updated.
  • Call refresh() to re-query subscription status.

Imperative API

For full control, use the default export and call connect() before any other method, then disconnect() when finished. See Cafe Bazaar – In-App Billing for concepts.

Connect (required first)

import CafeBazaarBilling from "expo-cafebazaar-billing";

await CafeBazaarBilling.connect({
  rsaPublicKey: "YOUR_RSA_PUBLIC_KEY", // optional; recommended for production
});

Purchase a product

const result = await CafeBazaarBilling.purchaseProduct("product_sku", {
  developerPayload: "optional-payload",
  dynamicPriceToken: undefined, // optional; for dynamic pricing
});
// result: PurchaseResult

Subscribe

const result = await CafeBazaarBilling.subscribeProduct("subscription_sku", {
  developerPayload: "optional-payload",
});

Consume a consumable

After granting the item to the user:

await CafeBazaarBilling.consumePurchase(purchaseToken);

Query status

const allPurchases = await CafeBazaarBilling.getPurchasedProducts();
const allSubs = await CafeBazaarBilling.getSubscribedProducts();
const one = await CafeBazaarBilling.queryPurchaseProduct("product_sku");
const sub = await CafeBazaarBilling.querySubscribeProduct("subscription_sku");

SKU details

const inApp = await CafeBazaarBilling.getInAppSkuDetails(["sku1", "sku2"]);
const subs = await CafeBazaarBilling.getSubscriptionSkuDetails(["sub_sku1"]);

Disconnect

await CafeBazaarBilling.disconnect();

Call disconnect() once per connect() when you are done with billing (e.g. user leaves the screen).


API reference

| Method | Description | Reference | |--------|-------------|-----------| | connect(options?) | Connect to Bazaar billing. Call before any other method. | Cafe Bazaar – In-App Billing | | disconnect() | Disconnect from Bazaar. | — | | purchaseProduct(productId, options?) | Start in-app product purchase flow. | Purchase flow | | subscribeProduct(productId, options?) | Start subscription purchase flow. | Subscriptions | | consumePurchase(purchaseToken) | Consume a consumable purchase. | Consumables | | getPurchasedProducts() | List all purchased in-app products. | — | | getSubscribedProducts() | List active subscriptions. | — | | queryPurchaseProduct(productId) | Get purchase for one product ID. | — | | querySubscribeProduct(productId) | Get subscription for one product ID. | — | | getInAppSkuDetails(productIds) | Get in-app product SKU details (price, title, etc.). | — | | getSubscriptionSkuDetails(productIds) | Get subscription SKU details. | — |


Types

Defined in the package; import when needed:

import type {
  ConnectOptions,
  PurchaseResult,
  PurchaseOptions,
  SkuDetails,
} from "expo-cafebazaar-billing";

| Type | Description | Reference | |------|-------------|-----------| | ConnectOptions | { rsaPublicKey?: string } — options for connect(). | Verification | | PurchaseResult | orderId, packageName, productId, purchaseTime, purchaseState, developerPayload, purchaseToken — result of a successful purchase or subscription. | Cafe Bazaar purchase object | | PurchaseOptions | { developerPayload?: string; dynamicPriceToken?: string } — options for purchaseProduct() / subscribeProduct(). | — | | SkuDetails | sku, type, price, title, description — product/subscription info from Bazaar. | — |


Errors

The module throws Expo-style errors (with code and message). Common codes:

| Code | Meaning | What to do | |------|---------|------------| | ERR_NOT_CONNECTED | Billing not connected. | Call connect() first. | | ERR_NO_ACTIVITY | No active activity (e.g. app in background). | Retry when the app is in foreground. | | ERR_PURCHASE / ERR_SUBSCRIBE | User cancelled or Bazaar returned an error. | Show message to the user; optionally call refresh(). | | ERR_CONNECT | Connection to Bazaar failed. | Ensure Cafe Bazaar app is installed and up to date. | | ERR_DISCONNECT | Billing was disconnected by Bazaar. | Call connect() again if needed. |

See Expo modules error handling for the error shape.


License

MIT