# Members Area & Paid Content (dfmembership) — Complete guide

> Overview The Members Area & Paid Content module (dfmembership) turns your shop into a membership platform: you sell subscriptions that unlock a members area and gated content (articles, videos, files,…

- Page: <https://www.datafirefly.com/en/documentation/dfmembership/>
- Language: en
- Last updated: 2026-06-25
- Other languages: [fr](https://www.datafirefly.com/documentation/dfmembership/index.md), [es](https://www.datafirefly.com/es/documentation/dfmembership/index.md), [de](https://www.datafirefly.com/de/documentation/dfmembership/index.md), [it](https://www.datafirefly.com/it/documentation/dfmembership/index.md), [pl](https://www.datafirefly.com/pl/documentation/dfmembership/index.md), [pt](https://www.datafirefly.com/pt/documentation/dfmembership/index.md), [nl](https://www.datafirefly.com/nl/documentation/dfmembership/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

## Overview

The **Members Area & Paid Content** module (`dfmembership`) turns your shop into a membership platform: you sell **subscriptions** that unlock a **members area** and **gated content** (articles, videos, files, links), group pricing, or private CMS pages. Each plan is tied to a **native PrestaShop customer group**, so the shop handles catalog visibility and group prices natively; the module adds billing, the paywall, the library, automatic expiry and group synchronisation on top.

Two business models are available **from the very first version** and are chosen per plan: **limited-duration access** (the customer pays once for N days, access expires automatically) and a **real recurring subscription** via Stripe Billing (renewal and cancellation driven by webhooks).

## Compatibility

- PrestaShop 8.0 to 9.x
- PHP 7.4 to 8.3
- Single-shop and multistore
- 5 languages: FR, EN, ES, DE, IT
- Classic theme and custom themes
- No dependency: no Composer, no bundled Stripe SDK (native cURL API calls)

## Key concepts

### A plan equals a customer group

A **plan** represents a membership tier (for example "Premium" or "Pro annual"). Each plan is **mapped to a PrestaShop customer group**: when a customer becomes a member, they are added to that group; when their subscription expires or is cancelled, they are removed from it. You therefore inherit all of PrestaShop's native mechanics: **group-specific prices**, **carrier restrictions**, **category visibility** and more — with no extra configuration in the module.

First create your customer groups in **Customers > Groups**, then map them to your plans. The same group can power your group prices and your content gating.

### The two billing modes

- **Limited duration**: the customer pays once and gets access for the plan's length (in days). A cron task expires access once the term is reached. Ideal for a one-off pass, a seasonal access or time-limited content. On early renewal, remaining time is stacked.
- **Stripe subscription**: a real recurring subscription managed by Stripe Billing. Renewal, payment failure and cancellation are received through **webhooks** and reflected automatically on the customer's access. Ideal for recurring revenue (MRR).

## Installation

1. In the back office, open **Modules > Module Manager**.
2. Click **Upload a module** and select the `dfmembership.zip` file.
3. Once installed, click **Configure**.

On install, the module creates its tables (plans, subscriptions, content), registers its hooks, generates a random **cron token** and adds a **Membership** menu under **Sell**, with three sub-tabs: **Plans**, **Subscriptions** and **Content**. A **My membership** link appears in the customer account.

## General configuration

The module's configuration page gathers the global settings as well as the two **integration URLs** (Stripe webhook and cron) to copy into your external tools.

### Stripe settings

- **Stripe secret key**: your `sk_live_…` key (or `sk_test_…` in test mode). Required for recurring billing.
- **Stripe publishable key**: your `pk_…` key.
- **Webhook signing secret**: the `whsec_…` value Stripe provides for the endpoint. It is used to **verify the signature** of every incoming webhook.

### Content and paywall

- **Protected CMS pages**: the comma-separated list of CMS page ids reserved for members (for example `4,7,9`). Non-members trying to open them are redirected to the library.
- **Paywall SEO schema**: injects `isAccessibleForFree` set to false structured data on gated content, so it stays indexable by Google without being treated as cloaking.
- **Teaser length (words)**: number of words shown before the paywall when no explicit teaser is set on a content item.
- **Regenerate cron token**: replaces the token in the cron URL (useful if it leaked).

The secret key and webhook secret are sensitive. Use the **test** keys while validating the flow, then switch to the **live** keys in production. Never share the cron URL with its token publicly.

## Creating a plan

In **Membership > Plans**, add a tier and fill in:

- **Name, tagline, description** (multilingual): shown on the subscribe page and the home block.
- **Billing mode**: "Limited-duration access (cron)" or "Recurring subscription (Stripe)".
- **Price**: the amount shown to the customer.
- **Duration (days)**: the access length in limited-duration mode.
- **Trial (days)**: an optional trial period (0 = none). In Stripe mode, the trial is passed to the Stripe subscription.
- **Stripe Price ID**: the `price_…` id of the recurring price created in Stripe (Stripe mode only).
- **Mapped customer group**: the group the member is placed in while their access is active.
- **Position** and **Active**: display order and availability of the plan.

## Limited-duration mode and cron

In duration mode, the subscription is created and then activated for the plan's length; its end date is computed from the activation date. Free plans (zero price) are activated immediately, and a plan with a trial first opens a trial window.

To automatically close access once the term is reached, schedule the **cron URL** shown in the configuration (token-protected). A daily run is enough:

```
curl "https://your-shop/index.php?fc=module&module=dfmembership&controller=cron&token=YOUR_TOKEN"
```

The cron sets any duration subscription whose end date has passed to "expired", then **resynchronises the groups** of the affected customer. Without the cron, access does not expire on its own: remember to schedule it.

## Stripe subscription mode

In Stripe mode, the customer is redirected to a **Stripe Checkout** page in subscription mode. When the payment completes, Stripe notifies your shop via webhook and access is activated. The full lifecycle is then driven by Stripe.

### Setup

1. In Stripe, create a **product** and a **recurring price** (monthly, yearly…), then copy its `price_…` into the matching plan.
2. Fill in your Stripe keys and the webhook secret in the module configuration.
3. In Stripe, add a **webhook endpoint** pointing to the URL shown in the configuration, and subscribe it to the events: `checkout.session.completed`, `invoice.paid`, `invoice.payment_failed`, `customer.subscription.updated` and `customer.subscription.deleted`.

### What the webhooks do

- **checkout.session.completed**: activates the subscription and stores the Stripe subscription id.
- **invoice.paid**: renews access and rolls the period end date forward.
- **invoice.payment_failed**: keeps access during the dunning grace period handled by Stripe.
- **customer.subscription.updated**: reflects a cancellation scheduled at period end or a date change.
- **customer.subscription.deleted**: closes access for good and removes the customer from their group.

Every webhook is signature-verified using the `whsec_…` secret. If the signature is invalid or the secret is empty, the request is rejected. Check this setting first if subscriptions are not activating.

## Content library and paywall

In **Membership > Content**, create the gated items. Each content item has:

- **Type**: article, video, file or link.
- **Title, teaser, content** (multilingual). The **teaser** is the public preview shown before the paywall; left empty, it is auto-generated from the first words of the content (adjustable length).
- **Media URL**: the video embed, the downloadable file or the external link.
- **Required plan**: the plan that grants access to the content. The value "Any active member" opens the content to any ongoing subscription.
- **Drip delay (days)**: number of days after the subscription start before the content unlocks, to release a library progressively (drip content).

On the storefront, the **library** lists items with their teaser. A member sees the full content; a visitor or a non-member sees the teaser then a **paywall** inviting them to subscribe. Access control combines the required plan and the drip delay.

### SEO for gated content

When the SEO schema option is on, the module adds structured data on unlocked content indicating that it is paid content (Google's flexible sampling). The reserved block is marked with a dedicated CSS class, which lets Google index the page without penalising the difference between what the crawler and the member see.

## Reserved CMS pages

To reserve existing CMS pages (a resources page, a private area…), enter their ids in **Protected CMS pages**. A non-member opening one of these pages is automatically redirected to the library, where they can discover the plans. Members access them normally.

## Members area

From **My account > My membership**, the customer finds their ongoing subscriptions: plan, status, billing mode, renewal or end date. They can **open the library** and, where relevant, **cancel** a subscription. Cancellation happens **at period end**: access stays open until the already-paid term, then closes. In Stripe mode, the request is forwarded to Stripe; in duration mode, access is simply not renewed.

## Managing subscriptions (back office)

In **Membership > Subscriptions**, you see every subscription (customer, email, plan, status, mode, dates) with search and export. The detail view lets you **force a status** (active, expired, cancelled) when needed: a goodwill gesture, a fix, a dispute. Any status change **immediately resynchronises** the customer's group.

Groups are also resynchronised on customer login and on order validation, to guarantee that access always reflects the genuinely active subscriptions.

## Going further

The billing architecture is **extensible**: a new provider (for example recurring PayPal) is added by implementing the `BillingManagerInterface` from the `src/Billing/` folder and registering it in the `BillingManagerFactory`, without touching the rest of the module. Possible evolutions include: AI-generated teasers, dunning emails, gift memberships, plan changes with proration, or an MRR / churn dashboard.

## FAQ and troubleshooting

### A customer paid but has no access

In Stripe mode, first check the **webhook secret** and that the endpoint is indeed receiving events in the Stripe dashboard (Webhooks tab). Activation depends on the `checkout.session.completed` event. In duration mode, check that the subscription has turned active; you can force it from the detail view.

### Access never expires

Duration mode relies on the cron. Make sure you have **scheduled the cron URL** (daily) with the correct token. You can call it manually to test: the JSON response shows the number of expired subscriptions.

### How do I link a plan to reduced prices?

Map the plan to a **customer group**, then set **specific prices** for that group on your product pages (or via a catalog price rule). As long as the member is active, they belong to the group and benefit from those prices automatically.

### Is gated content indexable by Google?

Yes, if the SEO schema option is on: paid content is flagged as such via structured data, which matches Google's guidance on flexible sampling. You keep indexing without hiding the paid nature.

### Can I offer a free trial?

Yes. Set a number of trial days on the plan. In duration mode, a trial window is opened; in Stripe mode, the trial is passed to the Stripe subscription (`trial_period_days`).

### Is PayPal supported?

Version 1 implements the duration and Stripe modes. Recurring PayPal can be added thanks to the extensible billing interface, with no module rewrite.

### Is it PrestaShop 9 compatible?

Yes. The module is compatible with PrestaShop 8 and 9, in multistore and multilingual setups (FR, EN, ES, DE, IT).
