Success! Link copied successfully
Back

Subscription Walls

The Subscription Wall is a system designed to recover revenue by dynamically controlling access to your application when a user's subscription is paused or has a failed payment.

It handles two cases:

  • Paused Subscription (Pause Wall): The user has paused their subscription. In this case, we prompt them to resume their subscription to restore access.
  • Failed Payment (Failed Payment Wall): The subscription is restricted due to a payment failure. In this case, we prompt the user to update their payment method.

In both cases, the system makes sure the user is guided through the correct recovery flow, making it easy to keep their subscription active and restore access.

Pause Wall

The Pause Wall controls access when a customer has paused their subscription. Depending on your configuration, you can show a dismissible wall or fully block access until the subscription is resumed.

Pause Wall UI

Failed Payment Wall

The Failed Payment Wall controls access when a subscription is active but a payment attempt has failed. It prompts the user to update their payment method in order to restore access.

Failed Payment Wall UI

Installation

Step 1: Place the embed code and launch snippet

Copy the code below. Paste it before the closing </body> tag on every page you want the subscription walls to appear on. It loads Churn Solution and checks the subscription whenever the page opens, showing the Pause Wall or the Failed Payment Wall when one applies.


<script>
    !function(){
        if (!window.churnSolution || !window.churnSolution.ready) {
            window.churnSolution = { ready: true };
            const s = document.createElement('script');
            s.src = 'https://app.churnsolution.com/sdk/index.min.js';
            s.async = true;
            const e = document.getElementsByTagName('script')[0];
            e.parentNode.insertBefore(s, e);
        }
    }();

    function runWallScript() {
        window.churnSolution?.checkWall({
            subscriptionId, // your subscription ID from your payment provider
            appId: 'APP_ID', // your App ID
            options: {
                // Gating (applies to both wall types)
                softWall: false, // false = hard (blocking, non-dismissable)
                gracePeriodDays: 7, // failed-payment only: soft until (invoice.created + days), then hard
                shouldShowWall: (session) => {
                    // session: { type, invoice, subscription, customer, resumes_at }
                    // type: FAILED_PAYMENT | PAUSE
                    return true; // true | false | 'soft' | 'hard'
                },
            },
        });
    }

    function onChurnSolutionLoad(callback, timeout = 10000, interval = 100) {
        const startTime = Date.now();
        const check = () => {
            if (typeof window.churnSolution?.checkWall === 'function') {
                callback();
            } else if (Date.now() - startTime < timeout) {
                setTimeout(check, interval);
            } else {
                console.warn("Churn Solution script did not load in time.");
            }
        };
        check();
    }

    onChurnSolutionLoad(() => runWallScript());
</script>
            

The options object controls how each wall behaves — see Configuration Options for every setting and callback available.

You can find your APP_ID in the Integration page, after connecting your payment provider.

Step 2: Generate Security TokenOptional

Use the code below to generate a hash on your server, so that every request sent to Churn Solution is secure. Send the generated hash to your front end and pass it as authKey in the launch call. Below are some examples of how this hash can be generated in different backend languages and frameworks.

You can find your secret key in the Account settings page. Keep it on your server — never ship it to the browser.

Update: Launch Churn Solution

Once your endpoint returns an auth key, pass it to the launch call you added in step 1:

Before


window.churnSolution?.checkWall({
    subscriptionId,
    appId: 'APP_ID',
    options: { /* ... */ },
});
            

After


window.churnSolution?.checkWall({
    authKey, // hash generated by your server
    subscriptionId,
    appId: 'APP_ID',
    options: { /* ... */ },
});
            

Configuration Options

Access Modes

Soft Wall

Allows users to close the wall and continue using the application.

softWall: true

Hard Wall

Fully blocks access until the user takes action (resume or update payment).

softWall: false

Grace Period (Failed Payments Only)

Allows continued access for a defined number of days before enforcing the wall.

gracePeriodDays: 7

Dynamic Control with shouldShowWall

While softWall sets a single mode for every user, the shouldShowWall callback lets you decide — per user, at runtime — whether the wall should appear at all and, if so, whether it should be soft or hard. It runs every time checkWall is called and receives a session object describing the current subscription state.

The callback receives a single session argument:

Property Description
type The wall type for this session: FAILED_PAYMENT or PAUSE.
invoice The related invoice (for failed payments), including amount, currency, and status.
subscription The subscription object, including its plan, status, and metadata.
customer The customer object, including email and any custom metadata.
resumes_at For paused subscriptions, the timestamp at which the subscription is scheduled to resume.

The value you return controls what happens next:

Return value Result
true Show the wall using the mode from softWall / gracePeriodDays.
false Do not show the wall; the user keeps full access.
'soft' Force a dismissible soft wall, overriding other settings.
'hard' Force a blocking hard wall, overriding other settings.

This lets you tailor the recovery experience to your business rules. For example, you can hard-block high-value invoices while giving smaller balances a dismissible wall, keep trusted or high-tier customers on a soft wall, or skip the wall entirely for a segment identified through subscription or customer metadata.


shouldShowWall: (session) => {
    const { type, invoice, subscription, customer, resumes_at } = session;

    // Never wall your internal or VIP accounts
    if (customer?.metadata?.plan_tier === 'vip') return false;

    if (type === 'FAILED_PAYMENT') {
        // Hard-block large unpaid invoices, keep small ones dismissible
        return invoice?.amount_due >= 10000 ? 'hard' : 'soft';
    }

    if (type === 'PAUSE') {
        // Only wall paused subscriptions that are still active plans
        return subscription?.metadata?.enforce_pause === 'true';
    }

    return true; // fall back to the softWall / gracePeriodDays settings
},
            
The shouldShowWall callback runs on the client, so treat it as a presentation rule. Access enforcement is always re-validated on the Churn Solution side.

System Events

System events are triggered globally for both the Pause Wall and the Failed Payment Wall.

Lifecycle Events

Event Trigger Description
onWallActivated Wall is displayed Fired when either the Pause or Failed Payment wall becomes visible to the user
onWallClose Wall is dismissed Fired when the user closes or exits the wall
onError Runtime error occurs Fired when an error occurs within the wall system

Subscription Actions

Subscription actions are triggered based on the type of recovery flow the user is in.

Failed Payment Flow

Triggered when the user updates their payment method successfully.

Event Signature Description
onUpdatePaymentInformation (customer) => {} Fired after the user successfully updates their payment details

Pause Flow

Triggered when the user interacts with a paused subscription.

Event Signature Description
onResumeSubscription (customer) => {} Fired when the user resumes their subscription
onCancelSubscription (customer) => {} Fired when the user cancels a paused subscription

Full Configuration Example

The following example brings every option together in a single checkWall call — the gating settings, the shouldShowWall decision callback, and all of the lifecycle and subscription-action callbacks for both wall types.


window.churnSolution?.checkWall({
    authKey, // hash generated by your server
    subscriptionId, // your subscription ID from your payment provider
    appId: 'APP_ID', // your App ID
    options: {
        // Gating (applies to both wall types)
        softWall: false, // false = hard (blocking, non-dismissable)
        gracePeriodDays: 0, // failed-payment only: soft until (invoice.created + days), then hard
        shouldShowWall: (session) => {
            // session: { type, invoice, subscription, customer, resumes_at }
            // type: FAILED_PAYMENT | PAUSE
            return true; // true | false | 'soft' | 'hard'
        },

        // Generic callbacks
        onWallActivated: ({ type }) => {
            console.log('wall activated, type:', type);
        },
        onWallClose: ({ type }) => {
            console.log('wall closed (soft), type:', type);
        },
        onError: (error, type) => {
            // type: WALL_INITIALIZATION_ERROR | FAILED_PAYMENT_WALL_UPDATE_CARD_ERROR
            //     | PAUSE_WALL_RESUME_ERROR | PAUSE_WALL_CANCEL_ERROR
            console.log('wall error', type, error);
        },

        // Failed-payment wall
        onUpdatePaymentInformation: (customer) => {
            console.log('payment updated, customer:', customer);
        },

        // Pause wall
        onResumeSubscription: (customer) => {
            console.log('subscription resumed, customer:', customer);
        },
        onCancelSubscription: (customer) => {
            console.log('subscription cancelled, customer:', customer);
        },
    },
});
            

Paid Memberships Pro

If you are using Paid Memberships Pro, no code changes are required.

Simply contact the Churn Solution team and we will enable the Pause Wall and the Failed Payment Wall for your site and configure it on your behalf. The integration is fully managed and does not require any additional implementation.