Failed Payment Wall

Block access to your application when payments fail
View Markdown

The Failed Payment Wall helps you recover revenue by automatically blocking access to your application when a customer's payment fails. It displays a streamlined UI for customers to update their payment information and immediately retry failed charges.

Currently available for Stripe only

Quick start

To implement the Failed Payment Wall:

  1. Ensure the Churnkey script is loaded
  2. Add the below code to your application initialization
  3. Customize the wall behavior (optional)
window.churnkey.check('failed-payment', {
  // Required - Authentication & identification
  customerId: 'CUSTOMER_ID',
  authHash: 'HMAC_HASH',
  appId: 'YOUR_APP_ID',
  provider: 'stripe',
  
  // Optional - Wall behavior
  mode: 'live',           // Use 'test' for development
  softWall: false,        // Allow users to exit the wall
  gracePeriodDays: 0,     // Days before enforcing hard wall
  forceCheck: false,      // Skip caching (not recommended)
  subscriptionId: 'SUBSCRIPTION_ID' // Target specific subscription
})

How it works

The Failed Payment Wall activates automatically when:

  • A customer has invoices with open status in the last 60 days
  • Their most recent invoice is not paid

When activated, it:

  1. Blocks access to your application
  2. Displays a payment update form
  3. Processes the new payment method
  4. Restores application access on success

3D Secure and card verification

Some failed payments do not need a new card at all. They fail because the bank requires the customer to confirm the charge through 3D Secure (3DS), the bank-side step behind Strong Customer Authentication (SCA). The card on file is valid; it just needs the customer to approve the payment once.

When the Failed Payment Wall detects a payment in this state, it shows a Verify your card flow instead of the usual update form:

  • The wall asks the customer to confirm the card already on file, so there is no need to re-enter card details.
  • The customer taps Verify your card and completes the 3D Secure challenge from their bank in place, inside the wall.
  • Once the challenge succeeds, the invoice is charged and application access is restored without a redirect to a hosted page.

If the customer would rather pay with a different card, a Use a different card link switches the wall back to the standard payment update form.

This flow runs entirely in your application — the customer never leaves the wall. Behind the scenes, Churnkey creates a payment intent, attaches it to the open invoice, applies any campaign discount that was in effect, and resumes an earlier interrupted authentication attempt if one exists, so a customer who started verifying and dropped off can finish where they left off.

At launch, in-app card verification applies to Stripe-billed subscriptions, so other billing providers are not covered yet. The same Verify your card flow is also available on the hosted Payment Recovery page and in Payment Recovery emails for payments blocked on authentication.

Recovered payments through your own Stripe account

By default, Churnkey authenticates the recovered payment with its own payment intent. If you connect Stripe through Churnkey's OAuth connection, you can optionally add a dedicated set of Payment Recovery keys — a recovery Secret Key and Publishable Key — on your Stripe connection so that recovery instead authenticates the invoice's own payment intent.

With those keys in place, a payment recovered through 3D Secure appears as a regular payment in your own Stripe Dashboard, and Stripe's one-click cancel-and-refund on the invoice keeps working. This is optional: an account without the recovery keys falls back to the default recovery flow, which still completes 3D Secure for the subscriber.

The recovery keys are separate from the API Key used for subscription management, so opting in does not change how pauses, discounts, or cancellations are processed. See Payment Recovery keys on the Stripe connection page for how to add them.

How verification-required payments are segmented

A payment blocked on 3D Secure is neither a hard decline nor a soft decline. The card is valid, so it is not a hard decline, but automated retries cannot recover it on their own, so it is not a soft decline either. Because of this, these payments are excluded from the Payment Decline Type segment (Soft Decline / Hard Decline) and fall through to your catch-all campaign unless you target them directly with the Authentication Required decline reason. See Customize your Campaigns for how decline-based segmentation works.

Configuration

Core options

OptionTypeDefaultDescription
modestring'live''live' or 'test' environment
softWallbooleanfalseAllow customers to exit the wall
gracePeriodDaysnumber0Days to show dismissible wall before enforcing
forceCheckbooleanfalseBypass payment status cache
ignoreInvoicesWithoutAttemptbooleanfalseOnly show wall for failed charge attempts

Custom display logic

Control when to show the wall based on your business rules:

window.churnkey.check('failed-payment', {
  // ... other options
  shouldShowFailedPaymentWall(overdueInvoice, customer) {
    // Examples:
    if (overdueInvoice.amountDue > 50000) {
      return false           // Skip for high-value invoices
    }
    if (customer.isVIP) {
      return 'soft'         // Allow VIPs to dismiss
    }
    return true             // Show wall for others
  }
})

Return values:

  • true - Show wall (follows softWall setting)
  • false - Don't show wall
  • 'soft' - Show dismissible wall
  • 'hard' - Show enforced wall

Optional UI elements

Add extra buttons to the wall:

window.churnkey.check('failed-payment', {
  // ... other options
  handleLogout() { },         // Add logout button
  handleSupportRequest() { }, // Add support button
  handleCancel() { }         // Add cancel subscription button
})

Event callbacks

Monitor wall activity:

EventDescription
onFailedPaymentWallActivated()Wall is displayed
onUpdatePaymentInformation(customer)Payment updated successfully
onFailedPaymentWallClose()Wall is dismissed (soft wall only)
onError(error, type)Error occurred

Error types:

  • FAILED_PAYMENT_WALL_INITIALIZATION_ERROR
  • FAILED_PAYMENT_WALL_UPDATE_CARD_ERROR
  • FAILED_PAYMENT_WALL_CANCEL_ERROR

Testing

Test the implementation using Stripe's test cards:

  1. Create a test customer
  2. Add the test card: 4000000000000341 (Stripe's "Decline After Attaching" card)
  3. Create and add a subscription item to an invoice
  4. Enable auto-charging for the invoice
  5. Finalize the invoice to trigger the failed payment
  6. Verify the Failed Payment Wall appears

Watch a complete walkthrough: