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:
- Choose events from Your account.
- Under the payload style, choose Thin.
- Select these events:
v1.checkout.session.completedv1.checkout.session.async_payment_succeededv1.invoice.paidv1.invoice.payment_failedv1.customer.subscription.updatedv1.customer.subscription.deletedv1.charge.refundedv1.charge.refund.updatedv1.charge.dispute.closed
- 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:
- Checkout Session:
client_reference_id, thenmetadata.voidhash_distinct_id. When you setclient_reference_id, it must be the Voidhash distinct ID, because it wins. For a subscription, also setsubscription_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 aclient_reference_idwith:, so the web SDK gives an anonymous ID asvh_anon_<uuid>there, and Voidhash reads it asvh:anon:<uuid>. - Subscription:
metadata.voidhash_distinct_idon the subscription. Stripe copies it to each invoice when it issues the invoice, so it only applies to invoices issued after you set it. - Customer:
metadata.voidhash_distinct_idon 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:
- Set
metadata.voidhash_distinct_idon each existing Stripe customer, for example with a script that maps your users to their Stripe customer IDs. - Check that your API key can read Customers.
- 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.