# Use a prebuilt payment page

MONEI's Hosted Payment Page offers the simplest, PCI-compliant way to securely collect payments from your customers using various methods.

![Hosted payments preview](/assets/images/payment-page-preview-d18ed094bf6323e0f15ac584d5b656e2.png)

[](https://js.monei.com/v2/payment-page/#ewogICJzaG9wTG9nb1VybCI6ICJodHRwczovL21vbmVpLXByb2Qtc2VydmljZS1idWNrZXQuczMuZXUtd2VzdC0xLmFtYXpvbmF3cy5jb20vcHJvdGVjdGVkL2V1LXdlc3QtMTo3NjU5Zjc4ZS1kZDA0LTRiYjItYTE2My1jOWVhYzk3ZmNkMzIvcmMtdXBsb2FkLTE3NDM0MzgxMTU1MjgtMi5zdmciLAogICJhY2NlbnRDb2xvciI6ICIjMDAwMDAwIiwKICAiYW1vdW50IjogMTUwMDAsCiAgImJnQ29sb3IiOiAiI2YwZjRmNSIsCiAgInRlc3QiOiB0cnVlLAogICJwYXltZW50TWV0aG9kcyI6IFsiYml6dW0iLCAiYXBwbGVQYXkiLCAiZ29vbGdlUGF5IiwgImNsaWNrVG9QYXkiLCAicGF5cGFsIiwgImNhcmQiXSwKICAiYWNjb3VudElkIjogIjI5NzViY2ZhLTdiYmMtNDIyZC1hZjQ4LWM2Njc1OWQ4N2I2OSIsCiAgInBheW1lbnRJZCI6ICI0NDMyMzI5MmJkNzdlNDY1ZWVkMmZkM2E1ZTQ4NjQ5MzA1N2I1OGM2IiwKICAic2hvcE5hbWUiOiAiWkFSQSIKfQo=)

[Live demo](https://js.monei.com/v2/payment-page/#ewogICJzaG9wTG9nb1VybCI6ICJodHRwczovL21vbmVpLXByb2Qtc2VydmljZS1idWNrZXQuczMuZXUtd2VzdC0xLmFtYXpvbmF3cy5jb20vcHJvdGVjdGVkL2V1LXdlc3QtMTo3NjU5Zjc4ZS1kZDA0LTRiYjItYTE2My1jOWVhYzk3ZmNkMzIvcmMtdXBsb2FkLTE3NDM0MzgxMTU1MjgtMi5zdmciLAogICJhY2NlbnRDb2xvciI6ICIjMDAwMDAwIiwKICAiYW1vdW50IjogMTUwMDAsCiAgImJnQ29sb3IiOiAiI2YwZjRmNSIsCiAgInRlc3QiOiB0cnVlLAogICJwYXltZW50TWV0aG9kcyI6IFsiYml6dW0iLCAiYXBwbGVQYXkiLCAiZ29vbGdlUGF5IiwgImNsaWNrVG9QYXkiLCAicGF5cGFsIiwgImNhcmQiXSwKICAiYWNjb3VudElkIjogIjI5NzViY2ZhLTdiYmMtNDIyZC1hZjQ4LWM2Njc1OWQ4N2I2OSIsCiAgInBheW1lbnRJZCI6ICI0NDMyMzI5MmJkNzdlNDY1ZWVkMmZkM2E1ZTQ4NjQ5MzA1N2I1OGM2IiwKICAic2hvcE5hbWUiOiAiWkFSQSIKfQo=)

**Key Features:**

* **Designed to remove friction:** Real-time card validation with built-in error messaging.
* **Mobile-ready:** Fully responsive design.
* **International:** Supports 13 languages.
* **Multiple payment methods:** Supports various [payments methods](https://monei.com/blog/multiple-payment-options/).
* **Customization and branding:** Customizable logo, buttons, and background color via your [MONEI Dashboard](https://dashboard.monei.com/settings/branding).
* **3D Secure:** Built-in support for SCA verification.
* **Fraud and compliance:** Simplified PCI compliance and SCA-ready.

You can customize the appearance in your [MONEI Dashboard → Settings → Branding](https://dashboard.monei.com/settings/branding).

## Before You Begin[​](#before-you-begin "Direct link to Before You Begin")

* You'll need a MONEI account and your API keys (test or live). Find them in your [MONEI Dashboard](https://dashboard.monei.com/settings/api).
* Use your [test mode keys](https://docs.monei.com/testing/.md) for integration testing.
* Ensure relevant payment methods are enabled in your account settings.
* You can monitor test payments in your [MONEI Dashboard → Payments](https://dashboard.monei.com/payments) (ensure Test Mode is active).

## How It Works[​](#how-it-works "Direct link to How It Works")

<!-- -->

1. Your **backend** creates a payment and receives a `redirectUrl`
2. The customer is **redirected** to the MONEI-hosted payment page
3. The customer selects a payment method and completes the payment (including 3D Secure if required)
4. After payment, the customer is redirected back to your `completeUrl` (or `cancelUrl` if cancelled)
5. MONEI sends the **final payment status** to your backend via webhook — always rely on this, not the redirect

## Integration Steps[​](#integration-steps "Direct link to Integration Steps")

### 1. Create Payment (Server-side)[​](#1-create-payment-server-side "Direct link to 1. Create Payment (Server-side)")

First, make a server-side API call to create a new [Payment object](https://docs.monei.com/apis/rest/schemas/payment/.md). This registers the payment intent with MONEI.

<!-- -->

* 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": 110,

    "currency": "EUR",

    "orderId": "14379133960355",

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

    "customer": {

       "name": "John Doe",

       "email": "email@example.com",

       "phone": "+34666555444"

    },

    "billingDetails": {

       "name": "John Doe",

       "address": {

          "country": "ES",

          "city": "Malaga",

          "line1": "Fake Street 123",

          "zip": "29001"

       }

    },

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

    "completeUrl": "https://example.com/checkout/complete",

    "cancelUrl": "https://example.com/checkout/cancel"

}'
```

(Replace `YOUR_API_KEY` with your actual MONEI API key)

server.js

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



// Replace YOUR_API_KEY with your actual MONEI API key

const monei = new Monei('YOUR_API_KEY');



const payment = await monei.payments.create({

  amount: 110,

  currency: 'EUR',

  orderId: '14379133960355',

  description: 'Test Shop - #14379133960355',

  customer: {

    name: 'John Doe',

    email: 'email@example.com',

    phone: '+34666555444'

  },

  billingDetails: {

    name: 'John Doe',

    address: {

      country: 'ES',

      city: 'Malaga',

      line1: 'Fake Street 123',

      zip: '29001'

    }

  },

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

  completeUrl: 'https://example.com/checkout/complete',

  cancelUrl: 'https://example.com/checkout/cancel'

});



// You will need the redirectUrl from the response in the next step

const redirectUrl = payment.nextAction.redirectUrl;
```

server.php

```
<?php

require_once 'vendor/autoload.php';



use Monei\Model\CreatePaymentRequest;

use Monei\Model\PaymentCustomer;

use Monei\Model\PaymentBillingDetails;

use Monei\Model\Address;

use Monei\MoneiClient;



// Replace YOUR_API_KEY with your actual MONEI API key

$monei = new MoneiClient('YOUR_API_KEY');



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

    new CreatePaymentRequest([

      'amount' => 110,

      'currency' => 'EUR',

      'order_id' => '14379133960355',

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

      'customer' => new PaymentCustomer([

        'name' => 'John Doe',

        'email' => 'email@example.com',

        'phone' => '+34666555444'

      ]),

      'billing_details' => new PaymentBillingDetails([

        'name' => 'John Doe',

        'address' => new Address([

          'country' => 'ES',

          'city' => 'Malaga',

          'line1' => 'Fake Street 123',

          'zip' => '29001'

        ])

      ]),

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

      'complete_url' => 'https://example.com/checkout/complete',

      'cancel_url' => 'https://example.com/checkout/cancel'

    ])

);



// You will need the redirectUrl from the response in the next step

$redirectUrl = $payment->getNextAction()->getRedirectUrl();

?>
```

server.py

```
import Monei

from Monei import CreatePaymentRequest, PaymentCustomer, PaymentBillingDetails, Address



# Replace YOUR_API_KEY with your actual MONEI API key

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



payment = monei.payments.create(

    CreatePaymentRequest(

        amount=110,

        currency="EUR",

        order_id="14379133960355",

        description="Test Shop - #14379133960355",

        customer=PaymentCustomer(

            name="John Doe",

            email="email@example.com",

            phone="+34666555444"

        ),

        billing_details=PaymentBillingDetails(

            name="John Doe",

            address=Address(

                country="ES",

                city="Malaga",

                line1="Fake Street 123",

                zip="29001"

            )

        ),

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

        complete_url="https://example.com/checkout/complete",

        cancel_url="https://example.com/checkout/cancel"

    )

)



// You will need the redirectUrl from the response in the next step

redirect_url = payment.next_action.redirect_url
```

**Key Parameters:**

* **amount** `positive integer`: Amount in the smallest currency unit (e.g., 110 for €1.10).
* **currency** `string`: Three-letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217) (e.g., `EUR`).
* **orderId** `string`: Your unique order identifier.
* **completeUrl** `string`: Where the customer is redirected **after** attempting payment (success or failure).
* **callbackUrl** `string`: Your server endpoint URL for asynchronous webhook notifications (crucial for final status).
* **cancelUrl** \`string\*\*: Where the customer is redirected if they click **cancel** or \*\*"Back to shop"\*\*.

Check all available [request parameters](https://docs.monei.com/apis/rest/payments-create/.md).

The API response includes the `payment.id` and, importantly, `payment.nextAction.redirectUrl`.

### 2. Handle Payment Interaction (Client-side via Redirect)[​](#2-handle-payment-interaction-client-side-via-redirect "Direct link to 2. Handle Payment Interaction (Client-side via Redirect)")

The API response from Step 1 contains a `nextAction` object with a `redirectUrl`. You **must** redirect your customer's browser to this URL.

Example Partial API Response

```
{

  "id": "af6029f80f5fc73a8ad2753eea0b1be0", // MONEI Payment ID

  // ... other fields ...

  "status": "PENDING", // Initial status

  "nextAction": {

    "type": "CONFIRM",

    "mustRedirect": true, // Indicates redirection is needed

    "redirectUrl": "https://secure.monei.com/payments/af6029f80f5fc73a8ad2753eea0b1be0" // <-- REDIRECT CUSTOMER HERE

  }

}
```

This `redirectUrl` leads the customer to the secure, MONEI-hosted payment page where they will select a payment method and enter their details.

Alternative: Payment Modal

Instead of a full page redirect, you can use [`monei.js`](https://docs.monei.com/monei-js/reference/.md) to present payment options within a modal on your own site. See the [Payment Modal Guide](https://docs.monei.com/integrations/use-payment-modal/.md) for details.

**Customer Actions:**

* The customer completes the payment details on the MONEI page.

* They might undergo 3D Secure verification if required by their bank.

* After completion, failure, or cancellation, they are redirected back to your site:

  <!-- -->

  * To the `completeUrl` if they attempted payment.
  * To the `cancelUrl` if they explicitly cancelled.

**Important:** The redirect to `completeUrl` **does not** guarantee payment success. You must rely on the webhook (Step 3) for the final status.

### 3. Process Webhook Notification (Server-side)[​](#3-process-webhook-notification-server-side "Direct link to 3. Process Webhook Notification (Server-side)")

MONEI sends the final, authoritative payment status via an asynchronous HTTP POST request to the `callbackUrl` you provided in Step 1. The request body contains the full [Payment object](https://docs.monei.com/apis/rest/schemas/payment/.md) in JSON format.

This webhook is the **only reliable way** to confirm the definitive payment outcome, regardless of customer browser actions or redirects.

**Crucially, you must:**

1. **Verify the `MONEI-Signature` header** included in the request. This confirms the webhook genuinely came from MONEI. See the [Verify Signatures guide](https://docs.monei.com/guides/verify-signature/.md) for implementation details.
2. **Return a `200 OK` HTTP status code** immediately upon receiving the webhook to acknowledge receipt. Any other status code tells MONEI the notification failed.

If MONEI doesn't receive a `200 OK`, it will retry sending the webhook.

Once the signature is verified, inspect the `status` field in the Payment object (`SUCCEEDED`, `FAILED`, `CANCELED`, etc.) to determine whether to fulfill the order or handle the failure.

Example Webhook Payload (POST to your callbackUrl)

```
{

  "id": "af6029f80f5fc73a8ad2753eea0b1be0",

  "amount": 110,

  // ... other fields ...

  "status": "SUCCEEDED", // <-- Check this for final status

  "createdAt": 1594215339,

  "updatedAt": 1594215345

}
```
