Recargo al cliente al pagar con paypal en una tienda Prestashop 1.5


Paypal es un método de pago bastante habitual en Internet, y por extendido, normalmente querremos añadirlo a nuestra tienda. Es bastante sencillo, tiene su módulo oficial, rellenamos 3 datos de nuestra cuenta paypal y listo. El problema son las comisiones, entre fijas y por baremos, seguramente paypal se lleve casi un 4% de nuestra venta. Por eso hay tiendas que optan por cargar ese porcentaje al cliente como recargo. Esta opción de cargar un extra según la opción de pago no está preparado en prestashop, pero vamos a ver cómo modificar el código del módulo de paypal para llevarlo a cabo. Es una primera aproximación, y lo describimos según donde va el código original, aunque para no tener problemas con las actualizaciones sería mejor poner overrides.


Al instalar el módulo de paypal por el administrador de prestashop, quedará a nombre del usuario del servidor web (www-root, apache o similar), por lo que no podremos modificar el código del módulo a través de ftp. Si nos sucede esto hay dos maneras de proceder:

Hacer un zip de paypal cada vez que hagamos un cambio e intentar volver a instalarlo. Si hubiera cualquier problema en el código, es posible que nos carguemos toda la página y no se pueda acceder a la tienda ni al panel de administrador. Así que no podríamos corregirlo por ftp ni por panel. Mala pinta. La solución si pasa esto es la segunda opción. (Añadiendo el borrar la fila de paypal en la tabla de módulos de la BBDD si la cosa se pone fea).

Hacer un script en php para borrar los archivos del módulo de paypal (en los comentarios de la documentación de rmdir hay una función para borrar recursivo) y subir nuestros propios ficheros por ftp. Así el módulo sigue instalado, pero los ficheros van a nombre del usuario ftp y ya podemos irlos modificando por ftp para hacer pruebas más rápidas.
1. Añadir en la lista de métodos de pagos el recargo que conlleva paypal

1a. Añadir mensaje en la plantilla
Express_checkout_payment.tpl en: (views/templates/hook)

Añadir un texto con una variable en la línea que dice:

{$PayPal_content.payment_choice}

Podría quedar algo como:

{$PayPal_content.payment_choice} (Se aplicará un recargo de {$PayPal_content.recargo} €)

La variable recargo nos la hemos inventado, habrá que darle valor en el código php. Si tenemos activo Ajax en el carrito podríamos quitar productos del carrito sin que recargue la página, ni se recalcule el recargo. En estos casos es más sencillo poner “Recargo del 3%” que la cifra ya calculada.

1b. Añadir la variable recargo y calcularla.
Paypal.php

Al principio, en la lista de variables, añadimos 2 varibales, una $porcentajeRecargo, donde especificaremos ese porcentaje a aplicar y la segunda $recargo, donde quedará el recargo que calculemos, ya en euros.

En el método hookPayment, en la parte del elseif (línea 462 aprox.) añadimos el siguiente código:

$cart = $this->context->cart;
$cart_details = $cart->getSummaryDetails(null, true);

$p=new Product(1);
$iva=1+($p->getTaxesRate()/100);
$rec=number_format(($cart_details['total_price']*$this->porcentajeRecargo)/100,2,'.','');
$rec=number_format($rec/$iva,2,'.','');
$this->recargo=number_format($rec*$iva,2,'.','');

Cargamos el carrito para saber el total de la compra hasta ahora, y el producto 1, será nuestro producto Recargo paypal que crearemos previamente y dejaremos desactivado para que no aparezca en la tienda. Este producto tiene el % de iva a aplicar (21% ahora mismo). El recargo que se calcula es el total que queremos aplicar, pero ese recargo lleva su iva, por lo que cuando lo grabemos en el producto lo tenemos que hacer sin iva, a veces esta operación de calcular el precio sin iva y que después se calcule con iva otra vez, hace que por redondeo el precio del recargo cambie 1 céntimo. El céntimo como tal no es un valor importante, sin embargo podemos tener problemas de rechazos de pagos en prestashop por ese céntimo, y aunque no los tuviéramos, es raro una factura de 1 céntimo más de lo cobrado, lo suyo es que siempre coincida hasta el céntimo. Por eso cada vez que calcule el recargo, le quito el iva y se lo vuelvo a poner para que quede exacto.

En la línea 734 (o alrededores dependiendo cómo separemos todo el código anterior), antes de:

$content += $default;

Añadimos la variable recargo al array, con el valor calculado.

$default['recargo']=$this->recargo;

Para que se pueda usar desde la plantilla para mostratr al usuario.

2. Mandar a paypal el recargo como un producto

Una vez que el cliente selecciona paypal, la tienda redirige a paypal, y claro allí tiene que aparecer ese recargo para poder cobrárselo.

process.php (en express_checkout)

Añadimos el siguiente código dentro de _setPaymentDetails debajo de la línea:

$this->setProductsList($fields, $index, $total, $taxes);

el siguiente código:

$cart = $this->context->cart;
$cart_details = $cart->getSummaryDetails(null, true);

$p=new Product(1);
$iva=1+($p->getTaxesRate()/100);
$rec=number_format(($cart_details['total_price']*$this->porcentajeRecargo)/100,2,'.','');
$rec=number_format($rec/$iva,2,'.','');
$this->recargo=number_format($rec*$iva,2,'.','');
$fields['L_PAYMENTREQUEST_0_NAME'.++$index] = "Recargo uso paypal";
$fields['L_PAYMENTREQUEST_0_AMT'.$index] = $this->recargo;
$total = $total + $fields['L_PAYMENTREQUEST_0_AMT'.$index];

Recalculo el recargo porque el objeto suele ser distinto, y por si han cambiado el carrito por ajax. Esa parte es análoga a la del primer paso. Después se rellenan las variables del nuevo producto, que será el último. Tiene más campos per solo relleno nombre y cantidad, hay que recalcular el total de la compra con el nuevo recargo.

3. Registrar el recargo en el encargo final al volver de paypal

3a. Añadir el recargo al carrito

Hay que añadir el recargo al carrito para que la compra coincida con la de paypal y no de error la tienda.

Creo el siguiente método en process.php (en express_checkout) para añadir el producto a la lista de productos del objeto de proceso de pago, como no es un objeto producto de verdad, sino un array, lo creo al vuelo:

public function addRecargoPaypal(){
  $cart = $this->context->cart;
  $cart_details = $cart->getSummaryDetails(null, true);

  $p=new Product(1);
  $iva=1+($p->getTaxesRate()/100);   $rec=number_format(($cart_details['total_price']*$this->porcentajeRecargo)/100,2,'.','');
  $rec=number_format($rec/$iva,2,'.','');
  $this->recargo=number_format($rec*$iva,2,'.','');

  $producto_recargo_paypal=array();
  $producto_recargo_paypal['id_product_atribute']=0;
  $producto_recargo_paypal['id_product']=1;
  $producto_recargo_paypal['cart_quantity']=1;
  $producto_recargo_paypal['quantity']=1;
  $producto_recargo_paypal['id_shop']=1;
  $producto_recargo_paypal['name']= "Recargo uso paypal";
  $producto_recargo_paypal['id_supplier']=0;
  $producto_recargo_paypal['id_manufacturer']=0;
  $producto_recargo_paypal['total']=$this->recargo;
  $producto_recargo_paypal['price_wt']=$this->recargo;
  $producto_recargo_paypal['total_wt']=$this->recargo;
  $this->product_list[]=$producto_recargo_paypal;
}

Aquí solo se pone el precio completo, con IVA, porque es para que coincidan cantidades, no se usa este nuevo producto para mucho.

3b. Función para que la llave del proceso no de error

El objeto proceso de pago tiene una llave de seguridad, una suma md5, para comprobar que no se ha añadido ni quitado nada del carrito que teníamos antes de pagar. Pero nosotros añadimos el recargo, por lo que hay que recalcular esa llave para que no de error. Añado la siguiente función a process.php (en express_checkout).

public function updateSecureKey(){
  $this->secure_key = $this->getSecureKey();
  $this->_storeCookieInfo();
}

3c. Añadir el recargo al carrito y a la orden

En el archivo payment.php (en express_checkout) hacemos todo lo referente a añadir el recargo a los diferentes sitios, usando las funciones anteriores entre otras. Después de la instrucción

$ppec->doExpressCheckout();

más o menos en la línea 287, añadimos:

$ppec->addRecargoPaypal();
$rp=$ppec->product_list[count($ppec->product_list)-1];

$p=new Product(1);

$iva=1+($p->getTaxesRate()/100);
$p->price=number_format($rp['total']/$iva,2,'.','');
$p->updateWs();
$cart->updateQty(1,1);
$ppec->updateSecureKey();
$cart->getPackageList(true);

Primero añadimos el recargo de paypal como producto al proceso. Después cargamos ese producto, para usar aquí su precio en $rp, hay que tener en cuenta que ese producto es un array, no un objeto producto. Después cargamos el producto 1, el que hemos creado para el recargo. Le ponemos el precio del recargo sin IVA. Después actualizamos el producto para que se guarde el precio, y en el carrito añadimos 1 unidad del producto. Al haber modificado el carrito hay que actualizar la llave del proceso, y por último actualizar la lista de paquetes, el true como argumento es importante, ya que indica que hay que desechar la cache y volver a generar la lista de paquetes con el nuevo producto. Si no hacemos esto, aunque tengamos el producto en el carrito, no llegará al detalle de la orden, ya que pasa a orden los paquetes, no la lista de productos del carrito.

Hay que tener en cuenta que con esta forma de hacerlo, en casos de mucho tráfico podrían darse casos de carreras. Si dos personas hacen el pago por paypal a la vez, que la primera modifique el precio del recargo y lo guarde en la base de datos y la segunda lo haga también antes de que la primera llegue a completar el paso a orden. En este tipo de casos extremos una posible solución sería crear productos al vuelo, uno por pedido, para cada recargo e irlos borrando al acabar.


Con esto, y si no se me ha olvidado ningún paso, se consigue tener un recargo funcional al pagar por paypal en prestashop. Si bien es una primera aproximación y se podría mejorar ciertos aspectos para hacer un funcionamiento más fino.