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:
- Ensure the Churnkey script is loaded
- Add the below code to your application initialization
- 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
openstatus in the last 60 days - Their most recent invoice is not
paid
When activated, it:
- Blocks access to your application
- Displays a payment update form
- Processes the new payment method
- 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
| Option | Type | Default | Description |
|---|---|---|---|
mode | string | 'live' | 'live' or 'test' environment |
softWall | boolean | false | Allow customers to exit the wall |
gracePeriodDays | number | 0 | Days to show dismissible wall before enforcing |
forceCheck | boolean | false | Bypass payment status cache |
ignoreInvoicesWithoutAttempt | boolean | false | Only 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:
| Event | Description |
|---|---|
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_ERRORFAILED_PAYMENT_WALL_UPDATE_CARD_ERRORFAILED_PAYMENT_WALL_CANCEL_ERROR
Testing
Test the implementation using Stripe's test cards:
- Create a test customer
- Add the test card:
4000000000000341(Stripe's "Decline After Attaching" card) - Create and add a subscription item to an invoice
- Enable auto-charging for the invoice
- Finalize the invoice to trigger the failed payment
- Verify the Failed Payment Wall appears
Watch a complete walkthrough: