diff --git a/.changeset/some-berries-teach.md b/.changeset/some-berries-teach.md
new file mode 100644
index 00000000..19425a12
--- /dev/null
+++ b/.changeset/some-berries-teach.md
@@ -0,0 +1,6 @@
+---
+"nextjs": minor
+"@godaddy/react": minor
+---
+
+Implementation of mercadopago
diff --git a/examples/nextjs/app/checkout.tsx b/examples/nextjs/app/checkout.tsx
index 0fe2ff77..9a381042 100644
--- a/examples/nextjs/app/checkout.tsx
+++ b/examples/nextjs/app/checkout.tsx
@@ -14,20 +14,52 @@ export function CheckoutPage({ session }: { session: CheckoutSession }) {
);
}
diff --git a/examples/nextjs/app/page.tsx b/examples/nextjs/app/page.tsx
index 9ae69eb8..7593cdbf 100644
--- a/examples/nextjs/app/page.tsx
+++ b/examples/nextjs/app/page.tsx
@@ -58,17 +58,13 @@ export default async function Home() {
processor: 'godaddy',
checkoutTypes: ['standard'],
},
- express: {
- processor: 'godaddy',
- checkoutTypes: ['express'],
+ mercadopago: {
+ processor: 'mercadopago',
+ checkoutTypes: ['standard'],
},
paypal: {
processor: 'paypal',
- checkoutTypes: ['standard'],
- },
- offline: {
- processor: 'offline',
- checkoutTypes: ['standard'],
+ checkoutTypes: ['express', 'standard'],
},
},
operatingHours: {
diff --git a/examples/nextjs/app/store/actions.ts b/examples/nextjs/app/store/actions.ts
index 1cc06f39..06dac3e7 100644
--- a/examples/nextjs/app/store/actions.ts
+++ b/examples/nextjs/app/store/actions.ts
@@ -77,8 +77,6 @@ export async function checkoutWithOrder(orderId: string) {
throw new Error('Failed to create checkout session');
}
- console.log({ session });
-
if (!session.url) {
throw new Error('No checkout URL returned');
}
diff --git a/examples/nextjs/app/store/product/[productId]/product.tsx b/examples/nextjs/app/store/product/[productId]/product.tsx
index 9aa51c71..9b2453e4 100644
--- a/examples/nextjs/app/store/product/[productId]/product.tsx
+++ b/examples/nextjs/app/store/product/[productId]/product.tsx
@@ -17,10 +17,7 @@ export default function Product({ productId }: { productId: string }) {
Back to Store
-
+
);
}
diff --git a/examples/nextjs/biome.json b/examples/nextjs/biome.json
index 808d736f..faa4f36f 100644
--- a/examples/nextjs/biome.json
+++ b/examples/nextjs/biome.json
@@ -1,20 +1,20 @@
{
- "$schema": "https://biomejs.dev/schemas/2.3.3/schema.json",
- "extends": ["biome-config-godaddy/biome.json"],
- "css": {
- "parser": {
- "cssModules": true,
- "tailwindDirectives": true
- }
- },
- "files": {
- "includes": ["**/*", "!!**/src/globals.css"]
- },
- "linter": {
- "rules": {
- "correctness": {
- "useUniqueElementIds": "off"
- }
- }
- }
+ "$schema": "https://biomejs.dev/schemas/2.3.3/schema.json",
+ "extends": ["biome-config-godaddy/biome.json"],
+ "css": {
+ "parser": {
+ "cssModules": true,
+ "tailwindDirectives": true
+ }
+ },
+ "files": {
+ "includes": ["**/*", "!!**/src/globals.css"]
+ },
+ "linter": {
+ "rules": {
+ "correctness": {
+ "useUniqueElementIds": "off"
+ }
+ }
+ }
}
diff --git a/examples/nextjs/env.sample b/examples/nextjs/env.sample
index 337ff345..abd13fa6 100644
--- a/examples/nextjs/env.sample
+++ b/examples/nextjs/env.sample
@@ -21,3 +21,7 @@ NEXT_PUBLIC_GODADDY_APP_ID=
NEXT_PUBLIC_SQUARE_APP_ID=
NEXT_PUBLIC_SQUARE_LOCATION_ID=
NEXT_PUBLIC_PAYPAL_CLIENT_ID=
+
+# MercadoPago Credentials
+NEXT_PUBLIC_MERCADOPAGO_PUBLIC_KEY=
+NEXT_PUBLIC_MERCADOPAGO_COUNTRY=AR
diff --git a/packages/localizations/src/deDe.ts b/packages/localizations/src/deDe.ts
index a58bf364..705ce2ea 100644
--- a/packages/localizations/src/deDe.ts
+++ b/packages/localizations/src/deDe.ts
@@ -114,6 +114,8 @@ export const deDe = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Verwende das MercadoPago-Formular unten, um deinen Kauf sicher abzuschließen.',
},
noMethodsAvailable: 'Keine Zahlungsmethoden verfügbar',
cardNumber: 'Kartennummer',
diff --git a/packages/localizations/src/enIe.ts b/packages/localizations/src/enIe.ts
index 5751044b..b1b7bbee 100644
--- a/packages/localizations/src/enIe.ts
+++ b/packages/localizations/src/enIe.ts
@@ -114,6 +114,8 @@ export const enIe = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Use the MercadoPago form below to complete your purchase securely.',
},
noMethodsAvailable: 'No payment methods available',
cardNumber: 'Card number',
diff --git a/packages/localizations/src/enUs.ts b/packages/localizations/src/enUs.ts
index b9472309..0c43e883 100644
--- a/packages/localizations/src/enUs.ts
+++ b/packages/localizations/src/enUs.ts
@@ -114,6 +114,7 @@ export const enUs = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago: 'Use the MercadoPago form below to complete your purchase securely.',
},
noMethodsAvailable: 'No payment methods available',
cardNumber: 'Card number',
diff --git a/packages/localizations/src/esAr.ts b/packages/localizations/src/esAr.ts
index 92f66711..b60b2005 100644
--- a/packages/localizations/src/esAr.ts
+++ b/packages/localizations/src/esAr.ts
@@ -115,6 +115,8 @@ export const esAr = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esCl.ts b/packages/localizations/src/esCl.ts
index d1438c84..bc5ef18a 100644
--- a/packages/localizations/src/esCl.ts
+++ b/packages/localizations/src/esCl.ts
@@ -115,6 +115,8 @@ export const esCl = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esCo.ts b/packages/localizations/src/esCo.ts
index e82930d5..b6771178 100644
--- a/packages/localizations/src/esCo.ts
+++ b/packages/localizations/src/esCo.ts
@@ -115,6 +115,8 @@ export const esCo = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esEs.ts b/packages/localizations/src/esEs.ts
index 51a51eec..77cc04c3 100644
--- a/packages/localizations/src/esEs.ts
+++ b/packages/localizations/src/esEs.ts
@@ -115,6 +115,8 @@ export const esEs = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esMx.ts b/packages/localizations/src/esMx.ts
index 2a34cb82..f8bda364 100644
--- a/packages/localizations/src/esMx.ts
+++ b/packages/localizations/src/esMx.ts
@@ -115,6 +115,8 @@ export const esMx = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esPe.ts b/packages/localizations/src/esPe.ts
index 9dbeca8e..82637e7e 100644
--- a/packages/localizations/src/esPe.ts
+++ b/packages/localizations/src/esPe.ts
@@ -115,6 +115,8 @@ export const esPe = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/esUs.ts b/packages/localizations/src/esUs.ts
index ad231176..b8bc226e 100644
--- a/packages/localizations/src/esUs.ts
+++ b/packages/localizations/src/esUs.ts
@@ -115,6 +115,8 @@ export const esUs = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa el formulario de MercadoPago a continuación para completar tu compra de forma segura.',
},
noMethodsAvailable: 'No hay métodos de pago disponibles',
cardNumber: 'Número de tarjeta',
diff --git a/packages/localizations/src/frCa.ts b/packages/localizations/src/frCa.ts
index 1e136e32..b8114e85 100644
--- a/packages/localizations/src/frCa.ts
+++ b/packages/localizations/src/frCa.ts
@@ -115,6 +115,8 @@ export const frCa = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Utilisez le formulaire MercadoPago ci-dessous pour finaliser votre achat en toute sécurité.',
},
noMethodsAvailable: 'Aucune méthode de paiement disponible',
cardNumber: 'Numéro de carte',
diff --git a/packages/localizations/src/frFr.ts b/packages/localizations/src/frFr.ts
index 04b2a67c..7fe289f2 100644
--- a/packages/localizations/src/frFr.ts
+++ b/packages/localizations/src/frFr.ts
@@ -115,6 +115,8 @@ export const frFr = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Utilisez le formulaire MercadoPago ci-dessous pour finaliser votre achat en toute sécurité.',
},
noMethodsAvailable: 'Aucune méthode de paiement disponible',
cardNumber: 'Numéro de carte',
diff --git a/packages/localizations/src/idId.ts b/packages/localizations/src/idId.ts
index a0b2b8a6..cfacf9ce 100644
--- a/packages/localizations/src/idId.ts
+++ b/packages/localizations/src/idId.ts
@@ -114,6 +114,8 @@ export const idId = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Gunakan formulir MercadoPago di bawah untuk menyelesaikan pembelian Anda dengan aman.',
},
noMethodsAvailable: 'Tidak ada metode pembayaran tersedia',
cardNumber: 'Nomor kartu',
diff --git a/packages/localizations/src/itIt.ts b/packages/localizations/src/itIt.ts
index e6d5a3d3..1a9f284a 100644
--- a/packages/localizations/src/itIt.ts
+++ b/packages/localizations/src/itIt.ts
@@ -115,6 +115,8 @@ export const itIt = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Usa il modulo MercadoPago qui sotto per completare l’acquisto in modo sicuro.',
},
noMethodsAvailable: 'Nessun metodo di pagamento disponibile',
cardNumber: 'Numero della carta',
diff --git a/packages/localizations/src/ptBr.ts b/packages/localizations/src/ptBr.ts
index 40dac240..f8880766 100644
--- a/packages/localizations/src/ptBr.ts
+++ b/packages/localizations/src/ptBr.ts
@@ -114,6 +114,8 @@ export const ptBr = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Use o formulário do MercadoPago abaixo para concluir sua compra com segurança.',
},
noMethodsAvailable: 'Nenhum método de pagamento disponível',
cardNumber: 'Número do cartão',
diff --git a/packages/localizations/src/qaPs.ts b/packages/localizations/src/qaPs.ts
index 2824a45a..c09a2f7e 100644
--- a/packages/localizations/src/qaPs.ts
+++ b/packages/localizations/src/qaPs.ts
@@ -115,6 +115,8 @@ export const qaPs = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ '[Üšë ţhë MërçâðöÞâgö förm këlöw ţö çömþlëţë ÿöür þürçhâšë šëçürëlÿ.]',
},
noMethodsAvailable: '[Ñö þâÿmëñţ mëţhödš âvâîlâblë âţ ţhîš ţîmë]',
cardNumber: '[Çârd ñümkër îñþüţ fîëld]',
diff --git a/packages/localizations/src/trTr.ts b/packages/localizations/src/trTr.ts
index 72afea53..aecf661a 100644
--- a/packages/localizations/src/trTr.ts
+++ b/packages/localizations/src/trTr.ts
@@ -114,6 +114,8 @@ export const trTr = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Satın alımınızı güvenle tamamlamak için aşağıdaki MercadoPago formunu kullanın.',
},
noMethodsAvailable: 'Kullanılabilir ödeme yöntemi yok',
cardNumber: 'Kart numarası',
diff --git a/packages/localizations/src/viVn.ts b/packages/localizations/src/viVn.ts
index c1460e6c..6b120014 100644
--- a/packages/localizations/src/viVn.ts
+++ b/packages/localizations/src/viVn.ts
@@ -114,6 +114,8 @@ export const viVn = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago:
+ 'Hãy sử dụng biểu mẫu MercadoPago bên dưới để hoàn tất mua hàng một cách an toàn.',
},
noMethodsAvailable: 'Không có phương thức thanh toán nào',
cardNumber: 'Số thẻ',
diff --git a/packages/localizations/src/zhCn.ts b/packages/localizations/src/zhCn.ts
index e71929b9..6f11c065 100644
--- a/packages/localizations/src/zhCn.ts
+++ b/packages/localizations/src/zhCn.ts
@@ -110,6 +110,7 @@ export const zhCn = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago: '请使用下方的 MercadoPago 表单安全完成购买。',
},
noMethodsAvailable: '暂无可用的付款方式',
cardNumber: '卡号',
diff --git a/packages/localizations/src/zhSg.ts b/packages/localizations/src/zhSg.ts
index 286d58ff..74088efe 100644
--- a/packages/localizations/src/zhSg.ts
+++ b/packages/localizations/src/zhSg.ts
@@ -110,6 +110,7 @@ export const zhSg = {
googlePay: '',
paze: '',
offline: '',
+ mercadopago: '请使用下方的 MercadoPago 表单安全完成购买。',
},
noMethodsAvailable: '无可用付款方式',
cardNumber: '卡号',
diff --git a/packages/react/src/components/checkout/checkout.tsx b/packages/react/src/components/checkout/checkout.tsx
index bc2a0b21..8ae642b4 100644
--- a/packages/react/src/components/checkout/checkout.tsx
+++ b/packages/react/src/components/checkout/checkout.tsx
@@ -80,6 +80,11 @@ export type PayPalConfig = {
disableFunding?: Array<'credit' | 'card' | 'paylater' | 'venmo'>;
};
+export type MercadoPagoConfig = {
+ publicKey: string;
+ country: 'AR' | 'BR' | 'CO' | 'CL' | 'PE' | 'MX';
+};
+
interface CheckoutContextValue {
elements?: CheckoutElements;
targets?: Partial<
@@ -92,6 +97,7 @@ interface CheckoutContextValue {
godaddyPaymentsConfig?: GodaddyPaymentsConfig;
squareConfig?: SquareConfig;
paypalConfig?: PayPalConfig;
+ mercadoPagoConfig?: MercadoPagoConfig;
isConfirmingCheckout: boolean;
setIsConfirmingCheckout: (isConfirming: boolean) => void;
checkoutErrors?: string[] | undefined;
@@ -202,6 +208,7 @@ export interface CheckoutProps {
godaddyPaymentsConfig?: GodaddyPaymentsConfig;
squareConfig?: SquareConfig;
paypalConfig?: PayPalConfig;
+ mercadoPagoConfig?: MercadoPagoConfig;
layout?: LayoutSection[];
direction?: 'ltr' | 'rtl';
showStoreHours?: boolean;
@@ -221,6 +228,7 @@ export function Checkout(props: CheckoutProps) {
godaddyPaymentsConfig,
squareConfig,
paypalConfig,
+ mercadoPagoConfig,
isCheckoutDisabled,
} = props;
@@ -385,6 +393,7 @@ export function Checkout(props: CheckoutProps) {
stripeConfig,
godaddyPaymentsConfig,
squareConfig,
+ mercadoPagoConfig,
paypalConfig,
requiredFields,
isConfirmingCheckout,
diff --git a/packages/react/src/components/checkout/payment/checkout-buttons/credit-card/mercadopago.tsx b/packages/react/src/components/checkout/payment/checkout-buttons/credit-card/mercadopago.tsx
new file mode 100644
index 00000000..befbfeef
--- /dev/null
+++ b/packages/react/src/components/checkout/payment/checkout-buttons/credit-card/mercadopago.tsx
@@ -0,0 +1,43 @@
+import { useFormContext } from 'react-hook-form';
+import { useMercadoPago } from '@/components/checkout/payment/utils/mercadopago-provider';
+import { Button } from '@/components/ui/button';
+import { useGoDaddyContext } from '@/godaddy-provider';
+
+export function MercadoPagoCreditCardCheckoutButton() {
+ const { t } = useGoDaddyContext();
+ const { handleBrickSubmit, isLoading } = useMercadoPago();
+ const form = useFormContext();
+
+ const handleSubmit = async () => {
+ // Validate form first
+ const valid = await form.trigger();
+ if (!valid) {
+ const firstError = Object.keys(form.formState.errors)[0];
+ if (firstError) {
+ form.setFocus(firstError);
+ }
+ return;
+ }
+
+ // Trigger MercadoPago brick submission
+ if (handleBrickSubmit) {
+ try {
+ await handleBrickSubmit();
+ } catch (error) {
+ console.error('MercadoPago submission error:', error);
+ }
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/packages/react/src/components/checkout/payment/icons/MercadoPago.tsx b/packages/react/src/components/checkout/payment/icons/MercadoPago.tsx
new file mode 100644
index 00000000..a33064b1
--- /dev/null
+++ b/packages/react/src/components/checkout/payment/icons/MercadoPago.tsx
@@ -0,0 +1,39 @@
+export const MercadoPagoIcon = props => {
+ return (
+
+ );
+};
+
+export default MercadoPagoIcon;
diff --git a/packages/react/src/components/checkout/payment/lazy-payment-loader.tsx b/packages/react/src/components/checkout/payment/lazy-payment-loader.tsx
index 00bb6c53..33e6d542 100644
--- a/packages/react/src/components/checkout/payment/lazy-payment-loader.tsx
+++ b/packages/react/src/components/checkout/payment/lazy-payment-loader.tsx
@@ -40,6 +40,13 @@ const LazyComponents = {
default: module.PayPalCreditCardForm,
}))
),
+ MercadoPagoCreditCardForm: lazy(() =>
+ import(
+ '@/components/checkout/payment/payment-methods/credit-card/mercadopago'
+ ).then(module => ({
+ default: module.MercadoPagoCreditCardForm,
+ }))
+ ),
// Credit Card Buttons
CreditCardCheckoutButton: lazy(() =>
@@ -70,7 +77,13 @@ const LazyComponents = {
default: module.PayPalCreditCardCheckoutButton,
}))
),
-
+ MercadoPagoCreditCardCheckoutButton: lazy(() =>
+ import(
+ '@/components/checkout/payment/checkout-buttons/credit-card/mercadopago'
+ ).then(module => ({
+ default: module.MercadoPagoCreditCardCheckoutButton,
+ }))
+ ),
// Express Buttons
ExpressCheckoutButton: lazy(() =>
import(
@@ -179,6 +192,12 @@ type PaymentComponentRegistry = {
button: PaymentComponentKey;
};
};
+ [PaymentMethodType.MERCADOPAGO]?: {
+ [PaymentProvider.MERCADOPAGO]: {
+ form: PaymentComponentKey;
+ button: PaymentComponentKey;
+ };
+ };
};
export const lazyPaymentComponentRegistry: PaymentComponentRegistry = {
@@ -228,6 +247,12 @@ export const lazyPaymentComponentRegistry: PaymentComponentRegistry = {
button: 'PazeCheckoutButton',
},
},
+ [PaymentMethodType.MERCADOPAGO]: {
+ [PaymentProvider.MERCADOPAGO]: {
+ form: 'MercadoPagoCreditCardForm',
+ button: 'MercadoPagoCreditCardCheckoutButton',
+ },
+ },
};
// Payment loading skeleton component
diff --git a/packages/react/src/components/checkout/payment/payment-form.tsx b/packages/react/src/components/checkout/payment/payment-form.tsx
index 3155d71e..41d15695 100644
--- a/packages/react/src/components/checkout/payment/payment-form.tsx
+++ b/packages/react/src/components/checkout/payment/payment-form.tsx
@@ -18,6 +18,7 @@ import {
} from '@/components/checkout/line-items';
import ApplePayIcon from '@/components/checkout/payment/icons/ApplePay';
import GooglePayIcon from '@/components/checkout/payment/icons/GooglePay';
+import MercadoPagoIcon from '@/components/checkout/payment/icons/MercadoPago';
import PayPalIcon from '@/components/checkout/payment/icons/PayPal';
import PazeIcon from '@/components/checkout/payment/icons/Paze';
import {
@@ -66,6 +67,7 @@ const PAYMENT_METHOD_ICONS: Record = {
applePay: ,
googlePay: ,
paze: ,
+ mercadopago: ,
offline: ,
};
@@ -114,6 +116,8 @@ export function PaymentForm(
return t.payment.methods.paze;
case PaymentMethodType.OFFLINE:
return t.payment.methods.offline;
+ case PaymentMethodType.MERCADOPAGO:
+ return 'MercadoPago';
default:
return key;
}
@@ -137,6 +141,8 @@ export function PaymentForm(
return t.payment.descriptions?.paze;
case PaymentMethodType.OFFLINE:
return t.payment.descriptions?.offline;
+ case PaymentMethodType.MERCADOPAGO:
+ return t.payment.descriptions?.mercadopago;
default:
return undefined;
}
@@ -430,6 +436,15 @@ export function PaymentForm(
/>
) : null}
+ {/* Render MercadoPago form outside accordion */}
+ {paymentMethod === PaymentMethodType.MERCADOPAGO && methodConfig ? (
+
+ ) : null}
+
{isShipping &&
session?.enableShipping &&
paymentMethod !== PaymentMethodType.CREDIT_CARD ? (
diff --git a/packages/react/src/components/checkout/payment/payment-methods/credit-card/mercadopago.tsx b/packages/react/src/components/checkout/payment/payment-methods/credit-card/mercadopago.tsx
new file mode 100644
index 00000000..4815f5f2
--- /dev/null
+++ b/packages/react/src/components/checkout/payment/payment-methods/credit-card/mercadopago.tsx
@@ -0,0 +1,275 @@
+import {
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useRef,
+ useState,
+} from 'react';
+import { useFormContext } from 'react-hook-form';
+import { useCheckoutContext } from '@/components/checkout/checkout';
+import { useDraftOrderTotals } from '@/components/checkout/order/use-draft-order';
+import { useMercadoPago } from '@/components/checkout/payment/utils/mercadopago-provider';
+import {
+ PaymentProvider,
+ useConfirmCheckout,
+} from '@/components/checkout/payment/utils/use-confirm-checkout';
+import { useLoadMercadoPago } from '@/components/checkout/payment/utils/use-load-mercadopago';
+import { useGoDaddyContext } from '@/godaddy-provider';
+import { GraphQLErrorWithCodes } from '@/lib/graphql-with-errors';
+import { eventIds } from '@/tracking/events';
+import { TrackingEventType, track } from '@/tracking/track';
+import { PaymentMethodType } from '@/types';
+
+export function MercadoPagoCreditCardForm() {
+ const { t } = useGoDaddyContext();
+ const { mercadoPagoConfig, setCheckoutErrors } = useCheckoutContext();
+ const { data: totals } = useDraftOrderTotals();
+ const form = useFormContext();
+ const {
+ setIsLoading: setMercadoPagoLoading,
+ setHandleBrickSubmit,
+ } = useMercadoPago();
+ const { isMercadoPagoLoaded } = useLoadMercadoPago();
+ const [error, setError] = useState('');
+ const [isInitialized, setIsInitialized] = useState(false);
+ const [mpInstance, setMpInstance] = useState(null);
+ const [bricksBuilder, setBricksBuilder] = useState(null);
+ const brickControllerRef = useRef(null);
+ const isInitializingRef = useRef(false);
+ const hasRenderedRef = useRef(false);
+ const onReadyRef = useRef<() => void>(null);
+ const onSubmitRef = useRef<(args: any) => void>(null);
+ const onErrorRef = useRef<(err: any) => void>(null);
+
+ const confirmCheckout = useConfirmCheckout();
+
+ // Memoize brick callbacks to prevent recreating the brick on every render
+ const handleReady = useCallback(() => {
+ setMercadoPagoLoading(false);
+
+ // Remove padding from internal form after brick loads
+ const container = document.getElementById('mercadopago-brick-container');
+ const form = container?.querySelector('form');
+ if (form) {
+ form.style.padding = '0';
+ }
+ }, [setMercadoPagoLoading]);
+
+ const handleSubmit = useCallback(
+ async ({ formData }: any) => {
+ setMercadoPagoLoading(true);
+
+ // Validate form before processing payment
+ const valid = await form.trigger();
+ if (!valid) {
+ const firstError = Object.keys(form.formState.errors)[0];
+ if (firstError) {
+ form.setFocus(firstError);
+ }
+ setMercadoPagoLoading(false);
+ return;
+ }
+
+ // Track MercadoPago click
+ track({
+ eventId: eventIds.mercadopagoClick,
+ type: TrackingEventType.CLICK,
+ properties: {
+ paymentType: PaymentMethodType.MERCADOPAGO,
+ },
+ });
+
+ try {
+ // MercadoPago SDK provides the payment token in formData.token
+ const paymentToken = formData?.token;
+
+ if (!paymentToken) {
+ throw new Error('No payment token received from MercadoPago');
+ }
+
+ await confirmCheckout.mutateAsync({
+ paymentToken,
+ paymentType: PaymentMethodType.MERCADOPAGO,
+ paymentProvider: PaymentProvider.MERCADOPAGO,
+ });
+ setError('');
+ } catch (err: unknown) {
+ if (err instanceof GraphQLErrorWithCodes) {
+ setCheckoutErrors(err.codes);
+ } else {
+ setError(t.errors.errorProcessingPayment);
+ }
+ } finally {
+ setMercadoPagoLoading(false);
+ }
+ },
+ [
+ form,
+ confirmCheckout,
+ setCheckoutErrors,
+ setMercadoPagoLoading,
+ t.errors.errorProcessingPayment,
+ ]
+ );
+
+ const handleError = useCallback(
+ (err: any) => {
+ const _errorMessage = err?.message || err?.error || 'Unknown error';
+ setError(t.errors.errorProcessingPayment);
+ setMercadoPagoLoading(false);
+ },
+ [setMercadoPagoLoading, t.errors.errorProcessingPayment]
+ );
+
+ useEffect(() => {
+ onReadyRef.current = handleReady;
+ onSubmitRef.current = handleSubmit;
+ onErrorRef.current = handleError;
+ }, [handleReady, handleSubmit, handleError]);
+
+ // Exposing a function to submit the brick from the external button
+ useEffect(() => {
+ if (brickControllerRef.current) {
+ const submitHandler = async () => {
+ try {
+ const { formData } = await brickControllerRef.current.getFormData();
+
+ // Manually trigger the onSubmit callback with the form data
+ if (onSubmitRef.current) {
+ await onSubmitRef.current({ formData });
+ }
+ } catch (error) {
+ console.error('Error getting/submitting MercadoPago form data:', error);
+ if (onErrorRef.current) {
+ onErrorRef.current(error);
+ }
+ }
+ };
+ setHandleBrickSubmit(submitHandler);
+ } else {
+ setHandleBrickSubmit(null);
+ }
+ }, [brickControllerRef.current, setHandleBrickSubmit]);
+
+ // Initialize MercadoPago instance
+ useLayoutEffect(() => {
+ if (
+ !isMercadoPagoLoaded ||
+ !mercadoPagoConfig?.publicKey ||
+ mpInstance ||
+ isInitialized
+ )
+ return;
+
+ try {
+ const mp = new (window as any).MercadoPago(mercadoPagoConfig.publicKey);
+ const builder = mp.bricks();
+ setMpInstance(mp);
+ setBricksBuilder(builder);
+ setIsInitialized(true);
+ } catch (_err) {
+ setError(t.errors.errorProcessingPayment);
+ }
+ }, [
+ isMercadoPagoLoaded,
+ mercadoPagoConfig?.publicKey,
+ mpInstance,
+ isInitialized,
+ setMpInstance,
+ setBricksBuilder,
+ ]);
+
+ // Render Payment Brick
+ useLayoutEffect(() => {
+ if (
+ !bricksBuilder ||
+ brickControllerRef.current ||
+ isInitializingRef.current ||
+ hasRenderedRef.current
+ )
+ return;
+
+ const renderBrick = async () => {
+ const total = totals?.total?.value || 0;
+
+ try {
+ isInitializingRef.current = true;
+ hasRenderedRef.current = true;
+ const container = document.getElementById(
+ 'mercadopago-brick-container'
+ );
+ if (container) {
+ container.innerHTML = '';
+ }
+ const settings = {
+ initialization: {
+ amount: total,
+ payer: { email: 'dummy@testuser.com' },
+ },
+ customization: {
+ visual: {
+ hideFormTitle: true,
+ hidePaymentButton: true,
+ style: {
+ theme: 'default',
+ },
+ },
+ paymentMethods: {
+ creditCard: 'all',
+ debitCard: 'all',
+ maxInstallments: 1,
+ },
+ },
+ callbacks: {
+ onReady: () => onReadyRef.current?.(),
+ onSubmit: (args: any) => onSubmitRef.current?.(args),
+ onError: (err: any) => onErrorRef.current?.(err),
+ },
+ };
+
+ const controller = await bricksBuilder.create(
+ 'payment',
+ 'mercadopago-brick-container',
+ settings
+ );
+
+ brickControllerRef.current = controller;
+ isInitializingRef.current = false;
+ } catch (_err) {
+ setError(t.errors.errorProcessingPayment);
+ isInitializingRef.current = false;
+ hasRenderedRef.current = false;
+ }
+ };
+
+ renderBrick();
+ }, [bricksBuilder]);
+
+ // Unmount MercadoPago brick on component unmount only
+ useEffect(() => {
+ return () => {
+ if (brickControllerRef.current) {
+ try {
+ brickControllerRef.current.unmount();
+ } catch (_e) {
+ // Ignore unmount errors
+ }
+ brickControllerRef.current = null;
+ }
+ const container = document.getElementById('mercadopago-brick-container');
+ if (container) {
+ container.innerHTML = '';
+ }
+ isInitializingRef.current = false;
+ };
+ }, []);
+
+ return (
+ <>
+
+ {error ? (
+ {error}
+ ) : null}
+ >
+ );
+}
diff --git a/packages/react/src/components/checkout/payment/utils/conditional-providers.tsx b/packages/react/src/components/checkout/payment/utils/conditional-providers.tsx
index 95df6409..e00ec293 100644
--- a/packages/react/src/components/checkout/payment/utils/conditional-providers.tsx
+++ b/packages/react/src/components/checkout/payment/utils/conditional-providers.tsx
@@ -1,5 +1,6 @@
import { PayPalScriptProvider } from '@paypal/react-paypal-js';
import { useCheckoutContext } from '@/components/checkout/checkout';
+import { MercadoPagoProvider } from './mercadopago-provider';
import { PayPalProvider } from './paypal-provider';
import { PoyntCollectProvider } from './poynt-provider';
import { SquareProvider } from './square-provider';
@@ -17,8 +18,13 @@ interface ConditionalPaymentProvidersProps {
export function ConditionalPaymentProviders({
children,
}: ConditionalPaymentProvidersProps) {
- const { stripeConfig, godaddyPaymentsConfig, squareConfig, paypalConfig } =
- useCheckoutContext();
+ const {
+ stripeConfig,
+ godaddyPaymentsConfig,
+ squareConfig,
+ paypalConfig,
+ mercadoPagoConfig,
+ } = useCheckoutContext();
const { payPalRequest } = useBuildPaymentRequest();
// Start with the children and conditionally wrap with providers
@@ -36,6 +42,13 @@ export function ConditionalPaymentProviders({
);
}
+ // Only wrap with MercadoPagoProvider if MercadoPago is configured
+ if (mercadoPagoConfig?.publicKey?.trim()) {
+ wrappedChildren = (
+ {wrappedChildren}
+ );
+ }
+
// Only wrap with StripeProvider if Stripe is configured
if (stripeConfig?.publishableKey?.trim()) {
wrappedChildren = {wrappedChildren};
diff --git a/packages/react/src/components/checkout/payment/utils/mercadopago-provider.tsx b/packages/react/src/components/checkout/payment/utils/mercadopago-provider.tsx
new file mode 100644
index 00000000..f21ee8d8
--- /dev/null
+++ b/packages/react/src/components/checkout/payment/utils/mercadopago-provider.tsx
@@ -0,0 +1,56 @@
+import React, {
+ createContext,
+ type ReactNode,
+ useCallback,
+ useContext,
+ useState,
+} from 'react';
+
+type MercadoPagoInstance = {
+ bricks: () => any;
+};
+
+type MercadoPagoContextType = {
+ isLoading: boolean;
+ setIsLoading: (loading: boolean) => void;
+ handleBrickSubmit: (() => Promise) | null;
+ setHandleBrickSubmit: (handler: (() => Promise) | null) => void;
+};
+
+const MercadoPagoContext = createContext(
+ undefined
+);
+
+export const MercadoPagoProvider = ({ children }: { children: ReactNode }) => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [handleBrickSubmit, setHandleBrickSubmit] = useState<(() => Promise) | null>(null);
+
+ const setIsLoadingCallback = useCallback((loading: boolean) => {
+ setIsLoading(loading);
+ }, []);
+
+ const setHandleBrickSubmitCallback = useCallback((handler: (() => Promise) | null) => {
+ setHandleBrickSubmit(() => handler);
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useMercadoPago = () => {
+ const context = useContext(MercadoPagoContext);
+ if (!context) {
+ throw new Error('useMercadoPago must be used within a MercadoPagoProvider');
+ }
+ return context;
+};
diff --git a/packages/react/src/components/checkout/payment/utils/use-confirm-checkout.ts b/packages/react/src/components/checkout/payment/utils/use-confirm-checkout.ts
index 93af44ba..155a6372 100644
--- a/packages/react/src/components/checkout/payment/utils/use-confirm-checkout.ts
+++ b/packages/react/src/components/checkout/payment/utils/use-confirm-checkout.ts
@@ -58,6 +58,7 @@ export enum PaymentProvider {
VANTIV_EXPRESS = 'VANTIV_EXPRESS',
EZETAP = 'EZETAP',
ADYEN = 'ADYEN',
+ MERCADOPAGO = 'MERCADOPAGO',
LETGO = 'LETGO',
CHECK_COMMERCE = 'CHECK_COMMERCE',
SQUARE = 'SQUARE',
@@ -154,6 +155,9 @@ export function useConfirmCheckout() {
case 'paze':
completedEventId = eventIds.pazePayCompleted;
break;
+ case 'mercadopago':
+ completedEventId = eventIds.mercadopagoCompleted;
+ break;
default:
completedEventId = null;
}
diff --git a/packages/react/src/components/checkout/payment/utils/use-load-mercadopago.ts b/packages/react/src/components/checkout/payment/utils/use-load-mercadopago.ts
new file mode 100644
index 00000000..47d67954
--- /dev/null
+++ b/packages/react/src/components/checkout/payment/utils/use-load-mercadopago.ts
@@ -0,0 +1,61 @@
+import { useEffect, useState } from 'react';
+import { useCheckoutContext } from '@/components/checkout/checkout';
+
+let isMercadoPagoLoaded = false;
+let isMercadoPagoCDNLoaded = false;
+const listeners = new Set<(loaded: boolean) => void>();
+const MERCADOPAGO_SDK_ID = 'mercadopago-sdk';
+
+export function useLoadMercadoPago() {
+ const { mercadoPagoConfig } = useCheckoutContext();
+ const [loaded, setLoaded] = useState(isMercadoPagoLoaded);
+
+ const mercadoPagoCDN = 'https://sdk.mercadopago.com/js/v2';
+
+ useEffect(() => {
+ // Register this component to be notified when MercadoPago loads
+ const updateLoaded = (newLoaded: boolean) => setLoaded(newLoaded);
+ listeners.add(updateLoaded);
+
+ // If already loaded, update immediately
+ if (isMercadoPagoLoaded) {
+ setLoaded(true);
+ }
+
+ return () => {
+ listeners.delete(updateLoaded);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (
+ isMercadoPagoLoaded ||
+ isMercadoPagoCDNLoaded ||
+ !mercadoPagoConfig ||
+ !mercadoPagoCDN
+ ) {
+ return;
+ }
+
+ const existingScript = document.getElementById(MERCADOPAGO_SDK_ID);
+ if (existingScript) {
+ isMercadoPagoCDNLoaded = true;
+ return;
+ }
+
+ isMercadoPagoCDNLoaded = true;
+ const script = document.createElement('script');
+ script.id = MERCADOPAGO_SDK_ID;
+ script.src = mercadoPagoCDN;
+ script.async = true;
+ script.onload = () => {
+ isMercadoPagoLoaded = true;
+ // Notify all components that MercadoPago has loaded
+ listeners.forEach(listener => listener(true));
+ };
+
+ document.body.appendChild(script);
+ }, [mercadoPagoConfig, mercadoPagoCDN]);
+
+ return { isMercadoPagoLoaded: loaded };
+}
diff --git a/packages/react/src/lib/godaddy/checkout-env.ts b/packages/react/src/lib/godaddy/checkout-env.ts
index 946b67bc..b9a84f3a 100644
--- a/packages/react/src/lib/godaddy/checkout-env.ts
+++ b/packages/react/src/lib/godaddy/checkout-env.ts
@@ -3002,6 +3002,15 @@ const introspection = {
args: [],
isDeprecated: false,
},
+ {
+ name: 'ccavenue',
+ type: {
+ kind: 'OBJECT',
+ name: 'CheckoutSessionPaymentMethodConfig',
+ },
+ args: [],
+ isDeprecated: false,
+ },
{
name: 'express',
type: {
@@ -3020,6 +3029,15 @@ const introspection = {
args: [],
isDeprecated: false,
},
+ {
+ name: 'mercadopago',
+ type: {
+ kind: 'OBJECT',
+ name: 'CheckoutSessionPaymentMethodConfig',
+ },
+ args: [],
+ isDeprecated: false,
+ },
{
name: 'offline',
type: {
@@ -3068,6 +3086,13 @@ const introspection = {
name: 'CheckoutSessionPaymentMethodConfigInput',
},
},
+ {
+ name: 'ccavenue',
+ type: {
+ kind: 'INPUT_OBJECT',
+ name: 'CheckoutSessionPaymentMethodConfigInput',
+ },
+ },
{
name: 'express',
type: {
@@ -3103,6 +3128,13 @@ const introspection = {
name: 'CheckoutSessionPaymentMethodConfigInput',
},
},
+ {
+ name: 'mercadopago',
+ type: {
+ kind: 'INPUT_OBJECT',
+ name: 'CheckoutSessionPaymentMethodConfigInput',
+ },
+ },
],
isOneOf: false,
},
@@ -9930,6 +9962,13 @@ const introspection = {
name: 'UpdateMoneyInput',
},
},
+ {
+ name: 'unitPrice',
+ type: {
+ kind: 'INPUT_OBJECT',
+ name: 'UpdateMoneyInput',
+ },
+ },
],
isOneOf: false,
},
diff --git a/packages/react/src/lib/godaddy/checkout-mutations.ts b/packages/react/src/lib/godaddy/checkout-mutations.ts
index c4e3815e..71464742 100644
--- a/packages/react/src/lib/godaddy/checkout-mutations.ts
+++ b/packages/react/src/lib/godaddy/checkout-mutations.ts
@@ -105,6 +105,10 @@ export const CreateCheckoutSessionMutation = graphql(`
processor
checkoutTypes
}
+ mercadopago {
+ processor
+ checkoutTypes
+ }
}
draftOrder {
id
diff --git a/packages/react/src/lib/godaddy/checkout-queries.ts b/packages/react/src/lib/godaddy/checkout-queries.ts
index 38491fb7..25d4b2dc 100644
--- a/packages/react/src/lib/godaddy/checkout-queries.ts
+++ b/packages/react/src/lib/godaddy/checkout-queries.ts
@@ -105,6 +105,10 @@ export const GetCheckoutSessionQuery = graphql(`
processor
checkoutTypes
}
+ mercadopago {
+ processor
+ checkoutTypes
+ }
}
locations {
id
diff --git a/packages/react/src/tracking/events.ts b/packages/react/src/tracking/events.ts
index d453b005..c0621e65 100644
--- a/packages/react/src/tracking/events.ts
+++ b/packages/react/src/tracking/events.ts
@@ -27,6 +27,8 @@ export const eventIds = {
pazePayImpression: 'paze_pay.impression',
pazePayClick: 'paze_pay.click',
pazePayCompleted: 'paze_pay_completed.event',
+ mercadopagoClick: 'mercadopago.click',
+ mercadopagoCompleted: 'mercadopago_completed.event',
expressCheckoutError: 'express_checkout_error.event',
// Express checkout coupon events
expressApplyCouponEvent: 'express_checkout_apply_coupon.event',
diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts
index 920c95b8..b726ff1c 100644
--- a/packages/react/src/types.ts
+++ b/packages/react/src/types.ts
@@ -43,6 +43,7 @@ export const PaymentProvider = {
PAYPAL: 'paypal',
PAZE: 'paze',
OFFLINE: 'offline',
+ MERCADOPAGO: 'mercadopago',
} as const;
export const CheckoutType = {
@@ -62,6 +63,7 @@ export const PaymentMethodType = {
GOOGLE_PAY: 'googlePay',
OFFLINE: 'offline',
PAZE: 'paze',
+ MERCADOPAGO: 'mercadopago',
} as const;
// Union of all payment method keys