Menu

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).

  1. Under Integrations → Website, add your website's origin (for example https://shop.example.com). Requests from any other website are refused.
  2. Optionally set a success and cancel URL (https only). aidafi adds aida_ref and aida_result to them.
  3. 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>
AttributeMeaning
data-merchantYour publishable key (pk_aida_…). It is safe to show on your website.
data-linkA payment link id: the price comes from aidafi, not from the page.
data-product + data-qtyA product from your aidafi catalogue, priced by aidafi.
data-amount + data-descriptionOnly 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-labelButton text (default "Pay with aidafi").

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

// 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

FieldRequiredNotes
typenosale (default) or refund
external_idyesYour receipt or transaction id. Unique per merchant: sending it again returns the existing sale (200, duplicate: true).
amount_minoryes, unless itemsTotal in minor units
itemsno[{ name, qty, unit_price_minor, vat_pct }]. If you also send amount_minor it must match the total.
currencyyesMust match the store's currency
methodnocard (default) or cash
store_idnoDefaults to the store chosen under Integrations, else your first store
customerno{ 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

StatusMeaning
201Recorded. The body has transaction with id, ref, status and amounts.
200Already recorded (same external_id)
400Invalid request (the code says why)
401Missing, invalid or revoked key, bad signature or old timestamp
404Original transaction not found. Keys only see their own merchant's data.
409Idempotency 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.

RequestWhat it does
GET /api/integrations/pos/terminal/readersYour readers (id, label, store_id, status)
POST /api/integrations/pos/terminal/readers/{id}/payStart 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}/cancelCancel 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-intentsFor 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