Help · Integrations
Integrations
Connect your website, your own till system and your back office to aidafi. Set everything up in the aidafi dashboard under Integrations (Owner or Admin).
Website checkout
Add a Pay with aidafi button to any page. When a customer clicks it, aidafi creates a secure card checkout on your own payment account and sends the customer to it. The payment appears in Payments with the channel Website, and the standard aidafi fee applies (see Pricing).
- Under Integrations → Website, add your website's origin (for example
https://shop.example.com). Requests from any other website are refused. - Optionally set a success and cancel URL (https only). aidafi adds
aida_refandaida_resultto them. - Create a payment link (fixed amount) or use a product id, then paste the snippet:
<script src="https://www.aidafi.eu/aida.js"
data-merchant="pk_aida_…"
data-link="YOUR_LINK_ID"></script>
| Attribute | Meaning |
|---|---|
data-merchant | Your publishable key (pk_aida_…). It is safe to show on your website. |
data-link | A payment link id: the price comes from aidafi, not from the page. |
data-product + data-qty | A product from your aidafi catalogue, priced by aidafi. |
data-amount + data-description | Only if you turn on Allow custom amounts. Amount in minor units (øre/cents). Anyone can change a page's HTML, so use links or products for fixed prices. |
data-label | Button text (default "Pay with aidafi"). |
Payment links
A payment link is a hosted page at https://www.aidafi.eu/l/<id> with a fixed amount. Share it by email, chat or QR code, or use its id in the website snippet. You can switch a link off at any time; old links then show "not active".
POS API: send sales from your own till
If you take payments on another till or terminal, send each sale to aidafi so your ledger, customers, rewards and analytics stay complete. These sales are recorded as paid by your system: aidafi does not move money for them and charges no aidafi fee on them.
Endpoint: POST https://www.aidafi.eu/api/integrations/pos/transactions
Authentication and signing
- Create a secret key under Integrations → POS API. It looks like
sk_aida_…and is shown once. aidafi stores only a hash of it. Revoke a key at any time; it stops working immediately. - Send
Authorization: Bearer sk_aida_…. - Send
aidafi-Timestamp: the current Unix time in seconds (must be within 5 minutes). - Send
aidafi-Signature: v1=<hex>, where hex is HMAC-SHA256 oftimestamp + "." + raw request body, keyed with your secret key. - Send
Idempotency-Key(8 to 100 characters, required). Retrying with the same key and body returns the first response (headerIdempotent-Replayed: true); the same key with a different body returns 409.
// Node.js 18+
import crypto from "node:crypto";
const key = process.env.AIDA_SECRET_KEY; // sk_aida_…
const body = JSON.stringify({
type: "sale", external_id: "till-7-000123",
amount_minor: 12500, currency: "DKK", method: "card",
customer: { email: "customer@example.com" }
});
const ts = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHmac("sha256", key).update(ts + "." + body).digest("hex");
const res = await fetch("https://www.aidafi.eu/api/integrations/pos/transactions", {
method: "POST",
headers: { "content-type": "application/json", authorization: "Bearer " + key,
"aida-timestamp": ts, "aida-signature": "v1=" + sig, "idempotency-key": "till-7-000123" },
body
});
Sale
| Field | Required | Notes |
|---|---|---|
type | no | sale (default) or refund |
external_id | yes | Your receipt or transaction id. Unique per merchant: sending it again returns the existing sale (200, duplicate: true). |
amount_minor | yes, unless items | Total in minor units |
items | no | [{ name, qty, unit_price_minor, vat_pct }]. If you also send amount_minor it must match the total. |
currency | yes | Must match the store's currency |
method | no | card (default) or cash |
store_id | no | Defaults to the store chosen under Integrations, else your first store |
customer | no | { email, phone, name }: links the sale to a customer profile and earns rewards |
Refund
Send type: "refund" with original_external_id (or original_transaction_id) and optionally amount_minor for a partial refund (default: the full amount left). Only sales sent through the POS API can be refunded this way. Refund aidafi card payments from Payments.
Responses
| Status | Meaning |
|---|---|
| 201 | Recorded. The body has transaction with id, ref, status and amounts. |
| 200 | Already recorded (same external_id) |
| 400 | Invalid request (the code says why) |
| 401 | Missing, invalid or revoked key, bad signature or old timestamp |
| 404 | Original transaction not found. Keys only see their own merchant's data. |
| 409 | Idempotency conflict, or a refund that is not allowed |
Webhooks
Add an https URL under Integrations → Webhooks. aidafi sends payment.paid, payment.refunded and payment.disputed as JSON POSTs. Use Send test event to try it (test.ping).
{ "id": "evt_…", "type": "payment.paid", "created": 1790790000, "livemode": true,
"merchant_id": "…", "data": { "object": { "id": "…", "ref": "…", "status": "paid",
"channel": "online", "amount_minor": 12500, "currency": "DKK", "aida_fee_minor": 150, … } } }
Verify every request. The header aidafi-Signature: t=<unix>,v1=<hex> is HMAC-SHA256 of t + "." + raw body, keyed with your signing secret (whsec_aida_…, shown once; rotate it any time). Reject old timestamps and use id to ignore repeats.
const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const ok = crypto.timingSafeEqual(Buffer.from(v1, "hex"),
crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest());
Retries: any answer other than 2xx within 8 seconds is retried after about 1 minute, 10 minutes, 1 hour, 6 hours and 24 hours (6 attempts). Recent deliveries and their status are listed under Integrations.
Pay with aidafi: card or pay by invoice
The same script tag shows one Pay with aidafi button. If you turn on pay by invoice under Integrations, the buyer can choose Pay by card or wallet (Stripe Checkout on your account, as above) or Pay by invoice with aidafi (for businesses). Add data-order with your own order number so a repeated click or reload never creates a second invoice.
<script src="https://www.aidafi.eu/aida.js"
data-merchant="pk_aida_…" data-link="YOUR_LINK_ID"
data-order="ORDER-10042"></script>
For pay by invoice the buyer enters the company name, CVR or VAT number, an EAN number (optional) and an email. aidafi creates a business customer in your account, a sale on account and then a B2B invoice with your default terms (net 8, 14 or 30) and payment method (card pay link, bank transfer or both). If approval is on, orders from buyers you haven't approved wait under Integrations until you approve or decline them. You can mark a buyer as approved so their next orders are invoiced straight away. The script fires a aida:order DOM event with the result.
You can also call it from your server or checkout code: POST /api/public/b2b-order (same allowed-origin rules as checkout) with { "merchant": "pk_aida_…", "link": "…", "order_ref": "ORDER-10042", "buyer": { "company_name": "…", "cvr_vat": "12345678", "ean": "", "email": "…" } }. The same order_ref always returns the same order ("duplicate": true).
Webhooks: order.invoiced when the invoice is issued (data.object has order, invoice with number, due date and pay link, and buyer), and invoice.paid when it's paid by card or marked paid after a bank transfer. Same signature and retries as above.
Credit risk: pay by invoice gives your buyer credit. aidafi doesn't run credit checks, pay you in advance, collect debts or offer financing, and isn't a lender or buy now, pay later. Card payments on the invoice carry the online all-in rate; bank transfers are 0%.
Card readers (Stripe Terminal)
Pair a Stripe smart reader (BBPOS WisePOS E or Stripe Reader S700/S710) under Settings → Card readers with the pairing code from the reader. aidafi registers it to your own payment account and links it to a Stripe Terminal Location for that aidafi location. In the aidafi POS, choose Card reader. Readers are sold by Stripe.
From your own POS or terminal. The same reader API is available with your POS API key. Every request has the same headers as POS ingest: Authorization: Bearer sk_aida_…, aidafi-Timestamp and aidafi-Signature: v1=<hex>. The signature is an HMAC-SHA256 of t + "." + raw body; for a GET it covers t + "." + path and query. Payment requests need an Idempotency-Key, and the same key always returns the same sale.
| Request | What it does |
|---|---|
GET /api/integrations/pos/terminal/readers | Your readers (id, label, store_id, status) |
POST /api/integrations/pos/terminal/readers/{id}/pay | Start a payment on the reader: { "amount_minor": 12500 } or { "items": [...] }. Retry after a decline with { "transaction_id": "…" } (same PaymentIntent, no double charge). |
GET /api/integrations/pos/terminal/readers/{id}?transaction_id=… | Poll: state is waiting, confirming, paid, declined, customer_canceled or cancelled |
POST /api/integrations/pos/terminal/readers/{id}/cancel | Cancel the payment on the reader and at Stripe |
POST /api/integrations/pos/terminal/connection-token | { "store_id": "…" } returns a Stripe Terminal connection token for that location, for apps using a Stripe Terminal SDK |
POST /api/integrations/pos/terminal/payment-intents | For SDK apps, including Tap to Pay on iPhone or Android: creates the sale and a card_present PaymentIntent and returns its client_secret. The app collects and confirms the payment with the SDK. |
Tap to Pay needs a native app built with Stripe's Terminal SDK. It isn't available in the browser-based aidafi POS.
A reader sale is a normal sale. It appears in Payments, the Z-report and the accounting export with your in-person rate. It counts as paid only when Stripe's signed payment_intent.succeeded arrives, and you get payment.paid as usual. A declined or cancelled payment is never recorded as paid.
Rate limits: the public checkout and pay-by-invoice endpoints accept a limited number of requests per minute per IP address and per merchant. Above that they answer 429 with code: "RATE_LIMITED" and a Retry-After header.
How payments are confirmed
Card payments through the website, payment links, QR codes and the POS count as paid only when aidafi receives the payment provider's signed confirmation, never because a page or browser says so. Sales from the POS API are recorded as reported by your own signed request.
Questions: Contact us · Help