Skip to main content
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:
TypeWhen it occursHow it’s handled
Validation errorsDuring form input (invalid card number, missing fields)Handled automatically by input components; prevents form submission
Payment failuresAfter 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:
  1. Prominently where it will be visible after a payment attempt
  2. Near the action where users will naturally look for feedback after submitting payment
  3. 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:
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,
    });
  };
});
This approach gives you complete control over payment failure presentation but requires you to implement the error handling logic yourself.

Choosing the right approach

ApproachBest forTrade-offs
<primer-error-message-container>Quick implementation, consistent stylingLess customization control
Custom error handlingFull control over UX and designMore code to maintain

Best practices

  1. Always handle errors - Never leave users without feedback after a failed payment
  2. Be specific - Show meaningful error messages that help users understand what went wrong
  3. Provide next steps - Guide users on how to resolve the issue (try another card, check details, etc.)
  4. Clear on retry - Hide error messages when users attempt a new payment
  5. Log for debugging - Capture error details including diagnosticsId for troubleshooting