Mercado Pago

Mercado Pago

Mercado Pago es una plataforma de pagos en línea desarrollada por Mercado Libre que permite a usuarios y comerciantes realizar y recibir pagos de manera segura y eficiente.


Operaciones soportadas

OperaciónDescripción
Pago directoCombina autorización y captura en un solo paso.
ReembolsoDevuelve el monto completo de una transacción.
Reembolso parcialDevuelve parte del monto capturado.

Integración

1. Configurar Mercado Pago como método de pago

Para realizar la integración de Mercado Pago, 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 Mercado Pago.


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 Mercado Pago 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.
  • completed_redirect_url: Esta URL es utilizada para redireccionar al usuario una vez que el pago ha sido completado con éxito.
  • canceled_redirect_url: Esta URL es utilizada para redireccionar al usuario si decide no completar el pago.

📘

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>MercadoPago 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 MercadoPago 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"></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"></small>
            </section>

            <section class="pt-3 col-12">
              <label for="completed-redirect-url" class="form-label">
                Completed redirect URL&nbsp;<span class="text-danger">*</span>
              </label>
              <input
                type="text"
                id="completed-redirect-url"
                class="form-control"
              />
              <small
                id="completed-redirect-url-error"
                class="text-danger"
              ></small>
            </section>

            <section class="pt-3 col-12">
              <label for="canceled-redirect-url" class="form-label">
                Canceled redirect URL&nbsp;<span class="text-danger">*</span>
              </label>
              <input
                type="text"
                id="canceled-redirect-url"
                class="form-control"
              />
              <small
                id="canceled-redirect-url-error"
                class="text-danger"
              ></small>
            </section>

            <section class="pt-3 col-12">
              <article class="form-check">
                <input type="checkbox" id="redirect" class="form-check-input" />
                <label for="redirect" class="form-check-label">
                  Redirect
                </label>
              </article>
            </section>
          </div>
        </article>
        <article
          id="button-container"
          class="pt-3 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 completedRedirectUrl = document.getElementById(
        'completed-redirect-url'
      );
      const canceledRedirectUrl = document.getElementById(
        'canceled-redirect-url'
      );
      const redirect = document.getElementById('redirect');    

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

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

        const mercadoPagoButton = document.createElement(
          'orkestapay-mercadopago-button'
        );
        await customElements.whenDefined('orkestapay-mercadopago-button');

        mercadoPagoButton.params = {
          payment_details: { createPayment, confirmPayment },
          theme: { button_color: 'default' },
        };

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

      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;

        let settings = null;
        if (redirect.checked) {
          const completed_redirect_url = completedRedirectUrl.value.trim();
          const canceled_redirect_url = canceledRedirectUrl.value.trim();
          if (completed_redirect_url && canceled_redirect_url) {
            settings = {
              redirection_url: {
                completed_redirect_url,
                canceled_redirect_url,
              },
            };
          }
        }

        const url = `${orkestaPay.getApiUrl()}/v1/payments`;
        const Authorization = await authenticate();
        const body = JSON.stringify({
          payment_source: {
            payment_method_id,
            type: 'MERCADO_PAGO',
            ...(settings && { settings }),
          },
          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: "<REPLACE_WITH_AN_EMAIL>"
          },
          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 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

  • Es una función asincrónica autoejecutable que:
    1. Inicializa el formulario con valores predeterminados (initFormValue()).
    2. Configura el SDK de OrkestaPay (initOrkestaPay).
    3. Crea un botón de MercadoPago (orkestapay-mercadopago-button) como un Web Component.
    4. Configura los parámetros del botón:
      • payment_details: Define funciones para crear y confirmar pagos.
      • theme: Estiliza el botón.
    5. Inserta el botón en el contenedor (button-container).

Operaciones de backend

Autenticación (authenticate)
  • Obtiene un token de acceso enviando las credenciales (client_id, client_secret) a la API.
  • Devuelve el token para autorizar otras solicitudes.

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

Creación de Pagos (createPayment)
  • Pasos:
    1. Obtiene el payment_method_id.
    2. Registra un pedido (createOrder).
    3. Obtiene información del dispositivo (getDeviceInfo).
    4. Si el checkbox redirect está activado, incluye URLs de redirección.
    5. Realiza una solicitud POST a la API para crear el pago.
    6. Procesa la respuesta y muestra los detalles del pago (handlePaymentCreated).

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

Manejo de Respuestas y Errores

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