Mercado Pago integration for websites
This model lets you offer Apple Pay on your website without managing payment certificates or decrypting tokens on your server. You can integrate via SDK, using the Mercado Pago JavaScript SDK in the frontend to display the Apple Pay button, or via API, implementing the flow in your backend. In both cases, Mercado Pago handles validation with Apple and returns a token ready to create the payment.
sequenceDiagram
title How it works
participant C as Buyer
participant V as Website (frontend)
participant MP as Mercado Pago
participant A as Apple
V->>MP: Initialize Apple Pay
MP->>A: Validate merchant and domain
A-->>V: Apple Pay button available
C->>V: Click Apple Pay
V->>A: Payment request (Touch ID/Face ID)
A-->>MP: Apple Pay token
MP-->>V: Mercado Pago token (callback)
V->>V: Send token to backend
V->>MP: Create payment (token + data)
MP-->>V: Payment response
In this integration you use the Mercado Pago JavaScript SDK in the frontend to display the Apple Pay button. Mercado Pago validates the session with Apple and returns a token ready to create the payment in your backend. Follow the steps below to integrate.
Having obtained and configured the required Apple Developer certificates, include the MercadoPago.js SDK on your page:
<script src="https://sdk.mercadopago.com/js/v2"></script>
Then, initialize the library with your test Public KeyPublic key of the application created in Mercado Pago, used in the frontend. You can access it in Your integrations > Application details > Test > Test credentials.:
javascript
const mp = new MercadoPago('YOUR_PUBLIC_KEY'); const quickCheckoutBuilder = mp.quickCheckout();
- Configure the payment and Apple Pay session data in your frontend. Below, you can see a configuration example and a description of the parameters necessary to do so.
const renderApplePayBrick = async (quickCheckoutBuilder) => {
const settings = {
checkoutTypes: ['applePay'],
applePay: {
paymentRequest: {
countryCode: 'BR',
currencyCode: 'BRL',
merchantCapabilities: ['supports3DS'],
supportedNetworks: ['visa', 'masterCard'],
total: {
label: 'My product',
amount: '10.00'
}
},
sessionRequest: {
id: 'PUBLIC_HASH_FROM_MERCADO_PAGO',
merchantIdentifier: 'merchant.your-identifier',
domainName: 'your-domain.com',
displayName: 'My store',
initiative: 'web',
initiativeContext: 'your-domain.com'
},
customization: {
locale: 'en-US'
},
callbacks: {
onReady: async () => {
console.log('Apple Pay ready');
},
onTokenGenerated: async (token, bin, paymentMethodId) => {
// Send token, bin and paymentMethodId to your backend to create the payment
const response = await fetch('/your-endpoint/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, bin, paymentMethodId })
});
const result = await response.json();
// Redirect or display result according to result
},
onCancel: async () => {
console.log('Payment cancelled');
},
onError: async (error) => {
console.error('Apple Pay error:', error);
}
}
}
};
window.applePayBrickController = await quickCheckoutBuilder.create(
'payment-container',
settings
);
};
renderApplePayBrick(quickCheckoutBuilder);
| Parameter | Description |
paymentRequest.merchantCapabilities | Merchant capabilities for the payment. Must include supports3DS to enable 3D Secure authentication in Apple Pay transactions. |
paymentRequest.supportedNetworks | Payment networks supported in the checkout. Only the visa and/or masterCard card brands are supported. |
sessionRequest.id | Public hash that Mercado Pago provides after you configure and submit the certificates in the Apple Developer Portal. It identifies your Apple Pay session with Mercado Pago. |
sessionRequest.merchantIdentifier | Unique identifier of your business in Apple Pay. Must match the Merchant ID you created and configured in the Apple Developer Portal. |
sessionRequest.domainName | Domain of your site where the Apple Pay button is displayed. Must match the domain you verified in the Apple Developer Portal and where the verification file is published in .well-known. |
sessionRequest.displayName | Merchant name shown to the user during the Apple Pay payment flow, for example on the Touch ID or Face ID authorization screen. |
sessionRequest.initiativeContext | Web payment initiative context. Must have the same value as domainName for the Apple Pay session to validate correctly. |
- After creating the configuration, define the container in your HTML where the Apple Pay button will be rendered and add the code below.
html
<div id="payment-container" class="payment-container"></div>
The rendered component should look similar to the image below.

Button visual customization
You can customize the button style, text type, and language in the customization object of your configuration:
javascript
customization: { buttonStyle: 'black', // 'black' | 'white' | 'white-outline' buttonType: 'pay', // 'pay' | 'buy' | 'check-out' | 'plain' | etc. locale: 'en-US' // The default is the language and region currently configured in the browser }
| Option | Description |
buttonStyle | Button style. Values can be: black, white, or white-outline. |
buttonType | Button type displayed. Values can be: pay, buy, check-out, plain, among others. |
locale | Language code. Values can be: es-ES, pt-BR, en-US, among others. |
When the buyer authorizes the payment with Apple Pay, the onTokenGenerated callback delivers the token, bin, and paymentMethodId to your frontend.
Send these values in the payment to Mercado Pago through our SDKs. Use your test Access TokenPrivate key of the application created in Mercado Pago, used in the backend. You can access it in Your integrations > Application details > Test > Test credentials..
<?php
use MercadoPago\Client\Payment\PaymentClient;
use MercadoPago\Client\Common\RequestOptions;
use MercadoPago\MercadoPagoConfig;
MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");
$client = new PaymentClient();
$request_options = new RequestOptions();
$request_options->setCustomHeaders(["X-Idempotency-Key: <SOME_UNIQUE_VALUE>"]);
$payment = $client->create([
"transaction_amount" => (float) $_POST['<TRANSACTION_AMOUNT>'],
"token" => $_POST['<TOKEN>'],
"description" => $_POST['<DESCRIPTION>'],
"installments" => $_POST['<INSTALLMENTS>'],
"payment_method_id" => $_POST['<PAYMENT_METHOD_ID>'],
"issuer_id" => $_POST['<ISSUER>'],
"payer" => [
"email" => $_POST['<EMAIL>'],
"identification" => [
"type" => $_POST['<IDENTIFICATION_TYPE>'],
"number" => $_POST['<NUMBER>']
]
]
], $request_options);
echo implode($payment);
?>
import { MercadoPagoConfig, Payment } from '@src/index';
const client = new MercadoPagoConfig({ accessToken: '<ACCESS_TOKEN>', options: { timeout: 5000 } });
const payment = new Payment(client);
payment
.create({
body: {
transaction_amount: 100,
token: '<TOKEN>',
description: '<DESCRIPTION>',
installments: 1,
payment_method_id: '<PAYMENT_METHOD_ID>',
issuer_id: 310,
payer: {
email: '<EMAIL>',
identification: {
number: '12345678909',
type: 'CPF',
},
},
},
}).then(console.log).catch(console.log);
Map<String, String> customHeaders = new HashMap<>();
customHeaders.put("x-idempotency-key", <SOME_UNIQUE_VALUE>);
MPRequestOptions requestOptions = MPRequestOptions.builder()
.customHeaders(customHeaders)
.build();
MercadoPagoConfig.setAccessToken("YOUR_ACCESS_TOKEN");
PaymentClient client = new PaymentClient();
PaymentCreateRequest paymentCreateRequest =
PaymentCreateRequest.builder()
.transactionAmount(request.getTransactionAmount())
.token(request.getToken())
.description(request.getDescription())
.installments(request.getInstallments())
.paymentMethodId(request.getPaymentMethodId())
.payer(
PaymentPayerRequest.builder()
.email(request.getPayer().getEmail())
.firstName(request.getPayer().getFirstName())
.identification(
IdentificationRequest.builder()
.type(request.getPayer().getIdentification().getType())
.number(request.getPayer().getIdentification().getNumber())
.build())
.build())
.build();
client.create(paymentCreateRequest, requestOptions);
require 'mercadopago'
sdk = Mercadopago::SDK.new('YOUR_ACCESS_TOKEN')
custom_headers = {
'x-idempotency-key': '<SOME_UNIQUE_VALUE>'
}
custom_request_options = Mercadopago::RequestOptions.new(custom_headers: custom_headers)
payment_data = {
transaction_amount: params[:transactionAmount].to_f,
token: params[:token],
description: params[:description],
installments: params[:installments].to_i,
payment_method_id: params[:paymentMethodId],
payer: {
email: params[:cardholderEmail],
identification: {
type: params[:identificationType],
number: params[:identificationNumber]
},
first_name: params[:cardholderName]
}
}
payment_response = sdk.payment.create(payment_data, custom_request_options)
payment = payment_response[:response]
puts payment
using System;
using MercadoPago.Client.Common;
using MercadoPago.Client.Payment;
using MercadoPago.Config;
using MercadoPago.Resource.Payment;
MercadoPagoConfig.AccessToken = "YOUR_ACCESS_TOKEN";
var requestOptions = new RequestOptions();
requestOptions.CustomHeaders.Add("x-idempotency-key", "<SOME_UNIQUE_VALUE>");
var paymentRequest = new PaymentCreateRequest
{
TransactionAmount = decimal.Parse(Request["transactionAmount"]),
Token = Request["token"],
Description = Request["description"],
Installments = int.Parse(Request["installments"]),
PaymentMethodId = Request["paymentMethodId"],
Payer = new PaymentPayerRequest
{
Email = Request["cardholderEmail"],
Identification = new IdentificationRequest
{
Type = Request["identificationType"],
Number = Request["identificationNumber"],
},
FirstName = Request["cardholderName"]
},
};
var client = new PaymentClient();
Payment payment = await client.CreateAsync(paymentRequest, requestOptions);
Console.WriteLine(payment.Status);
import mercadopago
sdk = mercadopago.SDK("ACCESS_TOKEN")
request_options = mercadopago.config.RequestOptions()
request_options.custom_headers = {
'x-idempotency-key': '<SOME_UNIQUE_VALUE>'
}
payment_data = {
"transaction_amount": float(request.POST.get("transaction_amount")),
"token": request.POST.get("token"),
"description": request.POST.get("description"),
"installments": int(request.POST.get("installments")),
"payment_method_id": request.POST.get("payment_method_id"),
"payer": {
"email": request.POST.get("cardholderEmail"),
"identification": {
"type": request.POST.get("identificationType"),
"number": request.POST.get("identificationNumber")
},
"first_name": request.POST.get("cardholderName")
}
}
payment_response = sdk.payment().create(payment_data, request_options)
payment = payment_response["response"]
print(payment)
| Element | Description | Required |
Authorization | Header with your test Access TokenPrivate key of the application created in Mercado Pago, used in the backend. You can access it in Your integrations > Application details > Test > Test credentials.. | Required |
X-Idempotency-Key | Unique value per request (for example, UUID v4) to avoid duplicate payments. | Required |
token | Card token received in the onTokenGenerated callback. | Required |
payment_method_id | Payment method identifier: visa or master. | Required |
transaction_amount | Transaction amount. | Required |
installments | Number of installments. | Required |
payer | Buyer data. | Required |
description | Payment description. | Required |
issuer_id | Card issuer ID. | Optional |
The API returns a response with a structure similar to the following example:
json
{ "status": "approved", "status_detail": "accredited", "id": 3055677, "date_approved": "2019-02-23T00:01:10.000-04:00", "payer": { ... }, "payment_method_id": "visa", "payment_type_id": "credit_card", "refunds": [], ... }
You can simulate different payment scenarios in the test environment using the testStatus property in the Apple Pay configuration. It only takes effect when you use a test Public KeyPublic key of the application created in Mercado Pago, used in the frontend. You can access it in Your integrations > Application details > Test > Test credentials..
testStatus property is optional and should be used for testing only. In production, do not include this configuration.- Add the
testStatusproperty insideapplePayin your configuration. Use the different values detailed in the table to simulate payment scenarios.
javascript
const settings = { checkoutTypes: ['applePay'], applePay: { paymentRequest: { /* ... */ }, sessionRequest: { /* ... */ }, callbacks: { /* ... */ }, testStatus: 'APRO' // Optional: for testing only } };
testStatus value | Payment scenario |
APRO | Payment approved |
OTHE | Rejected due to general error |
CONT | Pending payment |
CALL | Rejected with validation to authorize |
FUND | Rejected due to insufficient funds |
SECU | Rejected due to invalid security code |
EXPI | Rejected due to expiration date |
FORM | Rejected due to form error |
CARD | Rejected due to missing card_number |
INST | Rejected due to invalid installments |
DUPL | Rejected due to duplicate payment |
LOCK | Rejected due to disabled card |
CTNA | Rejected due to card type not allowed |
ATTE | Rejected due to PIN attempts exceeded |
BLAC | Rejected due to blacklist |
UNSU | Not supported |
TEST | Used to apply amount rule |
