# Adresse-autocomplete i Magento 2-checkout

> Installér Danadresse-modulet til autocomplete i checkout og adressetjek af hver ordre, eller kobl widget'en på selv med et theme-script og en CSP-whitelist.
>
> https://danadresse.dk/guides/magento · verified 2026-11-02
Der findes et Magento-modul til Danadresse: `lynbro/module-danadresse` (modulnavn `Lynbro_Danadresse`, MIT-licens) til Magento Open Source og Adobe Commerce 2.4.4–2.4.8 på PHP 8.1–8.4. Det giver checkout'en og kundens adressebog adresse-autocomplete, udfylder vej, postnr., by og den danske region, og tjekker adressen på hver ny ordre. Kan du ikke installere moduler, viser anden halvdel af guiden, hvordan du kobler widget'en på med din egen theme-kode.

## 1. Opret to nøgler

Under [Nøgler](/dashboard/keys):

- en **browsernøgle** til butikkens sider. Tilføj butikkens domæner under **Tilladte domæner**, fx `minbutik.dk, www.minbutik.dk`, og staging-domænet, hvis du tester der. En domænelåst nøgle virker kun på de domæner, så den kan stå i sidens kildekode. Se [Browsernøgler og tilladte domæner](/guides/browser-noegler-domaener).
- en **servernøgle** uden domæner til ordretjekket. Magento gemmer den krypteret, og den når aldrig browseren.

## 2. Installér modulet

Hent `lynbro-module-danadresse-<version>.zip` på [download-siden](/integrationer/download#magento2) (version, SHA-256 og ændringslog står der). Composer læser zip-filen direkte som artifact-repository — pak den ikke ud: læg den i et `artifacts/`-katalog i Magento-roden, kør `composer config repositories.danadresse artifact ./artifacts` og `composer require lynbro/module-danadresse`. Uden Composer pakker du zip-filen ud i `app/code/Lynbro/Danadresse/`. Kør derefter:

```bash id=module-install run=no
bin/magento module:enable Lynbro_Danadresse
bin/magento setup:upgrade
bin/magento setup:di:compile                          # kun i production mode
bin/magento setup:static-content:deploy da_DK en_US   # kun i production mode
bin/magento cache:flush
```

`setup:upgrade` tilføjer to kolonner, der må være tomme, til `sales_order` (`danadresse_status`, `danadresse_suggestion`) og én til `sales_order_grid`. Intet andet ændres.

## 3. Opsætning

Gå til **Stores → Configuration → LynBro → Danadresse — danske adresser**:

- **Generelt**: slå *Aktivér adresse-autocomplete* til (fra som standard), og sæt browsernøglen ind.
- **Butik**: autocomplete i checkout, i kundens adressebog og ved oprettelse, tjek af indtastede adresser ("Mente du …?") og *Udfyld by ud fra postnummer* er slået til som standard.
- **Ordrevalidering**: slå *Valider adresser på nye ordrer* til, sæt servernøglen ind, og vælg levering, fakturering eller begge. Resultatet tilføjes som intern ordrekommentar. Standard-timeout er 3 sekunder.

Alt kan sættes pr. website eller butiksvisning, fx kun i den danske butik.

Modulet tillader selv `https://api.danadresse.dk` i checkout'ens Content Security Policy, og scriptet hentes fra dine egne statiske filer.

## 4. Test

1. Læg en vare i kurven, og åbn checkout.
2. Skriv `Rentemestervej 8` i *Gadenavn*, og vælg forslaget.
3. Postnr. `2400`, by `København NV` og regionen *Hovedstaden* bliver udfyldt.
4. Gennemfør ordren. Under **Salg → Ordrer** står der *Gyldig* i kolonnen *Adressetjek*.

`API refused the request (403)` i browserens konsol betyder, at butikkens domæne mangler på browsernøglen. `401` betyder, at nøglen mangler eller er skrevet forkert. *Ikke tjekket (API-fejl)* på en ordre betyder, at servernøglen er forkert, eller at API'et ikke svarede inden for timeout.

Med et Hyvä-theme virker modulet på theme-siderne (adressebog, oprettelse) og i Luma-checkout'en, som Hyvä falder tilbage til. På Hyvä Checkout (Magewire) finder scriptet felterne via navn og `autocomplete`-attributter, men det er ikke testet på en live Hyvä Checkout. Test det, før du går live.

## Uden modulet: din egen theme-kode

Magento 2's standard-checkout (Luma) tegner adressefelterne med Knockout, efter siden er indlæst. Felterne er almindelige input-elementer, så widget'en kan kobles på, men scriptet skal vente på dem og sende en `change`-event, så Knockout gemmer værdien. Fra Magento 2.4.7 kører checkout-siden desuden med en streng Content Security Policy, så vores domæner skal tillades, og Danmark har obligatoriske regioner: koden nedenfor sætter ikke regionen, så kunden vælger den selv.

### Init-scriptet

Læg filen i dit theme som `app/design/frontend/<Vendor>/<theme>/web/js/danadresse-init.js`.

```js id=init-js run=syntax
const API_KEY = "DIN_BROWSER_NØGLE";

function setField(el, value, events) {
  if (!el || value == null) return;
  const setter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), "value").set;
  setter.call(el, value);
  for (const type of events || ["input", "change"]) {
    el.dispatchEvent(new Event(type, { bubbles: true }));
  }
}

function streetLine(a) {
  let line = a.vejnavn + " " + a.husnr;
  if (a.etage) line += ", " + a.etage + ".";
  if (a["dør"]) line += " " + a["dør"];
  return line;
}

function bind(input) {
  const form = input.form || document;
  DanadresseAutocomplete.attach(input, {
    apiKey: API_KEY,
    onSelect(a) {
      // change får Knockout til at opdatere sin observable
      setField(input, streetLine(a), ["change"]);
      setField(form.querySelector('input[name="postcode"]'), a.postnr);
      setField(form.querySelector('input[name="city"]'), a.postnrnavn);
    },
  });
}

function bindAll() {
  document.querySelectorAll('input[name="street[0]"]').forEach(bind);
}

function start() {
  bindAll();
  // Leverings- og betalingsadressen tegnes senere og kan tegnes igen.
  new MutationObserver(bindAll).observe(document.body, { childList: true, subtree: true });
}

if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start);
else start();
```

`street[0]`, `postcode` og `city` er feltnavnene i Luma-checkout'en for både leverings- og betalingsadresse. Koden udfylder postnr. og by i den formular, kunden skriver i.

### Indlæs scripts på checkout-siden

Opret `Magento_Checkout/layout/checkout_index_index.xml` i dit theme:

```xml id=layout-xml run=no
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <script src="https://danadresse.dk/static/js/danadresse-autocomplete-1.0.0.js" src_type="url"/>
        <script src="js/danadresse-init.js"/>
    </head>
</page>
```

Kør `bin/magento setup:static-content:deploy` og tøm cachen.

### Tillad domænerne i CSP

Uden denne fil blokerer Magento scriptet eller API-kaldene på checkout-siden. CSP-whitelists ligger i et modul, ikke i et theme, så læg den i et lille modul, fx `app/code/Vendor/Danadresse/etc/csp_whitelist.xml`:

```xml id=csp-whitelist run=no
<?xml version="1.0"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="danadresse-script" type="host">https://danadresse.dk</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="danadresse-api" type="host">https://api.danadresse.dk</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>
```

Test i checkout med browserens konsol åben. En CSP-fejl nævner det domæne, der mangler. `403 origin_not_allowed` betyder, at domænet mangler på nøglen.

### Tjek adressen, når ordren gemmes

I samme modul kan en observer på `sales_order_place_before` (registreret i `etc/events.xml`) tjekke leveringsadressen med servernøglen. Eksemplet stopper ikke ordren. Det skriver en kommentar på ordren, så nogen kan kigge på den.

```php id=observer-php run=syntax
<?php
declare(strict_types=1);

namespace Vendor\Danadresse\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class CheckAddress implements ObserverInterface
{
    public function execute(Observer $observer): void
    {
        $order = $observer->getEvent()->getOrder();
        $address = $order->getShippingAddress() ?: $order->getBillingAddress();
        if (!$address || $address->getCountryId() !== 'DK') {
            return;
        }
        // Vej og husnr. (før første komma) + postnr.: tjekker bygningen
        $street = trim((string) strtok((string) ($address->getStreet()[0] ?? ''), ','));
        $url = 'https://api.danadresse.dk/datavask/adgangsadresser?'
            . http_build_query(['betegnelse' => $street . ', ' . $address->getPostcode()]);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5,
            CURLOPT_HTTPHEADER => ['X-Api-Key: ' . getenv('DANADRESSE_API_KEY')],
        ]);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($body === false || $status !== 200) {
            return; // API'et svarer ikke: ordren går igennem
        }
        $vask = json_decode($body, true);
        if (($vask['kategori'] ?? '') === 'C') {
            $order->addCommentToStatusHistory('Danadresse: adressen kunne ikke verificeres (kategori C).');
        }
    }
}
```

Kategorierne er forklaret i [Datavask i checkout](/guides/datavask-i-checkout).

## Næste skridt

- [Magento-siden](/integrationer/magento) under Integrationer
- [Browsernøgler og tilladte domæner](/guides/browser-noegler-domaener)
- [Datavask i checkout](/guides/datavask-i-checkout)
