Integrar la pasalela de pago de PayPal en Laravel

PayPal es la billetera electrónica por excelencia y es muy utilizada para las compras en líneas, es decir, por Internet y la podemos emplear muy fácilmente en cualquier proyecto web y en Laravel no es la excepción y podemos configurarla fácilmente como una pasalera de pago.

En este apartado vamos a conocer los pasos para configurar PayPal como método de pago en base a algún producto que queramos vender.

Aunque no tenemos un paquete de PayPal exclusivo para Laravel, si tenemos una solución en JavaScript, la cual, no requiere de emplear algún paquete exclusivo para PHP como veremos más adelante:

https://www.npmjs.com/package/@paypal/paypal-js

Lo primero que debemos de hacer es instalar el paquete mediante el siguiente comando, solo si quieres emplear la opción de Node:

$ npm install @paypal/paypal-js

En el libro, usaremos la opción de la CDN.

Claves de acceso y usuarios de prueba

Para poder integrar PayPal en nuestra aplicación, debemos de tener una cuenta en PayPal, una vez conseguido, si vamos al sitio de desarrollo de PayPal:

https://developer.paypal.com/home

Damos click sobre la opción que dice "API Credentials" y crear una aplicación, para ello, presionamos el botón de "Create App" y aparecerá un diálogo como el siguiente:

 

Diálogo para crear una app en PayPa

 

 

Puedes dejar la opción de "Merchant" y "Create App".

Creada las credenciales para poder emplear la API de PayPal, aparecerán listadas en la parte de abajo:

 

Credenciales de PayPal

 

 

Tenemos una clave secreta que la usaremos en el servidor y una pública que la usaremos en el cliente, por lo tanto, quedará expuesta para cualquier persona que vea el código fuente de la página desde su navegador, a su vez, tenemos acceso a unas claves de prueba, con las cuales podemos ir realizando las peticiones a una cuenta de prueba o sandbox.

Aparte de las claves, se generan usuarios de prueba que emplearemos para realizar la conexiones a la API de pruebas de PayPal disponibles en "Sandbox accounts":

 

Cuenta de sandbox

 

En el servidor debemos de configurar las claves de acceso, como recomendación, define las de pruebas como variables de entorno:

.env

PAYPAL_CLIENT_ID="<YOUR_DEV_PAYPAL_CLIENT_ID>"
PAYPAL_SECRET="<YOUR_DEV_PAYPAL_SECRET>"

Y en las configuraciones las as de producción:

config\app.php

return [
   ***
    'paypal_id' => env('PAYPAL_CLIENT_ID', "<YOUR_PRO_PAYPAL_CLIENT_ID>"),
    'paypal_secrect' => env('PAYPAL_SECRET',"<YOUR_PRO_PAYPAL_SECRET>"
),

Implementar un sencillo sistema de pagos

Al emplear este paquete, tenemos que hacer desarrollos en ambos lados, en el cliente y en el servidor., comencemos por el cliente que es en donde hacemos la mayor configuración.

Cliente

En el cliente, comencemos creando un DIV que servirá como elemento contenedor para el widget de PayPal, puedes colocar cualquier identificador que luego colocaremos para referenciar el elemento HTML:

<div id="paypalCard"></div>

La siguiente función permite crear el widget de PayPal, primero, obtenemos una referencia al API de PayPal que usaremos para crear el widget mediante:

paypal = await loadScript

Esto lo tenemos que hacer si trabajamos con el paquete en Node y algunas opciones de la CDN.

El siguiente paso es crear la orden, existen muchos parámetros de configuración pero, en este ejemplo solamente usamos el monto:

{
     amount: {
       value: this.price,
     },
},

Siguiendo con la implementación, tenemos el callback de onApprove() que se ejecutoria cuando el usuario da clicks sobre el widget y autoriza el pago, en el mismo, tenemos referencia a la orden ya creada que consta de la información del pago al igual que la información del cliente que luego utilizaremos en el servidor, por lo tanto, el siguiente paso es pasar esta data al servidor para autorizarla (y es el el servidor donde se emplea la clave privada), por ejemplo, usando una petición por axios:

onApprove: function (data, actions) {
   // TODO send data.orderID to server
}

La implementación con la CDN de PayPal, tenemos dos formatos:

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    src="https://unpkg.com/@paypal/paypal-js@8.0.0/dist/iife/paypal-js.min.js"></script>
</head>

<body>
    <div id="paypalButtons"></div>
    <script>
        window.paypalLoadScript({
            clientId: "{{config('app')['paypal_id']}}"
        }).then((paypal) => {
            paypal.Buttons().render("#paypalButtons");
        });
    </script>
</body>

</html>

O la siguiente que es la que vamos a usar:

resources\views\paypal.blade.php

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <script src="https://www.paypal.com/sdk/js?client-id={{config('app')['paypal_id']}}"></script>
</head>

<body>
    <div id="paypalButtons"></div>
    <script>
        paypal.Buttons({
            createOrder: function(data, actions){
                return actions.order.create({
                    purchase_units:[
                        {
                            amount: {
                                value:50
                            }
                        }
                    ]
                })
            },
            onApprove: function(data, actions){
                // TODO send order to server
                console.log(data.orderID)
                axios.post('paypal-process-order/'+data.orderID)
            }
        }).render("#paypalButtons");
    </script>
</body>

</html>

En el código anterior, enviamos el identificador de la orden mediante axios, para eso, empleamos vite (recordemos que axios viene instalado por defecto en Laravel en el archivo resources\js\bootstrap.js).

Creamos el controlador y la ruta:

routes\web.php

Route::get('/paypal', [PaymentPaypalController::class, 'paypal']);
Route::post('/paypal-process-order/{order}', [PaymentPaypalController::class, 'paypalProcessOrder']);

app\Http\Controllers\PaymentPaypalController.php

<?php

namespace App\Http\Controllers;

class PaymentPaypalController extends Controller
{
    public function paypal()  {
        return view('paypal'); 
    }

    function paypalProcessOrder(string $order)  {
        dd($order);
    }
}

En el controlador puedes ver que hacemos un sencillo condicional para siempre emplear las claves de acceso en producción de PayPal cuando estamos en producción o las de desarrollo cuando estamos en desarrollo.

En caso de que quieras emplear Node, el código, queda como:

function setPaypal() {

    let paypal;
    try {
      paypal = await loadScript({
        "client-id":{{ config('app')['paypal_id']  }},
      });
    } catch (error) {
      console.error("failed to load the PayPal JS SDK script", error);
    }

    if (paypal) {
      try {
        await paypal
          .Buttons({
            createOrder: function (data, actions) {
              // This function sets up the details of the transaction, including the amount and line item details.
              return actions.order.create({
                purchase_units: [
                  {
                    amount: {
                      value: this.price,
                    },
                  },
                ],
              });
            }.bind(this),
            onApprove: function (data, actions) {
                 // TODO send data.orderID to server
            }.bind(this),
          })
          .render("#paypalCard");
      } catch (error) {
        console.error("failed to render the PayPal Buttons", error);
      }
    }
  },
}

Es importante que notes que cuando estás en un ambiente de pruebas, la URL de PayPal indica que está en modo sandbox:

 

Autenticación en Sandbox en PayPal

 

 

https://www.sandbox.paypal.com/checkoutnow?***

 

Y en producción luce de la siguiente manera:

https://www.paypal.com/checkoutnow?***

Con createOrder() creamos la orden, que se crea en base a los datos suministrados (el precio en este ejemplo de 50$) y la autenticación del usuario, que indica que va a pagar la orden, con la función de onApprove() se ejecuta cuando la orden ha sido aprobada y se pasa al servidor para su posterior completación.

Con esto, se genera el plugin de PayPal en el cliente, faltando el procesamiento de la orden del lado del servidor.

- Andrés Cruz

In english

Este material forma parte de mi curso y libro completo; puedes adquirirlos desde el apartado de libros y/o cursos Curso y libro Laravel 11 con Tailwind Vue 3, introducción a Jetstream Livewire e Inerta desde cero - 2024.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.