🛒 Personalizar el checkout — campos, pasos y validaciones
Actualizado: 2025-01-15
#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'),
]);
}
}
Descargar en Markdown
Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.