Make and restore purchases

Build a custom purchase UI with store-backed products.

Hosted paywalls already handle purchase and restore actions. Use the APIs on this page when you build your own purchase screen.

Load products

The SDK returns the store-backed products configured in your project, with prices already formatted for the customer's storefront:

const { data: products, error, isLoading } = voidhash.useProducts();
const monthly = products.get("monthly");

data is a Map keyed by the project's product slugs. monthly is null when the store does not return that product. This commonly means the product is not available in the current sandbox account, country, app version, or provider configuration.

Render the store-provided displayPrice:

if (!monthly) return null;

return <Text>{monthly.displayPrice}</Text>;

Start a purchase

The SDK opens the native store flow, sends the transaction to Voidhash for server validation, and refreshes the current person after success — so entitlement grants update without extra work.

purchase() resolves to a better-result Result and never rejects — cancellation is an Ok outcome, not an error:

const { purchase, error, isLoading } = voidhash.usePurchase({
  onSuccess: () => {
    navigation.goBack();
  },
  onError: (purchaseError) => {
    console.warn("Purchase failed", purchaseError);
  },
});

<Button
  disabled={!monthly || isLoading}
  title={isLoading ? "Purchasing…" : `Continue for ${monthly?.displayPrice ?? ""}`}
  onPress={async () => {
    if (!monthly) return;

    const result = await purchase(monthly);
    if (result.isOk() && result.value.status === "cancelled") {
      // The customer dismissed the store sheet. Expected — no error.
    }
  }}
/>

The Ok outcome is one of:

StatusMeaning
completedValidated by Voidhash; the person snapshot has been refreshed.
cancelledThe customer dismissed the native store sheet.
pendingThe purchase needs external action; access arrives once it reconciles.
disabledThe client was created with enabled: false.

The Err channel carries a coded VoidhashError — typically FAILED_TO_PURCHASE, or READ_ONLY_PURCHASE_NOT_ALLOWED in observer mode.

Grant access from Voidhash state

A successful store dialog is not the entitlement check. Unlock the feature only after the refreshed person contains an active grant.

Restore purchases

Apps should expose a visible restore action. Restore reconciles store transactions with Voidhash and refreshes the current person. It does not create a second purchase:

await voidhash.client.restorePurchases();

Common failure cases

  • Product is missing — verify the store product mapping and the current sandbox environment.
  • Purchase already in progress — disable repeated taps while a purchase is running.
  • Customer cancels — handle the cancelled outcome as an expected result, not an app crash.
  • Purchase succeeds but access is absent — retry the person fetch and verify the product grants the expected perk in Studio.
  • Restore finds nothing — confirm the device uses the same App Store or Google Play account as the original purchase.

Next steps