Valider adressen i checkout med datavask

Tjek kundens adresse, når formularen sendes, accepter sikre match, bekræft sandsynlige og bed om rettelse ved usikre.

4 min Niveau: Øvet ✓ Kode testet

En forkert leveringsadresse koster en returpakke, en ekstra fragt og en irriteret kunde. Autocomplete fanger de fleste fejl, mens kunden skriver, men ikke alle: nogle indsætter adressen fra et andet sted, nogle har slået JavaScript fra, og nogle ordrer kommer ind via API. Datavask på serveren er sikkerhedsnettet, der kører, når formularen sendes.

Endpointet er GET /datavask/adresser (med etage og dør) eller GET /datavask/adgangsadresser (kun bygningens indgang). Svaret har samme form som DAWA's datavask. Feltet kategori øverst i svaret er A for et præcist match, B for ét sikkert match med små stavefejl og C for usikkert.

1. Send adressen

Det enkleste er hele adressen som én tekst i betegnelse:

Shell#vask-curl
curl -s -G "https://api.danadresse.dk/datavask/adresser" \
  --data-urlencode "betegnelse=Strandvejen 100, 1., 2900 Hellerup" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"

Parseren læser etage og dør (2. tv, st. th, 1.mf), husbogstaver med små bogstaver (12a), et postnummer hvor som helst i teksten og aa/å-stavemåder (Aabenraa, Kobenhavn). Store bogstaver og et afsluttende punktum (1., TV) gør ingen forskel.

Har din formular separate felter, kan du sende dem i stedet: vejnavn, husnr, etage, dør, postnr og supplerendebynavn. Resultatet er det samme.

POST virker også: POST /datavask/adresser med én adresse som JSON-body, enten {"betegnelse": "…"} eller de separate felter. Hvert kald vasker én adresse. Skal du vaske mange adresser, så se Rens en CSV-fil fra CRM'et.

2. Læs svaret

Kunden skrev "Strandvej" i stedet for "Strandvejen". Vasken finder præcis én adresse og svarer B:

JSON#vask-svar
{
  "kategori": "B",
  "resultater": [
    {
      "adresse": {
        "id": "0a3f50a4-0c39-32b8-e044-0003ba298018",
        "vejnavn": "Strandvejen",
        "adresseringsvejnavn": "Strandvejen",
        "husnr": "100",
        "supplerendebynavn": null,
        "postnr": "2900",
        "postnrnavn": "Hellerup",
        "status": 1,
        "virkningstart": "2000-02-05T19:44:20.000Z",
        "virkningslut": null,
        "adgangsadresseid": "0a3f507c-0624-32b8-e044-0003ba298018",
        "etage": "1",
        "dør": null,
        "href": "https://api.danadresse.dk/adresser/0a3f50a4-0c39-32b8-e044-0003ba298018"
      },
      "aktueladresse": {
        "id": "0a3f50a4-0c39-32b8-e044-0003ba298018",
        "...": "samme felter som adresse"
      },
      "vaskeresultat": {
        "variant": {
          "vejnavn": "Strandvejen",
          "husnr": "100",
          "etage": "1",
          "dør": null,
          "supplerendebynavn": null,
          "postnr": "2900",
          "postnrnavn": "Hellerup"
        },
        "afstand": 2,
        "forskelle": {
          "vejnavn": 2,
          "husnr": 0,
          "postnr": 0,
          "postnrnavn": 0,
          "etage": 0
        },
        "parsetadresse": {
          "vejnavn": "Strandvej",
          "husnr": "100",
          "etage": "1",
          "postnr": "2900",
          "postnrnavn": "Hellerup"
        },
        "ukendtetokens": [],
        "anvendtstormodtagerpostnummer": null
      }
    }
  ]
}
FeltBrug
kategoriA, B eller C for hele vasken. Det er den eneste kategori i svaret
resultaterKandidaterne, bedste først. Ved A og B er der præcis én
adresse.idAdressens UUID. Gem den på ordren
adresse.adgangsadresseidBygningens indgang. Gem den også
adresse.vejnavn m.fl.Den officielle stavemåde. Brug den i stedet for kundens
aktueladresseDen gældende version af adressen. Afviger fra adresse, hvis matchet er en nedlagt adresse
vaskeresultat.afstandRedigeringsafstand mellem input og adressen. 0 er præcist
vaskeresultat.forskelleAfstanden pr. felt, f.eks. vejnavn: 2. Viser, hvad kunden skrev forkert
vaskeresultat.ukendtetokensOrd i input, der ikke matchede noget, f.eks. ["tv"], når adressen ikke har døren "tv"

Svaret har ikke et samlet tekstfelt. Byg linjen selv af felterne, eller hent adressebetegnelse med GET /adresser/{id}.

3. Beslut: accepter, bekræft eller spørg

Brug kategori øverst i svaret:

kategoriHandling
AAccepter. Gem id og adgangsadresseid
BAccepter, men gem den officielle tekst. Du kan vise den: "Vi leverer til Strandvejen 100, 1., 2900 Hellerup"
CVis kandidaterne fra resultater, og lad kunden vælge eller rette adressen. Vælg aldrig en automatisk
tom resultaterBed kunden tjekke adressen. Bloker ikke nødvendigvis ordren

Etage og dør tæller med i kategorien. Skriver kunden en etage eller dør, der ikke findes på adressen, svarer vasken C med de enheder, der findes, som kandidater.

I Python:

Python#vask-python
import os
import requests

def vask(betegnelse):
    r = requests.get(
        "https://api.danadresse.dk/datavask/adresser",
        params={"betegnelse": betegnelse},
        headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]},
        timeout=5,
    )
    r.raise_for_status()
    svar = r.json()
    resultater = svar["resultater"]
    if not resultater:
        return "ask", []
    if svar["kategori"] == "A":
        return "accept", resultater[:1]
    if svar["kategori"] == "B":
        return "confirm", resultater[:1]
    return "choose", resultater[:5]   # C: lad kunden vælge

beslutning, kandidater = vask("Strandvej 100, 1., 2900 Hellerup")
print(f"beslutning={beslutning}")
for k in kandidater:
    a = k["adresse"]
    print(a["id"], a["vejnavn"], a["husnr"], a["etage"], a["postnr"], k["vaskeresultat"]["forskelle"])

Samme logik i Node 18+:

JavaScript#vask-node
async function vask(betegnelse) {
  const url = new URL("https://api.danadresse.dk/datavask/adresser");
  url.searchParams.set("betegnelse", betegnelse);
  const res = await fetch(url, {
    headers: { "X-Api-Key": process.env.DANADRESSE_API_KEY },
    signal: AbortSignal.timeout(5000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const { kategori, resultater } = await res.json();
  if (resultater.length === 0) return { beslutning: "ask", kandidater: [] };
  if (kategori === "A")
    return { beslutning: "accept", kandidater: resultater.slice(0, 1) };
  if (kategori === "B")
    return { beslutning: "confirm", kandidater: resultater.slice(0, 1) };
  return { beslutning: "choose", kandidater: resultater.slice(0, 5) };
}

// Rentemestervej 8 har ingen "2. tv": vasken svarer C med de enheder, der findes
const { beslutning, kandidater } = await vask(
  "Rentemestervej 8, 2. tv, 2400 København NV",
);
console.log(`beslutning=${beslutning}`);
for (const { adresse: a } of kandidater) {
  console.log(
    `${a.vejnavn} ${a.husnr}, ${a.etage ?? "-"}. ${a["dør"] ?? ""}, ${a.postnr} ${a.postnrnavn}`,
  );
}

Og i PHP med curl-udvidelsen:

PHP#vask-php
<?php
function vask(string $betegnelse): array {
    $ch = curl_init('https://api.danadresse.dk/datavask/adresser?'
        . http_build_query(['betegnelse' => $betegnelse]));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 5,
        CURLOPT_HTTPHEADER => ['X-Api-Key: ' . getenv('DANADRESSE_API_KEY')],
    ]);
    $svar = json_decode(curl_exec($ch), true);
    curl_close($ch);
    $res = $svar['resultater'] ?? [];
    if (!$res) return ['ask', []];
    if ($svar['kategori'] === 'A') return ['accept', array_slice($res, 0, 1)];
    if ($svar['kategori'] === 'B') return ['confirm', array_slice($res, 0, 1)];
    return ['choose', array_slice($res, 0, 5)];
}

[$beslutning, $kandidater] = vask('Raadhuspladsen 1, 1550 København V');
echo "beslutning=$beslutning\n";
foreach ($kandidater as $k) echo $k['adresse']['id'] . ' ' . $k['adresse']['vejnavn'] . "\n";

4. Gem id'erne på ordren

Når adressen er accepteret eller bekræftet, gemmer du adresse.id og adresse.adgangsadresseid sammen med teksten. Id'erne er stabile UUID'er fra Danmarks Adresseregister, så du senere kan slå adressen op igen eller berige den. Se Gem adresse-id'et.

Kanttilfælde

  • Stavemåde. "Strandvej" og "Raadhuspladsen" giver B med det officielle navn, fordi der er præcis ét sikkert match. Gem den officielle stavemåde, ikke kundens.
  • Forkert postnummer. Et postnummer, der ikke passer til vejen, tæller som en forskel (forskelle.postnr) og giver C. Vis kunden kandidaterne, og lad dem rette.
  • Forkert husnummer. Et nummer, der ikke findes på vejen, giver C med nærliggende numre som kandidater.
  • Etage og dør udeladt. Uden etage og dør matcher vasken adressen på indgangsniveau, hvis registret har en. Spørg efter etage og dør i formularen, når du leverer til lejligheder.
  • Nye adresser. En adresse, der lige er oprettet, kan mangle et kort stykke tid. Lad kunden fortsætte med en markering på ordren, så I kan tjekke den manuelt.
  • Timeout eller fejl. Sæt en kort timeout (3-5 sekunder) og lad ordren gå igennem, hvis API'et ikke svarer. Valider den bagefter. En tabt ordre koster mere end en adresse, der skal rettes.
  • Nøgle. Kald datavask fra din server med en almindelig nøgle. En browsernøgle virker også på /datavask/adresser, men så kan kunden springe tjekket over.

Næste skridt

Relaterede guides

Markdown-kilde Fejl i guiden? Skriv til os API-reference (Swagger)

Prøv det med en gratis nøgle

2 000 kald/måned på gratisplanen. Intet kreditkort.

Opret konto →