> ## Documentation Index
> Fetch the complete documentation index at: https://developer.surecart.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout

> Hook into checkout events and customize validation

These hooks allow you to respond to checkout events and customize the checkout experience.

## Actions

Actions are triggered during the checkout process and when orders are confirmed. This is at the end, after payment is successuflly confirmed by the processor.

### `surecart/checkout_confirmed`

Fired after an order is confirmed and all purchases have been processed. Use this for post-checkout operations like analytics tracking, external notifications, or custom logging.

<ResponseField name="Parameters" type="Action Parameters">
  <Expandable title="properties">
    <ResponseField name="$checkout" type="\SureCart\Models\Checkout">
      The checkout model object containing order details.
    </ResponseField>

    <ResponseField name="$request" type="\WP_REST_Request">
      The REST API request object.
    </ResponseField>
  </Expandable>
</ResponseField>

```php theme={null}
add_action( 'surecart/checkout_confirmed', function( $checkout, $request ) {
    // Fetch the checkout with customer relation loaded.
    $checkout = \SureCart\Models\Checkout::with(['customer'])->find( $checkout->id );

    // Access order details
    $order_id = $checkout->id;
    $total = $checkout->total_amount;

    // Track conversion
    if ( function_exists( 'track_conversion' ) ) {
        track_conversion( $order_id, $total );
    }

    // Send to external system
    wp_remote_post( 'https://api.example.com/orders', [
        'body' => [
            'order_id' => $order_id,
            'total' => $total,
            'customer_email' => $checkout->customer->email,
        ]
    ]);
}, 10, 2 );
```

## Filters

### Form Validation

These filters run during the finalize step of checkout, before the order is submitted. Use them to validate form data and prevent checkout if validation fails.

#### `surecart/checkout/validate`

Add custom server-side validation to checkout forms. This filter runs during the finalize step, after client-side validation passes but before the order is submitted to the payment processor.

<ResponseField name="Parameters" type="Filter Parameters">
  <Expandable title="properties">
    <ResponseField name="$errors" type="\WP_Error">
      Error object to add validation errors to. Add errors using `$errors->add(
                  $code, $message )`.
    </ResponseField>

    <ResponseField name="$args" type="array">
      All checkout form data including `email`, `name`, `billing_address`,
      `shipping_address`, `metadata`, `line_items`, and custom fields.
    </ResponseField>

    <ResponseField name="$request" type="\WP_REST_Request">
      The REST API request object. Use `$request->get_param( 'key' )` to access
      specific parameters.
    </ResponseField>
  </Expandable>
</ResponseField>

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    // Require a custom field
    if ( empty( $args['metadata']['company_name'] ) ) {
        $errors->add( 'company_required', 'Company name is required.' );
    }

    return $errors;
}, 10, 3 );
```

##### Validate Email Domain

Block specific email domains from purchasing:

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $blocked_domains = [ 'tempmail.com', 'throwaway.com', 'mailinator.com' ];

    $email = $args['email'] ?? '';
    $domain = substr( strrchr( $email, '@' ), 1 );

    if ( in_array( $domain, $blocked_domains, true ) ) {
        $errors->add(
            'blocked_email',
            'Please use a valid email address. Temporary email addresses are not allowed.'
        );
    }

    return $errors;
}, 10, 3 );
```

##### Validate Custom Fields

Validate custom form fields added to your checkout:

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    // Get custom fields from metadata
    $metadata = $args['metadata'] ?? [];

    // Validate age requirement
    if ( isset( $metadata['your_age'] ) ) {
        if ( $metadata['your_age'] < 18 ) {
            $errors->add( 'age_restriction', 'You must be 18 or older to purchase.' );
        }
    }

    return $errors;
}, 10, 3 );
```

##### Validate Based on Form ID

Apply validation only to specific checkout forms:

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $form_id = $request->get_param( 'form_id' );

    // Only validate on specific form
    if ( $form_id !== 123 ) {
        return $errors;
    }

    // Form-specific validation
    if ( empty( $args['metadata']['license_number'] ) ) {
        $errors->add( 'license_required', 'Professional license number is required.' );
    }

    return $errors;
}, 10, 3 );
```

### User Creation

#### `surecart/checkout/auto-login-new-user`

Control whether newly created users are automatically logged in after checkout.

```php theme={null}
// Disable auto-login for new users
add_filter( 'surecart/checkout/auto-login-new-user', '__return_false' );
```

### Display

#### `surecart_checkout_show_converted_total`

Control whether converted currency totals are shown when using currency conversion.

```php theme={null}
add_filter( 'surecart_checkout_show_converted_total', function( $show, $checkout ) {
    // Don't show converted total for small orders
    if ( $checkout->total_amount < 1000 ) { // Less than $10
        return false;
    }
    return $show;
}, 10, 2 );
```

#### `sc_checkout_price_selector_first_price_as_default`

Control whether the first price is selected by default in price selectors.

```php theme={null}
add_filter( 'sc_checkout_price_selector_first_price_as_default', '__return_false' );
```

### Payment Mode

#### `surecart/payments/mode`

Filter the payment mode (live/test). Useful for testing or staging environments.

```php theme={null}
add_filter( 'surecart/payments/mode', function( $mode ) {
    // Force test mode for admins
    if ( current_user_can( 'manage_options' ) ) {
        return 'test';
    }
    return $mode;
} );

// Or force test mode on staging
add_filter( 'surecart/payments/mode', function( $mode ) {
    if ( wp_get_environment_type() === 'staging' ) {
        return 'test';
    }
    return $mode;
} );
```

## JavaScript Filters

SureCart provides JavaScript filters using the WordPress hooks system (`wp.hooks`) to customize the Stripe Payment Element. These filters allow you to modify payment method order, wallet visibility, legal terms display, and billing fields.

<Note>
  These filters use the
  [@wordpress/hooks](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-hooks/)
  package, which is automatically available on pages with SureCart checkout.
</Note>

### Payment Method Order

Use `surecart_stripe_payment_element_payment_method_order` to reorder the payment methods displayed in Stripe's Payment Element.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_payment_method_order",
  "my-customization",
  (paymentMethodOrder, checkout) => {
    // Return an array of payment method types in your preferred order
    return ["card", "us_bank_account", "klarna"];
  }
);
```

See the [Stripe Payment Element documentation](https://docs.stripe.com/js/elements_object/create_payment_element#payment_element_create-options-business) for available payment method types.

### Wallet Visibility

Use `surecart_stripe_payment_element_wallets` to control the visibility of digital wallets like Apple Pay, Google Pay, and Link within the Payment Element.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_wallets",
  "my-customization",
  (wallets, checkout) => {
    return {
      applePay: "auto", // 'auto' | 'never'
      googlePay: "auto", // 'auto' | 'never'
      link: "never", // 'auto' | 'never'
    };
  }
);
```

See the [Stripe wallets documentation](https://docs.stripe.com/js/elements_object/create_payment_element#payment_element_create-options-wallets) for more options.

### Legal Terms Display

Use `surecart_stripe_payment_element_terms` to manage the display of mandates and other legal agreements within the Payment Element. By default, these are shown only when necessary.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_terms",
  "my-customization",
  (terms, checkout) => {
    return {
      card: "never", // Hide card terms
      usBankAccount: "always", // Always show bank account terms
    };
  }
);
```

See the [Stripe terms documentation](https://docs.stripe.com/js/elements_object/create_payment_element#payment_element_create-options-terms) for available options.

### Billing Fields

Use `surecart_stripe_payment_element_fields` to control which billing address fields are displayed within the Stripe Payment Element. This is useful when you're already collecting billing information elsewhere in your checkout form.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_fields",
  "my-customization",
  (fields) => {
    return {
      billingDetails: {
        name: "never",
        email: "never",
        address: {
          line1: "never",
          line2: "never",
          city: "never",
          state: "never",
          postalCode: "never",
          country: "never",
        },
      },
    };
  }
);
```

To show specific fields, simply remove them from the object or set them to `'auto'`. For example, to hide all fields except country:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_fields",
  "my-customization",
  (fields) => {
    return {
      billingDetails: {
        address: {
          line1: "never",
          line2: "never",
          city: "never",
          state: "never",
          postalCode: "never",
          // country is not set, so it will display
        },
      },
    };
  }
);
```

See the [Stripe fields documentation](https://docs.stripe.com/js/elements_object/create_payment_element#payment_element_create-options-fields) for all available options.

### Address Countries

Use `surecart_address_countries` to customize the list of countries available in address fields. You can add, remove, reorder, or translate country names.

<Note>
  To add custom JavaScript filters to your site, you can use plugins like
  [Simple Custom CSS and JS](https://wordpress.org/plugins/custom-css-js/). Be
  sure to load scripts in the footer for checkout page compatibility.
</Note>

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_countries",
  "my-customization",
  (countries) => {
    // Return only specific countries
    return [
      { value: "US", label: "United States" },
      { value: "CA", label: "Canada" },
      { value: "GB", label: "United Kingdom" },
    ];
  }
);
```

Each country object must include both `value` (ISO country code) and `label` (display name) properties.

### Address Regions

Use `surecart_address_regions` to customize the regions (states, provinces, etc.) available for specific countries. You can add, remove, or translate region names.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_regions",
  "my-customization",
  (regions, country) => {
    // Add custom regions for Taiwan
    if (country === "TW") {
      regions.push(
        { value: "test-region-one", label: "Test Region 1" },
        { value: "test-region-two", label: "Test Region 2" }
      );
    }
    return regions;
  }
);
```

The filter receives the current `regions` array and the `country` code. Each region object must include both `value` and `label` properties.

## Use Cases

### Analytics & Conversion Tracking

```php theme={null}
add_action( 'surecart/checkout_confirmed', function( $checkout, $request ) {
    // Fetch the checkout with customer relation loaded.
    $checkout = \SureCart\Models\Checkout::with(['customer'])->find( $checkout->id );

    // Google Analytics 4 - Server-side tracking
    $measurement_id = 'G-XXXXXXXXXX';
    $api_secret = 'your-api-secret';

    wp_remote_post( "https://www.google-analytics.com/mp/collect?measurement_id={$measurement_id}&api_secret={$api_secret}", [
        'body' => json_encode([
            'client_id' => $checkout->customer->id,
            'events' => [[
                'name' => 'purchase',
                'params' => [
                    'transaction_id' => $checkout->id,
                    'value' => $checkout->total_amount / 100,
                    'currency' => $checkout->currency,
                ]
            ]]
        ])
    ]);
}, 10, 2 );
```

### Send to Slack

```php theme={null}
add_action( 'surecart/checkout_confirmed', function( $checkout, $request ) {
    // Fetch the checkout with customer relation loaded.
    $checkout = \SureCart\Models\Checkout::with(['customer'])->find( $checkout->id );

    $webhook_url = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL';

    wp_remote_post( $webhook_url, [
        'body' => json_encode([
            'text' => sprintf(
                '🎉 New order! %s just purchased for %s',
                $checkout->customer->email,
                sc_format_amount( $checkout->total_amount, $checkout->currency )
            )
        ]),
        'headers' => [
            'Content-Type' => 'application/json'
        ]
    ]);
}, 10, 2 );
```

### Validate Minimum Order Amount

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $checkout = $request->get_param( 'checkout' );

    if ( isset( $checkout['total_amount'] ) && $checkout['total_amount'] < 500 ) {
        $errors->add(
            'minimum_order',
            'Minimum order amount is $5.00.'
        );
    }

    return $errors;
}, 10, 3 );
```

### Restrict Purchases by Country

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $blocked_countries = [ 'XX', 'YY' ]; // Country codes to block

    $billing_address = $args['billing_address'] ?? [];
    $country = $billing_address['country'] ?? '';

    if ( in_array( $country, $blocked_countries, true ) ) {
        $errors->add(
            'country_blocked',
            'Sorry, we cannot ship to your country.'
        );
    }

    return $errors;
}, 10, 3 );
```

### Require Terms Acceptance

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $metadata = $args['metadata'] ?? [];

    if ( empty( $metadata['accept_terms'] ) || $metadata['accept_terms'] !== 'yes' ) {
        $errors->add(
            'terms_required',
            'You must accept the terms and conditions to continue.'
        );
    }

    return $errors;
}, 10, 3 );
```

### Validate Tax Identifier

Use the built-in `tax_identifier` field to validate VAT/tax ID numbers:

```php theme={null}
add_filter( 'surecart/checkout/validate', function( $errors, $args, $request ) {
    $tax_identifier = $args['tax_identifier'] ?? [];
    $billing_address = $args['billing_address'] ?? [];

    // Require tax ID for EU business customers
    $eu_countries = [ 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE' ];

    $country = $billing_address['country'] ?? '';

    if ( in_array( $country, $eu_countries, true ) ) {
        // Check if tax identifier number is provided
        if ( empty( $tax_identifier['number'] ) ) {
            $errors->add( 'tax_id_required', 'A VAT number is required for EU business purchases.' );
        }
    }

    return $errors;
}, 10, 3 );
```

### Force Test Mode for Non-Production

```php theme={null}
add_filter( 'surecart/payments/mode', function( $mode ) {
    // Always use test mode unless on production
    $allowed_hosts = [ 'www.example.com', 'example.com' ];

    if ( ! in_array( $_SERVER['HTTP_HOST'], $allowed_hosts ) ) {
        return 'test';
    }

    return $mode;
} );
```

## JavaScript Filter Use Cases

### Sell Only to Specific Countries

Restrict your checkout to only allow purchases from certain countries:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_countries",
  "my-customization",
  (countries) => {
    // Only sell to North American countries
    const allowedCountries = ["US", "CA", "MX"];
    return countries.filter((country) =>
      allowedCountries.includes(country.value)
    );
  }
);
```

### Exclude Specific Countries

Remove certain countries from the dropdown while keeping all others:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_countries",
  "my-customization",
  (countries) => {
    // Exclude countries you don't ship to
    const excludedCountries = ["RU", "BY", "KP"];
    return countries.filter(
      (country) => !excludedCountries.includes(country.value)
    );
  }
);
```

### Change County Name Translations

Change the transled country names to something different.

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_countries",
  "my-customization",
  (countries) => {
    // Spanish translations for common countries
    const translations = {
      US: "Estados Unidos",
      CA: "Canadá",
      MX: "México",
      ES: "España",
      GB: "Reino Unido",
      FR: "Francia",
      DE: "Alemania",
    };

    return countries.map((country) => ({
      value: country.value,
      label: translations[country.value] || country.label,
    }));
  }
);
```

### Prioritize Common Countries

Move your most common customer countries to the top of the list:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_address_countries",
  "my-customization",
  (countries) => {
    const priorityCountries = ["US", "CA", "GB", "AU"];

    // Separate priority and other countries
    const priority = countries.filter((c) =>
      priorityCountries.includes(c.value)
    );
    const others = countries.filter(
      (c) => !priorityCountries.includes(c.value)
    );

    // Sort priority countries in specified order
    priority.sort(
      (a, b) =>
        priorityCountries.indexOf(a.value) - priorityCountries.indexOf(b.value)
    );

    return [...priority, ...others];
  }
);
```

### Prioritize Card Payments

Show card payment first for faster checkout:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_payment_method_order",
  "my-customization",
  (paymentMethodOrder, checkout) => {
    return ["card", "apple_pay", "google_pay", "klarna", "afterpay_clearpay"];
  }
);
```

### Disable Link Wallet for Subscriptions

Hide Stripe Link for recurring payments:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_wallets",
  "my-customization",
  (wallets, checkout) => {
    // Check if checkout contains a subscription
    const hasSubscription = checkout?.line_items?.data?.some(
      (item) => item.price?.recurring_interval
    );

    if (hasSubscription) {
      return {
        ...wallets,
        link: "never",
      };
    }

    return wallets;
  }
);
```

### Show Bank Terms for High-Value Orders

Always display ACH terms for orders over a certain amount:

```javascript theme={null}
wp.hooks.addFilter(
  "surecart_stripe_payment_element_terms",
  "my-customization",
  (terms, checkout) => {
    // Show bank account terms for orders over $500
    if (checkout?.total_amount > 50000) {
      return {
        ...terms,
        usBankAccount: "always",
      };
    }
    return terms;
  }
);
```

## Modifying Templates

You can customize the HTML output of SureCart blocks using WordPress's `render_block` filter and the HTML Tag Processor.

<Card title="Templates" icon="code" href="/documentation/actions-filters/templates">
  Learn how to modify block HTML, add custom attributes, wrap content, and inject elements into templates.
</Card>

## Related

<CardGroup cols={2}>
  <Card title="Cart" icon="cart-shopping" href="/documentation/actions-filters/cart">
    Customize cart icon and visibility.
  </Card>

  <Card title="Login" icon="right-to-bracket" href="/documentation/actions-filters/login">
    Customize login redirects and authentication.
  </Card>

  <Card title="Purchases" icon="bag-shopping" href="/documentation/actions-filters/purchases">
    Hook into individual purchase events.
  </Card>

  <Card title="Orders" icon="receipt" href="/documentation/actions-filters/orders">
    Hook into order and payment events.
  </Card>

  <Card title="Templates" icon="code" href="/documentation/actions-filters/templates">
    Modify blocks, shortcodes, and inject content.
  </Card>
</CardGroup>
