# Express Checkout

El express checkout te permite recopilar direcciones de envío y facturación directamente desde las interfaces de Apple Pay, Google Pay o PayPal, sin necesidad de un formulario de dirección independiente.

Disponible en los componentes [`PaymentRequest`](https://docs.monei.com/es/es/monei-js/reference/.md#paymentrequest-component) (Apple Pay / Google Pay) y [`PayPal`](https://docs.monei.com/es/es/monei-js/reference/.md#paypal-component).

## Antes de empezar[​](#antes-de-empezar "Enlace directo al Antes de empezar")

* Necesitarás una cuenta MONEI y tus claves de API (de prueba o de producción). Encuéntralas en tu [MONEI Dashboard](https://dashboard.monei.com/settings/api).
* Usa tus [claves en modo de prueba](https://docs.monei.com/es/es/testing/.md) para las pruebas de integración.
* Apple Pay requiere [verificación de dominio](https://docs.monei.com/es/es/payment-methods/apple-pay/.md#register-your-domain-with-apple-pay) tanto en desarrollo como en producción.
* Google Pay requiere al menos un [procesador de tarjetas](https://dashboard.monei.com/settings/payment-methods/card) configurado.
* PayPal requiere una [cuenta de empresa PayPal](https://dashboard.monei.com/settings/payment-methods) conectada.

## Cómo funciona[​](#cómo-funciona "Enlace directo al Cómo funciona")

<!-- -->

1. El **navegador** abre la interfaz de cartera (Apple Pay, Google Pay o PayPal) con tu ID de cuenta y el importe inicial
2. El cliente selecciona las opciones de envío/facturación — tus callbacks actualizan los importes de forma dinámica
3. Tras la aprobación, el **token y los detalles** se envían a tu backend
4. Tu **backend** crea el pago con el token — no es necesario crear el pago previamente
5. MONEI envía el estado final del pago mediante webhook

## Express checkout con envío[​](#express-checkout-con-envío "Enlace directo al Express checkout con envío")

Con envío, el importe autorizado por la cartera puede diferir del importe inicial cuando cambian los costes de envío. Este flujo usa el enfoque de **accountId** — no es necesario crear el pago previamente. El pago se crea en el servidor una vez que el cliente confirma en la interfaz de cartera.

### 1. Incluir monei.js (lado del cliente)[​](#1-incluir-moneijs-lado-del-cliente "Enlace directo al 1. Incluir monei.js (lado del cliente)")

Añade la etiqueta script al `head` de tu archivo HTML.

checkout.html

```
<head>

  <script src="https://js.monei.com/v3/monei.js"></script>

</head>

<body>

  <!-- Use "payment_request" for Apple Pay / Google Pay, "paypal_container" for PayPal -->

  <div id="payment_request"></div>

  <div id="paypal_container"></div>

  <script src="client.js"></script>

</body>
```

### 2. Inicializar el Component con express checkout (lado del cliente)[​](#2-inicializar-el-component-con-express-checkout-lado-del-cliente "Enlace directo al 2. Inicializar el Component con express checkout (lado del cliente)")

* Apple Pay / Google Pay
* PayPal

client.js

```
const paymentRequest = monei.PaymentRequest({

  accountId: 'your_account_id',

  sessionId: 'unique_session_id',

  amount: 1099, // base amount in minor units (before shipping)

  currency: 'EUR',

  requestShipping: true,

  requestBilling: true,

  shippingOptions: [

    {id: 'standard', label: 'Standard Shipping', amount: 500},

    {id: 'express', label: 'Express Shipping', amount: 1200, description: '1-2 business days'}

  ],

  onShippingAddressChange(address) {

    // Called when the customer changes their shipping address.

    // address contains: country, city, state, zip (no street-level data mid-flow).

    // Return updated shipping options and/or amount based on the address.

    if (address.country !== 'ES') {

      return {

        shippingOptions: [{id: 'intl', label: 'International', amount: 2000}],

        amount: 3099 // product (1099) + intl shipping (2000)

      };

    }

    return {shippingOptions: [{id: 'standard', label: 'Standard', amount: 500}], amount: 1599};

  },

  onShippingOptionChange(option) {

    // Called when the customer selects a different shipping option.

    // Return updated total amount.

    return {amount: 1099 + option.amount};

  },

  async onSubmit(result) {

    // Called when the customer approves the payment in the wallet UI.

    // Create the payment server-side (see Step 3).

    await fetch('/create-payment', {

      method: 'POST',

      headers: {'Content-Type': 'application/json'},

      body: JSON.stringify({

        amount: result.finalAmount,

        currency: 'EUR',

        sessionId: 'unique_session_id',

        paymentToken: result.token,

        billingDetails: result.billingDetails,

        shippingDetails: result.shippingDetails

      })

    });

  },

  onError(error) {

    console.error(error);

  }

});



paymentRequest.render('#payment_request');
```

client.js

```
const paypal = monei.PayPal({

  accountId: 'your_account_id',

  sessionId: 'unique_session_id',

  amount: 1099,

  currency: 'EUR',

  requestShipping: true,

  shippingOptions: [

    {id: 'standard', label: 'Standard Shipping', amount: 500, selected: true},

    {id: 'express', label: 'Express Shipping', amount: 1200}

  ],

  onShippingAddressChange(address) {

    return {

      shippingOptions: [{id: 'standard', label: 'Standard', amount: 500, selected: true}],

      amount: 1599

    };

  },

  onShippingOptionChange(option) {

    return {amount: 1099 + option.amount};

  },

  async onSubmit(result) {

    // PayPal always returns payer info (name, email) in billingDetails.

    // Create the payment server-side (see Step 3).

    await fetch('/create-payment', {

      method: 'POST',

      headers: {'Content-Type': 'application/json'},

      body: JSON.stringify({

        amount: result.finalAmount,

        currency: 'EUR',

        sessionId: 'unique_session_id',

        paymentToken: result.token,

        billingDetails: result.billingDetails,

        shippingDetails: result.shippingDetails

      })

    });

  },

  onError(error) {

    console.error(error);

  }

});



paypal.render('#paypal_container');
```

nota

PayPal siempre devuelve la información del pagador (nombre, email) independientemente de la configuración — no existe una propiedad `requestBilling` independiente.

**Opciones principales:**

* **accountId** `string`: Tu ID de cuenta MONEI.
* **sessionId** `string`: Un identificador de sesión único. Debe coincidir con el `sessionId` pasado al [crear el pago en el servidor](#3-create-payment-server-side).
* **amount** `number`: Importe inicial en la unidad monetaria más pequeña (p. ej., 1099 = 10,99 EUR).
* **requestShipping** `boolean`: Activa la recopilación de la dirección de envío en la interfaz de cartera.
* **requestBilling** `boolean`: Activa la recopilación de la dirección de facturación (solo PaymentRequest — PayPal siempre devuelve la información de facturación).
* **shippingOptions** [`ShippingOption[]`](https://docs.monei.com/es/es/monei-js/reference/.md#shippingoption): Opciones de envío disponibles.

Consulta las opciones de [`PaymentRequest`](https://docs.monei.com/es/es/monei-js/reference/.md#paymentrequest-options) y [`PayPal`](https://docs.monei.com/es/es/monei-js/reference/.md#paypal-options) para la lista completa.

### 3. Crear el pago (lado del servidor)[​](#3-crear-el-pago-lado-del-servidor "Enlace directo al 3. Crear el pago (lado del servidor)")

Cuando el cliente confirma en la interfaz de cartera, tu manejador `onSubmit` envía el resultado a tu servidor. Crea el pago pasando el `paymentToken` — esto confirma el pago automáticamente.

important

Debes pasar el mismo `sessionId` utilizado para inicializar el componente en el frontend. Esto garantiza que el cliente que generó el token sea el mismo que realiza el pago.

* cURL
* Node.js
* PHP
* Python

POST https\://api.monei.com/v1/payments

```
curl --request POST 'https://api.monei.com/v1/payments' \

--header 'Authorization: YOUR_API_KEY' \

--header 'Content-Type: application/json' \

--data-raw '{

  "amount": 1599,

  "currency": "EUR",

  "orderId": "14379133960355",

  "description": "Test Shop - #14379133960355",

  "sessionId": "unique_session_id",

  "paymentToken": "token_from_client",

  "callbackUrl": "https://example.com/checkout/callback",

  "customer": {

    "email": "email@example.com"

  },

  "billingDetails": {},

  "shippingDetails": {}

}'
```

server.js

```
import {Monei} from '@monei-js/node-sdk';



const monei = new Monei('YOUR_API_KEY');



// In your /create-payment route handler:

app.post('/create-payment', async (req, res) => {

  const {amount, currency, sessionId, paymentToken, billingDetails, shippingDetails} = req.body;



  const payment = await monei.payments.create({

    amount,

    currency,

    orderId: '14379133960355',

    description: 'Test Shop - #14379133960355',

    sessionId,

    paymentToken,

    callbackUrl: 'https://example.com/checkout/callback',

    customer: {email: billingDetails?.email},

    billingDetails,

    shippingDetails

  });



  res.json({status: payment.status});

});
```

server.php

```
<?php

require_once 'vendor/autoload.php';



use Monei\Model\CreatePaymentRequest;

use Monei\MoneiClient;



$monei = new MoneiClient('YOUR_API_KEY');



$body = json_decode(file_get_contents('php://input'), true);



$payment = $monei->payments->create(

  new CreatePaymentRequest([

    'amount' => $body['amount'],

    'currency' => $body['currency'],

    'order_id' => '14379133960355',

    'description' => 'Test Shop - #14379133960355',

    'session_id' => $body['sessionId'],

    'payment_token' => $body['paymentToken'],

    'callback_url' => 'https://example.com/checkout/callback',

    'billing_details' => $body['billingDetails'],

    'shipping_details' => $body['shippingDetails']

  ])

);



echo json_encode(['status' => $payment->getStatus()]);

?>
```

server.py

```
import Monei

from Monei import CreatePaymentRequest



monei = Monei.MoneiClient(api_key="YOUR_API_KEY")



# In your /create-payment route handler:

payment = monei.payments.create(

    CreatePaymentRequest(

        amount=body["amount"],

        currency=body["currency"],

        order_id="14379133960355",

        description="Test Shop - #14379133960355",

        session_id=body["sessionId"],

        payment_token=body["paymentToken"],

        callback_url="https://example.com/checkout/callback",

        billing_details=body["billingDetails"],

        shipping_details=body["shippingDetails"]

    )

)
```

Consulta todos los [parámetros de la solicitud](https://docs.monei.com/es/es/apis/rest/payments-create/.md) disponibles.

### 4. Procesar la notificación de webhook (lado del servidor)[​](#4-procesar-la-notificación-de-webhook-lado-del-servidor "Enlace directo al 4. Procesar la notificación de webhook (lado del servidor)")

MONEI envía el estado final y definitivo del pago mediante una solicitud HTTP POST asíncrona a la `callbackUrl` que proporcionaste en el Paso 3. El cuerpo de la solicitud contiene el [objeto Payment](https://docs.monei.com/es/es/apis/rest/schemas/payment/.md) completo en formato JSON.

Este webhook es la **única forma fiable** de confirmar el resultado definitivo del pago.

1. **Verifica la cabecera `MONEI-Signature`** para confirmar que el webhook proviene realmente de MONEI. Consulta la [guía de verificación de firmas](https://docs.monei.com/es/es/guides/verify-signature/.md) para los detalles de implementación.
2. **Devuelve un código de estado HTTP `200 OK`** inmediatamente para confirmar la recepción.

Si MONEI no recibe un `200 OK`, reintentará el envío del webhook.

## Checkout solo con facturación[​](#checkout-solo-con-facturación "Enlace directo al Checkout solo con facturación")

Si solo necesitas la dirección de facturación (sin envío), el flujo es más sencillo — usa `paymentId` en lugar de `accountId` y confirma en el lado del cliente con `confirmPayment`. Sigue los mismos pasos que en [Crea un checkout personalizado](https://docs.monei.com/es/es/integrations/build-custom-checkout/.md), pero añade `requestBilling: true` para recopilar la dirección de facturación desde la cartera:

client.js

```
const paymentRequest = monei.PaymentRequest({

  paymentId: '{{payment_id}}',

  requestBilling: true,

  async onSubmit(result) {

    await monei.confirmPayment({

      paymentId: '{{payment_id}}',

      paymentToken: result.token,

      billingDetails: result.billingDetails

    });

  },

  onError(error) {

    console.error(error);

  }

});



paymentRequest.render('#payment_request');
```

## Referencia de tipos[​](#referencia-de-tipos "Enlace directo al Referencia de tipos")

Consulta las definiciones de tipos completas en la [Referencia](https://docs.monei.com/es/es/monei-js/reference/.md#type-reference):

* [`SubmitResult`](https://docs.monei.com/es/es/monei-js/reference/.md#submitresult) — devuelto por todos los callbacks `onSubmit`
* [`ShippingOption`](https://docs.monei.com/es/es/monei-js/reference/.md#shippingoption) — configuración de opciones de envío
* [`ShippingAddressChangeResult`](https://docs.monei.com/es/es/monei-js/reference/.md#shippingaddresschangeresult) — tipo de retorno para `onShippingAddressChange`
* [`ShippingOptionChangeResult`](https://docs.monei.com/es/es/monei-js/reference/.md#shippingoptionchangeresult) — tipo de retorno para `onShippingOptionChange`
* [`BillingDetails`](https://docs.monei.com/es/es/monei-js/reference/.md#billingdetails) — detalles de facturación/envío
* [`Address`](https://docs.monei.com/es/es/monei-js/reference/.md#address) — dirección postal

## Antes de pasar a producción[​](#antes-de-pasar-a-producción "Enlace directo al Antes de pasar a producción")

* Asegúrate de usar el ID de cuenta y la clave de API en [modo de producción (live)](https://docs.monei.com/es/es/testing/.md).
* Asegúrate de tener los [métodos de pago relevantes configurados y habilitados](https://dashboard.monei.com/settings/payment-methods) en modo de producción.
* Para Apple Pay, verifica tu [dominio de producción](https://docs.monei.com/es/es/payment-methods/apple-pay/.md#register-your-domain-with-apple-pay).
