Hvad du skal have klar
Du skal have et udviklerarbejdsrum, en app og en sandbox med status ready. Udsted et kortlivet hsk_-token med configuration:read, journal:prepare, journal:post og operations:read. Disse er testrettigheder til appens syntetiske data. Appen skal ikke forbindes med en produktionskunde for at gennemføre øvelsen.
Brug Node.js til hjælpekommandoerne, curl til HTTP-eksemplerne og jq til at læse JSON i terminalen. jq er et hjælpeværktøj i eksemplet, ikke et serverkrav. Opret en tom arbejdsmappe til testens JSON-filer. Indsæt ikke tokens i et delt dokument, og slå shellens set -x fra, før hemmelige variable bruges.
Angiv basisadressen uden en afsluttende skråstreg. Adressen vises i App Console for appen; gæt ikke en adresse.
export HOURS_ORIGIN='https://DIN-BASISADRESSE-FRA-APP-CONSOLE'
read -r -s -p 'Sandbox-token: ' HOURS_SANDBOX_TOKEN; printf '\n'
export HOURS_SANDBOX_TOKEN
export HOURS_BASE="$HOURS_ORIGIN/api/partner/sandbox/v1"1. Hent sandboxens kontoreferencer
configuration er et begrænset referenceopslag. I sandbox returnerer det syntetiske konti. I produktion returnerer det kun de konti, kunden har valgt til appen. Det er ikke et bankkontoopslag og indeholder ingen kontosaldo.
curl --silent --show-error --fail-with-body \
"$HOURS_BASE/configuration" \
--header "Authorization: Bearer $HOURS_SANDBOX_TOKEN" \
--output configuration.json
jq . configuration.jsonForvent HTTP 200, currency: "DKK" og en accounts-liste med id, kontonummer og navn. Id'erne genereres ved provisionering og varierer mellem apps og generationer. De syntetiske referencer omfatter driftsudgift 4000 og leverandørgæld 6500. Det følgende eksempel vælger dem med kontonummer og afviser et manglende resultat:
DEBIT_ID=$(jq -er '.accounts[] | select(.account_no=="4000") | .id' configuration.json)
CREDIT_ID=$(jq -er '.accounts[] | select(.account_no=="6500") | .id' configuration.json)Manglende konto-id'er må ikke erstattes af et tilfældigt UUID. Kontrollér miljø, generation, token og scopes. Efter en nulstilling skal konfigurationen hentes igen.
2. Opret journalinput
Posteringen nedenfor bogfører 125,00 DKK som 12.500 hele øre. Den har én debetlinje og én kreditlinje. Eksemplet udregner ikke moms; det tester den dobbelte postering og de tekniske kontroller.
jq -n --arg debit "$DEBIT_ID" --arg credit "$CREDIT_ID" \
--arg date "$(date +%F)" \
'{posting_date:$date,currency:"DKK",description:"Syntetisk API-test",
lines:[
{account_id:$debit,debit_minor:12500,credit_minor:0},
{account_id:$credit,debit_minor:0,credit_minor:12500}
]}' > journal.jsonposting_date er en kalenderdato, ikke et tidspunkt med timezone. account_id er en konto-id, ikke kontonummeret som tekst. Hver linje skal have enten et positivt debetbeløb eller et positivt kreditbeløb. Begge positive, begge nul, negative beløb og decimale ørebeløb afvises.
Der må ikke sendes customer_id, organization_id, source, created_by_actor, bank_link eller en banktransaktionsreference. Organisation, aktør og intern kildekategori bestemmes af serveren. Ukendte felter afvises i stedet for lydløst at blive ignoreret.
3. Kontrollér input uden bogføring
curl --silent --show-error --fail-with-body \
--request POST "$HOURS_BASE/journal/prepare" \
--header "Authorization: Bearer $HOURS_SANDBOX_TOKEN" \
--header 'Content-Type: application/json' \
--data-binary @journal.jsonEt vellykket svar har valid: true og posted: false. Det angiver kontroller af input, grant og referencer og opregner, at periodelåse på commit-tidspunktet og selve regnskabstransaktionen ikke er afprøvet. En konto eller periode kan ændres efter denne kontrol. Det endelige bogføringskald skal derfor kontrollere igen.
En lokal browserpreflight eller MCP-validering går endnu kortere: den kan ikke kontrollere kontoen i databasen. Læs altid resultatets checked og not_checked i stedet for at fortolke "valid" som en samlet godkendelse.
4. Bogfør testen idempotent
Generér én nøgle til netop denne tilsigtede testpostering. Gem både den og inputfilen, indtil resultatet er afklaret. En ændret nøgle ved et transportgenforsøg kan skabe en ny postering.
IDEMPOTENCY_KEY="guide-$(node -e 'console.log(require("node:crypto").randomUUID())')"
printf '%s\n' "$IDEMPOTENCY_KEY" > idempotency-key.txt
curl --silent --show-error --fail-with-body \
--request POST "$HOURS_BASE/journal" \
--header "Authorization: Bearer $HOURS_SANDBOX_TOKEN" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data-binary @journal.json --output posted.json
jq . posted.jsonVed HTTP 201 indeholder kvitteringen entry_id, entry_no, posted: true, operation_id, replayed og miljø. I denne øvelse skal miljøet være sandbox. Posteringen ligger kun i den isolerede testgeneration.
Gentag præcis samme kommando med præcis samme nøgle og input. Det skal returnere det registrerede resultat med replayed: true, ikke bogføre igen. Ændr derefter beløbet, men behold nøglen: det skal give en idempotenskonflikt. Gendan filen bagefter eller start en ny, bevidst testoperation med en ny nøgle.
5. Hent kvitteringen
OPERATION_ID=$(jq -er '.operation_id' posted.json)
curl --silent --show-error --fail-with-body \
"$HOURS_BASE/operations/$OPERATION_ID" \
--header "Authorization: Bearer $HOURS_SANDBOX_TOKEN"Kvitteringsopslaget kræver operations:read. Det giver ikke fri læsning af hovedbogen. I produktion er kvitteringen bundet til samme app, organisation og grant; en anden kunde eller app skal ikke kunne hente den ved at gætte id'et.
Når et HTTP-svar går tabt, ved klienten ikke automatisk, om databasen har committet. Genbrug derfor idempotensnøglen og det samme input. En timeout er ikke en instruktion om at skabe en ny postering. Er grantet blevet tilbagekaldt, må genforsøget heller ikke omgå autorisationen for at hente en gammel kvittering.
6. Afprøv afvisningerne
Prøv kald uden token, med forkert tokenfamilie, uden nødvendigt scope og med en konto fra en anden generation. Prøv også ugyldig kalenderdato, ubalance, ukendt felt, negativt beløb og manglende idempotensnøgle. Fejl skal være synlige som HTTP-status og en maskinlæsbar kode, ikke skjult som et tomt succesresultat.
Et HTTP 401 betyder normalt, at tokenet ikke er gyldigt på denne adgangsvej. HTTP 403 betyder, at den identificerede adgang ikke tillader handlingen. HTTP 409 kan være en konflikt, eksempelvis idempotens eller en ændret revision. HTTP 422 kan være et fagligt afvist input. HTTP 503 kan betyde, at partner-API'et eller sandboxen ikke er tilgængelig.
Afslut øvelsen ved at tilbagekalde sandboxtokenet og rydde hemmelige shellvariable. Nulstil eventuelt sandboxen gennem konsollen. Dette skifter generation; det åbner ikke produktionsadgang. Produktion følger det særskilte review- og autorisationsforløb.