Bli med i Kassalapp-fellesskapet på Discord

Kassalapp API - Komplett PHP-guide

Innledning

Kassalapp API gir deg tilgang til Norges mest omfattende database over dagligvarepriser. Med over 50 000 produkter fra alle de store kjedene kan du bygge kraftige prissammenligningstjenester, handleliste-apper eller analyseverktøy for norske forbrukere.

Denne guiden viser deg hvordan du kommer i gang med API-et ved hjelp av PHP, med praktiske eksempler du kan bruke direkte i dine egne prosjekter.

Innholdsfortegnelse

  1. Kom i gang
  2. Autentisering
  3. Produktsøk
  4. Filtrering og sortering
  5. Strekkode-oppslag
  6. Butikklokasjoner
  7. Kategorier og merker
  8. Avanserte eksempler
  9. Feilhåndtering
  10. Ytelse og beste praksis

Kom i gang

Steg 1: Registrer deg for API-nøkkel

Først må du registrere deg for å få en API-nøkkel:

  1. Gå til https://kassal.app/api
  2. Opprett en gratis konto
  3. Kopier din API-nøkkel fra dashboardet

Steg 2: Installer Guzzle

Vi bruker Guzzle HTTP-klient for å gjøre API-kall enklere:

bash
composer require guzzlehttp/guzzle

Steg 3: Grunnleggende oppsett

php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class KassalappAPI {
    private $client;
    private $apiKey;
    private const BASE_URL = 'https://kassal.app/api/v1/';

    public function __construct(string $apiKey) {
        $this->apiKey = $apiKey;
        $this->client = new Client([
            'base_uri' => self::BASE_URL,
            'timeout'  => 10.0,
            'headers' => [
                'Authorization' => 'Bearer ' . $this->apiKey,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ]);
    }

    public function request(string $endpoint, array $params = []) {
        try {
            $response = $this->client->get($endpoint, [
                'query' => $params
            ]);

            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            $this->handleError($e);
        }
    }

    private function handleError(RequestException $e) {
        if ($e->hasResponse()) {
            $statusCode = $e->getResponse()->getStatusCode();
            $body = json_decode($e->getResponse()->getBody(), true);

            switch ($statusCode) {
                case 401:
                    throw new Exception('Ugyldig API-nøkkel');
                case 429:
                    throw new Exception('For mange forespørsler - vent litt');
                case 404:
                    throw new Exception('Ressursen ble ikke funnet');
                default:
                    throw new Exception($body['message'] ?? 'En feil oppstod');
            }
        }
        throw new Exception('Kunne ikke koble til API-et');
    }
}

Autentisering

API-et bruker Bearer Token-autentisering. Send med din API-nøkkel i Authorization-headeren:

php
$apiKey = 'din-api-nøkkel-her';
$api = new KassalappAPI($apiKey);

Rate limiting

  • Gratis tier: 100 forespørsler per minutt
  • Pro tier: 1000 forespørsler per minutt
  • Enterprise: Ubegrenset

Hvis du overskrider grensen, får du en 429 Too Many Requests-respons. Vent 60 sekunder før du prøver igjen.

Produktsøk

Enkelt søk

Søk etter produkter med fritekst:

php
// Søk etter melk
$resultater = $api->request('products', [
    'search' => 'melk'
]);

foreach ($resultater['data'] as $produkt) {
    echo "📦 {$produkt['name']}\n";
    echo "   Pris: {$produkt['current_price']['price']} kr hos {$produkt['current_price']['store']['name']}\n";
    echo "   EAN: {$produkt['ean']}\n\n";
}

Søk med paginering

API-et returnerer maks 100 produkter per side:

php
function hentAlleProdukter($api, $søkeord) {
    $alleProdukter = [];
    $side = 1;

    do {
        $respons = $api->request('products', [
            'search' => $søkeord,
            'page' => $side
        ]);

        $alleProdukter = array_merge($alleProdukter, $respons['data']);
        $side++;

        // Sjekk om det er flere sider
        $harFlereSider = $respons['pagination']['current_page'] <
                        $respons['pagination']['last_page'];
    } while ($harFlereSider);

    return $alleProdukter;
}

// Hent alle melkeprodukter
$alleMelkeprodukter = hentAlleProdukter($api, 'melk');
echo "Fant totalt " . count($alleMelkeprodukter) . " melkeprodukter\n";

Filtrering og sortering

Filtrer på kategori

Reduser antall API-kall ved å filtrere på kategori:

php
// Hent alle kategorier først
$kategorier = $api->request('categories');

// Finn meieri-kategorien
$meieriKategori = null;
foreach ($kategorier['data'] as $kategori) {
    if (stripos($kategori['name'], 'meieri') !== false) {
        $meieriKategori = $kategori['id'];
        break;
    }
}

// Søk kun i meieriprodukter
$meieriProdukter = $api->request('products', [
    'category_id' => $meieriKategori,
    'search' => 'yoghurt'
]);

Filtrer på pris

Finn produkter innenfor et prisintervall:

php
// Finn billige alternativer (under 20 kr)
$billigeProdukter = $api->request('products', [
    'search' => 'brød',
    'price_max' => 20,
    'sort' => 'price_asc'  // Sorter billigste først
]);

echo "🍞 Brød under 20 kr:\n";
foreach ($billigeProdukter['data'] as $produkt) {
    $pris = $produkt['current_price'];
    echo "- {$produkt['name']}: {$pris['price']} kr hos {$pris['store']['name']}\n";
}

Filtrer på merke

php
// Finn alle Tine-produkter
$tineProdukter = $api->request('products', [
    'brand' => 'Tine',
    'sort' => 'name_asc'
]);

// Eller finn produkter fra flere merker
$premiumMerker = $api->request('products', [
    'brand' => 'First Price',  // Billigmerke
    'category' => 'Kjøtt'
]);

Strekkode-oppslag

Perfekt for barcode-scanning i apper:

php
class StrekkkodeScanner {
    private $api;

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function skannProdukt(string $ean) {
        try {
            $produkt = $this->api->request("products/ean/{$ean}");

            if (empty($produkt['data'])) {
                return null;
            }

            return $this->formaterProduktInfo($produkt['data']);
        } catch (Exception $e) {
            return null;
        }
    }

    private function formaterProduktInfo($produkt) {
        $info = [
            'navn' => $produkt['name'],
            'merke' => $produkt['brand'] ?? 'Ukjent',
            'beskrivelse' => $produkt['description'],
            'bilde' => $produkt['image'],
            'priser' => [],
            'næringsinnhold' => []
        ];

        // Samle priser fra alle butikker
        foreach ($produkt['pricing'] as $pris) {
            $info['priser'][] = [
                'butikk' => $pris['store']['name'],
                'pris' => $pris['price'],
                'tilbud' => $pris['price'] < $produkt['current_price']['price']
            ];
        }

        // Sorter etter pris
        usort($info['priser'], fn($a, $b) => $a['pris'] <=> $b['pris']);

        // Legg til næringsinnhold hvis tilgjengelig
        if (!empty($produkt['nutrition'])) {
            $info['næringsinnhold'] = $produkt['nutrition'];
        }

        return $info;
    }
}

// Bruk scanner
$scanner = new StrekkkodeScanner($api);
$produktInfo = $scanner->skannProdukt('7035620033148');  // Norvegia ost

if ($produktInfo) {
    echo "🧀 {$produktInfo['navn']}\n";
    echo "Billigste pris: {$produktInfo['priser'][0]['pris']} kr hos {$produktInfo['priser'][0]['butikk']}\n";
}

Butikklokasjoner

Finn nærmeste butikker basert på posisjon:

php
class ButikkFinner {
    private $api;

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function finnNærmesteButikker($lat, $lng, $radius = 5) {
        $alleButikker = $this->api->request('physical-stores');
        $nærmesteButikker = [];

        foreach ($alleButikker['data'] as $butikk) {
            $avstand = $this->beregnAvstand(
                $lat, $lng,
                $butikk['lat'], $butikk['lng']
            );

            if ($avstand <= $radius) {
                $butikk['avstand'] = round($avstand, 1);
                $nærmesteButikker[] = $butikk;
            }
        }

        // Sorter etter avstand
        usort($nærmesteButikker, fn($a, $b) => $a['avstand'] <=> $b['avstand']);

        return $nærmesteButikker;
    }

    private function beregnAvstand($lat1, $lng1, $lat2, $lng2) {
        // Haversine-formel for å beregne avstand
        $jordRadius = 6371; // km

        $deltaLat = deg2rad($lat2 - $lat1);
        $deltaLng = deg2rad($lng2 - $lng1);

        $a = sin($deltaLat/2) * sin($deltaLat/2) +
             cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
             sin($deltaLng/2) * sin($deltaLng/2);

        $c = 2 * atan2(sqrt($a), sqrt(1-$a));

        return $jordRadius * $c;
    }

    public function grupperButikkerEtterKjede($butikker) {
        $kjeder = [];

        foreach ($butikker as $butikk) {
            $kjede = $butikk['chain'] ?? 'Annet';
            if (!isset($kjeder[$kjede])) {
                $kjeder[$kjede] = [];
            }
            $kjeder[$kjede][] = $butikk;
        }

        return $kjeder;
    }
}

// Finn butikker nær Oslo sentrum
$butikkFinner = new ButikkFinner($api);
$nærmesteButikker = $butikkFinner->finnNærmesteButikker(59.9139, 10.7522, 3);

echo "📍 Butikker innen 3 km:\n";
foreach ($nærmesteButikker as $butikk) {
    echo "- {$butikk['name']} ({$butikk['chain']}): {$butikk['avstand']} km\n";
    echo "  Adresse: {$butikk['address']}\n";
    if (!empty($butikk['opening_hours'])) {
        echo "  Åpningstider: {$butikk['opening_hours']}\n";
    }
    echo "\n";
}

Kategorier og merker

Hent alle kategorier

php
class KategoriNavigator {
    private $api;
    private $kategorier = [];

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
        $this->lastKategorier();
    }

    private function lastKategorier() {
        $respons = $this->api->request('categories');

        foreach ($respons['data'] as $kategori) {
            $this->kategorier[$kategori['id']] = [
                'navn' => $kategori['name'],
                'antall_produkter' => $kategori['products_count'] ?? 0
            ];
        }
    }

    public function visKategoritre() {
        echo "📂 Produktkategorier:\n";
        foreach ($this->kategorier as $id => $kategori) {
            echo "  [{$id}] {$kategori['navn']} ({$kategori['antall_produkter']} produkter)\n";
        }
    }

    public function finnKategoriId($søkeord) {
        foreach ($this->kategorier as $id => $kategori) {
            if (stripos($kategori['navn'], $søkeord) !== false) {
                return $id;
            }
        }
        return null;
    }
}

$navigator = new KategoriNavigator($api);
$navigator->visKategoritre();

// Finn produkter i en spesifikk kategori
$fruktKategori = $navigator->finnKategoriId('frukt');
if ($fruktKategori) {
    $fruktProdukter = $api->request('products', [
        'category_id' => $fruktKategori,
        'sort' => 'price_asc'
    ]);
}

Avanserte eksempler

Prissammenligning for handleliste

php
class Handleliste {
    private $api;
    private $produkter = [];

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function leggTilProdukt($søkeord, $antall = 1) {
        $resultat = $this->api->request('products', [
            'search' => $søkeord,
            'unique' => true  // Få kun unike produkter
        ]);

        if (!empty($resultat['data'])) {
            $produkt = $resultat['data'][0];
            $this->produkter[] = [
                'produkt' => $produkt,
                'antall' => $antall
            ];
            return true;
        }
        return false;
    }

    public function beregnTotalpriser() {
        $butikkPriser = [];

        foreach ($this->produkter as $item) {
            $produkt = $item['produkt'];
            $antall = $item['antall'];

            // Gå gjennom alle butikker som har produktet
            foreach ($produkt['pricing'] as $pris) {
                $butikk = $pris['store']['name'];

                if (!isset($butikkPriser[$butikk])) {
                    $butikkPriser[$butikk] = [
                        'total' => 0,
                        'produkter' => [],
                        'mangler' => []
                    ];
                }

                $butikkPriser[$butikk]['total'] += $pris['price'] * $antall;
                $butikkPriser[$butikk]['produkter'][] = [
                    'navn' => $produkt['name'],
                    'pris' => $pris['price'],
                    'antall' => $antall,
                    'sum' => $pris['price'] * $antall
                ];
            }
        }

        // Sorter butikker etter totalpris
        uasort($butikkPriser, fn($a, $b) => $a['total'] <=> $b['total']);

        return $butikkPriser;
    }

    public function visAnbefaling() {
        $priser = $this->beregnTotalpriser();

        if (empty($priser)) {
            echo "Handlelisten er tom!\n";
            return;
        }

        $billigste = array_key_first($priser);
        $billigstePris = $priser[$billigste];

        echo "\n💰 ANBEFALING:\n";
        echo "Handle hos {$billigste} - Total: {$billigstePris['total']} kr\n\n";

        echo "📋 Din handleliste hos {$billigste}:\n";
        foreach ($billigstePris['produkter'] as $produkt) {
            echo "- {$produkt['antall']}x {$produkt['navn']}: {$produkt['sum']} kr\n";
        }

        echo "\n💸 Sammenligning med andre butikker:\n";
        foreach ($priser as $butikk => $info) {
            $forskjell = $info['total'] - $billigstePris['total'];
            $prosent = round(($forskjell / $billigstePris['total']) * 100, 1);

            if ($butikk !== $billigste) {
                echo "- {$butikk}: {$info['total']} kr (+{$forskjell} kr / +{$prosent}%)\n";
            }
        }
    }
}

// Lag handleliste
$handleliste = new Handleliste($api);
$handleliste->leggTilProdukt('melk lettmelk', 2);
$handleliste->leggTilProdukt('brød grovt', 1);
$handleliste->leggTilProdukt('egg 12 stk', 1);
$handleliste->leggTilProdukt('norvegia', 1);
$handleliste->leggTilProdukt('bananer', 1);

$handleliste->visAnbefaling();

Tilbudsovervåking

php
class Tilbudsovervåker {
    private $api;
    private $favoritter = [];

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function leggTilFavoritt($ean, $ønsketPris = null) {
        $this->favoritter[$ean] = $ønsketPris;
    }

    public function sjekkTilbud() {
        $tilbud = [];

        foreach ($this->favoritter as $ean => $ønsketPris) {
            try {
                $produkt = $this->api->request("products/ean/{$ean}");

                if (empty($produkt['data'])) {
                    continue;
                }

                $data = $produkt['data'];
                $laveste = $this->finnLavestePris($data['pricing']);

                // Sjekk om produktet er på tilbud
                if ($data['current_price']['price'] > $laveste['price']) {
                    $besparelse = $data['current_price']['price'] - $laveste['price'];
                    $prosent = round(($besparelse / $data['current_price']['price']) * 100);

                    $tilbud[] = [
                        'produkt' => $data['name'],
                        'butikk' => $laveste['store']['name'],
                        'tilbudspris' => $laveste['price'],
                        'normalpris' => $data['current_price']['price'],
                        'besparelse' => $besparelse,
                        'prosent' => $prosent,
                        'under_ønsket' => $ønsketPris && $laveste['price'] <= $ønsketPris
                    ];
                }
            } catch (Exception $e) {
                // Ignorer feil for enkeltprodukter
            }
        }

        // Sorter etter besparelse
        usort($tilbud, fn($a, $b) => $b['besparelse'] <=> $a['besparelse']);

        return $tilbud;
    }

    private function finnLavestePris($priser) {
        return array_reduce($priser, function($lavest, $pris) {
            return !$lavest || $pris['price'] < $lavest['price'] ? $pris : $lavest;
        });
    }

    public function sendVarsler($tilbud) {
        echo "🔔 TILBUDSVARSLER:\n\n";

        foreach ($tilbud as $t) {
            $emoji = $t['under_ønsket'] ? '🎯' : '💸';
            echo "{$emoji} {$t['produkt']}\n";
            echo "   {$t['butikk']}: {$t['tilbudspris']} kr (spar {$t['besparelse']} kr / -{$t['prosent']}%)\n";

            if ($t['under_ønsket']) {
                echo "   ✅ Under ønsket pris!\n";
            }
            echo "\n";
        }
    }
}

// Overvåk favoritter
$overvåker = new Tilbudsovervåker($api);
$overvåker->leggTilFavoritt('7035620033148', 100);  // Norvegia, ønsket pris 100 kr
$overvåker->leggTilFavoritt('7037710000457', 25);   // Pepsi Max

$tilbud = $overvåker->sjekkTilbud();
$overvåker->sendVarsler($tilbud);

Næringsinnhold-analyse

php
class NæringsAnalyse {
    private $api;

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function analyserProdukt($ean) {
        $produkt = $this->api->request("products/ean/{$ean}");

        if (empty($produkt['data']['nutrition'])) {
            return null;
        }

        $næring = $produkt['data']['nutrition'];
        $navn = $produkt['data']['name'];

        return $this->vurderSunnhet($navn, $næring);
    }

    private function vurderSunnhet($navn, $næring) {
        $vurdering = [
            'produkt' => $navn,
            'score' => 0,
            'advarsler' => [],
            'positive' => []
        ];

        // Sjekk sukkerinnhold (per 100g)
        if (isset($næring['sugars_per_100g'])) {
            $sukker = $næring['sugars_per_100g'];
            if ($sukker > 15) {
                $vurdering['advarsler'][] = "⚠️ Høyt sukkerinnhold ({$sukker}g/100g)";
                $vurdering['score'] -= 2;
            } elseif ($sukker < 5) {
                $vurdering['positive'][] = "✅ Lavt sukkerinnhold ({$sukker}g/100g)";
                $vurdering['score'] += 1;
            }
        }

        // Sjekk mettet fett
        if (isset($næring['saturated_fat_per_100g'])) {
            $fett = $næring['saturated_fat_per_100g'];
            if ($fett > 5) {
                $vurdering['advarsler'][] = "⚠️ Høyt mettet fett ({$fett}g/100g)";
                $vurdering['score'] -= 1;
            }
        }

        // Sjekk fiber
        if (isset($næring['fiber_per_100g'])) {
            $fiber = $næring['fiber_per_100g'];
            if ($fiber > 6) {
                $vurdering['positive'][] = "✅ Høyt fiberinnhold ({$fiber}g/100g)";
                $vurdering['score'] += 2;
            }
        }

        // Sjekk protein
        if (isset($næring['protein_per_100g'])) {
            $protein = $næring['protein_per_100g'];
            if ($protein > 20) {
                $vurdering['positive'][] = "✅ Høyt proteininnhold ({$protein}g/100g)";
                $vurdering['score'] += 1;
            }
        }

        // Gi en totalvurdering
        if ($vurdering['score'] >= 2) {
            $vurdering['konklusjon'] = '🌟 Sunt valg!';
        } elseif ($vurdering['score'] <= -2) {
            $vurdering['konklusjon'] = '⚠️ Bør nytes med måte';
        } else {
            $vurdering['konklusjon'] = '👍 OK valg';
        }

        return $vurdering;
    }

    public function sammenlignAlternativer($søkeord) {
        $produkter = $this->api->request('products', [
            'search' => $søkeord,
            'unique' => true
        ]);

        $alternativer = [];

        foreach ($produkter['data'] as $produkt) {
            if (!empty($produkt['nutrition'])) {
                $vurdering = $this->vurderSunnhet($produkt['name'], $produkt['nutrition']);
                $vurdering['pris'] = $produkt['current_price']['price'];
                $vurdering['ean'] = $produkt['ean'];
                $alternativer[] = $vurdering;
            }
        }

        // Sorter etter sunnhet-score
        usort($alternativer, fn($a, $b) => $b['score'] <=> $a['score']);

        return $alternativer;
    }
}

// Analyser næringsinnhold
$analyse = new NæringsAnalyse($api);

// Sammenlign ulike yoghurt-alternativer
$alternativer = $analyse->sammenlignAlternativer('yoghurt');

echo "🥛 YOGHURT-SAMMENLIGNING:\n\n";
foreach (array_slice($alternativer, 0, 5) as $alt) {
    echo "{$alt['konklusjon']} {$alt['produkt']} - {$alt['pris']} kr\n";

    foreach ($alt['positive'] as $pos) {
        echo "  {$pos}\n";
    }

    foreach ($alt['advarsler'] as $adv) {
        echo "  {$adv}\n";
    }

    echo "\n";
}

Feilhåndtering

Robust feilhåndtering

php
class RobustKassalappAPI extends KassalappAPI {
    private $maxRetries = 3;
    private $retryDelay = 1; // sekunder

    public function requestWithRetry(string $endpoint, array $params = []) {
        $forsøk = 0;
        $sisteFeil = null;

        while ($forsøk < $this->maxRetries) {
            try {
                return $this->request($endpoint, $params);
            } catch (Exception $e) {
                $sisteFeil = $e;
                $forsøk++;

                // Sjekk om det er en midlertidig feil
                if ($this->erMidlertidigFeil($e)) {
                    $this->loggFeil("Forsøk {$forsøk} feilet: {$e->getMessage()}");

                    if ($forsøk < $this->maxRetries) {
                        sleep($this->retryDelay * $forsøk); // Eksponentiell backoff
                        continue;
                    }
                }

                // Permanent feil, ikke prøv igjen
                throw $e;
            }
        }

        throw $sisteFeil;
    }

    private function erMidlertidigFeil(Exception $e) {
        $midlertidigeFeilkoder = [429, 500, 502, 503, 504];

        if ($e instanceof RequestException && $e->hasResponse()) {
            $statusCode = $e->getResponse()->getStatusCode();
            return in_array($statusCode, $midlertidigeFeilkoder);
        }

        return false;
    }

    private function loggFeil($melding) {
        error_log("[Kassalapp API] " . $melding);
    }

    public function håndterRateLimiting() {
        static $forespørsler = [];
        $nå = time();

        // Fjern gamle forespørsler (eldre enn 60 sekunder)
        $forespørsler = array_filter($forespørsler, fn($tid) => $nå - $tid < 60);

        // Sjekk om vi nærmer oss grensen
        if (count($forespørsler) >= 95) { // 95 av 100 tillatte
            if (!empty($forespørsler)) {
                $eldsteForespørsel = min($forespørsler);
                $ventetid = max(0, 60 - ($nå - $eldsteForespørsel));
                
                if ($ventetid > 0 && $ventetid <= 60) {
                    sleep($ventetid);
                }
                
                // Rens kun gamle forespørsler, ikke alle
                $forespørsler = array_filter($forespørsler, fn($tid) => $nå - $tid < $ventetid);
            }
        }

        $forespørsler[] = $nå;
    }
}

Validering av input

php
class InputValidator {
    public static function validerEAN($ean) {
        // Fjern mellomrom og bindestrek
        $ean = preg_replace('/[\s-]/', '', $ean);

        // Sjekk lengde (EAN-8 eller EAN-13)
        if (!in_array(strlen($ean), [8, 13])) {
            throw new InvalidArgumentException("EAN må være 8 eller 13 siffer");
        }

        // Sjekk at det kun er tall
        if (!ctype_digit($ean)) {
            throw new InvalidArgumentException("EAN kan kun inneholde tall");
        }

        // Valider kontrollsiffer
        if (!self::validerKontrollsiffer($ean)) {
            throw new InvalidArgumentException("Ugyldig EAN kontrollsiffer");
        }

        return $ean;
    }

    private static function validerKontrollsiffer($ean) {
        $siffer = str_split($ean);
        $kontroll = array_pop($siffer);

        $sum = 0;
        foreach ($siffer as $index => $tall) {
            $sum += $tall * (($index % 2 === 0) ? 1 : 3);
        }

        $beregnetKontroll = (10 - ($sum % 10)) % 10;

        return $beregnetKontroll == $kontroll;
    }

    public static function validerSøkeord($søkeord) {
        $søkeord = trim($søkeord);

        if (strlen($søkeord) < 2) {
            throw new InvalidArgumentException("Søkeord må være minst 2 tegn");
        }

        if (strlen($søkeord) > 100) {
            throw new InvalidArgumentException("Søkeord kan ikke være lengre enn 100 tegn");
        }

        return $søkeord;
    }
}

// Bruk validering
try {
    $ean = InputValidator::validerEAN('7035620033148');
    $produkt = $api->request("products/ean/{$ean}");
} catch (InvalidArgumentException $e) {
    echo "Ugyldig input: " . $e->getMessage();
}

Ytelse og beste praksis

1. Caching for bedre ytelse

php
class CachedKassalappAPI extends KassalappAPI {
    private $cache = [];
    private $cacheVarighet = 3600; // 1 time

    public function requestCached(string $endpoint, array $params = []) {
        $cacheNøkkel = $this->lagCacheNøkkel($endpoint, $params);

        // Sjekk cache
        if (isset($this->cache[$cacheNøkkel])) {
            $cacheData = $this->cache[$cacheNøkkel];
            if (time() - $cacheData['tid'] < $this->cacheVarighet) {
                return $cacheData['data'];
            }
        }

        // Hent fra API
        $data = $this->request($endpoint, $params);

        // Lagre i cache
        $this->cache[$cacheNøkkel] = [
            'data' => $data,
            'tid' => time()
        ];

        return $data;
    }

    private function lagCacheNøkkel($endpoint, $params) {
        return md5($endpoint . serialize($params));
    }

    public function tømCache() {
        $this->cache = [];
    }
}

2. Batch-operasjoner

php
class BatchProcessor {
    private $api;
    private $batch = [];
    private $batchStørrelse = 10;

    public function __construct(KassalappAPI $api) {
        $this->api = $api;
    }

    public function leggTilBatch($ean) {
        $this->batch[] = $ean;

        if (count($this->batch) >= $this->batchStørrelse) {
            return $this->prosesserBatch();
        }

        return [];
    }

    public function prosesserBatch() {
        $resultater = [];

        foreach ($this->batch as $ean) {
            try {
                $produkt = $this->api->request("products/ean/{$ean}");
                $resultater[$ean] = $produkt['data'];
            } catch (Exception $e) {
                $resultater[$ean] = null;
            }

            // Vent litt mellom forespørsler for å unngå rate limiting
            usleep(100000); // 100ms
        }

        $this->batch = [];
        return $resultater;
    }
}

3. Asynkrone forespørsler

php
use GuzzleHttp\Pool;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;

class AsyncKassalappAPI extends KassalappAPI {
    public function hentFlereProdukterAsync(array $eanListe): array {
        $requests = function () use ($eanListe) {
            foreach ($eanListe as $ean) {
                yield $ean => $this->client->requestAsync('GET', "products/ean/{$ean}");
            }
        };

        $resultater = [];

        $pool = new Pool($this->client, $requests(), [
            'concurrency' => 5,  // Maks 5 samtidige forespørsler
            'fulfilled' => function (ResponseInterface $response, $ean) use (&$resultater) {
                $body = $response->getBody()->getContents();
                $data = json_decode($body, true);
                
                if (json_last_error() === JSON_ERROR_NONE) {
                    $resultater[$ean] = $data;
                } else {
                    $resultater[$ean] = ['error' => 'Invalid JSON response'];
                }
            },
            'rejected' => function (RequestException $reason, $ean) use (&$resultater) {
                $resultater[$ean] = ['error' => $reason->getMessage()];
            }
        ]);

        // Vent til alle forespørsler er fullført
        $pool->promise()->wait();

        return $resultater;
    }
}

4. Tips for optimal bruk

php
// ✅ GJØR DETTE:

// 1. Bruk kategorier for å redusere API-kall
$kategoriProdukter = $api->request('products', [
    'category_id' => 123,
    'sort' => 'price_asc'
]);

// 2. Bruk 'unique' parameter for å få kun unike produkter
$unikeProdukter = $api->request('products', [
    'search' => 'melk',
    'unique' => true
]);

// 3. Bruk EAN-oppslag når mulig (raskere enn søk)
$produkt = $api->request("products/ean/7035620033148");

// 4. Cache statiske data som kategorier og butikker
$kategorier = $cachedApi->requestCached('categories');

// ❌ UNNGÅ DETTE:

// 1. Ikke gjør for mange samtidige forespørsler
// FEIL:
for ($i = 0; $i < 100; $i++) {
    $api->request('products', ['page' => $i]);
}

// 2. Ikke ignorer rate limiting
// FEIL:
while (true) {
    $api->request('products');  // Vil treffe rate limit
}

// 3. Ikke hent alle produkter hvis du kun trenger noen få
// FEIL:
$alleProdukter = $api->request('products');  // Henter 100 produkter
$melk = array_filter($alleProdukter, fn($p) => strpos($p['name'], 'melk'));

Avslutning

Med Kassalapp API har du nå tilgang til Norges mest omfattende database over dagligvarepriser. Denne guiden har vist deg hvordan du:

  • Setter opp og autentiserer API-tilgang
  • Søker og filtrerer produkter effektivt
  • Bygger prissammenlignings-tjenester
  • Håndterer feil og rate limiting profesjonelt
  • Optimaliserer ytelse med caching og batch-operasjoner

Lykke til med utviklingen! Vi gleder oss til å se hva du bygger med Kassalapp API.