# Konverterings-API

> Endpointet, der tager imod salg – til server-side og egne integrationer, med parametre, svar og grænser.

Alle salg fra [track.js](https://www.affilyflow.com/docs/reference/track-js) ender i det samme endpoint. Bygger I selv integrationen – fx fra jeres egen backend, når betalingen er gennemført – kan I kalde det direkte.

```http title="Endpoint"
POST https://app.affilyflow.com/api/track/conversion
GET  https://app.affilyflow.com/api/track/conversion
```

- Både `GET` og `POST` virker. `OPTIONS` svarer på CORS-forespørgsler.
- CORS er åben (`Access-Control-Allow-Origin: *`), så endpointet kan kaldes fra enhver browser.
- Der kræves ingen nøgle. Det, der binder salget til en affiliate, er klik-id'et.

## Klik-id'et først

Uden klik-id'et kan salget ikke tilskrives nogen. Det kommer fra affiliate-linket som `?afid=` på jeres side, og track.js gemmer det i cookien `af_click` på jeres domæne. Jeres server kan derfor læse cookien fra kundens anmodning, når ordren oprettes, og sende den med.

Har I ikke track.js på siden, så gem selv `afid` fra adressen. Tag kun værdier, der ligner vores: `c_` efterfulgt af 4–64 tegn (bogstaver, tal, `_` og `-`).

> [!TIP]
> Gem klik-id'et på ordren, når den oprettes. Så kan salget meldes fra serveren, også når betalingen bliver bekræftet senere, og kunden aldrig ser kvitteringen.

## Parametre

| Parameter | Alias | Påkrævet | Betydning |
|---|---|---|---|
| `afid` | `click_id` | Ja | Klik-id'et (`c_…`). |
| `store` | – | Anbefalet | Butikkens id (det samme som `data-store`). Skal passe til klikkets butik, ellers `store_mismatch`. |
| `order` | `order_id` | Anbefalet | Ordrenummeret. Samme nummer tælles aldrig to gange i samme program. Højst 120 tegn. |
| `total` | `order_total` | Ja | Beløbet, provisionen regnes af: varerne efter rabat, uden moms og fragt. Et maskintal (`1249.5`). |
| `currency` | – | Nej | Valutakode efter ISO 4217, fx `DKK` eller `EUR`. Standard er `DKK`. |

> [!WARNING]
> Endpointet læser `total` som et maskintal. `"1.249,00"` er ikke et tal her og bliver til 0. Det er kun track.js, der renser danske beløb – send `1249` eller `1249.00` fra jeres server.

Ældre direkte links uden klik-id kan i stedet sende `aff` (eller `aff_id`) – affiliatens tracking-id – sammen med `store`. Så skal affiliaten være tilmeldt butikkens program, ellers svares `not_joined`.

## GET

Parametrene i adressen. Det er det, track.js bruger som reserve, når browseren ikke har `sendBeacon`.

```bash title="GET"
curl "https://app.affilyflow.com/api/track/conversion?afid=c_AbC123xYz_-9&store=DIN_BUTIK&order=1008&total=499&currency=DKK"
```

## POST

Send felterne som JSON i kroppen. Felterne kan også stå i adressen – findes et felt begge steder, vinder kroppen. Det er sådan track.js sender med `navigator.sendBeacon`: en `POST` med felterne i adressen og ingen krop.

```bash title="POST med JSON"
curl -X POST https://app.affilyflow.com/api/track/conversion \
  -H "Content-Type: application/json" \
  -d '{"afid":"c_AbC123xYz_-9","store":"DIN_BUTIK","order":"1008","total":499,"currency":"DKK"}'
```

```js title="Node · efter betalingen"
await fetch('https://app.affilyflow.com/api/track/conversion', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    afid: order.afClick,       // gemt fra cookien af_click
    store: 'DIN_BUTIK',
    order: order.number,
    total: order.subtotalExVat, // varer efter rabat, uden moms og fragt
    currency: order.currency
  })
});
```

Kroppen må højst fylde 64 KB.

### Valgfrie felter i kroppen

| Felt | Betydning |
|---|---|
| `items` | Varelinjer: en liste af `{ title, variant, qty, price, product_id }`. `title` er påkrævet pr. linje. Højst 50 linjer; lange tekster afkortes. |
| `subtotal`, `tax`, `shipping` | Ordrens opdeling (tal, 0 eller større). Se nedenfor. |
| `buyer_hash` | Fingeraftryk af køberens e-mail: `hex(sha256(lowercase(trim(e-mail))))` – 64 hex-tegn. Matcher det affiliatens e-mail, registreres salget som afvist (`self_referral`). E-mailen selv sendes aldrig. |

**Opdelingen:** Sender I `total` som ordrens samlede beløb og `subtotal` med, regnes provisionen af `subtotal` i stedet – dog aldrig af mere end `total`:

- Er `tax` 0 eller mangler, eller er `subtotal + shipping + tax = total` (moms lagt oven i), er grundlaget `subtotal`.
- Er `subtotal + shipping = total` (moms inkluderet i priserne), er grundlaget `subtotal × (total − tax) / total`.
- Ellers bruges det forsigtige `min(subtotal, total − tax − shipping)`.

Det enkleste er at sende `total` som varernes beløb uden moms og fragt og udelade opdelingen.

## Svar

Svaret er altid JSON med en `status`. Hvad hver status betyder, og hvad I gør, står i [Statuskoder](https://www.affilyflow.com/docs/reference/statuskoder).

| HTTP | Krop | Hvornår |
|---|---|---|
| 200 | `{ "status": "ok" }` | Salget er registreret. |
| 200 | `{ "status": "duplicate" }`, `"expired"` m.fl. | Anmodningen blev behandlet, men salget gav ikke provision. |
| 404 | `{ "status": "unknown" }` | Klik-id'et findes ikke. |
| 400 | `{ "status": "error", "reason": "…" }` | Hverken `afid` eller `aff` + `store` var med. |
| 413 | `{ "status": "too_large" }` | Kroppen var over 64 KB. |
| 429 | `{ "status": "rate_limited" }` | For mange kald fra samme IP-adresse. |
| 500 | `{ "status": "error" }` | Registreringen fejlede. Prøv igen. |

Et registreret salg afventer butikkens godkendelse. Provisionen krediteres først, når salget er godkendt.

## Dubletter og ordrenumre

- Samme ordrenummer i samme program tælles aldrig to gange – andet kald svarer `duplicate`. Det er derfor sikkert at sende igen, hvis I er i tvivl.
- Uden ordrenummer er der ingen dubletkontrol på serveren. Send altid ordrenummeret.
- En uudfyldt pladsholder (`DIN_ORDRE_ID`, `ORDER_ID`, `{{ … }}`, `[ … ]`, `< … >`, `% … %` m.fl.) behandles som intet ordrenummer, så den aldrig optager nummeret for alle senere salg.

## Valuta

Salget kan være i enhver valuta med en kurs fra Den Europæiske Centralbank. Det omregnes til butikkens afregningsvaluta, før provision og gebyr regnes, og kursen låses på salget. En valutakode, der ikke er tre bogstaver, eller en valuta uden en kurs fra de seneste 10 dage, afvises som `unsupported_currency`.

## Grænser

- Endpointet er begrænset pr. IP-adresse. Grænsen rammer ikke en butiks egen server ved normal brug; svaret er `429` med `rate_limited`, hvis den nås.
- Hvert program har et beløbsloft pr. ordre (standard 50.000 i afregningsvalutaen). En ordre over loftet afvises som `value_too_high` – typisk et beløb sendt i øre eller cent.
- Lange værdier afkortes i stedet for at blive afvist: klik-id 64 tegn, butik og ordrenummer 120 tegn.

## Refusioner

Endpointet melder kun salg. Refunderes en ordre, afviser butikken salget under Salg i dashboardet inden for programmets godkendelsesperiode. `/api/track/conversion/reverse` bruges af WooCommerce-pluginnet og kræver butikkens nøgle – det er ikke en offentlig vej.

Se også [Specialbygget webshop](https://www.affilyflow.com/docs/butik/specialbygget) og [Test og fejlfinding](https://www.affilyflow.com/docs/butik/test-og-fejlfinding).

Kilde: https://www.affilyflow.com/docs/reference/konverterings-api
