# Een webshop aan FenoFin koppelen

Dezelfde REST API ondersteunt sandbox en live. Uw eigen software gebruikt een
API-sleutel op de server. De browser van uw webshop krijgt deze sleutel niet.
De [API-referentie](/api/v1/docs/) en het [OpenAPI-schema](/api/v1/schema/)
beschrijven de exacte velden en foutcodes.

## Voorbereiden

1. Ga als kantoorhoofd met connector-toegang naar **Mijn kantoor → Sandbox** (de REST-API-sleutels, test en live).
2. Maak een sandbox aan en kopieer de eenmalig getoonde testsleutel.
3. Een beperkte webshopsleutel heeft `producten:read`, `relaties:read`,
   `relaties:write`, `verkoopfacturen:read` en `verkoopfacturen:write` nodig.
   Kies uitsluitend de administratie waarop deze koppeling mag werken.
   Een bestaande sleutel krijgt nieuwe scopes niet vanzelf; maak zo nodig een
   vervangende sleutel met de juiste scopes. Rotatie behoudt de bestaande instellingen.
4. Richt in FenoFin het open boekjaar, verkoopdagboek, factuurnummerreeks en
   verkoopproducten met hun omzetrekening en btw-code in. Voor deze proef:
   `WS-TAS` (Weekendtas, € 50,00 exclusief 21% btw) en `WS-VERZEND`
   (Verzendkosten, € 5,00 exclusief 21% btw). Productbeheer staat onder Factureren.

Een sandbox bevat voorbeeldproducten. Voor uw eigen assortiment maakt u producten
in FenoFin aan. De publieke product-API kan deze lezen, niet aanmaken of wijzigen.

## De verbinding controleren

De basis-URL is `https://www.fenofin.nl`; voor lokaal testen gebruikt u de URL van
uw lokale FenoFin-server. Alle API-aanroepen dragen:

```http
Authorization: Bearer <uw sleutel>
Accept: application/json
```

1. `GET /api/v1/status/`: controleer `omgeving`, de vijf benodigde `scopes` en
   `mag_schrijven`. Die laatste vlag is de schrijfvrijgave; zonder de benodigde
   schrijf-scope geeft hij op zichzelf geen schrijfrecht.
2. `GET /api/v1/administraties/`: laat de gebruiker de juiste administratie kiezen.
   Het publieke veld `id` is de administratie-ID voor de vervolgaanroepen.
3. `GET /api/v1/administraties/{administratie_id}/`: controleer de bedrijfsnaam
   en `is_sandbox` (`true` bij test, `false` bij live).
4. `GET /api/v1/administraties/{administratie_id}/producten/`: kies de producten.
   Het resultaat bevat `id`, `identificatie` (uw interne code/SKU), `naam` en
   `prijs_excl_btw` als decimale string. Alleen actieve reguliere producten worden
   getoond; systeemproducten ontbreken. De prijs is de opgeslagen productprijs.
   De factuurrespons bepaalt de uiteindelijke bedragen en btw.

Lijsten hebben `resultaten`, `volgende` en `vorige`. Volg `volgende` totdat deze
`null` is. Producten staan op ID gesorteerd. `per_pagina` is standaard 50 en maximaal
200. Controleer dat een vervolg-URL dezelfde API-host heeft voordat u de sleutel meestuurt.

Bewaar de gekozen product-ID's **per administratie en omgeving**. Een SKU helpt
bij het kiezen; gok bij een ontbrekende of onduidelijke match niet op een ander product.

Pas na deze controles toont uw software bijvoorbeeld:
**Live-koppeling gecontroleerd voor Weekendwinkel BV — [controletijd]**.
Bij gewijzigde instellingen of een mislukte controle vervalt die actuele succesmelding.

## Eén bestelling verwerken

Elke schrijfactie krijgt naast de bovenstaande headers:

```http
Content-Type: application/json
Idempotency-Key: <unieke waarde voor deze bestelling en deze stap>
```

Maak de drie idempotency-keys aan en bewaar ze met de bestelreferentie **vóór**
het eerste verzoek. Gebruik een andere key voor afnemer, concept en definitief.
Bij een timeout of herhaling gebruikt u voor dezelfde stap exact dezelfde key en
JSON-body. De herhaalgarantie is 24 uur en is aan de gebruikte sleutel gebonden.
Bewaar ook de ontvangen FenoFin-ID's. Na sleutelrotatie of na 24 uur controleert
u een onzekere uitkomst eerst in FenoFin; een nieuwe key kan een nieuwe factuur maken.
Een bestelreferentie (`klantreferentie`) is op zichzelf geen duplicaatblokkade.

### 1. Afnemer

Zoek een bestaande klant in `GET .../afnemers/` en bewaar de koppeling naar uw
klant-ID. Voor een nieuwe proefklant gebruikt u `POST .../afnemers/`:

```json
{"naam": "Demo klant WS-DEMO-1042", "adres": "Voorbeeldstraat 42", "postcode": "1000 AA", "plaats": "Demostad"}
```

Verwacht HTTP 201; bewaar `id` als `afnemer_id`.

### 2. Conceptfactuur

`POST /api/v1/administraties/{administratie_id}/verkoopfacturen/`:

```json
{
  "afnemer_id": 123,
  "factuurdatum": "2026-09-08",
  "klantreferentie": "WS-DEMO-1042",
  "regels": [
    {"product_id": 456, "aantal": "2"},
    {"product_id": 789, "aantal": "1"}
  ]
}
```

Vervang de voorbeeld-ID's door de opgehaalde ID's en gebruik een datum in een open
boekjaar. Verwacht HTTP 201 en status `Concept`. Bewaar het factuur-`id`.
Er is nog geen factuurnummer of journaalpost. Prijs, omzetrekening en btw volgen
uit de geselecteerde FenoFin-producten; deze API accepteert geen vrije artikelprijs.

### 3. Definitief maken en teruglezen

`POST /api/v1/administraties/{administratie_id}/verkoopfacturen/{factuur_id}/definitief/`
met de eigen idempotency-key voor deze stap; er is geen JSON-body nodig.

Verwacht HTTP 200, een factuurnummer en een definitieve status. FenoFin maakt de
openstaande post en journaalpost. **Dit endpoint verstuurt geen factuur per e-mail.**
Facturen verzenden is een afzonderlijke handeling in FenoFin.

Lees `GET .../verkoopfacturen/` en zoek het opgeslagen factuur-ID. Toon pas na
succesvol teruglezen **Bestelling verwerkt**. Controleer deze proef ook in FenoFin:

| Regel | Exclusief btw | Btw | Inclusief btw |
|---|---:|---:|---:|
| 2 × weekendtas | € 100,00 | € 21,00 | € 121,00 |
| Verzendkosten | € 5,00 | € 1,05 | € 6,05 |
| Totaal | € 105,00 | € 22,05 | € 127,05 |

De boeking is € 127,05 debet op debiteuren, € 105,00 credit op omzet en € 22,05
credit op af te dragen btw. De openstaande post is exact € 127,05.

## Van sandbox naar live

1. Maak in FenoFin een **live-sleutel** met de benodigde scopes en expliciet
   geselecteerde live-administratie. Bevestig het vinkje voor live-schrijven.
2. De sleutel is direct bruikbaar voor geautoriseerde verzoeken. Stel hem in uw
   software in. Sleutelaanmaak start zelf geen synchronisatie, factuur of verzending.
3. Kies de live-administratie en haal de product-ID's opnieuw op. De basis-URL
   blijft gelijk. Sandboxgegevens worden niet gekopieerd. Deel ook de lokale
   orderregistratie niet tussen sandbox en live.
4. Voer alle verbindingscontroles opnieuw uit en controleer daarna de eerste
   definitieve bestelling in FenoFin. Een succesvolle `/status/`-aanroep alleen
   bewijst nog geen volledige webshopkoppeling.

In sandbox zijn uitgaande mail, WhatsApp, ondertekening en aangiften geblokkeerd;
eigen webhooks kunnen wel uitgaan met `X-FenoFin-Omgeving: test`.
Live heeft deze sandboxblokkade niet. Definitief maken blijft ook live iets anders
dan verzenden. De demo-webshop registreert geen webhooks en verstuurt geen berichten.

## Herstellen en stoppen

| Situatie | Gedrag en vervolgstap |
|---|---|
| Ongeldige, verlopen of ingetrokken sleutel | HTTP 401; stel een geldige sleutel in en controleer opnieuw. |
| Ontbrekende scope of live-schrijven uit | HTTP 403; corrigeer de rechten of zet schrijven aan. |
| Verkeerde omgeving of geen administratietoegang | HTTP 404; controleer administratie en sleutel. |
| Body geen geldige JSON, of zonder `Content-Type: application/json` | HTTP 400 `ongeldige_json` of 415 `mediatype_niet_ondersteund`; er is niets vastgelegd. Stuur JSON met die header, met dezelfde key. |
| Dezelfde idempotency-key met andere gegevens | HTTP 409; controleer de opgeslagen bestelling, maak niet blind een nieuwe key. |
| Product inactief of boekjaar gesloten | Validatiefout; herstel de administratie en lees de foutdetails. |
| Timeout, HTML-respons of onbereikbare server | Toon geen succes; controleer opnieuw met behoud van de oorspronkelijke bestelling. |

**Schrijven uitzetten** blokkeert schrijfacties direct; lezen blijft werken.
**Intrekken** beëindigt alle toegang met die sleutel. Reeds geboekte facturen
blijven bestaan. **Roteren** maakt de oude sleutel direct ongeldig en behoudt de
rechten op de nieuwe sleutel; werk uw software bij. ‘Laatst gebruikt’ op de
sleutelpagina toont sleutelgebruik, geen bewijs van een verwerkte bestelling.
