Skip to main content
Use your own submit button placed outside the Primer Checkout component to trigger payment.

Recipe

<primer-checkout client-token="your-token">
  <primer-main slot="main">
    <primer-card-form></primer-card-form>
  </primer-main>
</primer-checkout>

<button id="my-pay-button">Pay Now</button>
document.getElementById('my-pay-button').addEventListener('click', () => {
  document.dispatchEvent(
    new CustomEvent('primer:card-submit', {
      bubbles: true,
      composed: true,
    }),
  );
});

How it works

  1. Place your custom button outside the <primer-checkout> component
  2. Listen for click events on your button
  3. Dispatch the primer:card-submit custom event to trigger form submission
  4. The event bubbles up to the card form and initiates the payment
The bubbles: true and composed: true options are required so the event can cross shadow DOM boundaries and reach the card form component.

Variations

With loading state

const payButton = document.getElementById('my-pay-button');
const checkout = document.querySelector('primer-checkout');

payButton.addEventListener('click', () => {
  document.dispatchEvent(
    new CustomEvent('primer:card-submit', {
      bubbles: true,
      composed: true,
    }),
  );
});

// Update button state during processing
checkout.addEventListener('primer:state-change', (event) => {
  const { isProcessing } = event.detail;
  payButton.disabled = isProcessing;
  payButton.textContent = isProcessing ? 'Processing...' : 'Pay Now';
});

Using the card form submit method

Alternatively, you can call the submit method directly on the card form:
const cardForm = document.querySelector('primer-card-form');
const payButton = document.getElementById('my-pay-button');

payButton.addEventListener('click', async () => {
  try {
    await cardForm.submit();
  } catch (error) {
    console.error('Submission failed:', error);
  }
});

See also