# Card payments

A card payment with Payment components is: mount the card field components, let the customer type their card, and call `checkout.continue()` from your own pay button. The result comes back through [events](/guides/core-concepts/checkout/payment-components/events-and-errors).

## Full example

```html
<script src="https://js.shift4.com/v1/shift4.js"></script>

<div id="number"></div>
<div id="expiry"></div>
<div id="cvc"></div>
<input id="cardholder" type="text" placeholder="Cardholder name" />
<button id="pay">Pay</button>

<div id="result"></div>
<div id="error"></div>

<script>
  (async function () {
    var shift4 = Shift4('pk_sandbox_XXXXXXXXXXXXXXXXXXXX');

    var checkout = await shift4.createCheckout('{{clientSecret}}');

    // Mount individual card fields
    checkout.createComponent('number').mount('#number');
    checkout.createComponent('expiry').mount('#expiry');
    checkout.createComponent('cvc').mount('#cvc');

    // Or use a single combined card field:
    // checkout.createComponent('card').mount('#card');

    checkout.addEventListener('checkout_session.succeeded', function () {
      document.getElementById('result').textContent = 'Payment successful';
    });
    checkout.addEventListener('payment.failed', function (event) {
      document.getElementById('error').textContent = event.error.message;
    });

    document.getElementById('pay').addEventListener('click', function () {
      checkout.continue({
        email: 'customer@example.com',
        billing: { name: document.getElementById('cardholder').value }
      });
    });
  })();
</script>
```

Note that the cardholder name input is a plain element of your own page — only the card number, expiry, and CVC are Shift4-hosted.

## continue() parameters

| Parameter  | Type | Description |
|  --- | --- | --- |
| `email` | string | Customer email. Optional if provided by a wallet response. |
| `billing` | object | Billing address sent to the payment processor. |
| `billing.name` | string | Cardholder name, used for tokenization. |
| `billing.address.line1` | string | Billing street address. |
| `billing.address.zip` | string | Billing postal / ZIP code. |
| `billing.address.city` | string | Billing city. |
| `billing.address.country` | string | Billing country (two-letter ISO code). |
| `shipping` | object | Shipping address, same structure as `billing`. |


## Billing and shipping addresses

If the Checkout Session was created with `collectBillingAddress: true` or `collectShippingAddress: true`, a card payment must supply the corresponding `billing` / `shipping` object in `continue()` — collect the values with your own form fields and pass them along. Wallet payments don't need this: shift4.js fetches the addresses from Apple Pay / Google Pay. See [Collect an address](/guides/core-concepts/checkout/create-a-checkout-session#collect-an-address).

## Offer wallets alongside cards

Wallet buttons and card fields represent separate payment flows with independent Checkout Sessions — each requires its own `createCheckout()` call:

```html
<script src="https://js.shift4.com/v1/shift4.js"></script>

<div id="apple-pay-button"></div>
<div id="google-pay-button"></div>
<hr />
<div id="number"></div>
<div id="expiry"></div>
<div id="cvc"></div>
<input id="cardholder" type="text" placeholder="Cardholder name" />
<button id="pay">Pay by card</button>

<div id="result"></div>
<div id="error"></div>

<script>
  (async function () {
    var shift4 = Shift4('pk_sandbox_XXXXXXXXXXXXXXXXXXXX');

    // One Checkout Session for wallet payments
    var walletCheckout = await shift4.createCheckout('{{clientSecret}}');
    walletCheckout.createComponent('apple-pay-button').mount('#apple-pay-button');
    walletCheckout.createComponent('google-pay-button').mount('#google-pay-button');
    walletCheckout.addEventListener('checkout_session.succeeded', onSuccess);
    walletCheckout.addEventListener('payment.failed', onError);

    // Separate Checkout Session for card payment
    var cardCheckout = await shift4.createCheckout('{{clientSecret}}');
    cardCheckout.createComponent('number').mount('#number');
    cardCheckout.createComponent('expiry').mount('#expiry');
    cardCheckout.createComponent('cvc').mount('#cvc');
    cardCheckout.addEventListener('checkout_session.succeeded', onSuccess);
    cardCheckout.addEventListener('payment.failed', onError);

    document.getElementById('pay').addEventListener('click', function () {
      cardCheckout.continue({
        email: 'customer@example.com',
        billing: { name: document.getElementById('cardholder').value }
      });
    });

    function onSuccess() {
      document.getElementById('result').textContent = 'Payment successful';
    }
    function onError(event) {
      document.getElementById('error').textContent = event.error.message;
    }
  })();
</script>
```

Each `{{clientSecret}}` comes from its own [Checkout Session created on your backend](/guides/core-concepts/checkout/create-a-checkout-session).

## Next steps

Events & error handling
The event model, the error object, and how to handle interruptions.

Confirm the payment
Verify the Checkout Session server-side before you fulfil.

Test cards
Sandbox card numbers for success, failure, and 3D Secure scenarios.