
# Automatisk genopfyldning af underkonto (Brugerdefineret betalingsudbyder)

Hvis du foretrækker at håndtere kreditbetalinger for underkonti via din egen udbyder i stedet for Stripe (bankoverførsler, lokale betalingsgateways, brugerdefinerede faktureringssystemer), stiller platformen et webhook + API-par til rådighed, så du selv kan køre hele processen med opkrævning og tildeling. Den ikke-tekniske oversigt findes på siden [Agency Accounts](agency-accounts.md#option-2-custom-payment-provider); denne side dækker det præcise webhook-payload og det API-kald, du foretager for at tildele kreditter bagefter.

Webhook'en er kun én af måderne, hvorpå automatiske top-ups betales. Hvis du har forbundet [Stripe](agency-accounts.md#option-1-connect-stripe) eller [PayPal](agency-accounts.md#option-3-connect-paypal) i SaaS-tilstand, debiterer platformen selv kundens gemte kort eller PayPal-konto og tildeler kreditterne, så der er intet på denne side, du behøver at bygge. Læs kun videre, hvis du selv ønsker at håndtere betalingen.

***

## Flowet i korte træk

1. En underkontos kreditsaldo falder til under deres tærskel for automatisk genopfyldning.
2. Platformen kalder din **webhook-URL** med oplysninger om underkontoen og hvor mange kreditter de har brug for.
3. Din server opkræver betaling fra kunden via den udbyder, du bruger.
4. Din server kalder **Grant Credits API** for at tilføje kreditter til den pågældende underkonto.
5. Din server svarer `200` for at bekræfte modtagelsen af webhooket.

***

## 1. Webhook: `agency_sub_account_auto_recharge`

Konfigurer webhook-URL'en ved at klikke på **SaaS Mode** i hovedsidepanelet, vælge **Use a Custom Payment Provider Instead** (eller, når den er konfigureret, åbne kortet **Custom Payment Provider**), og udfylde feltet **Auto-Recharge Webhook**.

### Hvornår den udløses

Når en underkontos kreditsaldo falder til under deres konfigurerede tærskel for automatisk genopfyldning.

### Payload

```json
{
  "event": "agency_sub_account_auto_recharge",
  "sub_account_id": "<sub-account-id>",
  "sub_account_email": "customer@example.com",
  "sub_account_name": "John Doe",
  "agency_id": "<agency-id>",
  "credits_requested": 500,
  "current_balance": 42,
  "threshold": 100,
  "price_per_credit_cents": 10,
  "price_per_credit_currency": "usd",
  "total_amount_cents": 5000,
  "timestamp": "2026-04-13T12:00:00.000Z",
  "idempotency_key": "auto_recharge_abc123_1681387200000"
}
```

| Felt | Beskrivelse |
|---|---|
| `event` | Altid `agency_sub_account_auto_recharge` for dette webhook. |
| `sub_account_id` | Det unikke ID for den underkonto, der har brug for kreditter. |
| `sub_account_email` | Underkontoens e-mailadresse. |
| `sub_account_name` | Underkontoens viste navn. |
| `agency_id` | Din agency-kontos unikke ID. |
| `credits_requested` | Hvor mange kreditter der skal tildeles. Dette er dit konfigurerede genopfyldningsbeløb, men hvis saldoen er faldet længere under tærsklen, end det beløb ville dække, hæves det automatisk til det, der kræves for at bringe saldoen over tærsklen igen i én omgang. Opkræv (og tildel) altid værdien `credits_requested` fra payloadet — hardkod ikke dit genopfyldningsbeløb. |
| `current_balance` | Underkontoens kreditsaldo på det tidspunkt, hvor webhooket blev sendt. |
| `threshold` | Den saldotærskel, der udløste genopfyldningen. |
| `price_per_credit_cents` | Din konfigurerede pris pr. kredit i øre. |
| `price_per_credit_currency` | Valutaen for prisen (f.eks. `usd`). |
| `total_amount_cents` | Det samlede beløb, der skal opkræves, i øre (`credits_requested` × `price_per_credit_cents`). |
| `timestamp` | Hvornår webhooket blev sendt (ISO 8601-format). |
| `idempotency_key` | En unik nøgle for denne specifikke genopfyldningsanmodning. Brug denne til at forhindre, at kreditter tildeles to gange, hvis din server modtager det samme webhook mere end én gang. |

### Vigtige bemærkninger

- **5-minutters nedkøling** — Efter et genopfyldningsforsøg for en underkonto sender platformen ikke et nyt webhook for den underkonto i mindst 5 minutter, selvom deres saldo falder yderligere. Dette forhindrer dobbelte opkrævninger under behandling.
- **Brug idempotens-nøglen** — Tjek altid `idempotency_key`, før du tildeler kreditter. Hvis din server gik ned efter at have tildelt kreditter, men før den svarede, kan platformen sende webhooket igen ved næste kreditfald.
- **Fejl er sikre og selvhelbredende** — Hvis din webhook-URL ikke kan nås eller returnerer en fejl, tildeles der ingen kreditter. Platformen bliver ved med at sende webhooket igen (én gang pr. 5-minutters nedkøling), så længe underkontoen forbliver under tærsklen — det kræver **ikke**, at saldoen først skal stige over tærsklen og falde igen. En enkelt mistet opkrævning kan ikke længere efterlade en underkonto permanent uden genopfyldning.
- **Sæt dit genopfyldningsbeløb til eller over tærsklen** — Dit konfigurerede genopfyldningsbeløb skal være større end eller lig med tærsklen for automatisk genopfyldning, så én genopfyldning altid bringer saldoen over tærsklen. (Hvis du nogensinde har brug for at rette en underkonto, der er drevet langt under tærsklen, fylder platformen den automatisk op med hele underskuddet — se `credits_requested` ovenfor.)

***

## 2. Grant Credits API

Når din server har behandlet betalingen, skal du kalde dette endpoint for at tilføje kreditterne til underkontoen.

### Anmodning

```http
POST https://api.dmchamp.com/v1/subaccounts/credits
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "email": "customer@example.com",
  "amount": 500,
  "description": "Auto-recharge via webhook"
}
```

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `email` | Ja | Underkontoens e-mailadresse (skal matche en eksisterende underkonto under dit agency). |
| `amount` | Ja | Antallet af kreditter, der skal tildeles. |
| `description` | Nej | En note, der beskriver, hvorfor kreditter blev tilføjet (vises i kredittransaktionshistorikken). |

**Godkendelse:** Send din API-nøgle i en anmodningsheader — enten `X-API-Key: YOUR_API_KEY` eller `Authorization: Bearer YOUR_API_KEY`. Headere er den anbefalede metode, da en nøgle i URL'en ender i browserhistorik, proxylogfiler og serveradgangslogfiler. `?apiKey=YOUR_API_KEY`-forespørgselsparameteren og et `apiKey`-felt i JSON-brødteksten fungerer også stadig, så ældre integrationer fortsætter med at køre uændret.

### Svar

```json
{
  "success": true,
  "sub_account_id": "abc123xyz",
  "credits_added": 500,
  "new_balance": 542
}
```

::: tip
**Tip:** Gem `idempotency_key` fra webhook-nyttelasten og tjek den, før du kalder dette slutpunkt. Dette forhindrer, at du ved et uheld tildeler kreditter to gange, hvis din server modtager den samme webhook mere end én gang.
:::


### Hvad sker der med tildelte kreditter ved den månedlige nulstilling

Dette afhænger af, hvordan underkontoen er konfigureret:

- **Videresalg** (underkontoen betaler dig for kreditter): kreditter, du tildeler her, registreres som købte kreditter og **overføres** hver måned. Det, som underkontoen ikke har brugt, bliver stående på saldoen.
- **Tildeling** (du giver underkontoen en månedlig kvote): saldoen genopfyldes til den månedlige kvote på nulstillingsdatoen, så alt ubrugt beløb overføres ikke. Dette er tilsigtet – kvoten tildeles på ny hver måned.

Hvis du ønsker, at en underkontos resterende saldo skal lægges oven i dens nye månedlige kvote i stedet for at erstatte den, skal du aktivere kreditoverførsel:

```
PUT https://api.dmchamp.com/v1/subaccounts/{subAccountUid}/limits
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "usageLimits": { "roll_over_to_next_month": true }
}
```

Hvis underkontoen – eller den plan, den er tilknyttet – også har en grænse for overførsel eller en udløbsdato, er nulstillingen det tidspunkt, hvor disse træder i kraft: den overførte kvote beskæres til de måneder, du tillader, og kvote, der ikke er brugt i længere tid end udløbsvinduet, slettes, før den nye kvote tilføjes. Kreditter tildelt via dette endpoint, automatisk genopladning og kundens egne top-ups er engangskreditter og bliver aldrig beskåret. Se [Begrænsning af hvad der overføres](sub-accounts.md#capping-what-rolls-over).

***

## End-to-end eksempel

En typisk håndtering ser således ud:

```pseudo
on POST /your-recharge-webhook:
  payload = request.body

  if seen(payload.idempotency_key):
    return 200  // already processed, ack and exit

  charge_result = your_payment_provider.charge(
    email = payload.sub_account_email,
    amount_cents = payload.total_amount_cents,
    currency = payload.price_per_credit_currency,
  )

  if not charge_result.ok:
    return 500  // platform will retry on next balance drop

  api.post("/v1/subaccounts/credits", {
    email = payload.sub_account_email,
    amount = payload.credits_requested,
    description = "Auto-recharge via " + your_provider_name,
  })

  mark_seen(payload.idempotency_key)
  return 200
```
