---
title: Personalizar el checkout — campos, pasos y validaciones
section: examples
slug: checkout-customization
description: Anadir campos custom al checkout de PrestaShop, validar datos, crear pasos adicionales y modificar el flujo de compra via hooks y overrides.
keywords: prestashop checkout personalizar campo custom validacion paso step hook modulo
last_updated: 2025-01-15
source_url: "https://ayudaprestashop.es/examples/checkout-customization"
---

# Personalizar el checkout — campos, pasos y validaciones

> Anadir campos custom al checkout de PrestaShop, validar datos, crear pasos adicionales y modificar el flujo de compra via hooks y overrides.

## Hooks disponibles en el checkout

| Hook | Posicion | Uso tipico |
| --- | --- | --- |
| displayPersonalInformationTop | Arriba del paso de login | Banner, aviso legal, promocion |
| additionalCustomerFormFields | Formulario de registro | Campos: NIF, fecha nacimiento, empresa |
| displayCheckoutSummaryTop | Arriba del resumen | Cupones destacados, mensaje |
| displayBeforeCarrier | Antes de seleccion de envio | Info de envio, estimacion |
| displayAfterCarrier | Despues de seleccion de envio | Seguro de envio, recogida en tienda |
| displayPaymentTop | Antes de metodos de pago | Mensaje de seguridad |
| displayPaymentByBinaries | Metodos de pago | Pagos propios |
| displayOrderConfirmation | Pagina de confirmacion | Tracking, upsell, encuesta |
| actionValidateOrder | Al confirmar pedido | Logica post-compra, integraciones |
| displayExpressCheckout | Boton de compra rapida | PayPal Express, Apple Pay, Google Pay |

## Anadir campo al formulario de direccion

*Campo NIF/CIF en el formulario de direccion*

```php
<?php
/**
 * Hook additionalCustomerFormFields
 * Anadir campos al formulario de registro/direccion
 */
public function hookAdditionalCustomerFormFields($params)
{
    // Campo NIF para facturacion
    $nifField = (new FormField())
        ->setName('nif')
        ->setType('text')
        ->setLabel($this->trans('NIF / CIF', [], 'Modules.Ecomcheckout.Shop'))
        ->setRequired(true)
        ->addConstraint('isGenericName')   // Validacion PS
        ->setAvailableValues([])           // Sin opciones predefinidas
        ->setMaxLength(20);

    // Campo tipo de cliente
    $typeField = (new FormField())
        ->setName('customer_type')
        ->setType('select')
        ->setLabel($this->trans('Tipo de cliente', [], 'Modules.Ecomcheckout.Shop'))
        ->setRequired(true)
        ->setAvailableValues([
            'particular' => 'Particular',
            'empresa'    => 'Empresa',
            'autonomo'   => 'Autonomo',
        ]);

    return [$nifField, $typeField];
}

/**
 * Hook actionSubmitAccountBefore — validar antes de guardar
 */
public function hookActionSubmitAccountBefore($params)
{
    $nif = Tools::getValue('nif');

    // Validar formato NIF/CIF espanol
    if (!$this->isValidNif($nif)) {
        // Anadir error al formulario
        $params['errors'][] = $this->trans(
            'El NIF/CIF no tiene un formato valido.',
            [], 'Modules.Ecomcheckout.Shop'
        );
    }
}

/**
 * Hook actionObjectCustomerAddAfter — guardar campo custom
 */
public function hookActionObjectCustomerAddAfter($params)
{
    $customer = $params['object'];
    $nif = Tools::getValue('nif');
    $type = Tools::getValue('customer_type');

    if ($nif) {
        // Guardar en tabla propia
        Db::getInstance()->insert('ecom_customer_extra', [
            'id_customer'   => (int) $customer->id,
            'nif'           => pSQL($nif),
            'customer_type' => pSQL($type),
            'date_add'      => date('Y-m-d H:i:s'),
        ], false, true, Db::ON_DUPLICATE_KEY);
    }
}

protected function isValidNif(string $nif): bool
{
    $nif = strtoupper(trim($nif));
    // DNI: 8 digitos + letra
    if (preg_match('/^[0-9]{8}[A-Z]$/', $nif)) return true;
    // NIE: X/Y/Z + 7 digitos + letra
    if (preg_match('/^[XYZ][0-9]{7}[A-Z]$/', $nif)) return true;
    // CIF: letra + 8 digitos
    if (preg_match('/^[ABCDEFGHJNPQRSUVW][0-9]{7}[0-9A-J]$/', $nif)) return true;
    return false;
}
```

## Paso de checkout personalizado

En PS 1.7+ el checkout es modular. Cada paso es una clase que extiende `AbstractCheckoutStep`. Puedes anadir pasos nuevos sobreescribiendo el `CheckoutProcess` o via hooks que inyectan contenido en pasos existentes.

*Inyectar contenido en el paso de envio*

```php
<?php
/**
 * displayAfterCarrier — opciones extra despues de elegir transportista
 */
public function hookDisplayAfterCarrier($params)
{
    $cart = $this->context->cart;
    $carrier = new Carrier((int) $cart->id_carrier);

    // Solo mostrar para transportistas con recogida
    if (!$carrier->is_module) {
        return '';
    }

    $this->context->smarty->assign([
        'carrier_name'  => $carrier->name,
        'delivery_days' => $carrier->delay[$this->context->language->id] ?? '',
        'show_insurance' => true,
        'insurance_price' => Tools::displayPrice(4.99),
    ]);

    return $this->display(__FILE__, 'views/templates/hook/after-carrier.tpl');
}
```

## Validar datos antes de confirmar pedido

*actionValidateOrder — validacion y logica post-pedido*

```php
<?php
/**
 * Este hook se ejecuta DESPUES de que el pedido se crea en la BD.
 * No puedes cancelar el pedido aqui, pero puedes:
 * - Enviar datos a ERP/CRM
 * - Registrar la venta en analytics
 * - Asignar puntos de fidelidad
 * - Actualizar inventario externo
 */
public function hookActionValidateOrder($params)
{
    /** @var Order $order */
    $order    = $params['order'];
    /** @var Customer $customer */
    $customer = $params['customer'];
    /** @var Currency $currency */
    $currency = $params['currency'];
    /** @var OrderState $orderStatus */
    $orderStatus = $params['orderStatus'];

    // Ejemplo 1: Enviar a webhook externo (ERP, Zapier, etc.)
    $payload = json_encode([
        'event'     => 'order.created',
        'order_id'  => $order->id,
        'reference' => $order->reference,
        'total'     => $order->total_paid_tax_incl,
        'currency'  => $currency->iso_code,
        'customer'  => [
            'id'    => $customer->id,
            'email' => $customer->email,
            'name'  => $customer->firstname . ' ' . $customer->lastname,
        ],
        'products'  => $this->getOrderProducts($order),
    ]);

    // Enviar async (no bloquear el checkout)
    $webhookUrl = Configuration::get('ECOM_WEBHOOK_URL');
    if ($webhookUrl) {
        $this->sendWebhookAsync($webhookUrl, $payload);
    }

    // Ejemplo 2: Asignar puntos de fidelidad
    $points = (int) floor($order->total_paid_tax_incl);
    Db::getInstance()->insert('ecom_loyalty_points', [
        'id_customer' => (int) $customer->id,
        'id_order'    => (int) $order->id,
        'points'      => $points,
        'type'        => 'earn',
        'date_add'    => date('Y-m-d H:i:s'),
    ]);
}

protected function sendWebhookAsync(string $url, string $payload): void
{
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $payload,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 5,       // Max 5 segundos
        CURLOPT_CONNECTTIMEOUT => 3,
    ]);
    curl_exec($ch);
    curl_close($ch);
}
```

## Modificar la pagina de confirmacion

*displayOrderConfirmation — contenido post-compra*

```php
<?php
public function hookDisplayOrderConfirmation($params)
{
    $order = $params['order'];

    // Productos comprados para upselling
    $products = $order->getProducts();
    $categoryIds = array_unique(array_column($products, 'id_category_default'));

    // Sugerir productos de las mismas categorias
    $suggestions = [];
    foreach ($categoryIds as $idCat) {
        $catProducts = Product::getProducts(
            $this->context->language->id,
            0, 4,  // offset, limit
            'position', 'ASC',
            $idCat,
            true   // solo activos
        );
        $suggestions = array_merge($suggestions, $catProducts);
    }

    // Quitar productos ya comprados
    $boughtIds = array_column($products, 'product_id');
    $suggestions = array_filter($suggestions, fn($p) => !in_array($p['id_product'], $boughtIds));
    $suggestions = array_slice($suggestions, 0, 4);

    $this->context->smarty->assign([
        'order_reference' => $order->reference,
        'suggestions'     => $suggestions,
        'module_dir'      => $this->_path,
    ]);

    return $this->display(__FILE__, 'views/templates/hook/order-confirmation.tpl');
}
```

## Carrito: anadir info custom

*hookActionCartSave — guardar datos custom en el carrito*

```php
<?php
/**
 * Guardar datos adicionales cada vez que el carrito se actualiza
 * Ejemplo: mensaje de regalo, fecha de entrega deseada
 */
public function hookActionCartSave($params)
{
    $cart = $params['cart'] ?? $this->context->cart;
    if (!$cart || !$cart->id) return;

    // Solo procesar si viene del checkout
    $giftMessage = Tools::getValue('gift_message');
    $desiredDate = Tools::getValue('desired_delivery_date');

    if ($giftMessage || $desiredDate) {
        Db::getInstance()->insert('ecom_cart_extra', [
            'id_cart'       => (int) $cart->id,
            'gift_message'  => pSQL($giftMessage),
            'desired_date'  => pSQL($desiredDate),
            'date_upd'      => date('Y-m-d H:i:s'),
        ], false, true, Db::ON_DUPLICATE_KEY);
    }
}

/**
 * Recuperar datos en actionValidateOrder para copiarlos al pedido
 */
public function hookActionValidateOrder($params)
{
    $order = $params['order'];

    $extra = Db::getInstance()->getRow(
        'SELECT * FROM `' . _DB_PREFIX_ . 'ecom_cart_extra`
         WHERE id_cart = ' . (int) $order->id_cart
    );

    if ($extra) {
        Db::getInstance()->insert('ecom_order_extra', [
            'id_order'      => (int) $order->id,
            'gift_message'  => pSQL($extra['gift_message']),
            'desired_date'  => pSQL($extra['desired_date']),
            'date_add'      => date('Y-m-d H:i:s'),
        ]);
    }
}
```


---

*Fuente: [https://ayudaprestashop.es/examples/checkout-customization](https://ayudaprestashop.es/examples/checkout-customization). Version Markdown generada automaticamente para consumo por LLMs.*
