Skip to content

Instantly share code, notes, and snippets.

@muZk
Created October 5, 2020 01:16
Show Gist options
  • Save muZk/e11931b3df6aab7c7dd6dd53058c3e41 to your computer and use it in GitHub Desktop.
Save muZk/e11931b3df6aab7c7dd6dd53058c3e41 to your computer and use it in GitHub Desktop.
MercadoPago React
import React, { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";
const FORM_ID = 'payment-form';
export default function Product() {
const { id } = useParams(); // id de producto
const [preferenceId, setPreferenceId] = useState(null);
useEffect(() => {
// luego de montarse el componente, le pedimos al backend el preferenceId
axios.post('/api/orders', { productId: id }).then((order) => {
setPreferenceId(order.preferenceId);
});
}, [id]);
useEffect(() => {
if (preferenceId) {
// con el preferenceId en mano, inyectamos el script de mercadoPago
const script = document.createElement('script');
script.type = 'text/javascript';
script.src =
'https://www.mercadopago.cl/integrations/v1/web-payment-checkout.js';
script.setAttribute('data-preference-id', preferenceId);
const form = document.getElementById(FORM_ID);
form.appendChild(script);
}
}, [preferenceId]);
return (
<form id={FORM_ID} method="GET" />
);
}
@florez18399
Copy link

Buen artículo c:

@eduardotomassiwakapi
Copy link

Hola me pregunto para que sirve ese preferenceId?
Porque hay q agregarle ese atributo al script?

@muZk
Copy link
Author

muZk commented May 28, 2021

Hola @eduardotomassiwakapi el preferenceId es como la orden de pago para el usuario actual, así que eso le dice a mercadopago que es lo que va a pagar la persona.

Gracias a eso, toda la interfaz de que carga MercadoPago es en relación con el producto que el usuario quiere comprar.

@eduardotomassiwakapi
Copy link

@muZk gracias por la aclaracion.
Estoy usando la checkout api v2 para cobro sin redireccion a mpago por eso no entendia para que el preferenceID

@marcosdipaolo
Copy link

hola @muZk, gracias por el aporte, igual no se como hacer para renderizar el botón, el cual luego levanta el mnodal de MP, tengo que agregar dos scripts,

<script src="https://sdk.mercadopago.com/js/v2"></script>
          
<script>
// Agrega credenciales de SDK
  const mp = new MercadoPago('PUBLIC_KEY', {
        locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
      preference: {
          id: 'YOUR_PREFERENCE_ID'
      },
      render: {
            container: '.cho-container', // Indica el nombre de la clase donde se mostrará el botón de pago
            label: 'Pagar', // Cambia el texto del botón de pago (opcional)
      }
});
</script>

y no se como agregarlo a un compnente de react y que me funcione

@muZk
Copy link
Author

muZk commented Aug 24, 2021

Hola @marcosdipaolo, no he usado la v2, pero por el código que me dices yo lo haría más o menos así:

import React, { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";

const FORM_ID = 'payment-form';

function addCheckout() {
  const mp = new window.MercadoPago('PUBLIC_KEY', {
    locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
    preference: {
      id: preferenceId,
    },
    render: {
      container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
      label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
  });
}

export default function Product() {
  const { id } = useParams(); // id de producto
  const [preferenceId, setPreferenceId] = useState(null);

  useEffect(() => {
    // luego de montarse el componente, le pedimos al backend el preferenceId
    axios.post('/api/orders', { productId: id }).then((order) => {
      setPreferenceId(order.preferenceId);
    });
  }, [id]);

  useEffect(() => {
    if (preferenceId) {
      // con el preferenceId en mano, inyectamos el script de mercadoPago
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://sdk.mercadopago.com/js/v2';
      script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
      document.body.appendChild(script);
    }
  }, [preferenceId]);

  return (
    <form id={FORM_ID} method="GET" />
  );
}

No he probado el código, pero la idea es agregar el script a mano en el useEffect al igual que mi código original, y luego cuando este cargue, ejecutar la lógica que pusiste tú (que está en la función addCheckout).

@zebaseta
Copy link

Hola! Muy buena la información!!!
Por casualidad, sabés cómo hacer para compilar el const mp = new MercadoPago('PUBLIC_KEY', {
locale: 'es-AR'
});
Básicamente estoy luchando hace un buen rato, ya que no me compila la clase MercadoPago.
Ps: estoy trabajando con Angular.
Gracias!

@muZk
Copy link
Author

muZk commented Aug 26, 2021

@zebaseta buen punto!!! No te compila porque MercadoPago no existe en tiempo de "compilación".

MercadoPago solo existe en tiempo de ejecución luego de que cargue el script agregado con document.body.appendChild(script).

Una posible solución es hacer esto:

const mp = new window.MercadoPago('PUBLIC_KEY', {
  locale: 'es-AR'
});

Debería funcionar, porque window siempre está definido y porque el script carga MercadoPago a dicho objeto.

Quizás el linter de tu proyecto te llora por usar window, pero eso lo puedes suprimir 😂

@zebaseta
Copy link

Buenisimo! Vamos a probar!

@Shumaister
Copy link

Hola!

Estuve probando con este ultimo codigo que contempla de V2 de la sdk, y cuando ejecuto me tira este error en la consola y no entiendo que pasa

Uncaught TypeError: Cannot read property 'childNodes' of null at Ne.render (VM697 v2:1) at gt.render (VM697 v2:1) at new gt (VM697 v2:1) at Dt.checkout (VM697 v2:1) at HTMLScriptElement.addCheckout (mi archivito js :20)

Mi codigo que compila quedo asi:

const FORM_ID = 'payment-form';

export default function Product() {
    const [preferenceId, setPreferenceId] = useState(null);

    const addCheckout = () => {
        const mp = new window.MercadoPago('PUBLIC_KEY', {
            locale: 'es-AR'
        });

        console.log(`mp`, mp)
        console.log(`preferenceId en addChec`, preferenceId)

        // Inicializa el checkout Aca seria la linea 20
        mp.checkout({ 
            preference: {
                id: preferenceId,
            },
            render: {
                container: `${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
                label: 'Pagar', // Cambia el texto del botón de pago (opcional)
            },
        });
    }

    useEffect(() => {
        // luego de montarse el componente, le pedimos al backend el preferenceId
        paymentsApi.testmp().then(res => {
            console.log(`CARGO EL ITEM`, res)
            setPreferenceId(res.data.id);
        })
            .catch(err => {
                console.log(`err`, err)
            })
    }, []);

    useEffect(() => {
        if (preferenceId) {

            console.log(`preferenceId`, preferenceId)
            // con el preferenceId en mano, inyectamos el script de mercadoPago
            const script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = 'https://sdk.mercadopago.com/js/v2';

            script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
            document.body.appendChild(script);
        }
    }, [preferenceId]);

    return (
        <div>
            <form id={FORM_ID} method="GET" />
        </div>
    );
}`

Si alguien tiene idea que esta pasando me salva la vida, Gracias!

@muZk
Copy link
Author

muZk commented Aug 30, 2021

Hola!

Estuve probando con este ultimo codigo que contempla de V2 de la sdk, y cuando ejecuto me tira este error en la consola y no entiendo que pasa

Uncaught TypeError: Cannot read property 'childNodes' of null at Ne.render (VM697 v2:1) at gt.render (VM697 v2:1) at new gt (VM697 v2:1) at Dt.checkout (VM697 v2:1) at HTMLScriptElement.addCheckout (mi archivito js :20)

Mi codigo que compila quedo asi:

const FORM_ID = 'payment-form';

export default function Product() {
    const [preferenceId, setPreferenceId] = useState(null);

    const addCheckout = () => {
        const mp = new window.MercadoPago('PUBLIC_KEY', {
            locale: 'es-AR'
        });

        console.log(`mp`, mp)
        console.log(`preferenceId en addChec`, preferenceId)

        // Inicializa el checkout Aca seria la linea 20
        mp.checkout({ 
            preference: {
                id: preferenceId,
            },
            render: {
                container: `${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
                label: 'Pagar', // Cambia el texto del botón de pago (opcional)
            },
        });
    }

    useEffect(() => {
        // luego de montarse el componente, le pedimos al backend el preferenceId
        paymentsApi.testmp().then(res => {
            console.log(`CARGO EL ITEM`, res)
            setPreferenceId(res.data.id);
        })
            .catch(err => {
                console.log(`err`, err)
            })
    }, []);

    useEffect(() => {
        if (preferenceId) {

            console.log(`preferenceId`, preferenceId)
            // con el preferenceId en mano, inyectamos el script de mercadoPago
            const script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = 'https://sdk.mercadopago.com/js/v2';

            script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
            document.body.appendChild(script);
        }
    }, [preferenceId]);

    return (
        <div>
            <form id={FORM_ID} method="GET" />
        </div>
    );
}`

Si alguien tiene idea que esta pasando me salva la vida, Gracias!

Hola! Interpreto que el error es porque no encuentra el contenedor donde se hace el render

Así que creo que la línea que está mal es la del checkout (linea 20?), debería quedar así:

mp.checkout({ 
    preference: {
        id: preferenceId,
    },
    render: {
        container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
        label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
});

Notar que el valor de la propiedad container le agregué un # antes del ${FORM_ID}.

PD: actualicé el código de ejemplo de v2. Espero pronto migrar de v1 a v2 para poder hacer una prueba de verdad 😅

@mattgle
Copy link

mattgle commented Sep 23, 2021

Si quieren usar el CheckoutPro de MercadoPago en ReactJS en una funcion comun y corriente sin necesidad de utilizar class components o function componentes, les dejo mi codigo que funciona:

La funcion loadScript se puede utilizar para otros scripts tambien.
process.env.REACT_APP_MERCADOPAGO_KEY es la KEY de mercadoPago que tengo guardada en el .env

export const redirectToMercadoPago = (preferenceId: string) => {
  const loadScript = (url: string, callback: () => void) => {
    let script = document.createElement('script');
    script.type = 'text/javascript';

    if (script.readyState) {
      script.onreadystatechange = () => {
        if (
          script.readyState === 'loaded' ||
          script.readyState === 'complete'
        ) {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else {
      script.onload = () => callback();
    }
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  };

  const handleScriptLoad = () => {
    const mp = new window.MercadoPago(process.env.REACT_APP_MERCADOPAGO_KEY, {
      locale: 'es-AR'
    });
    mp.checkout({
      preference: {
        id: preferenceId
      },
      autoOpen: true
    });
  };

  loadScript('https://sdk.mercadopago.com/js/v2', handleScriptLoad);
};

@Julian-quintero
Copy link

Hola @marcosdipaolo, no he usado la v2, pero por el código que me dices yo lo haría más o menos así:

import React, { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";

const FORM_ID = 'payment-form';

function addCheckout() {
  const mp = new window.MercadoPago('PUBLIC_KEY', {
    locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
    preference: {
      id: preferenceId,
    },
    render: {
      container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
      label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
  });
}

export default function Product() {
  const { id } = useParams(); // id de producto
  const [preferenceId, setPreferenceId] = useState(null);

  useEffect(() => {
    // luego de montarse el componente, le pedimos al backend el preferenceId
    axios.post('/api/orders', { productId: id }).then((order) => {
      setPreferenceId(order.preferenceId);
    });
  }, [id]);

  useEffect(() => {
    if (preferenceId) {
      // con el preferenceId en mano, inyectamos el script de mercadoPago
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://sdk.mercadopago.com/js/v2';
      script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
      document.body.appendChild(script);
    }
  }, [preferenceId]);

  return (
    <form id={FORM_ID} method="GET" />
  );
}

No he probado el código, pero la idea es agregar el script a mano en el useEffect al igual que mi código original, y luego cuando este cargue, ejecutar la lógica que pusiste tú (que está en la función addCheckout).

No he podido hacer que funcione :(, el boton de pagar se renderiza pero cuando le doy click me hace una peticion POST a la misma url en la que estoy, no veo el problema, estoy usando el mismo codigo, igual lo dejo abajo por si alguien sabe como corregirlo.

image

image

export const PlacerOrder = () => {


  const FORM_ID = 'payment-form';
  const [preferenceId, setPreferenceId] = useState<null | string>(null);

  function addCheckout() {
    const mp = new window.MercadoPago('TEST-59e115b4-a4b3-4860-883f-0da6e30d4cad', {
      locale: 'es-AR'
    });
  
    // Inicializa el checkout
    mp.checkout({ 
      preference: {
          id: preferenceId,
      },
      render: {
          container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
          label: 'Pagar', // Cambia el texto del botón de pago (opcional)
      },
  });
  }

  useEffect(() => {
   //el item de prueba lo tengo en el back por eso no mando nada en este caso, solo quiero que funcione.
    axios.post('/api/payments/mercadopago').then((item:any) => {
      setPreferenceId(item.data.idpago)
      
    });
  }, []);

  useEffect(() => {
    if (preferenceId) {

      console.log(preferenceId)
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://sdk.mercadopago.com/js/v2';
      script.setAttribute('data-preference-id', preferenceId);
      script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
      document.body.appendChild(script);
    }
  }, [preferenceId]);

    return (
    
        <form id={FORM_ID} method="GET" />
     
             
    )
}

@nahuelDev23
Copy link

Tengo un problema y una duda..
El problema:

  • cuando presiono el boton pagar del checkout pro , me sale la suma de los precios de los productos, pero no la lista de items a que estos corresponden, esto es asi? o es un error en la integracion?
 let preference = {
        items: [
          {
            title: 'casa',
            description:'una casa grande',
            unit_price: 1,
            quantity: 1,
          },
          {
            title: 'auto',
            description:'un auto grande',
            unit_price: 2,
            quantity: 2,
          }
        ],
        "back_urls": {
            "success": "http://localhost:3000/success",
        },
      };

Tambien note que este checkout pro no devuelve ningun estado el cual yo pueda evaluar directamente..
se me ocurre que al redireccionar al /success puedo utilizar el store para crear la orden hacia el backend con la lista de productos que tenia en el carrito. Esta bien planteado o podria llegar a tener algun inconveniente?

@muZk
Copy link
Author

muZk commented Oct 25, 2021

Tengo un problema y una duda.. El problema:

* cuando presiono el boton pagar del checkout pro , me sale la suma de los precios de los productos, pero no la lista de items a que estos corresponden, esto es asi? o es un error en la integracion?
 let preference = {
        items: [
          {
            title: 'casa',
            description:'una casa grande',
            unit_price: 1,
            quantity: 1,
          },
          {
            title: 'auto',
            description:'un auto grande',
            unit_price: 2,
            quantity: 2,
          }
        ],
        "back_urls": {
            "success": "http://localhost:3000/success",
        },
      };

Tambien note que este checkout pro no devuelve ningun estado el cual yo pueda evaluar directamente.. se me ocurre que al redireccionar al /success puedo utilizar el store para crear la orden hacia el backend con la lista de productos que tenia en el carrito. Esta bien planteado o podria llegar a tener algun inconveniente?

Para lo primero, la verdad no lo tengo muy claro, la integración que hice fue con productos únicos 😢

Para lo segundo, lo que yo hice fue agregar un action al form que armo para mercado pago, de manera que luego de realizar el pago me redireccionara ahí. La gracia es que esa URL del action tiene el ID de la orden que yo creé en el backend, y con ese ID puedo ir a mi backend a consultar por el estado de la orden.

Es decir, el form es algo así:

 <form
      id={FORM_ID}
      method="GET"
      action={`/orders/${order.id}`} {/* <--- luego de que todo salga bien me, MP me va a mandar ahí */}
    />

Y luego el componente para mostrar un order es algo así:

export default function Order() {
  const [order, setOrder] = useState({});
  const dispatch = useDispatch();
  const { id } = useParams();

  useEffect(() => {
    axios.get(`/api/orders/${id}`)
     .then(order => {
       setOrder(order);
     })
     .catch(() => setOrder(null));
  }, [dispatch, id]);

  if (!order) {
    return (
      <Container>
        <Alert color="danger">Esta orden no existe.</Alert>
      </Container>
    );
  }

  return (
    <Container>
      {order.status === 'approved' && <AcceptedOrder order={order} />}
      {order.status === 'rejected' && <RejectedOrder order={order} />}
      {order.status === 'pending' && <PendingOrder order={order} />}
      <hr />
      <OrderSummary order={order} />
    </Container>
  );
}

¿Y cómo se actualiza el status de mi orden en el backend?

Eso lo hice con notification_url de la preferencia:

preference_data = {
      "items": [
        /** etc **/
      ],
      "notification_url": `${ENV.fetch('HOST')}/mercado_pago/ipn`,
    }

Esa notification_url se llama una vez el pago está realizado, y es conocido en mercadopago como "Instant Payment Notification" (IPN)

Entonces el flujo es más o menos así:

  1. Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.
  2. Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el notification_url y el external_reference = ID de mi orden). Mando como respuesta el preferenceId y el ID de mi orden.
  3. La APP React toma el preferenceId para insertar el script de mercadopago. También toma el ID de la orden para armar el action del form.
  4. Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:
    1. Se gatillará un POST a notification_url (que le dije en el paso 2). Ese es un endpoint de mi backend, que lo que hace es actualizar el estado de mi orden interna y de "entregar" los productos. Este request tiene el ID de la preferencia, y con eso puedo obtener el ID de mi orden (con el atributo external_reference que le di en el punto 2).
    2. Se redireccionará al usuario a la URL del action del form, que en mi caso era algo como /order/{id} (que apunta a mi APP react)
    3. Mi app monta el componente del orden, mostrando el estado de la orden.

Espero que se entienda y si no, feliz de responder ante dudas / inquietudes 😄

@nahuelDev23
Copy link

sos crack amigo! muy buena tu respuesta, me despejaste varias dudas.Gracias.

@facundonrs
Copy link

Hola @zebaseta

Hola! Muy buena la información!!!
Por casualidad, sabés cómo hacer para compilar el const mp = new MercadoPago('PUBLIC_KEY', {
locale: 'es-AR'
});
Básicamente estoy luchando hace un buen rato, ya que no me compila la clase MercadoPago.
Ps: estoy trabajando con Angular.
Gracias!

Pudiste resolver eso, intenté con lo que propuso @muZk pero no tuve éxito.

@BrunoArrighi
Copy link

Hola! muy buena toda la info!
tengo una pregunta, me sale este error, por que podría ser??
image

pero eso lo puedes suprimir

@muZk
Copy link
Author

muZk commented Apr 19, 2022

Los errores de compilación son porque el Linter o la herramienta que usan no encuentra la definición de la clase "MercadoPago" en tiempo de compilación. Eso ocurre porque la clase MercadoPago se agrega en tiempo de ejecución. Para arreglarlo, una forma es suprimir el error, otra forma es usar window.MercadoPago.

El error en tiempo de ejecución de window.MercadoPago ocurre porque el script de mercado pago aún no se ha cargado, lo cual puede ser por:

  • problemas de conexión
  • internet muy lento
  • cambiaron la URL del script
  • en el script cambiaron el nombre de la variable MercadoPago a otra cosa

Para debuggear ese problema, recomiendo revisar el "Network" con su browser (ej: https://developer.chrome.com/docs/devtools/network/)

@beralbanesi
Copy link

Hola, estoy empezando con react y copie el codigo tal cual esta pero me tira error. Hay que instalar algo previamente?

@amiotti
Copy link

amiotti commented May 24, 2022

alguno pudo hacerlo funcionar con la v2 del SDK?

@amiotti
Copy link

amiotti commented May 24, 2022

function addCheckout() {
  const mp = new window.MercadoPago('PUBLIC_KEY', {
    locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
    preference: {
      id: preferenceId,
    },
    render: {
      container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
      label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
  });
}

lo solucionaste? me tira el mismo error. Me está queriendo hacer el POST a una ruta pero en el puerto del front.... @Julian-quintero

@amiotti
Copy link

amiotti commented May 25, 2022

@marcosdipaolo pudiste hacer andar la v2? estoy intentando hace un par de dias y no puedo.

@amiotti
Copy link

amiotti commented May 25, 2022

Hola @marcosdipaolo, no he usado la v2, pero por el código que me dices yo lo haría más o menos así:

import React, { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";

const FORM_ID = 'payment-form';

function addCheckout() {
  const mp = new window.MercadoPago('PUBLIC_KEY', {
    locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
    preference: {
      id: preferenceId,
    },
    render: {
      container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
      label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
  });
}

export default function Product() {
  const { id } = useParams(); // id de producto
  const [preferenceId, setPreferenceId] = useState(null);

  useEffect(() => {
    // luego de montarse el componente, le pedimos al backend el preferenceId
    axios.post('/api/orders', { productId: id }).then((order) => {
      setPreferenceId(order.preferenceId);
    });
  }, [id]);

  useEffect(() => {
    if (preferenceId) {
      // con el preferenceId en mano, inyectamos el script de mercadoPago
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://sdk.mercadopago.com/js/v2';
      script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
      document.body.appendChild(script);
    }
  }, [preferenceId]);

  return (
    <form id={FORM_ID} method="GET" />
  );
}

No he probado el código, pero la idea es agregar el script a mano en el useEffect al igual que mi código original, y luego cuando este cargue, ejecutar la lógica que pusiste tú (que está en la función addCheckout).

hola @muZk. he probado tu solución, pero a la hora de hacer click en el boton de pago me aparece lo siguiente:

image

hacer un POST en la misma url que estoy.. alguna sugerencia?

@seba-lc
Copy link

seba-lc commented Jun 2, 2022

Tengo un problema y una duda.. El problema:

* cuando presiono el boton pagar del checkout pro , me sale la suma de los precios de los productos, pero no la lista de items a que estos corresponden, esto es asi? o es un error en la integracion?
 let preference = {
        items: [
          {
            title: 'casa',
            description:'una casa grande',
            unit_price: 1,
            quantity: 1,
          },
          {
            title: 'auto',
            description:'un auto grande',
            unit_price: 2,
            quantity: 2,
          }
        ],
        "back_urls": {
            "success": "http://localhost:3000/success",
        },
      };

Tambien note que este checkout pro no devuelve ningun estado el cual yo pueda evaluar directamente.. se me ocurre que al redireccionar al /success puedo utilizar el store para crear la orden hacia el backend con la lista de productos que tenia en el carrito. Esta bien planteado o podria llegar a tener algun inconveniente?

Para lo primero, la verdad no lo tengo muy claro, la integración que hice fue con productos únicos 😢

Para lo segundo, lo que yo hice fue agregar un action al form que armo para mercado pago, de manera que luego de realizar el pago me redireccionara ahí. La gracia es que esa URL del action tiene el ID de la orden que yo creé en el backend, y con ese ID puedo ir a mi backend a consultar por el estado de la orden.

Es decir, el form es algo así:

 <form
      id={FORM_ID}
      method="GET"
      action={`/orders/${order.id}`} {/* <--- luego de que todo salga bien me, MP me va a mandar ahí */}
    />

Y luego el componente para mostrar un order es algo así:

export default function Order() {
  const [order, setOrder] = useState({});
  const dispatch = useDispatch();
  const { id } = useParams();

  useEffect(() => {
    axios.get(`/api/orders/${id}`)
     .then(order => {
       setOrder(order);
     })
     .catch(() => setOrder(null));
  }, [dispatch, id]);

  if (!order) {
    return (
      <Container>
        <Alert color="danger">Esta orden no existe.</Alert>
      </Container>
    );
  }

  return (
    <Container>
      {order.status === 'approved' && <AcceptedOrder order={order} />}
      {order.status === 'rejected' && <RejectedOrder order={order} />}
      {order.status === 'pending' && <PendingOrder order={order} />}
      <hr />
      <OrderSummary order={order} />
    </Container>
  );
}

¿Y cómo se actualiza el status de mi orden en el backend?

Eso lo hice con notification_url de la preferencia:

preference_data = {
      "items": [
        /** etc **/
      ],
      "notification_url": `${ENV.fetch('HOST')}/mercado_pago/ipn`,
    }

Esa notification_url se llama una vez el pago está realizado, y es conocido en mercadopago como "Instant Payment Notification" (IPN)

Entonces el flujo es más o menos así:

  1. Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.

  2. Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el notification_url y el external_reference = ID de mi orden). Mando como respuesta el preferenceId y el ID de mi orden.

  3. La APP React toma el preferenceId para insertar el script de mercadopago. También toma el ID de la orden para armar el action del form.

  4. Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:

    1. Se gatillará un POST a notification_url (que le dije en el paso 2). Ese es un endpoint de mi backend, que lo que hace es actualizar el estado de mi orden interna y de "entregar" los productos. Este request tiene el ID de la preferencia, y con eso puedo obtener el ID de mi orden (con el atributo external_reference que le di en el punto 2).
    2. Se redireccionará al usuario a la URL del action del form, que en mi caso era algo como /order/{id} (que apunta a mi APP react)
    3. Mi app monta el componente del orden, mostrando el estado de la orden.

Espero que se entienda y si no, feliz de responder ante dudas / inquietudes 😄

Hola @muZk te pasaste con este post, muchas gracias.

Te quería hacer una consulta porque estoy renegando hace un rato.. cómo haces, o como hiciste con el pedido get de respuesta al post que ellos te hacen en las notificaciones IPN. Porque los de mercado pago explican con CURL, y yo estoy trabajando con node. Estoy tratando de hacer ese pedido get con https.get, mandando como header la autorización con el ACCESS_TOKEN, pero probando de varias formas todavía no me llega la notificación como debería.

En la documentación de la API aparece:

curl -X GET
'https://api.mercadopago.com/v1/payments/{id}'
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Muchas gracias de nuevo!

@seba-lc
Copy link

seba-lc commented Jun 3, 2022

Tengo un problema y una duda.. El problema:

* cuando presiono el boton pagar del checkout pro , me sale la suma de los precios de los productos, pero no la lista de items a que estos corresponden, esto es asi? o es un error en la integracion?
 let preference = {
        items: [
          {
            title: 'casa',
            description:'una casa grande',
            unit_price: 1,
            quantity: 1,
          },
          {
            title: 'auto',
            description:'un auto grande',
            unit_price: 2,
            quantity: 2,
          }
        ],
        "back_urls": {
            "success": "http://localhost:3000/success",
        },
      };

Tambien note que este checkout pro no devuelve ningun estado el cual yo pueda evaluar directamente.. se me ocurre que al redireccionar al /success puedo utilizar el store para crear la orden hacia el backend con la lista de productos que tenia en el carrito. Esta bien planteado o podria llegar a tener algun inconveniente?

Para lo primero, la verdad no lo tengo muy claro, la integración que hice fue con productos únicos 😢
Para lo segundo, lo que yo hice fue agregar un action al form que armo para mercado pago, de manera que luego de realizar el pago me redireccionara ahí. La gracia es que esa URL del action tiene el ID de la orden que yo creé en el backend, y con ese ID puedo ir a mi backend a consultar por el estado de la orden.
Es decir, el form es algo así:

 <form
      id={FORM_ID}
      method="GET"
      action={`/orders/${order.id}`} {/* <--- luego de que todo salga bien me, MP me va a mandar ahí */}
    />

Y luego el componente para mostrar un order es algo así:

export default function Order() {
  const [order, setOrder] = useState({});
  const dispatch = useDispatch();
  const { id } = useParams();

  useEffect(() => {
    axios.get(`/api/orders/${id}`)
     .then(order => {
       setOrder(order);
     })
     .catch(() => setOrder(null));
  }, [dispatch, id]);

  if (!order) {
    return (
      <Container>
        <Alert color="danger">Esta orden no existe.</Alert>
      </Container>
    );
  }

  return (
    <Container>
      {order.status === 'approved' && <AcceptedOrder order={order} />}
      {order.status === 'rejected' && <RejectedOrder order={order} />}
      {order.status === 'pending' && <PendingOrder order={order} />}
      <hr />
      <OrderSummary order={order} />
    </Container>
  );
}

¿Y cómo se actualiza el status de mi orden en el backend?
Eso lo hice con notification_url de la preferencia:

preference_data = {
      "items": [
        /** etc **/
      ],
      "notification_url": `${ENV.fetch('HOST')}/mercado_pago/ipn`,
    }

Esa notification_url se llama una vez el pago está realizado, y es conocido en mercadopago como "Instant Payment Notification" (IPN)
Entonces el flujo es más o menos así:

  1. Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.

  2. Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el notification_url y el external_reference = ID de mi orden). Mando como respuesta el preferenceId y el ID de mi orden.

  3. La APP React toma el preferenceId para insertar el script de mercadopago. También toma el ID de la orden para armar el action del form.

  4. Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:

    1. Se gatillará un POST a notification_url (que le dije en el paso 2). Ese es un endpoint de mi backend, que lo que hace es actualizar el estado de mi orden interna y de "entregar" los productos. Este request tiene el ID de la preferencia, y con eso puedo obtener el ID de mi orden (con el atributo external_reference que le di en el punto 2).
    2. Se redireccionará al usuario a la URL del action del form, que en mi caso era algo como /order/{id} (que apunta a mi APP react)
    3. Mi app monta el componente del orden, mostrando el estado de la orden.

Espero que se entienda y si no, feliz de responder ante dudas / inquietudes 😄

Hola @muZk te pasaste con este post, muchas gracias.

Te quería hacer una consulta porque estoy renegando hace un rato.. cómo haces, o como hiciste con el pedido get de respuesta al post que ellos te hacen en las notificaciones IPN. Porque los de mercado pago explican con CURL, y yo estoy trabajando con node. Estoy tratando de hacer ese pedido get con https.get, mandando como header la autorización con el ACCESS_TOKEN, pero probando de varias formas todavía no me llega la notificación como debería.

En la documentación de la API aparece:

curl -X GET 'https://api.mercadopago.com/v1/payments/{id}' -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

Muchas gracias de nuevo!

Al final encontré un método para hacer el pedido a partir de la instancia de mercado pago: mercado.payment.capture(id, mercadopago);
La verdad que floja la documentación de la API..

Algo que me queda resonando es que las notificaciones las mandan continuamente cuando el pago queda pendiente (por lo menos en el modo de prueba). Y en la documentación dice que el post se hace solamente cuando el pago de mercadopago sufre una modificación justamente para no sobrecargar el sistema.. alguien tuvo el mismo problema? le encontró una solución?

@esanchezfolderit
Copy link

Hola @marcosdipaolo, no he usado la v2, pero por el código que me dices yo lo haría más o menos así:

import React, { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";

const FORM_ID = 'payment-form';

function addCheckout() {
  const mp = new window.MercadoPago('PUBLIC_KEY', {
    locale: 'es-AR'
  });

  // Inicializa el checkout
  mp.checkout({
    preference: {
      id: preferenceId,
    },
    render: {
      container: `#${FORM_ID}`, // Indica el nombre de la clase donde se mostrará el botón de pago
      label: 'Pagar', // Cambia el texto del botón de pago (opcional)
    },
  });
}

export default function Product() {
  const { id } = useParams(); // id de producto
  const [preferenceId, setPreferenceId] = useState(null);

  useEffect(() => {
    // luego de montarse el componente, le pedimos al backend el preferenceId
    axios.post('/api/orders', { productId: id }).then((order) => {
      setPreferenceId(order.preferenceId);
    });
  }, [id]);

  useEffect(() => {
    if (preferenceId) {
      // con el preferenceId en mano, inyectamos el script de mercadoPago
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://sdk.mercadopago.com/js/v2';
      script.addEventListener('load', addCheckout); // Cuando cargue el script, se ejecutará la función addCheckout
      document.body.appendChild(script);
    }
  }, [preferenceId]);

  return (
    <form id={FORM_ID} method="GET" />
  );
}

No he probado el código, pero la idea es agregar el script a mano en el useEffect al igual que mi código original, y luego cuando este cargue, ejecutar la lógica que pusiste tú (que está en la función addCheckout).

hola @muZk. he probado tu solución, pero a la hora de hacer click en el boton de pago me aparece lo siguiente:

image

hacer un POST en la misma url que estoy.. alguna sugerencia?

Me esta pasando lo mismo, pudo alguien encontrar la solución?

@daramayo90
Copy link

Hola! muy buena toda la info! tengo una pregunta, me sale este error, por que podría ser?? image

pero eso lo puedes suprimir

Hola, pudiste solucionarlo?
Estoy teniendo el mismo problema.

Gracias!!

@esanchezfolderit
Copy link

Hola! muy buena toda la info! tengo una pregunta, me sale este error, por que podría ser?? image

pero eso lo puedes suprimir

Hola, pudiste solucionarlo? Estoy teniendo el mismo problema.

Gracias!!

Te recomiendo usar la sdk para react si es que estas con un proyecto de ese tipo. Maneja todas estas cuestiones por atras. https://www.npmjs.com/package/react-sdk-mercadopago

@scherpablo
Copy link

  • cuando presiono el boton pagar del checkout pro , me sale la suma de los precios de los productos, pero no la lista de items a que estos corresponden, esto es asi? o es un error en la integracion?

Hola, tengo el mismo problema y googleando encontré ete post. Pudiste solucionarlo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment