Documentation
Set up Restrict With Stripe
Everything you need to install the EmDash plugin, connect Stripe, and start selling access. Using WordPress? Jump to the WordPress plugin section.
Overview
Restrict With Stripe sells access to EmDash posts and pages with Stripe. You restrict an entry to one or more Stripe products. Visitors buy through Stripe Checkout and are logged in when they return; returning customers log in with an emailed link.
Version 0.2.0 targets EmDash 1.x (tested on 1.3.0) as a native plugin. It uses the same Stripe Connect server and the same access rule as the WordPress plugin.
Requirements
- EmDash
>=1.0.0with React (@astrojs/react), which every EmDash admin already uses. - An encryption key. Set
EMDASH_ENCRYPTION_KEYin the site's environment. The Stripe key is stored as an encrypted plugin setting, and the plugin refuses to store a key it cannot encrypt. Generate one withemdash secrets generate. - An absolute site URL. Set
siteUrlin theemdash()integration, theEMDASH_SITE_URLenvironment variable, or Settings > General > Site URL. Use an HTTPS origin; plain HTTP works only for localhost. - An email provider for login links. See Email provider below.
- A theme layout that renders
<EmDashHead>and<EmDashBodyEnd>and passescontent: { collection, id, slug }tocreatePublicPageContext()on entry pages. The EmDash blog template already does this.
Install
The package will be published to npm as emdash-restrict-with-stripe. Until then, build it from GitHub. Version 0.2.0 lands on the repository's default branch when pull request #2 merges.
git clone https://github.com/strangerstudios/emdash-restrict-with-stripe.git
cd emdash-restrict-with-stripe
npm install && npm pack
# then, in your EmDash site
npm install ../emdash-restrict-with-stripe/emdash-restrict-with-stripe-0.2.0.tgzThen register the plugin in your Astro config:
import { defineConfig } from "astro/config";
import react from "@astrojs/react";
import emdash from "emdash/astro";
import { restrictWithStripe } from "emdash-restrict-with-stripe";
export default defineConfig({
integrations: [
react(),
emdash({
siteUrl: "https://example.com",
plugins: [restrictWithStripe()],
}),
],
});The plugin declares these capabilities: content:read (entry titles on the Restrictions page), network:request limited to api.stripe.com, hooks.page-fragments:register (paywall CSS and script), and email:send (login links).
Connect Stripe
Open Restrict With Stripe in the admin sidebar and click Connect to Stripe. You'll go through connect.restrictwithstripe.com and back. Tick the sandbox box to connect in test mode.
- The return URL carries a single-use state value issued for your admin account, so a crafted link with someone else's keys can't be saved.
- The returned key is checked against Stripe before it is stored.
- Disconnect From Stripe confirms on the Connect server. Keys stay on your site until Stripe confirms the disconnect.
- If Stripe stops accepting the key, the settings page shows Reconnect to Stripe.
Prefer not to use Connect? Enter an API key manually accepts a secret key (sk_…) or a restricted key (rk_…) with read access to products, prices, customers, subscriptions, and invoices, and write access to customers, Checkout Sessions, and the customer portal.
Create products in Stripe
In your Stripe dashboard, create a product with a default price for each thing you sell. One-time prices check out in payment mode with an invoice, so the purchase can be verified later. Recurring prices check out as subscriptions.
Only active prices of products that restrict at least one entry can be sold on your site.
Restrict content
Open a saved entry in the admin and use the Restrict With Stripe panel in the editor sidebar to tick the products that grant access. Restricted Content lists every restricted entry.
- The panel and the admin routes behind it require the
plugins:managepermission (administrators). - Restrictions are stored per entry ID (
collection:id), so renaming a slug doesn't drop a restriction.
What visitors see
On a restricted entry, visitors without access see the title, an optional excerpt, and a paywall with two tabs:
- Purchase: email plus product, then on to Stripe Checkout. Buyers return to the page already logged in.
- Log In: email only. Returning customers get a login link that expires after 15 minutes and works once.
Access means a Stripe customer with the session's email has an active or trialing subscription to one of the products, or a paid invoice for one. Positive results are cached for five minutes. Member sessions last 30 days, and access is re-checked against Stripe throughout.
The paywall replaces the element marked data-rwstripe-content, else the first <article>, else <main>.
Server-side gating with getAccess()
getAccess() reads the member session cookie with Astro.cookies and calls the plugin's access route in-process. It fails closed (restricted: true, hasAccess: false) if the plugin can't be reached.
---
// src/pages/posts/[slug].astro
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
import { getAccess } from "emdash-restrict-with-stripe/astro";
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
const access = await getAccess(Astro, { collection: "posts", id: post.data.id });
const locked = access.restricted && !access.hasAccess;
---
<article data-rwstripe-content>
<h1>{post.data.title}</h1>
{locked ? <p>{post.data.excerpt}</p> : <PortableText value={post.data.content} />}
</article>Pages that use it vary per visitor, so don't cache them publicly. The paywall script still renders the purchase and login forms on the gated page.
Email provider
Login links are sent through EmDash's email API, which exists only when your site has an email:deliver provider plugin installed.
- In
astro dev, EmDash's console provider prints messages to the server log. - Without a provider, buyers are still logged in automatically after Checkout, but returning visitors can't request a login link.
Login-link requests always get the same response, and only addresses with a Stripe customer who holds a restricting product receive email, so the form can't be used to send mail to arbitrary addresses.
Known limits
- Stripe's customer search by email is case-sensitive. The plugin creates customers with lowercased emails and checks both spellings at login.
- Sessions are bearer tokens valid for 30 days. A cancelled subscription or refund stops access even while the session is valid.
- Per-IP rate limits apply only when EmDash knows the client IP: on Cloudflare, or when
trustedProxyHeadersis configured. - Upgrading from 0.1.x: reconnect Stripe and re-save restrictions from the editor panel. 0.1.x keys and slug-based restrictions aren't read.
The README on GitHub documents every route, error code, and rate limit.
WordPress plugin
The original Restrict With Stripe is a free WordPress plugin. It sells access to posts, pages, categories, and tags, and links members to the Stripe Customer Portal.
- In WordPress, go to Plugins > Add New, search for Restrict With Stripe, then install and activate it.
- Go to Settings > Restrict With Stripe and click Connect to Stripe.
- In Stripe, create a product and default price for each post, page, category, or tag you want to sell.
- Edit a post, page, category, or tag and choose the Stripe product that unlocks it.
- Add a Customer Portal link to a menu, or the Customer Portal block to an account page.
To connect in test mode, add this line to a code snippet before you connect:
add_action( 'rwstripe_connect_in_test_mode', '__return_true' );Fees: the WordPress plugin is free to install. A 2% application fee, in addition to Stripe's processing fees, applies to payments and supports ongoing development.
Restrict With Stripe releases on GitHub has the download, FAQ, and full changelog.

