When implementing custom layouts, you need to handle payment failures and display appropriate error messages to your users. This guide covers the available options and best practices.
Two types of errors
Before implementing error handling, it’s important to understand the difference between validation errors and payment failures:
| Type | When it occurs | How it’s handled |
|---|
| Validation errors | During form input (invalid card number, missing fields) | Handled automatically by input components; prevents form submission |
| Payment failures | After form submission (declined card, insufficient funds) | Requires explicit handling with error container or custom code |
The <primer-error-message-container> specifically handles payment failures that occur after form submission, not card validation errors. Card validation is handled by the input components themselves and prevents form submission until valid.
Option 1: Using the error message container
The <primer-error-message-container> provides a convenient way to display payment failures without writing custom code:
<!-- Using primer-main with custom content -->
<primer-checkout client-token="your-token">
<primer-main slot="main">
<div slot="payments">
<!-- Your payment methods -->
<primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
<!-- Include error message container for payment failure display -->
<primer-error-message-container></primer-error-message-container>
</div>
</primer-main>
</primer-checkout>
<!-- Fully custom implementation -->
<primer-checkout client-token="your-token">
<div slot="main" id="custom-checkout">
<!-- Your completely custom checkout implementation -->
<div id="payment-methods">
<primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
<!-- Include error message container for payment failure messages -->
<primer-error-message-container></primer-error-message-container>
</div>
</div>
</primer-checkout>
Placement recommendations
For optimal user experience, place the error container:
- Prominently where it will be visible after a payment attempt
- Near the action where users will naturally look for feedback after submitting payment
- In context within the same visual area as the payment method it relates to
Option 2: Custom error handling
You can implement your own payment failure handling using the SDK callbacks and events. Choose one of the following approaches:
Callbacks (primer:ready)
State change event
const checkout = document.querySelector('primer-checkout');
checkout.addEventListener('primer:ready', (event) => {
const primer = event.detail;
primer.onPaymentSuccess = ({ payment, paymentMethodType }) => {
// Hide error container on success
document.getElementById('my-custom-error').style.display = 'none';
// Redirect to success page or show success UI
window.location.href = `/order/confirmation?id=${payment.orderId}`;
};
primer.onPaymentFailure = ({ error, payment, paymentMethodType }) => {
// Display the payment failure using your own UI
const customErrorElement = document.getElementById('my-custom-error');
customErrorElement.textContent = error.message;
customErrorElement.style.display = 'block';
// Log for debugging
console.error('Payment failed:', {
code: error.code,
message: error.message,
diagnosticsId: error.diagnosticsId,
});
};
});
const checkout = document.querySelector('primer-checkout');
checkout.addEventListener('primer:state-change', (event) => {
const { primerJsError, paymentFailure } = event.detail;
if (primerJsError || paymentFailure) {
// Display the failure using your own UI
const customErrorElement = document.getElementById('my-custom-error');
const errorMessage = primerJsError?.message || paymentFailure?.message;
customErrorElement.textContent = errorMessage;
customErrorElement.style.display = 'block';
} else {
// Hide error when not present
document.getElementById('my-custom-error').style.display = 'none';
}
});
This approach gives you complete control over payment failure presentation but requires you to implement the error handling logic yourself.
Choosing the right approach
| Approach | Best for | Trade-offs |
|---|
<primer-error-message-container> | Quick implementation, consistent styling | Less customization control |
| Custom error handling | Full control over UX and design | More code to maintain |
Best practices
- Always handle errors - Never leave users without feedback after a failed payment
- Be specific - Show meaningful error messages that help users understand what went wrong
- Provide next steps - Guide users on how to resolve the issue (try another card, check details, etc.)
- Clear on retry - Hide error messages when users attempt a new payment
- Log for debugging - Capture error details including
diagnosticsId for troubleshooting