# Payment Links

The [Payment Links API](/api/reference#payment-link) allows you to generate hosted payment pages
that can be shared with customers via email, SMS, chat, or social media.
These links support both simple **one-time purchases** and complex **recurring subscriptions**.

## Manual Creation via Merchant Dashboard

While this guide focuses on the API for automated integrations,
please note that Payment Links can also be created manually
without any coding.

**When to use the Dashboard:**

- **Ad-hoc Requests:** Quickly creating a link for a specific customer support interaction.
- **No-Code Testing:** Verifying the payment flow before integrating the API.
- **Simple Operations:** Merchants who do not need programmatic generation.


You can access this functionality by logging into your **xMoney Merchant Dashboard**
and navigating to the **Payment Links** section.
The dashboard provides a visual interface to set the amount, currency, and description, instantly generating a URL you can copy and paste.

## API Base Configuration

#### Server Endpoint

Important
Payment Links use a specific server URL different
from the standard API transaction endpoints.

Base URL:

- Stage Env: https://office-api-stage.xmoney.com
- Live Env: https://office-api.xmoney.com


#### Authentication

All requests require authentication using a Bearer Token in the header.


```http
Authorization: <YOUR_API_PRIVATE_KEY>
```

## 1. Create a Payment Link

Generate a new payment link via API. You can define the link to be a
simple description-based charge or a detailed itemized cart.

- Endpoint: `POST /payment-link`
- Content-Type: `application/x-www-form-urlencoded`


**Required Parameters**

| Parameter | Type | Description |
|  --- | --- | --- |
| siteId | Integer | The ID of your site |
| amount | Float | The total amount to charge (e.g., 100.00). |
| currency | String | ISO Currency code (e.g., USD, EUR, GBP). |
| orderType | String | `purchase` (one-off) or `recurring` (subscription). |
| active | Integer | `1` (active) or `0` (inactive). |
| contentType | String | `Description` (simple) or `Items` (detailed cart). |


#### Scenario A: Simple One-Time Payment

Use `contentType: Description` for a generic charge.


```bash curl
curl -X POST "https://office-api-stage.xmoney.com/payment-link" \
-H "Authorization: YOUR_PRIVATE_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "siteId=123" \
-d "amount=50.00" \
-d "currency=USD" \
-d "orderType=purchase" \
-d "active=1" \
-d "contentType=Description" \
-d "content=Consulting Services" \
-d "customerEmail=client@example.com"
```


```ts index.ts
const axios = require('axios');
const qs = require('qs');

const data = qs.stringify({
  'siteId': '123',
  'amount': '50.00',
  'currency': 'USD',
  'orderType': 'purchase',
  'active': '1',
  'contentType': 'Description',
  'content': 'Consulting Services',
  'customerEmail': 'client@example.com'
});

const config = {
  method: 'post',
  url: 'https://office-api-stage.xmoney.com/payment-link',
  headers: { 
    'Authorization': 'YOUR_PRIVATE_KEY', 
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});
```


```php index.php
<?php

$curl = curl_init();

$payload = [
  'siteId' => '123',
  'amount' => '50.00',
  'currency' => 'USD',
  'orderType' => 'purchase',
  'active' => '1',
  'contentType' => 'Description',
  'content' => 'Consulting Services',
  'customerEmail' => 'client@example.com'
];

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://office-api-stage.xmoney.com/payment-link',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS => http_build_query($payload),
  CURLOPT_HTTPHEADER => array(
    'Authorization: YOUR_PRIVATE_KEY',
    'Content-Type: application/x-www-form-urlencoded'
  ),
));

$response = curl_exec($curl);
curl_close($curl);
echo $response;
```


```python Python
import requests

url = "https://office-api-stage.xmoney.com/payment-link"

payload = {
    'siteId': '123',
    'amount': '50.00',
    'currency': 'USD',
    'orderType': 'purchase',
    'active': '1',
    'contentType': 'Description',
    'content': 'Consulting Services',
    'customerEmail': 'client@example.com'
}

headers = {
  'Authorization': 'YOUR_PRIVATE_KEY',
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
```

#### Scenario B: Itemized Cart

Use `contentType: Items` to list specific products.
You must use array notation for the items.


```bash curl
curl -X POST "https://office-api-stage.xmoney.com/payment-link" \
-H "Authorization: YOUR_PRIVATE_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "siteId=123" \
-d "amount=30.00" \
-d "currency=USD" \
-d "orderType=purchase" \
-d "active=1" \
-d "contentType=Items" \
# Item 1
-d "content[0][item]=T-Shirt" \
-d "content[0][unitPrice]=20.00" \
-d "content[0][units]=1" \
-d "content[0][type]=physical" \
# Item 2
-d "content[1][item]=Stickers" \
-d "content[1][unitPrice]=5.00" \
-d "content[1][units]=2" \
-d "content[1][type]=physical"
```


```ts index.ts
const axios = require('axios');
const qs = require('qs');

// qs library automatically handles nested objects like content[0][item]
const data = qs.stringify({
  'siteId': '123',
  'amount': '30.00',
  'currency': 'USD',
  'orderType': 'purchase',
  'active': '1',
  'contentType': 'Items',
  'content': [
    {
      'item': 'T-Shirt',
      'unitPrice': '20.00',
      'units': '1',
      'type': 'physical'
    },
    {
      'item': 'Stickers',
      'unitPrice': '5.00',
      'units': '2',
      'type': 'physical'
    }
  ]
});

const config = {
  method: 'post',
  url: 'https://office-api-stage.xmoney.com/payment-link',
  headers: { 
    'Authorization': 'YOUR_PRIVATE_KEY', 
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data : data
};

axios(config)
.then((response) => console.log(JSON.stringify(response.data)))
.catch((error) => console.log(error));
```


```php index.php
<?php

$curl = curl_init();

// PHP http_build_query automatically handles the array indices content[0], content[1]
$payload = [
  'siteId' => '123',
  'amount' => '30.00',
  'currency' => 'USD',
  'orderType' => 'purchase',
  'active' => '1',
  'contentType' => 'Items',
  'content' => [
      [
          'item' => 'T-Shirt',
          'unitPrice' => '20.00',
          'units' => '1',
          'type' => 'physical'
      ],
      [
          'item' => 'Stickers',
          'unitPrice' => '5.00',
          'units' => '2',
          'type' => 'physical'
      ]
  ]
];

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://office-api-stage.xmoney.com/payment-link',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => http_build_query($payload),
  CURLOPT_HTTPHEADER => array(
    'Authorization: YOUR_PRIVATE_KEY',
    'Content-Type: application/x-www-form-urlencoded'
  ),
));

$response = curl_exec($curl);
curl_close($curl);
echo $response;
```


```python Python
import requests

url = "https://office-api-stage.xmoney.com/payment-link"

# Python requests does not automatically nest dicts for form-data efficiently.
# We define keys explicitly to ensure correct structure.
payload = {
    'siteId': '123',
    'amount': '30.00',
    'currency': 'USD',
    'orderType': 'purchase',
    'active': '1',
    'contentType': 'Items',
    # Item 0
    'content[0][item]': 'T-Shirt',
    'content[0][unitPrice]': '20.00',
    'content[0][units]': '1',
    'content[0][type]': 'physical',
    # Item 1
    'content[1][item]': 'Stickers',
    'content[1][unitPrice]': '5.00',
    'content[1][units]': '2',
    'content[1][type]': 'physical'
}

headers = {
  'Authorization': 'YOUR_PRIVATE_KEY',
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
```

#### Scenario C: Recurring Subscription

To create a subscription link, set `orderType` to `recurring`
and provide interval details.

| Parameter | Description |
|  --- | --- |
| intervalType | `day` or `month`. |
| intervalValue | Number of intervals (e.g., 1 for every month). |
| trialAmount (Optional) | Amount for the first charge. |
| firstBillDate (Optional) | ISO 8601 date for the first recurring charge. |


**Example:**


```bash curl
curl -X POST "https://office-api-stage.xmoney.com/payment-link" \
-H "Authorization: YOUR_PRIVATE_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "siteId=123" \
-d "amount=10.00" \
-d "currency=USD" \
-d "orderType=recurring" \
-d "contentType=Description" \
-d "content=Monthly Sub" \
-d "intervalType=month" \
-d "intervalValue=1"
```


```ts index.ts
const axios = require('axios');
const qs = require('qs');

const data = qs.stringify({
  'siteId': '123',
  'amount': '10.00',
  'currency': 'USD',
  'orderType': 'recurring',
  'contentType': 'Description',
  'content': 'Monthly Sub',
  'intervalType': 'month',
  'intervalValue': '1'
});

const config = {
  method: 'post',
  url: 'https://office-api-stage.xmoney.com/payment-link',
  headers: { 
    'Authorization': 'YOUR_PRIVATE_KEY', 
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data : data
};

axios(config)
.then((response) => console.log(JSON.stringify(response.data)))
.catch((error) => console.log(error));
```


```php index.php
<?php

$curl = curl_init();

$payload = [
  'siteId' => '123',
  'amount' => '10.00',
  'currency' => 'USD',
  'orderType' => 'recurring',
  'contentType' => 'Description',
  'content' => 'Monthly Sub',
  'intervalType' => 'month',
  'intervalValue' => '1'
];

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://office-api-stage.xmoney.com/payment-link',
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POSTFIELDS => http_build_query($payload),
  CURLOPT_HTTPHEADER => array(
    'Authorization: YOUR_PRIVATE_KEY',
    'Content-Type: application/x-www-form-urlencoded'
  ),
));

$response = curl_exec($curl);
curl_close($curl);
echo $response;
```


```python Python
import requests

url = "https://office-api-stage.xmoney.com/payment-link"

payload = {
    'siteId': '123',
    'amount': '10.00',
    'currency': 'USD',
    'orderType': 'recurring',
    'contentType': 'Description',
    'content': 'Monthly Sub',
    'intervalType': 'month',
    'intervalValue': '1'
}

headers = {
  'Authorization': 'YOUR_PRIVATE_KEY',
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
```

#### Response


```JSON
{
  "code": 0,
  "message": "string",
  "data": {
    "hash": string,
  }
}
```

## 2. Retrieve & Manage Links

#### Get Payment Link Details

Retrieve a single payment link using its unique hash.

- Endpoint: `GET /payment-link/{hash}`


#### List Payment Links

Search for payment links associated with a site.

- Endpoint: `GET /payment-link`
- Required Parameter: `siteId`


**Filtering Options (Query Parameters):**

| Parameter | Description |
|  --- | --- |
| amount | Filter by specific amount. |
| active | `1` or `0`. |
| startDate / endDate | Filter by creation date (format: yyyy-mm-dd hh:mm:ss). |
| customerEmail | Filter by customer email. |
| limitAccess | Filter by number of allowed accesses. |


#### Update Payment Link

Modify an existing payment link.
Note that changing amounts or items will affect
future customers using this link.

- Endpoint: `PUT /payment-link/{hash}`
- Body: Same parameters as the **Create** endpoint.


#### Delete Payment Link

Permanently remove a payment link.

- Endpoint: `DELETE /payment-link/{hash}`


## 3. Advanced Features

#### Controlling Link Access

You can limit how a link is used or when it expires via the
following parameters during Creation or Update:

- `limitAccess`: Integer. The maximum number of times the link can be successfully paid.
- `expireDate`: String (Date format: yyyy-mm-dd). The date after which the link becomes invalid.
- `active`: Set to 0 to temporarily disable the link without deleting it.


#### Customer Data Pre-fill

If you know the customer details beforehand,
you can pre-fill them to simplify their checkout experience:

- `customerEmail`
- `customerIdentifier`
- `customerTags[]`


#### Required Customer Fields

You can force specific fields to be mandatory during checkout by setting these parameters to 1:

- `customerRequiredFields[firstName]`
- `customerRequiredFields[lastName]`
- `customerRequiredFields[address]`
- `customerRequiredFields[phone]`
- (and others as defined in the schema)


#### Level 3 Data (Airline & Tourism)

For specialized industries, you can pass Level 3 data using the `level3Type` parameter
(set to `airline` or `tourism`) and the associated fields (e.g.,
`level3AirlineTicketNumber`, `level3TourismDepartureDate`, etc.).
Refer to the full API schema for the complete list of Level 3 fields.

## Conclusion & Further Reference

This guide covers the core functionalities for creating and managing
Payment Links, including one-time purchases, subscriptions,
and itemized carts.

For a complete list of all available parameters,
error codes, and specific data constraints
(including detailed Level 3 data fields for Airline and Tourism sectors),
please refer to the official API documentation:

- Full API Reference: [https://docs.xmoney.com/api/reference/payment-links](https://docs.xmoney.com/api/reference/payment-links)