Kernconcepten

Beloningen & Inwisselingen

Beloningen zijn wat je klanten met hun gespaarde loyaliteitspunten kunnen kopen. Besteedt een klant punten aan een beloning, dan legt Puntjes dat vast als een inwisseling, met een eigen bevestigingscode.


Beloningstypen

GET /rewards houdt de twee typen uit elkaar met type, en geeft van elke beloning ook payment_amount, product_reference, discount_type en discount_value. Zo kan de kassa vóór het inwisselen nagaan of het product van een korting op de rekening staat, en of er op de verkoop een percentage af mag of alleen een vast bedrag. De kassa krijgt alle velden hieronder in type_specific_data zodra ze de beloning inwisselt met POST /redemptions, en rekent de beloning daarmee af, niet met de lijst: de inwisseling bewaart de waarden van het moment zelf (zie de Verzilvering-endpoints).

Korting

Korting op een volgende aankoop, op de hele aankoop of op één product:

VeldBeschrijving
discount_valueHoeveel korting de klant krijgt. Bij fixed_amount een bedrag in eurocenten (500 is EUR 5,00; het minimum is 1, EUR 0,01). Bij percentage een geheel getal van 1 tot 100
discount_typefixed_amount (bv. EUR 5,00 korting) of percentage (bv. 10% korting)
product_referenceHet artikelnummer (external_id) van het ene product waarop de korting geldt. null als ze op de hele aankoop geldt

Een vast bedrag kan hoger uitvallen dan de prijs van het product. Hoe je dat begrenst, bepaal je in je kassa.

Gratis product

Een gratis product of dienst:

VeldBeschrijving
product_referenceVerwijzing naar het gratis product (die bepaal je zelf)
payment_amountWat de kassa bovenop de punten aanrekent, in eurocenten (200 is EUR 2,00). 0 betekent alleen punten

Dat bedrag betaalt de klant aan je kassa; Puntjes houdt die betaling niet bij. Lees het bedrag uit de inwisseling, niet uit GET /rewards: de inwisseling bewaart het bedrag zoals het was bij het inwisselen, ook als de beloning daarna verandert.


Beloningseigenschappen

Wat GET /rewards per beloning teruggeeft:

VeldTypeBeschrijving
idintegerUniek belonings-ID
namestringNaam van de beloning
descriptionstring|nullOmschrijving van de beloning, of null als die er niet is
typestringdiscount of free_product
point_costintegerPunten die het inwisselen kost
payment_amountintegerWat de kassa bovenop de punten aanrekent, in eurocenten. 0 betekent alleen punten, en bij een korting is het altijd 0
image_urlstring|nullAbsolute URL naar de afbeelding van de beloning, of null als die er geen heeft
remaining_stockintegerInwisselingen die nog over zijn. 0 bij een beloning zonder limiet: kijk eerst naar is_unlimited
total_stockinteger|nullAantal inwisselingen waarmee de beloning begon. null betekent onbeperkt
available_fromstring|nullVanaf wanneer de beloning te krijgen is, een datum als YYYY-MM-DD. null betekent zonder begindatum
available_untilstring|nullTot wanneer de beloning te krijgen is, een datum als YYYY-MM-DD. null betekent dat de beloning niet verloopt
branchesarray|nullDe vestigingen waar klanten deze beloning kunnen inwisselen, elk { "external_id", "name", "type" }. null betekent elke vestiging
product_referencestring|nullHet artikelnummer van het product waar de beloning over gaat. Bij een discount is dat het ene product waarvan de korting afgaat; null betekent de hele aankoop. Bij een free_product is het het product dat je meegeeft; null betekent dat de winkel geen artikelnummer opgaf. Kijk eerst naar type
is_unlimitedbooleantrue als de beloning geen voorraadlimiet heeft. total_stock is dan null en remaining_stock is 0
discount_typestring|nullpercentage of fixed_amount bij een discount, dezelfde waarden als type_specific_data.discount_type. null bij een free_product
discount_valueinteger|nullHoe groot de korting is: een percentage als geheel getal (10 is 10%), een vast bedrag in eurocenten (500 is € 5,00). null bij een free_product
max_redemptions_per_customerinteger|nullHoe vaak één klant deze beloning kan inwisselen. null betekent geen grens. Een inwisseling die de winkel annuleerde, telt niet mee
customer_redemptionsinteger|nullHoe vaak de klant van identifier deze beloning inwisselde, zonder de geannuleerde. null als je geen identifier meestuurde of niemand erbij hoort

Beschikbaarheid

Met available_from en available_until beperk je een beloning tot een periode. GET /rewards filtert alleen op status: ook beloningen buiten hun periode en uitverkochte beloningen staan erin, en POST /redemptions weigert die met REWARD_UNAVAILABLE of OUT_OF_STOCK. Vergelijk de datums en kijk naar remaining_stock en is_unlimited voor je een beloning aanbiedt.

Een beloning kan ook vastleggen hoe vaak één klant ze krijgt, met max_redemptions_per_customer. Die grens telt voor altijd. Een geannuleerde inwisseling geeft de klant die kans terug; een code die ongebruikt verliep, telt wel mee, want de klant betaalde er de punten al voor. Vraag GET /rewards?identifier=CARD-001 op en elke beloning krijgt customer_redemptions mee: hoe vaak die klant ze al inwisselde. Bied de beloning alleen aan zolang dat getal onder de grens ligt. Staat het op de grens, dan weigert POST /redemptions met REDEMPTION_LIMIT_REACHED (422).


Inwisselproces

1. De klant kiest een beloning

Het kassasysteem toont de beloningen die te krijgen zijn en de klant kiest er een. Haal de actieve beloningen op met het beloningen-endpoint.

2. Dien een inwisseling in

POST /api/v1/redemptions
{
    "identifier": "CARD-001",
    "reward_id": 5,
    "idempotency_key": "redeem-2024-0042"
}

De API controleert:

  • of het walletsaldo van de klant volstaat
  • of de beloning actief en beschikbaar is
  • of er nog voorraad is
  • of de beloning de vestiging toestaat waar je de inwisseling boekt
  • of de klant de beloning nog niet zo vaak inwisselde als de winkel toestaat

Dankzij de idempotency_key kun je de aanroep gerust opnieuw sturen: dezelfde sleutel levert de oorspronkelijke inwisseling op, en schrijft geen punten een tweede keer af.

Waar de inwisseling plaatsvond

branch is je eigen sleutel voor de winkel: de external_id die je die vestiging gaf. De volgorde is dezelfde als bij een transactie: de sleutel in de payload wint, anders de standaardvestiging van de API-credential, anders geen vestiging. Een sleutel die bij geen van je vestigingen hoort, wordt geweigerd met BRANCH_NOT_FOUND (422). Zo gaat een typfout niet door voor een inwisseling zonder vestiging.

Een beloning die vestigingen noemt, kunnen klanten alleen daar inwisselen. Stuur je geen vestiging, of een vestiging die er niet bij staat, dan wordt de aanroep geweigerd met BRANCH_REQUIRED (422). Eén code voor allebei, want allebei hebben dezelfde oplossing: noem een vestiging die de beloning toestaat.

De vestiging wordt één keer vastgelegd. Stuur je dezelfde idempotency_key opnieuw, dan krijg je de oorspronkelijke inwisseling terug, met de vestiging van de eerste aanroep, ook als de herhaling een andere noemt.

3. De bevestigingscode komt terug

Lukt de aanroep, dan gaan de punten van de wallet van de klant af en krijg je een inwisseling terug met een bevestigingscode:

{
    "data": {
        "redemption_id": 12,
        "confirmation_code": "PNTJ-K4MP7QRS",
        "reward": {
            "name": "Gratis koffie",
            "type": "free_product"
        },
        "points_deducted": 500,
        "remaining_balance": 1000,
        "redeemed_at": "2024-03-15T12:30:00+00:00",
        "expires_at": "2024-03-15T14:30:00+00:00"
    }
}

4. Verifieer de inwisseling

Toont de klant de bevestigingscode, dan verifieer je die:

POST /api/v1/redemptions/{code}/verify

Daarmee gaat de inwisseling van valid naar used. Een code verifieer je één keer: een tweede verificatie antwoordt CODE_ALREADY_USED.


Inwisselstatus

StatusBeschrijving
validCode uitgegeven, de klant heeft de beloning nog niet opgehaald
usedDe klant kreeg de beloning: de code is geverifieerd, of de zaak gaf ze meteen
expiredDe code liep af voordat iemand ze verifieerde
cancelledDe zaak annuleerde de inwisseling: de punten en de voorraad kwamen terug

Een beloning die de zaak in het beheerportaal meteen aan de klant geeft, is meteen used. De klant heeft ze al in handen, dus de code is gesloten en geen kassa kan ze een tweede keer verifiëren.

Een beheerder van de zaak kan een inwisseling met status valid of used annuleren op de klantpagina. De klant krijgt de punten terug die de inwisseling kostte, en de beloning gaat terug in de voorraad. Een kassa die een geannuleerde code verifieert, krijgt CODE_CANCELLED. Een verlopen code kun je niet annuleren.


Hoe lang een bevestigingscode geldig is

Stelt de handelaar in het portaal in hoeveel uur de bevestigingscode van een beloning geldig blijft, dan verloopt de code na die tijd. Daarna kun je hem niet meer verifiëren. Het veld expires_at in de response van de inwisseling zegt wanneer dat moment valt.


Inwisseling opzoeken

Je zoekt een inwisseling op met de bevestigingscode:

GET /api/v1/redemptions/{code}

Handig voor een kassasysteem dat de inwisseling eerst wil tonen en pas daarna verifieert.

Zie de Verzilvering-endpoints voor de volledige API-referentie.

Volgende
Campagnes