Log inTalk to us
Billing and settlement

Set up a custom payment domain

A payment domain is a hostname you control, such as pay.example.com, where drivers pay on Ivora's checkout, charging status and receipt pages with your branding. Once it is your project's checkout_domain and ready, it becomes your checkout_origin: the origin of your checkout links, QR codes and charger screens, and the page hosted checkout returns drivers to.

Payment domains use manual DNS in production: you add two records by hand, and no DNS provider sign-in is offered.

Before you start

  • Set your branding first, so the pages show your name and logo as soon as the domain is ready.
  • Choose a hostname that serves nothing else, such as pay.example.com. A hostname serves one purpose: OCPP, payment, QR or return. Registering it for a second purpose is refused (409) until you disable the first.
  • Ivora terminates TLS for the hostname with its own certificate, so nothing may sit in front of it: no CDN, proxy or web application firewall.
  • Changes need stations:write (a tenant administrator); reads need stations:read.

Ivora serves only the driver pages on it: /checkout/…, /charging/… and /receipt/… for your own chargers, the files those pages load and the API they call. Every other path, a page for a charger that is not yours and any write to a page answer 404.

1. Register the hostname

POST /v1/tenants/{tenant_id}/payment-domains
Content-Type: application/json

{"hostname": "pay.example.com"}

The request is idempotent by tenant and hostname. The response (201 Created) lists the two DNS records the domain needs and the checkout URL a QR code encodes on it:

{
  "id": "dom_7a1e...",
  "purpose": "payment",
  "hostname": "pay.example.com",
  "status": "pending_dns",
  "ready": false,
  "checkout_url_template": "https://pay.example.com/checkout/{station_name}-{evse_number}",
  "dns_records": [
    {"type": "CNAME", "name": "pay.example.com", "value": "ocpp-edge.ivoracharge.com", "ttl": 300, "proxied": false},
    {"type": "TXT", "name": "_ivora-payment.pay.example.com", "value": "ivora-payment-verification=<generated token>", "ttl": 300}
  ]
}

Show both records to whoever manages your DNS exactly as returned. Always take them from the response, never from this page: the address record's value is the one Ivora names for your environment.

2. Add the two records

TypeFull DNS nameValueSetting
CNAMEpay.example.comthe value returned, such as ocpp-edge.ivoracharge.comDNS only, TTL 300 or Auto
TXT_ivora-payment.pay.example.comivora-payment-verification=<generated token>TTL 300 or Auto

The CNAME target is Ivora's stable DNS-only name for its edge. Point the CNAME at it directly, not through another name of yours. An A record to the address it resolves to does not verify: if the edge address changes, Ivora updates its own name and your CNAME keeps working.

Warning

Keep the address record DNS only. In Cloudflare, the proxy status must be the grey cloud ("DNS only"), not the orange cloud ("Proxied"). A proxied record, or any CDN or proxy in front of the hostname, does not verify, stops Ivora from issuing the hostname's certificate and breaks checkout over HTTPS.

  • Editors that append your zone automatically use pay and _ivora-payment.pay as the names.
  • Keep both records for as long as you use the domain. Ivora rechecks them every 15 minutes; a domain whose records stop checking out stops serving the pages, and new links fall back to the default payment host.
  • Certificate issuance needs public port 80 to reach Ivora (only the certificate challenge is served over HTTP) and any CAA records on your zone to allow Let's Encrypt.

Cloudflare

  1. In the Cloudflare dashboard, open your zone (example.com) and go to DNS > Records.
  2. Add record: type CNAME, name pay, target the value returned, proxy status DNS only (grey cloud), TTL Auto. Save.
  3. Add record: type TXT, name _ivora-payment.pay, content the TXT value returned. Save.

If your domain is registered elsewhere (for example GoDaddy) but its nameservers are Cloudflare's, add the records in Cloudflare, not at the registrar.

3. Check DNS

POST /v1/tenants/{tenant_id}/payment-domains/{domain_id}/verify

checks reports each record: {"address": true, "ownership": true} once both are published, and the status moves to dns_verified. Check again at most every 15 seconds (429 otherwise); DNS changes can take longer than that to appear.

dns_verified does not mean the pages work yet. Ivora then issues the hostname's certificate, normally within 15 minutes, and the domain becomes active with ready: true. Read it with GET /v1/tenants/{tenant_id}/payment-domains/{domain_id}.

StatusMeaning
pending_dnsOne or both records do not check out yet
dns_verifiedBoth records check out; the certificate is being issued
activeCertificate installed. ready is true while DNS was rechecked within 24 hours and the certificate has not expired
disabledYou disabled it; its pages are no longer served

activation_error explains an activation that needs attention; contact Ivora support if it stays set.

GET /v1/tenants/{tenant_id}/branding returns checkout_origin: the origin of your project's checkout_domain (https://pay.example.com) while it is ready, or the default payment host. Your first payment domain to become active is your checkout_domain automatically; choose another one under Project default domains. Encode <checkout_origin>/checkout/<station>-<evse> in checkout links and charger screens, or print sticker_url_template on stickers; Ivora's charger screens and QR sticker redirects use it already. Hosted checkout with return_surface: driver returns drivers to the same origin automatically.

When the domain is disabled or stops being ready, checkout_origin and new checkout returns fall back to the default payment host at once. QR codes you printed with your own hostname stop working until it is ready again, so keep the records in place.

Project default domains

PATCH /v1/tenants/{tenant_id} picks which of your domains drivers see. Both fields take a hostname, or null for Ivora's default; leave a field out to keep it. GET /v1/tenants/{tenant_id} returns both, and the sticker_url_template they produce.

FieldAcceptsUsed for
checkout_domainOne of the project's active payment domainscheckout_origin: checkout links, QR codes, charger screens and hosted checkout returns
qr_domainOne of the project's active QR domainsWhat new stickers encode: https://<qr_domain>/{station_name}
curl -X PATCH "$IVORA_API/v1/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"checkout_domain": "payment.example.com", "qr_domain": "qr.example.com"}'

New stickers use qr_domain first, because a QR domain is a stable redirect name: its scans follow later payment-domain changes and each charger's migration. Without one, they use checkout_domain, then Ivora's default.

  • Only your own domains of that purpose are accepted (422 domain_not_in_project). A domain whose certificate is not active and ready yet is refused with 409 domain_certificate_inactive; retry once ready is true.
  • Each change is in the project's audit log as project.checkout_domain_changed or project.qr_domain_changed, with the previous and new hostname.
  • Once you choose (a domain or null), a payment domain that becomes active later never replaces your choice.
  • If a chosen domain stops being ready, links and stickers fall back at once (stickers to the checkout domain, then Ivora's default) and use it again when it is ready.
  • Ivora's default checkout URLs (/checkout/<station>-<evse> on the default payment host) keep working for every project, whatever you choose, so codes printed before you set a domain never break.

Removing a default domain

While a domain is the project's checkout_domain or qr_domain, DELETE on it answers 409 domain_is_project_default. Its details count what still uses it: qr_stickers (imported stickers whose scans it serves) and checkout_links (charger screen QR codes on that hostname). Send the request again with ?confirm=true to remove it; the field becomes null, and the change is audited with the reason domain_removed.

Open https://pay.example.com/checkout/<station>-<evse> for one of your chargers to check the page before you print codes.

Disable a domain

DELETE /v1/tenants/{tenant_id}/payment-domains/{domain_id} stops serving the pages at once; QR codes and checkout returns fall back to the default payment host. The project's checkout_domain needs ?confirm=true (Removing a default domain). Your DNS records stay in place. Registering the hostname again starts over with a new ownership token.

Troubleshooting

SymptomLikely cause
address stays falseThe address record is proxied (orange cloud), points somewhere else, or (production) is an A record instead of the CNAME
ownership stays falseThe TXT name carries your zone twice (_ivora-payment.pay.example.com.example.com), or the value is not exactly the one returned
Stays dns_verified for more than an hourPort 80 does not reach Ivora, a CDN or proxy is in front of the hostname, or a CAA record does not allow Let's Encrypt
409 on registrationThe hostname is registered for another purpose or verified by another tenant
A page answers 404The domain is not ready (check ready and activation_error), the charger is not yours, or (production) the path is not a driver page
Browser shows a certificate errorA proxy in front of the hostname presents its own certificate: set the record to DNS only