-
-
Save muZk/e11931b3df6aab7c7dd6dd53058c3e41 to your computer and use it in GitHub Desktop.
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" /> | |
); | |
} |
Hola me pregunto para que sirve ese preferenceId?
Porque hay q agregarle ese atributo al script?
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.
@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
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
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! 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!
@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 😂
Buenisimo! Vamos a probar!
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!
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 😅
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);
};
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ónaddCheckout
).
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.
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" />
)
}
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?
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í:
- Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.
- Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el
notification_url
y elexternal_reference
= ID de mi orden). Mando como respuesta elpreferenceId
y el ID de mi orden. - La APP React toma el
preferenceId
para insertar el script de mercadopago. También toma el ID de la orden para armar elaction
del form. - Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:
- 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). - 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) - Mi app monta el componente del orden, mostrando el estado de la orden.
- Se gatillará un POST a
Espero que se entienda y si no, feliz de responder ante dudas / inquietudes 😄
sos crack amigo! muy buena tu respuesta, me despejaste varias dudas.Gracias.
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.
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/)
Hola, estoy empezando con react y copie el codigo tal cual esta pero me tira error. Hay que instalar algo previamente?
alguno pudo hacerlo funcionar con la v2 del SDK?
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
@marcosdipaolo pudiste hacer andar la v2? estoy intentando hace un par de dias y no puedo.
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ónaddCheckout
).
hola @muZk. he probado tu solución, pero a la hora de hacer click en el boton de pago me aparece lo siguiente:
hacer un POST en la misma url que estoy.. alguna sugerencia?
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
alform
que armo para mercado pago, de manera que luego de realizar el pago me redireccionara ahí. La gracia es que esa URL delaction
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í:
Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.
Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el
notification_url
y elexternal_reference
= ID de mi orden). Mando como respuesta elpreferenceId
y el ID de mi orden.La APP React toma el
preferenceId
para insertar el script de mercadopago. También toma el ID de la orden para armar elaction
del form.Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:
- 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).- 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)- 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!
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 unaction
alform
que armo para mercado pago, de manera que luego de realizar el pago me redireccionara ahí. La gracia es que esa URL delaction
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 connotification_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í:
Desde el frontend (App React) llamo a mi Backend para que me cree una preferencia para un producto.
Mi backend crea una orden (para uso interno), y luego crea la preferencia en MercadoPago (diciéndole el
notification_url
y elexternal_reference
= ID de mi orden). Mando como respuesta elpreferenceId
y el ID de mi orden.La APP React toma el
preferenceId
para insertar el script de mercadopago. También toma el ID de la orden para armar elaction
del form.Cuando el usuario presiona "Pagar", mercadopago toma el control. Si paga y todo sale bien, pasa lo siguiente:
- 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).- 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)- 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?
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ónaddCheckout
).hola @muZk. he probado tu solución, pero a la hora de hacer click en el boton de pago me aparece lo siguiente:
hacer un POST en la misma url que estoy.. alguna sugerencia?
Me esta pasando lo mismo, pudo alguien encontrar la solución?
Hola! muy buena toda la info! tengo una pregunta, me sale este error, por que podría ser??
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
- 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?
Buen artículo c: