<!-- canonical: https://docs.costplus.io/docs/guides/payment-links -->

> Create reusable payment links
Payment links are reusable URLs that allow customers to pay for an order. Unlike standard orders that expire after a single failed attempt, payment links support multiple retry attempts, making them ideal for invoices, email-based payments, and scenarios where the customer may not pay immediately.

## Key Features

- **Reusable:** Customers can retry payment up to 25 times if previous attempts fail.
- **Long-lived:** Default expiration is 30 days (configurable via `expiration_period`).
- **Shareable:** Send the link via email, SMS, chat, or embed it on your website.

## Creating a Payment Link

Send a **POST** request to `/v1/paymentlinks/`:

```bash title="POST /v1/paymentlinks/"
curl -X POST https://api.costplus.online/v1/paymentlinks/ \
  -u YOUR_API_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_order_id": "invoice-1234",
    "amount": 995,
    "currency": "EUR",
    "description": "Invoice #1234"
  }'
```

The response includes the `payment_url` to share with the customer and a unique `id` for tracking:

```json title="Response (201 Created)"
{
  "id": "e6eecc6a-47c5-4948-bcc0-d8b73f5c55a1",
  "merchant_order_id": "invoice-1234",
  "amount": 995,
  "currency": "EUR",
  "description": "Invoice #1234",
  "expiration_period": "P30D",
  "payment_url": "https://api.costplus.online/paymentlinks/e6eecc6a.../",
  "status": "new",
  "reason": "Payment Link was created, not yet visited",
  "orders": {},
  "created": "2026-01-15T12:00:00.000000Z"
}
```

Save the `id` — you'll use it to check the payment link status later.

### Required Fields

| Field | Description |
| --- | --- |
| `merchant_order_id` | Your own reference ID for the payment link |
| `amount` | Amount in cents (e.g., 9.95 EUR = `995`) |
| `currency` | ISO 4217 currency code (e.g., `EUR`, `GBP`) |

### Optional Fields

| Field | Description |
| --- | --- |
| `description` | Description shown to the customer |
| `expiration_period` | ISO 8601 duration. Default is `P30D` (30 days) |
| `return_url` | URL to redirect the customer after successful payment |
| `failure_url` | URL to redirect the customer on cancellation, expiry, or error |
| `webhook_url` | URL to receive status change notifications |
| `customer` | Customer details object (name, email, etc.) |

> [!TIP]
> If you provide both `return_url` and `failure_url`, customers are redirected to `failure_url` when the order status is `cancelled`, `expired`, or `error`. Otherwise, all redirects go to `return_url`.

## Retrieving a Payment Link

Send a **GET** request to `/v1/paymentlinks/{id}/` using the payment link `id` from the create response:

```bash title="GET /v1/paymentlinks/{id}/"
curl -u YOUR_API_KEY: \
  https://api.costplus.online/v1/paymentlinks/e6eecc6a-47c5-4948-bcc0-d8b73f5c55a1/
```

The response includes the current status and references to all orders created from the link, grouped by their status:

```json title="Response (completed example)"
{
  "id": "e6eecc6a-47c5-4948-bcc0-d8b73f5c55a1",
  "merchant_order_id": "invoice-1234",
  "amount": 995,
  "currency": "EUR",
  "description": "Invoice #1234",
  "expiration_period": "P30D",
  "payment_url": "https://api.costplus.online/paymentlinks/e6eecc6a.../",
  "status": "completed",
  "reason": "Completed",
  "completed": "2026-01-15T12:05:30.123456+00:00",
  "completed_order_id": "3bb663cc-2a20-400d-8bf6-18d9695d0c66",
  "orders": {
    "error": ["0d79014c-0aaa-4fd6-87c5-c8cfa5f5ac69"],
    "completed": ["3bb663cc-2a20-400d-8bf6-18d9695d0c66"]
  }
}
```

In this example, the customer made one failed attempt (order `0d79014c...` in `error` status) before succeeding (order `3bb663cc...` in `completed` status). You can retrieve full details of any order via `GET /v1/orders/{order_id}/`.

## Payment Link Statuses

| Status | Description |
| --- | --- |
| `new` | The link has been created but no payment attempt has been made. |
| `processing` | A payment attempt is currently in progress. |
| `all_unsuccessful` | All payment attempts so far have failed. The customer can still retry (up to 25 attempts). |
| `completed` | Payment was successful. The link is no longer active. |
| `expired` | The link has expired before a successful payment was made. |

> [!NOTE]
> The `all_unsuccessful` status is not a final status. The customer can still attempt to pay again until either the payment succeeds, the maximum number of attempts (25) is reached, or the link expires.

> [!WARNING]
> Once a payment link reaches the `completed` or `expired` status, it cannot be used again. Create a new payment link if the customer needs to pay again.

## Example Workflow

1. Create a payment link via `POST /v1/paymentlinks/`.
2. Share the returned `payment_url` with your customer (e.g., via email, SMS, or invoice).
3. The customer opens the link and completes payment.
4. Cost+ sends a webhook to your `webhook_url` when the status changes.
5. Verify the payment link status via `GET /v1/paymentlinks/{id}/`.
6. Fulfill the order once the status is `completed`.

## Related Endpoints

- [Create Payment Link](/api-reference/payment-links/createPaymentLink) — create a reusable payment link
- [Get Payment Link](/api-reference/payment-links/getPaymentLink) — retrieve the current status of a payment link
