PayPal

PayPal es una plataforma que actúa como intermediario en transacciones de pago en línea, permitiendo a los usuarios realizar y recibir pagos de manera segura y rápida.

En esencia, PayPal simplifica el proceso de pago digital proporcionando una experiencia segura, global y conveniente para compradores y vendedores.


Operaciones soportadas

OperaciónDescripción
Autorización de pagoBloquea fondos en la tarjeta del cliente.
Captura de pagoCompleta la transacción y transfiere los fondos.
Pago directoCombina autorización y captura en un solo paso.
ReembolsoDevuelve el monto completo de una transacción.
Reembolso parcialDevuelve parte del monto capturado.
Cancelación de autorizaciónRevierte una autorización que no fue capturada.

Integración

1. Configurar PayPal como método de pago

Para realizar la integración de PayPal, primero deberás de realizar una serie de configuraciones desde el portal de OrkestaPay. Visita nuestra guía de configuración de Proveedores y busca el método de pago PayPal.


2. Implementar botón de pago

Para realizar la tokenización de los datos de pago hay que seguir los siguientes pasos:

  1. Incluir scripts de OrkestaPay: Utilizar los scripts de OrkestaPay en tu aplicación web.
  2. Inicializar parámetros: Aquí se definen las credenciales de OrkestaPay, así como datos referentes al pago a procesar.
  3. Renderizar el botón de pago: Colocar el botón de PayPal en tu frontend.

Recursos necesarios:

CSS

  • https://checkout.orkestapay.com/web/components/payment-methods/styles.css

JS

  • https://checkout.orkestapay.com/script/orkestapay.js
  • https://checkout.orkestapay.com/web/components/payment-methods/main.js

Ejemplo de código

En el siguiente snippet de código hay ciertos parámetros que son necesarios de reemplazar con los datos de tu comercio:

  • public_key : Llave pública, estas credenciales las puedes obtener en el portal de OrkestaPay.
  • merchant_id: ID del comercio, estas credenciales las puedes obtener en el portal de OrkestaPay.
  • is_sandbox: Define si las credenciales que estás por operar son de pruebas o productivas.

En este ejemplo se capturan credenciales sensibles en un formulario, estas credenciales deberán de ser manejadas desde el backend.

  • client_id : Esta llave en conjunto con la llave secreta, permitirá la autenticación para poder comunicarse con el API de OrkestaPay. Es utilizada en el servidor.
  • client_secret: Esta llave en conjunto con la llave de acceso, permitirá la autenticación para poder comunicarse con el API de OrkestaPay. Es utilizada en el servidor.

📘

INFORMACIÓN

Para consultar las credenciales de OrkestaPay, puedes visitar nuestra guía Obtener llaves de API.


<!DOCTYPE html>
<html lang="en">
  <head>
    <base href="/" />
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="apple-touch-icon" type="image/x-icon" href="favicon.ico" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <title>PayPal web component</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
      integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="https://checkout.orkestapay.com/web/components/payment-methods/styles.css" />
    <style>
      .code {
        color: inherit;
        overflow-wrap: break-word;
        white-space: pre-wrap;
      }
    </style>
  </head>
  <body>
    <main class="container">
      <section class="row mt-5">
        <h1 id="card-title" class="col-12 text-center">
          OrkestaPay payment method PayPal web component
        </h1>
      </section>

      <section class="row">
        <article class="col-12 col-md-6 offset-md-3">
          <div class="row">
            <section class="pt-3 col-12 col-md-6">
              <label for="client-id" class="form-label">
                OrkestaPay client ID&nbsp;<span class="text-danger">*</span>
              </label>
              <input type="text" id="client-id" class="form-control" />
              <small id="client-id-error" class="text-danger">&nbsp;</small>
            </section>

            <section class="pt-3 col-12 col-md-6">
              <label for="client-secret" class="form-label">
                OrkestaPay client secret&nbsp;<span class="text-danger">*</span>
              </label>
              <input type="password" id="client-secret" class="form-control" />
              <small id="client-secret-error" class="text-danger">&nbsp;</small>
            </section>
          </div>
        </article>
        <article
          id="button-container"
          class="col-12 col-md-6 offset-md-3"
        ></article>
        <article class="pt-3 col-12">
          <div id="payment-details" class="w-100 alert" role="alert"></div>
        </article>
      </section>
    </main>

    <noscript>
      Please enable JavaScript to continue using this application.
    </noscript>

    <script type="text/javascript" src="https://checkout.orkestapay.com/script/orkestapay.js"></script>
    <script
      type="text/javascript"
      src="https://checkout.orkestapay.com/web/components/payment-methods/main.js"
    ></script>
    <script type="text/javascript">
      //#region FRONTEND

      let orkestaPay;
      let paymentId = null;

      const clientId = document.getElementById('client-id');
      const clientSecret = document.getElementById('client-secret');      

      const {
        public_key,
        merchant_id,
        is_sandbox,
        country_code,
        currency,
        amount,
      } = initValues();

      (async function main() {        
        orkestaPay = initOrkestaPay({ is_sandbox, merchant_id, public_key });

        const payPalButton = document.createElement('orkestapay-paypal-button');
        await customElements.whenDefined('orkestapay-paypal-button');

        payPalButton.params = {
          payment_details: { currency, createPayment, confirmPayment },
          theme: {
            color: 'gold',
            height: 48,
            shape: 'rect',
          },
        };

        const container = document.getElementById('button-container');
        container.appendChild(payPalButton);
      })();

      function handlePaymentCreated(payment) {
        const code = document.createElement('code');
        code.innerHTML = JSON.stringify(payment, null, 2);
        code.classList.add('code');

        const paymentDetails = document.getElementById('payment-details');
        paymentDetails.classList.add('alert-success');
        paymentDetails.replaceChildren(code);

        paymentId = payment.payment_id;
        return paymentId ?? null;
      }

      function initValues() {
        const url = new URL(window.location.href);
        return {
          public_key: "<REPLACE_WITH_YOUR_PUBLIC_KEY>",
          merchant_id: "<REPLACE_WITH_YOUR_MERCHANT_ID>",
          is_sandbox: true,
          country_code: "MX",
          currency: "MXN",
          amount: "100.00",
        };
      }      

      //#endregion FRONTEND

      //#region BACKEND

      async function createPayment(paymentMethod) {
        if (!paymentMethod) return null;
        const { payment_method_id } = paymentMethod;

        const order = await createOrder();
        if (!order) return null;
        const { order_id } = order;

        const deviceInfo = await orkestaPay.getDeviceInfo();
        if (!deviceInfo) return;
        const { device_session_id } = deviceInfo;

        const url = `${orkestaPay.getApiUrl()}/v1/payments`;
        const Authorization = await authenticate();
        const body = JSON.stringify({
          payment_source: { type: 'PAYPAL', payment_method_id },
          device_session_id,
          order_id,
        });

        return fetch(url, {
          method: 'POST',
          headers: {
            Authorization,
            'Content-Type': 'application/json',
            'Idempotency-Key': crypto.randomUUID(),
          },
          body,
        })
          .then(handleFetchResponse)
          .then(handlePaymentCreated)
          .catch(handleFetchError);
      }

      async function confirmPayment() {
        if (!paymentId) return;

        const url = `${orkestaPay.getApiUrl()}/v1/payments/${paymentId}/confirm`;
        const Authorization = await authenticate();
        const body = JSON.stringify({});

        return fetch(url, {
          method: 'POST',
          headers: {
            Authorization,
            'Content-Type': 'application/json',
            'Idempotency-Key': crypto.randomUUID(),
          },
          body,
        })
          .then(handleFetchResponse)
          .then(handlePaymentCreated)
          .catch(handleFetchError);
      }

      async function createOrder() {
        const url = `${orkestaPay.getApiUrl()}/v1/orders`;
        const Authorization = await authenticate();
        const body = JSON.stringify({
          merchant_order_id: crypto.randomUUID(),
          subtotal_amount: amount,
          total_amount: amount,
          country_code,
          customer: {
            email: "[email protected]",
          },
          currency,
          products: [
            {
              product_id: crypto.randomUUID(),
              quantity: 1,
              unit_price: amount,
              name: 'A random product',
            },
          ],
        });

        return fetch(url, {
          method: 'POST',
          headers: { Authorization, 'Content-Type': 'application/json' },
          body,
        })
          .then(handleFetchResponse)
          .catch(handleFetchError);
      }

      async function authenticate() {
        const url = `${orkestaPay.getApiUrl()}/v1/oauth/tokens`;
        const body = JSON.stringify({
          client_id: clientId.value.trim(),
          client_secret: clientSecret.value.trim(),
          grant_type: 'client_credentials',
        });

        const response = await fetch(url, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body,
        })
          .then(handleFetchResponse)
          .catch(handleFetchError);
        if (!response) return null;

        const { token_type, access_token } = response;
        return `${token_type} ${access_token}`;
      }

      function handleFetchResponse(response) {
        return response.ok ? response.json() : Promise.reject(body);
      }

      function handleFetchError(error) {
        console.error(error);
        return null;
      }

      //#endregion BACKEND
    </script>
  </body>
</html>

📘

NOTA

Este código es solo con fines demostrativos, que integra código de frontend y código que debería de ejecutarse desde un backend, por lo tanto es tú responsabilidad realizar una implementación con buenas prácticas.


Explicación del código

Variables y Funciones Iniciales

  • orkestaPay: Objeto principal para interactuar con la API de OrkestaPay.
  • paymentId: Almacena el ID del pago actual.
  • initValues(): Configuración inicial, como claves y detalles de transacción (monto, moneda, etc.).

Función main

  • Función autoejecutable que:
    1. Inicializa OrkestaPay con valores iniciales.
    2. Crea un botón de PayPal como un Web Component (<orkestapay-paypal-button>).
    3. Configura parámetros del botón:
      • payment_details: Define las funciones para crear y confirmar pagos.
      • theme: Personalización visual (color, forma, altura).
    4. Agrega el botón al contenedor (button-container).

Operaciones de backend

Autenticación (authenticate)
  • Genera un token de acceso:
    • Envía las credenciales del cliente (client_id y client_secret) a la API.
    • Devuelve un token que se usa para autorizar las solicitudes.

Registro de Pedidos (createOrder)
  • Crea un nuevo pedido:
    • Incluye información básica como el monto total, moneda, y productos (ficticios en este caso).

Creación de Pago (createPayment)
  • Recibe un método de pago (paymentMethod).
  • Pasos:
    1. Registra un pedido (createOrder).
    2. Obtiene información del dispositivo.
    3. Envía una solicitud POST a la API para crear un pago.
    4. Maneja la respuesta y actualiza los detalles del pago (handlePaymentCreated).

Confirmación de Pagos (confirmPayment)
  • Confirma el pago previamente creado mediante su paymentId.

Manejo de respuestas y errores

  • handleFetchResponse(response): Devuelve el cuerpo de la respuesta si es exitosa; en caso contrario, rechaza la promesa.
  • handleFetchError(error): Muestra errores en la consola.

Pruebas

Para finalizar una prueba de integración deberás de contar con una cuenta de sandbox de PayPal de tipo personal.

📘

NOTA

Para más información sobre pruebas con PayPal dirígete a su documentación oficial: https://developer.paypal.com/tools/sandbox/