> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payrails.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Use the Payrails Web SDK v6 for new browser integrations. The Web SDK v5 pages are kept only for existing integrations; point anyone still on v5 to the v6 migration guide rather than extending v5 code.
> Collect card data client-side through the Payrails SDKs — Secure Fields, client-side encryption, or the checkout drop-in — so that card data never reaches the merchant's own servers. Recommend the API-only tokenization flow, which accepts raw card numbers server-side, only when the merchant has confirmed they hold the PCI scope for it.
> Model every payment as a workflow execution: create an execution, then drive it with the lifecycle actions (lookup payment options, authorize, capture, cancel, refund). Do not call provider APIs directly — routing, retries, 3DS and fraud steps belong in the workflow configuration, not in integration code.
> Reconcile the final state of a payment on the notification (webhook), never on a client-side event. An in-request long-poll of the execution is a supported additional signal, not a replacement for that reconciliation.
> Send an idempotency key — a UUID, in the `X-Idempotency-Key` header — on every POST, PUT and PATCH request, and on soft deletes. GET requests need none, and hard deletes cannot be idempotent.
> Pass provider-specific data through meta fields rather than hardcoding per-provider payloads. Payrails translates meta fields into each provider's own format.
> Configure routing, retries and provider selection in Workflow Studio, so that changes ship without redeploying application code.

# 2C2P

> Connect 2C2P to accept card and VietQR payments across South-East Asia through its hosted payment page redirect flow.

<picture noZoom>
  ![2C2P logo](https://assets.payrails.io/img/logos/2c2p/logo-full-bleed.svg)
</picture>

> [!NOTE]
>
> **Who should use this guide**
>
> This guide is intended for merchants who:
>
> * Use **Payrails** as a payment orchestrator
> * Use **2C2P** to accept payments across South-East Asia
> * Want to accept **card** payments (and **VietQR** in Vietnam) through 2C2P's **hosted payment page**
>
> 2C2P uses a **hosted payment page (redirect)** integration: your shopper is redirected to 2C2P to enter their card details or scan the QR and to complete the 3DS/OTP challenge, so raw card data never passes through your checkout.

## Create and configure a 2C2P integration in Payrails

Following are the initial steps to set up 2C2P:

1. Log in to the **Payrails** portal.
2. Go to **Settings → Integrations → Payments**.
3. Select **Add instance** to create a new 2C2P integration configuration.
4. Select the **workspaces** where this integration should be available.

> [!NOTE]
>
> **About workspaces**
>
> Workspaces determine where this integration is available. They let you isolate provider setups by region or business line, or share the same configuration across multiple workspaces.

## Step 1: Choose the integration type

Select **Payment** and continue to the next step.

![A screenshot showing the integration type selection form.](https://mintcdn.com/payrails-42074109/To59SEkYYPq19nmx/images/docs/orchestration/integrations/shared/integration-type-selection.png?fit=max\&auto=format\&n=To59SEkYYPq19nmx\&q=85\&s=2a1afe79c3ac25f3a8437846a1d36ffb)

## Step 2: Choose the provider

Select **2C2P** and continue to the next step.

![Payrails provider selection with 2C2P selected](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-01.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=bddf8ff3779872010e854e2e7fb66963)

## Step 3: Configure your 2C2P integration

> [!TIP]
>
> **What you'll need from 2C2P**
>
> Before you begin, make sure you have access to your **2C2P Merchant Portal** and the following:
>
> * **Merchant ID**
> * **Secret key** (used to sign requests to 2C2P's Payment API)
> * *For capture, void and refund only:* an **RSA key pair** — your **merchant private key** and **2C2P's public key** (log in to your 2C2P Merchant Portal to complete this step)

![2C2P integration prerequisites and account configuration section](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-02.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=723d3555ee7e14110d0b8089108c2533)

### Integration instance name

> An integration instance is a specific payment provider setup in Payrails. You can create multiple instances for each provider based on region, currency, or business needs. Choose a clear, consistent name, as it is used in routing.

**In Payrails**

* Enter an **Instance name** for your integration.

  ![Payrails 2C2P integration instance name field](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-03.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=6ec22004a87cff5d4e90910df3882467)

### Merchant ID

**In 2C2P**

* In your 2C2P Merchant Portal, locate and copy your **Merchant ID**.

**In Payrails**

* Paste the value into the **Merchant ID** field.

  ![Payrails Merchant ID field for 2C2P integration](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-04.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=86337aadb43e669f187c2342d8a3773d)

### Secret key

> [!TIP]
>
> Use your merchant secret key and store it securely.

**In 2C2P**

* In your 2C2P Merchant Portal, open your API / developer settings and copy the **Secret key**.

**In Payrails**

* Paste the value into the **Secret key** field.

  ![Payrails Secret key field for 2C2P integration](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-05.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=9ad2f3f7b8aeb35b6dc84beaac128ac1)

### Merchant private key & 2C2P public key (for capture, void & refund)

> [!NOTE]
>
> These credentials are **only** required to **capture, void or
> refund** payments through Payrails. Authorization and the hosted payment page
> work with the Merchant ID and Secret key alone, so you can add these keys later.

Capture, void and refund go through 2C2P's **PaymentAction** API, which is protected by an RSA key exchange instead of the secret key. You exchange two keys:

| Key | Who creates it | What it does |
| :- | :- | :- |
| Merchant key pair (private and public halves) | You | The **private** half is stored in Payrails and signs every request sent to 2C2P. The **public** half is uploaded to the 2C2P Merchant Portal as an **X.509 certificate** so 2C2P can verify the signature. |
| 2C2P public key (JWE) | 2C2P | Payrails uses it to encrypt each request so only 2C2P can read it. |

> [!WARNING]
>
> Your merchant private key is a signing authority for refunds. Generate it offline on a company device, never with an online key generator, and share it only through a password manager, never by email, chat or support ticket.

**Before you start**, make sure you have:

* Access to the **2C2P Merchant Portal**, with the **Merchant Public Keys** panel visible on the account settings page.
* Someone who can run two terminal commands. No development work is involved.
* Confirmation of the environment you're configuring, **sandbox** or **production**. Each environment needs its own keys.

#### 1. Create the key pair and certificate

2C2P requires the public half as an **X.509 certificate**, not a bare public key. The first command creates the private key and a matching self-signed certificate. The second converts the certificate into the single-line value the 2C2P portal expects.

```bash theme={null}
openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
  -keyout payrails-2c2p-private.pem -out payrails-2c2p-cert.pem \
  -subj "/CN=<your-company-name>"

openssl x509 -in payrails-2c2p-cert.pem -outform DER \
  | base64 | tr -d '\n' > payrails-2c2p-cert-base64.txt
```

This produces three files:

* `payrails-2c2p-private.pem`: the private key. It goes to Payrails only.
* `payrails-2c2p-cert.pem`: the certificate. Keep it to check the fingerprint and expiry date.
* `payrails-2c2p-cert-base64.txt`: the value to paste into the 2C2P portal.

> [!TIP]
>
> `-days 3650` sets a ten-year validity. Shorten it to match your key rotation policy, and note the expiry date.

Payrails accepts a PEM private key without a passphrase, in PKCS#8 (`PRIVATE KEY`) or PKCS#1 (`RSA PRIVATE KEY`) format. Convert a PKCS#12 bundle or a passphrase-protected key before you use it.

#### 2. Add your certificate to the 2C2P portal

**In 2C2P**

1. In the **Merchant Public Keys** panel, select **Add new**.
2. Enter a **KeyID** that identifies the key, for example `Payrails-2026`.
3. Paste the full contents of `payrails-2c2p-cert-base64.txt` into **Public Key**, as one line with no line breaks or spaces. Paste only the base64 value, without the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines.
4. Select **Key Is Default**, then select **Create**.

![2C2P Merchant Public Keys panel with one key marked as default](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-07.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=afc77bc6693b0cea8e9adf9556359148)

![2C2P Add Public Key dialog with KeyID, Public Key and Key Is Default fields](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-08.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=2c238c4a4e0587c501b8ef495f5ce541)

2C2P verifies Payrails' signatures against the key marked **Default**, so make sure your new key is the default one.

Then register the same certificate for the server-to-server API:

1. In the **Payment Result URL** panel, select **Edit**.
2. Paste the same single-line certificate into **Server-to-server API - Public key**. Leave the other fields as they are.
3. Select **Save Changes**.

![2C2P Edit Payment Result URL dialog with the certificate in Server-to-server API - Public key](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-09.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=436939d751db33229f3e8c3629a0b934)

![2C2P Payment Result URL panel showing the saved server-to-server public key](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-10.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=64b8c1f4985b484e0594dabad0a3441a)

> [!WARNING]
>
> **Merchant Public Keys** and **Server-to-server API - Public key** must hold the same certificate. They're set separately, so update both whenever the key changes. The 2C2P portal doesn't validate the value you paste: after saving, open the default key with **View** and confirm it matches the base64 certificate file from step 1.

#### 3. Download 2C2P's public key

**In 2C2P**

* In the **2C2P Public Keys** panel, select the **JWE** tab and download the **Default Key JWT** entry. Don't use the **PKCS7** key. The downloaded file is already PEM, despite its `.cer` extension.

![2C2P Public Keys panel with the JWE tab selected and the Default Key JWT download](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-11.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=0134234749146c920685b84ca18efa43)

#### 4. Add both keys in Payrails

**In Payrails**

* Paste the contents of `payrails-2c2p-private.pem` into the **Merchant private key** field.
* Paste the contents of the file downloaded from the **JWE** tab into the **2C2P public key** field.

![Payrails 2C2P credentials with Secret key, Merchant private key and 2C2P public key fields](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-12.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=f40a93d65edafdb502bd72f4386b6e31)

Saved values appear masked. Once the integration is configured and verified, delete the key files from the device you created them on.

#### 5. Verify the keys

Make a small test payment, then refund it. A successful refund confirms that both keys are correct.

To check the keys without a live request, confirm that the certificate matches the private key. The two hashes must be identical:

```bash theme={null}
openssl x509 -in payrails-2c2p-cert.pem -pubkey -noout | openssl sha256
openssl rsa -in payrails-2c2p-private.pem -pubout | openssl sha256
```

Then confirm that the 2C2P portal holds this certificate. Open the default key with **View** and check that it starts with the same 40 characters as the output of:

```bash theme={null}
openssl x509 -in payrails-2c2p-cert.pem -outform DER | base64 | tr -d '\n' | cut -c1-40
```

![2C2P Public Key Details dialog showing the default key as one base64 string](https://mintcdn.com/payrails-42074109/IufFELZ1KLjD-mNx/images/docs/2c2p-image-13.png?fit=max\&auto=format\&n=IufFELZ1KLjD-mNx\&q=85\&s=b1fe823925b41a75421b073887dc3a60)

To record which key is live, read the certificate's subject, fingerprint and expiry date:

```bash theme={null}
openssl x509 -in payrails-2c2p-cert.pem -noout -subject -fingerprint -sha1 -enddate
```

#### Common errors

| Symptom | Usual cause |
| :- | :- |
| Payments succeed, but every capture, void and refund fails | One of the two keys is wrong or missing. Payments use the secret key; capture, void and refund use these keys. |
| Payrails reports a provider configuration error, and 2C2P returns an HTTP 401 HTML page | 2C2P couldn't verify the signature. The default key in the 2C2P portal doesn't match the private key in Payrails, or it was pasted with a line break. |
| HTTP 400 from the PaymentAction API | The request token is malformed. This isn't a key mismatch. |
| Refunds worked, then stopped after a change in the 2C2P portal | **Key Is Default** moved to a different key. |

#### Rotate the keys

Payrails doesn't send a key ID, so 2C2P uses whichever key is the default. Switch the key in the 2C2P portal and in Payrails at the same time, and plan the rotation before the certificate expires.

1. Create a new key pair and certificate (step 1), and add it in the 2C2P portal under a new KeyID. Keep **Default** on the old key.
2. Agree on a quiet window with your Payrails contact.
3. In that window, make the new key the default, replace **Server-to-server API - Public key** with the new certificate, and update the **Merchant private key** in Payrails.
4. Verify with a refund before you delete the old key in the 2C2P portal.

### Payment methods

**In Payrails**

* Under **Payment methods**, select the methods you want to enable for this integration: **2C2P** (cards) and/or **VietQR** (Vietnam).

> [!NOTE]
>
> **Notifications**
>
> Payrails sends the notification (webhook) URL to 2C2P
> automatically with every payment, so there is **no separate webhook to
> configure** in the 2C2P portal.

![Payrails payment methods selection for 2C2P and VietQR](https://mintcdn.com/payrails-42074109/zqk7oWHZbW3YniA1/images/docs/2c2p-image-06.png?fit=max\&auto=format\&n=zqk7oWHZbW3YniA1\&q=85\&s=fbf0ad0368dde6848f30fcda176a54f0)

## Request fields for 2C2P

Send these fields in the [authorize request](https://docs.payrails.com/reference/authorizeaction.md) for card payments, in addition to the standard authorize fields. **Required** fields must be present for the payment to succeed. Payrails sends **Optional** fields to 2C2P when you include them. For more about meta fields, refer to [Meta fields](https://docs.payrails.com/docs/orchestration/meta-fields.md).

| Field | Status | Description |
| :- | :- | :- |
| `amount` | Required | Sent to 2C2P as the amount and currency. |
| `meta.order.description` | Required | Sent as the payment description. |
| `merchantReference` | Optional | Sent as the invoice number. |
| `meta.customer.name`, `lastName`, `email`, `phone`, `country.code` | Optional | Pre-fill the customer details on the 2C2P payment page. |
| `meta.order.billingAddress`, `meta.order.deliveryAddress` | Optional | Sent as the billing and shipping address when `street`, `city`, `postalCode` and `country.code` are all present. |
| `meta.clientContext.ipAddress`, `userAgent` | Optional | Sent as the client IP and user agent. |
| `meta.clientContext.acceptHeader`, `language`, `colorDepth`, `screenHeight`, `screenWidth`, `timeZoneOffset`, `javaEnabled`, `javaScriptEnabled` | Optional | Sent as browser details for 3D Secure when all are present. |
| `meta.risk.threeDSMode` | Optional | `Force` or `Skip`. Sets whether 2C2P requests 3D Secure. By default it does. |

***

## Enable the integration

**In Payrails**

* Select **Save account** to save the configuration and enable the integration.
* The **Enabled** status confirms that the 2C2P integration is active.

Your 2C2P integration is now ready to process payments.

## Next steps

1. Run a test payment in **test mode** using a 2C2P test card (for example, Visa `4111 1111 1111 1111`, any future expiry date, CVV `123`; complete the challenge on 2C2P's hosted page with OTP `123456`). Use a supported currency such as **THB** or **VND**.
2. Verify that:
   * The payment appears in your **2C2P Merchant Portal**.
   * The payment status updates correctly in **Payrails**.
3. Once verified in test mode, repeat the setup in **live mode**.

→ Continue to: [Test a payment](https://docs.payrails.com/docs/orchestration/payment-acceptance/test-payments.md)


## Related topics

- [VietQR via 2C2P](https://docs.payrails.com/docs/integrations/payment-methods/vietqr/2c2p.md)
- [VietQR](https://docs.payrails.com/docs/integrations/payment-methods/vietqr.md)
- [Payment providers](https://docs.payrails.com/docs/integrations/payment-providers.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.