Guides
Payment links
A payment link is a reusable address on pay.orrerie.com. Anyone who opens it can pay you, and each payment that starts creates one order. It needs no website and no code.
What a payment link is
A payment link has an id that starts with plink_ and an address of the form https://. The link itself is never paid. Each payer who presses Continue to payment gets an order and a checkout session of their own, so one link can be paid any number of times.
The amount is one of three kinds:
| Kind | In the API | The payer |
|---|---|---|
| Fixed | amount_type: "fixed" with amount and currency | Pays the amount you set. |
| A product's price | amount_type: "fixed" with line_items | Pays for the price you picked. You may let them change the quantity. |
| The customer chooses | amount_type: "open" with min_amount and max_amount | Types an amount between your minimum and maximum. Use it for donations, tips and "pay what you owe". |
Create a link in the dashboard
- Open Payment linksIn the left rail choose Payment links, then Create payment link.
- Name it and set the amountThe name is the title the payer sees. Choose Fixed, Product or Customer chooses.
- Choose what to ask the payer forYou can ask for a name, a note and a delivery address, and mark each as required or optional. The checkout asks for no email while Orrerie emails no customer: the payer gets the receipt as a link.
- Set its limits, if anyLimit uses closes the link after that many paid orders. Close on closes it at a date and time. Each order is valid for sets how long one payer has from opening to paying: 15 minutes, 1 hour or 24 hours.
- Choose what happens after paymentShow a thank-you page with your message, or send the payer to an address on your site.
- Press Create linkThe result screen has the link with Copy link, Download QR, Embed a button and Open as a customer.




Create a link with the API
curl
curl https://api.orrerie.com/v1/payment_links \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a" \
-H "Content-Type: application/json" \
-d '{
"name": "Ethiopia Yirgacheffe, 1 kg",
"currency": "USD",
"line_items": [
{
"price": "price_mbk5s5r45kwheaq89dbmk2gz",
"quantity": 1,
"adjustable_quantity": {
"enabled": true,
"minimum": 1,
"maximum": 10
}
}
],
"success_url": "https://acme.example/thanks"
}'JavaScript
const response = await fetch("https://api.orrerie.com/v1/payment_links", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ORRERIE_SECRET_KEY}`,
"Idempotency-Key": "8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a",
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Ethiopia Yirgacheffe, 1 kg",
"currency": "USD",
"line_items": [
{
"price": "price_mbk5s5r45kwheaq89dbmk2gz",
"quantity": 1,
"adjustable_quantity": {
"enabled": true,
"minimum": 1,
"maximum": 10
}
}
],
"success_url": "https://acme.example/thanks"
}),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result);Python
import os
import requests
response = requests.post(
"https://api.orrerie.com/v1/payment_links",
headers={"Authorization": f"Bearer {os.environ['ORRERIE_SECRET_KEY']}", "Idempotency-Key": "8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a"},
json={
"name": "Ethiopia Yirgacheffe, 1 kg",
"currency": "USD",
"line_items": [
{
"price": "price_mbk5s5r45kwheaq89dbmk2gz",
"quantity": 1,
"adjustable_quantity": {
"enabled": True,
"minimum": 1,
"maximum": 10,
},
},
],
"success_url": "https://acme.example/thanks",
},
timeout=30,
)
result = response.json()
if not response.ok:
raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result)PHP
<?php
$request = curl_init('https://api.orrerie.com/v1/payment_links');
curl_setopt_array($request, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ORRERIE_SECRET_KEY'),
'Idempotency-Key: 8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Ethiopia Yirgacheffe, 1 kg',
'currency' => 'USD',
'line_items' => [
[
'price' => 'price_mbk5s5r45kwheaq89dbmk2gz',
'quantity' => 1,
'adjustable_quantity' => [
'enabled' => true,
'minimum' => 1,
'maximum' => 10,
],
],
],
'success_url' => 'https://acme.example/thanks',
]),
]);
$body = curl_exec($request);
if ($body === false) {
throw new RuntimeException(curl_error($request));
}
$result = json_decode($body, true);
if (curl_getinfo($request, CURLINFO_HTTP_CODE) >= 400) {
throw new RuntimeException($result['error']['code'] . ': ' . $result['error']['message']);
}
print_r($result);The answer is a payment_link. Its url is the address to share. Create a payment link lists every parameter: max_uses, expires_at, order_expiry_seconds, collect, success_url and the rest.
To pause a link, set active to false. Changes apply to orders created afterwards, and currency, amount_type and line_items cannot change: make a new link instead.
curl
curl -X PATCH https://api.orrerie.com/v1/payment_links/plink_gg6as78pk82ndt25m36vjeey \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"active": false
}'JavaScript
const response = await fetch("https://api.orrerie.com/v1/payment_links/plink_gg6as78pk82ndt25m36vjeey", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.ORRERIE_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"active": false
}),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result);Python
import os
import requests
response = requests.patch(
"https://api.orrerie.com/v1/payment_links/plink_gg6as78pk82ndt25m36vjeey",
headers={"Authorization": f"Bearer {os.environ['ORRERIE_SECRET_KEY']}"},
json={
"active": False,
},
timeout=30,
)
result = response.json()
if not response.ok:
raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result)PHP
<?php
$request = curl_init('https://api.orrerie.com/v1/payment_links/plink_gg6as78pk82ndt25m36vjeey');
curl_setopt_array($request, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ORRERIE_SECRET_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'active' => false,
]),
]);
$body = curl_exec($request);
if ($body === false) {
throw new RuntimeException(curl_error($request));
}
$result = json_decode($body, true);
if (curl_getinfo($request, CURLINFO_HTTP_CODE) >= 400) {
throw new RuntimeException($result['error']['code'] . ': ' . $result['error']['message']);
}
print_r($result);Share it
- As a link, in a message, an email or a social profile.
- As a QR code, on a counter, a poster or a printed bill. Download it from the dashboard as PNG or SVG.
- As a button on your site. A plain link needs no script. The pay button has both forms.
- In a site builder. Site builders has the recipe for a button block.
The address may carry quantity, reference, locale, and amount on a link where the customer chooses. The hosted checkout says what each does. There is no email parameter: an address in a URL ends up in logs.
What the payer sees
The link's page shows your logo and name, the link's name and description, the amount or an amount box, and the fields you asked for. Continue to payment creates the order and opens the hosted checkout, where the payer chooses how to pay.

When a link cannot take a payment, its page says why:
| State of the link | Words on the page |
|---|---|
| Paused | "This link is paused." and "Try again later or contact your business." |
| Ended, at its use limit, or past its close date | "This link is closed." |
| Your business is at a limit of its tier | "This business can't take a new order right now. Try again later." |
| The amount typed is outside your minimum and maximum | "Enter an amount between your minimum and your maximum." |
| More than 10 orders started from one connection in 10 minutes | "Too many attempts from this connection. Try again in a few minutes." |
| Too many unpaid orders are open on this link | "This link is busy. Try again in a few minutes." |
An order started from a link that never got as far as payment details expires after 15 minutes, whatever the link's own setting, and at most 32 such orders can be open on one link. That keeps a stranger from filling your open-order limit by opening the link over and over.
Know that it was paid
Each paid order sends order.paid to your webhook endpoint, once. The order's payment_link field names the link it came from, and the client_reference_id of its checkout session carries the reference from the address, if there was one. The dashboard shows the same under Orders, and each link's row counts its uses and its takings.

A link holds no money
A payment made through a link goes to your own wallet, like every other order. Ending or pausing a link stops new orders. It does not touch orders already started, which can still be paid.
Three events follow the link itself: payment_link.created, payment_link.updated and payment_link.deactivated.
Try it in test mode
Create the link with an sk_test_ key, or in the dashboard with the test switch on. Open it, press Continue to payment, and on the checkout press Pay in full in its "Test payment" panel. The order becomes paid in about 6 seconds and order.paid arrives with livemode: false.

To simulate from your server instead:
curl
curl https://api.orrerie.com/v1/test/orders/ord_0zymngngvf12b8b2cra7tb8e/simulate_payment \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"scenario": "exact",
"asset": "ethereum:usdc"
}'JavaScript
const response = await fetch("https://api.orrerie.com/v1/test/orders/ord_0zymngngvf12b8b2cra7tb8e/simulate_payment", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ORRERIE_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"scenario": "exact",
"asset": "ethereum:usdc"
}),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result);Python
import os
import requests
response = requests.post(
"https://api.orrerie.com/v1/test/orders/ord_0zymngngvf12b8b2cra7tb8e/simulate_payment",
headers={"Authorization": f"Bearer {os.environ['ORRERIE_SECRET_KEY']}"},
json={
"scenario": "exact",
"asset": "ethereum:usdc",
},
timeout=30,
)
result = response.json()
if not response.ok:
raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result)PHP
<?php
$request = curl_init('https://api.orrerie.com/v1/test/orders/ord_0zymngngvf12b8b2cra7tb8e/simulate_payment');
curl_setopt_array($request, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ORRERIE_SECRET_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'scenario' => 'exact',
'asset' => 'ethereum:usdc',
]),
]);
$body = curl_exec($request);
if ($body === false) {
throw new RuntimeException(curl_error($request));
}
$result = json_decode($body, true);
if (curl_getinfo($request, CURLINFO_HTTP_CODE) >= 400) {
throw new RuntimeException($result['error']['code'] . ': ' . $result['error']['message']);
}
print_r($result);The same in brief, without code: Crypto payment links.