Ask for an invitation
Menu

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.

A payment link has an id that starts with plink_ and an address of the form https://pay.orrerie.com/l/<plink_id>. 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:

Table: What a payment link is
KindIn the APIThe payer
Fixedamount_type: "fixed" with amount and currencyPays the amount you set.
A product's priceamount_type: "fixed" with line_itemsPays for the price you picked. You may let them change the quantity.
The customer choosesamount_type: "open" with min_amount and max_amountTypes an amount between your minimum and maximum. Use it for donations, tips and "pay what you owe".

Create a link in the dashboard

  1. Open Payment linksIn the left rail choose Payment links, then Create payment link.
  2. Name it and set the amountThe name is the title the payer sees. Choose Fixed, Product or Customer chooses.
  3. 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.
  4. 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.
  5. Choose what happens after paymentShow a thank-you page with your message, or send the payer to an address on your site.
  6. Press Create linkThe result screen has the link with Copy link, Download QR, Embed a button and Open as a customer.
The Create payment link form: the name Coffee beans, 1 kg, a fixed amount of US$25.00, a name and a delivery address asked of the payer, and beside it the preview of what the payer sees.The Create payment link form: the name Coffee beans, 1 kg, a fixed amount of US$25.00, a name and a delivery address asked of the payer, and beside it the preview of what the payer sees.
The form, with the preview of what the payer sees beside it. Open the full picture
The result screen of a new payment link: its address on pay.orrerie.com with Copy link, the QR code with Download QR, Embed a button and Open as a customer.The result screen of a new payment link: its address on pay.orrerie.com with Copy link, the QR code with Download QR, Embed a button and Open as a customer.
The result screen of a new link. Open the full picture

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.

The payment link's page on a phone: the business, Coffee beans, 1 kg, US$25.00, the name and delivery address asked for, and Continue to payment.
A link's page on a payer's phone. Open the full picture

When a link cannot take a payment, its page says why:

Table: What the payer sees
State of the linkWords 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.

The Orders list in test mode with the link's order marked Paid.
The link's order under Orders, paid in test mode. Open the full picture

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.

The hosted checkout in test mode on a phone: US$25.00 and the Test payment panel with Pay in full and the other simulated outcomes.
The checkout of a test-mode link, with its Test payment panel. Open the full picture

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.