VoidhashDocs
DocumentationAPI reference
Dashboard
Get started
Introduction
Quickstart
Setup
Setup overview
Create a project
Products and perks
Paywall locations
CLI
Paywall components
Components
Develop a component
Editor panels
Component definition
Publishing
Installing and capabilities
SDKs
Swift
Kotlin
React Native
Web
Store setup
Connect the App Store
Connect Google Play
Connect Stripe
API libraries
Node.js
PHP
Rust
Go
Resources
MCP server
Meta ads
Migrate in observer mode
Delete a person's data

Connect Stripe

Connect your Stripe account to Voidhash so web purchases grant access and appear in revenue analytics.

Connecting Stripe lets Voidhash record purchases your customers make through Stripe Checkout and Stripe Billing, keep subscription state current, and grant the matching access. You need the Stripe products and prices you sell to exist first.

Voidhash keeps live and test data apart. Connect live mode for production revenue, and test mode or a sandbox for development; each mode has its own key and event destination.

Create an API key

In the Stripe Dashboard, open Developers → API keys and create a restricted key. Voidhash only reads from Stripe, so grant Read access to:

  • Balance, for the Stripe fee of each payment
  • Charges, which also covers refunds
  • Checkout Sessions
  • Customers, for the Voidhash distinct ID in a customer's metadata
  • Disputes
  • Events, for the details of each event Voidhash receives
  • Invoices, which also covers invoice payments
  • Payment Intents
  • Subscriptions

A secret key also works, but a restricted key limits what Voidhash can do.

Add the provider in Voidhash

In Studio, open Settings → Payment providers → Stripe. Enter your Stripe account ID (acct_…) and the API key for each mode, and copy the Webhook endpoint URL.

Add an event destination

In the Stripe Dashboard, open Developers → Webhooks and add a destination:

  1. Choose events from Your account.
  2. Under the payload style, choose Thin.
  3. Select these events:
    • v1.checkout.session.completed
    • v1.checkout.session.async_payment_succeeded
    • v1.invoice.paid
    • v1.invoice.payment_failed
    • v1.customer.subscription.updated
    • v1.customer.subscription.deleted
    • v1.charge.refunded
    • v1.charge.refund.updated
    • v1.charge.dispute.closed
  4. Choose Webhook endpoint and paste the URL from Studio.

Copy the destination's signing secret (whsec_…) into Studio for the same mode, then save and enable the provider. Repeat for the other mode.

Thin events carry no object data, so they don't depend on your account's API version. Voidhash reads each object from Stripe when the event arrives.

Connect products

Open each Voidhash product and add the Stripe product ID (prod_…) and price ID (price_…) it corresponds to. Purchases of a price that isn't connected yet are held and applied once you connect it.

Import your Stripe history

Webhooks only report what happens after you connect Stripe. To bring in the purchases your customers made before that, open the provider's Import history tab in Studio and start an import for live or test mode. It uses the saved API key of that mode. A restricted key also needs read access to Invoices, Checkout Sessions, Refunds, Disputes, and Subscriptions; the import checks this first and stops right away, naming what the key can't read.

The import reads what Stripe still holds for everything created before the import started:

  • paid subscription invoices, as subscription purchases and renewals
  • paid one-time Checkout Sessions, and subscription Checkout Sessions, which link their customer to the buyer they name
  • refunds and lost disputes
  • cancellations, ended subscriptions, and subscriptions whose payment is failing now

Each is recorded as of when it happened, the same way its webhook would have recorded it, so access and revenue analytics reflect your history. Amounts in other currencies are converted to USD at the exchange rate of the day they were paid, for payments since 2010. Changes Stripe keeps no record of, such as a cancellation that was later undone, aren't imported. Stripe records no closing date for a dispute, so a lost dispute is dated when it was opened.

Things to know:

  • Connect your Stripe prices to products first. Purchases of a price that isn't connected wait and are imported once you connect it; the tab lists those prices.
  • Imported events don't send your project's webhooks, since your systems already handled them when they happened.
  • The import runs in the background, and you can leave the page. One import per mode runs at a time.
  • Running it again is safe: anything already recorded, by a webhook or an earlier import, is skipped. Run it again if an import failed or you canceled it.
  • An object the import can't read or record is counted as failed, and the import carries on. If Stripe is unavailable or rate limits the import, it waits and resumes.
  • Past purchases rarely carry a Voidhash distinct ID. The import also reads it from the Stripe customer's metadata; without one, a purchase is attributed to a person tied to the Stripe customer, as described below.

Identify the buyer

Tell Voidhash which of your users made a purchase by passing their Voidhash distinct ID. Voidhash looks for it in this order and uses the first it finds:

  1. Checkout Session: client_reference_id, then metadata.voidhash_distinct_id. When you set client_reference_id, it must be the Voidhash distinct ID, because it wins. For a subscription, also set subscription_data.metadata.voidhash_distinct_id: its payments are recorded from its invoices, which carry the subscription's metadata. A subscription session with an ID of its own links the Stripe customer to that person when it completes, if the customer belongs to nobody yet, or to a stand-in an earlier invoice created: a named user takes over the stand-in, and an anonymous visitor receives the stand-in's subscriptions with their access, while the payments already recorded stay with the stand-in. A customer that already belongs to a person stays with them. Payment Links and the pricing table drop a client_reference_id with :, so the web SDK gives an anonymous ID as vh_anon_<uuid> there, and Voidhash reads it as vh:anon:<uuid>.
  2. Subscription: metadata.voidhash_distinct_id on the subscription. Stripe copies it to each invoice when it issues the invoice, so it only applies to invoices issued after you set it.
  3. Customer: metadata.voidhash_distinct_id on the Stripe customer. It applies to every purchase of that customer, including past ones.

If you create subscriptions through the API for a signed-in user, set the distinct ID on the customer. It's the one place you can add it later, and it covers every subscription and invoice of the customer:

await stripe.customers.update(customerId, {
  metadata: { voidhash_distinct_id: user.voidhashDistinctId },
});

Without a distinct ID anywhere, Voidhash attributes the purchase to a person tied to the Stripe customer, and later purchases by that customer go to the same person. Once the customer names a user, that person is merged into the user: their purchases, subscriptions, and access become the user's, alongside anything the user already bought. Voidhash reads the customer when an event for it arrives, so a change to its metadata applies from the customer's next event.

Link existing customers

To attribute purchases that are already recorded:

  1. Set metadata.voidhash_distinct_id on each existing Stripe customer, for example with a script that maps your users to their Stripe customer IDs.
  2. Check that your API key can read Customers.
  3. Import your history again. Purchases already recorded aren't counted twice; their customers are linked to the users their metadata names.

Customers without a recorded purchase need nothing more: their next purchase uses the metadata.

A one-time Checkout Session creates no Stripe customer unless you set customer_creation to always. Without a distinct ID, such a purchase is still recorded, but for an anonymous person of its own that no user can be matched to later, so always pass the distinct ID.

What a purchase grants

  • A subscription grants one Voidhash product: its first price that is connected to a product. Other prices on the subscription, such as add-ons, count toward its revenue but grant nothing.
  • A one-time Checkout Session grants every connected product among its line items, each as a purchase of its own. A product's revenue is what its line items cost, with quantity, discounts, and tax. Line items that aren't connected, such as shipping or fees, count toward the revenue of the first connected product. A session that is free with a discount grants its products too.
  • When a subscription switches to another connected price, the new product is granted as soon as Stripe applies the switch, whether Stripe invoices it right away or with the next renewal. A switch scheduled for later, for example with a subscription schedule, is granted when it takes effect. An invoice drafted before the switch and paid after it, for example after Stripe retried its payment, renews the subscription on the new product.

How Stripe events affect access

  • A paid subscription invoice starts or renews the subscription.
  • A failed renewal payment keeps access until Stripe's next retry. If the last retry fails, access ends then, whether Stripe cancels the subscription, marks it unpaid, or leaves it past due. If the subscription is still active and the invoice is paid later, access returns.
  • Canceling at the end of the period turns off auto-renewal; access lasts until the period ends, even when the customer cancels while that period's payment is still pending. Canceling immediately ends access.
  • Fully refunding a one-time payment removes the access of every product it bought. Refunding a subscription payment records the refund but does not end the subscription; cancel the subscription in Stripe to end access.
  • Partial refunds and lost disputes are deducted from revenue. A partial refund of a payment for several products is shared among them in proportion to what each cost.
PreviousConnect Google PlayNextNode.js library

On this page

Create an API keyAdd the provider in VoidhashAdd an event destinationConnect productsImport your Stripe historyIdentify the buyerLink existing customersWhat a purchase grantsHow Stripe events affect access