Verkkokurssikassa
Perusteet

Oston jälkeiset toiminnot

Oston jälkeiset toiminnot suoritetaan automaattisesti, kun maksu on vahvistettu. Niillä voidaan esimerkiksi lähettää kuitti, aktivoida kurssi Kajabissa tai Systeme.io:ssa, lähettää webhook ulkoiseen järjestelmään tai lähettää asiakkaalle muokattava sähköposti.

Missä toimintoja voi määrittää?

Toiminnot määritellään kolmella tasolla:

  • Tuote — toiminnot suoritetaan aina, kun kyseinen tuote on mukana tilauksessa
  • Tarjous — toiminnot suoritetaan, kun tilaus tehdään tämän tarjouksen kautta
  • Kassa — toiminnot suoritetaan kaikilla kassan tilauksilla

Samaan tilaukseen voi kohdistua toimintoja kaikilta kolmelta tasolta yhtä aikaa.

Toimintotyypit

Lähetä kuitti sähköpostiin

Lähettää asiakkaalle tilausvahvistuksen sähköpostitse. Ei vaadi lisäasetuksia — kuitti generoidaan automaattisesti tilauksen tietojen perusteella.

Saatavilla: kassa-tasolla.

Kuitti lähetetään vain, jos tämä toiminto on aktiivinen kassan oston jälkeisissä toiminnoissa. Toiminto lisätään automaattisesti uuden kassan luomisen yhteydessä, mutta se ei ole oletuksena päällä — muista aktivoida se kassan asetuksista.

Aktivoi Kajabi Offer

Aktivoi Kajabi-tarjouksen asiakkaalle lähettämällä asiakkaan tiedot tarjouksen webhook-osoitteeseen.

Saatavilla: tuote-, tarjous- ja kassa-tasolla.

AsetusKuvaus
NimiSisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa.
Webhook-osoiteKajabi-integraation webhook-URL, joka löytyy Kajabi-hallintapaneelista.
Lähetä tarjouksen vahvistussähköpostiJos aktiivinen, Kajabi lähettää asiakkaalle sähköpostiviestin tarjouksen aktivoimisesta.
Piilota sukunimiJos aktiivinen, Kajabin lähetetään sukunimen sijaan vain sen ensimmäinen kirjain. Hyödyllinen, jos asiakkaat kommentoivat Kajabin yhteisössä eivätkä välttämättä tiedosta, että heidän koko nimensä näkyy muille.

Kajabi täytyy olla otettuna käyttöön kohdassa Asetukset → Integraatiot → Kurssialustat ennen kuin tämä toiminto on käytettävissä.

Katso tarkemmat ohjeet: Kajabi-integraatio

Lisää Systeme.io-contact ja tagit

Lisää asiakkaan Systeme.io-contactiksi ja lisää yhden tai useamman tagin, jotka käynnistävät Systeme.io-automaatiot kurssin aktivoimiseksi.

Saatavilla: tuote-, tarjous- ja kassa-tasolla.

AsetusKuvaus
NimiSisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa.
TagitOston jälkeen asiakkaan contactille lisättävät Systeme.io-tagit. Kukin tagi voi käynnistää erillisen automaation.

Systeme.io täytyy olla otettuna käyttöön API-avaimella kohdassa Asetukset → Integraatiot → Kurssialustat ennen kuin tämä toiminto on käytettävissä.

Katso tarkemmat ohjeet: Systeme.io-integraatio

Webhook

Lähettää HTTP POST -pyynnön valitsemaasi osoitteeseen tilauksen tiedoilla. Sopii esimerkiksi CRM-järjestelmiin, automaatiotyökaluihin tai ulkoiseen taustajärjestelmään.

Saatavilla: tuote-, tarjous- ja kassa-tasolla.

AsetusKuvaus
NimiSisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa.
URLOsoite, johon webhook-pyyntö lähetetään.
SalaisuusHMAC-allekirjoitusavain, jolla voit varmistaa pyynnön aitouden vastaanottavassa päässä. Generoituu automaattisesti.

Milloin pyyntö lähetetään

Pyyntö lähetetään, kun tilauksen maksu on vahvistettu. Jokainen määritetty webhook-toiminto tuottaa yhden pyynnön tilausta kohden. Ylläpitäjä voi lisäksi suorittaa toiminnon uudelleen tilausnäkymästä, jolloin pyyntö lähetetään samalle tilaukselle uudelleen.

Pyyntö

OminaisuusArvo
MetodiPOST
Content-Typeapplication/json
SignaturePyynnön HMAC-SHA256-allekirjoitus pienaakkosellisena hexinä
X-Webhook-TimestampAllekirjoitushetki millisekunteina Unix-ajan alusta

Rungossa on yksi JSON-objekti, joka kuvaa tilauksen.

Sisältö

{
   "id": "clw3x8k7t0001s0mn2f9qz1ab",
   "status": "PAID",
   "locale": "fi",
   "totalPrice": "59.80",
   "currency": "EUR",
   "taxRegime": "VAT_CHARGED",
   "updatedAt": "2026-02-17T09:41:12.883Z",
   "billingInformation": {
      "firstName": "Matti",
      "lastName": "Meikäläinen",
      "email": "matti@example.com",
      "country": "FI",
      "region": null
   },
   "products": [
      {
         "displayName": "Verkkokurssi: Kirjanpidon perusteet",
         "description": "Kuusi moduulia ja ladattava työkirja.",
         "imageUrl": "https://cdn.example.com/products/kirjanpito.jpg",
         "originalPrice": "49.90",
         "discountedPrice": "39.90",
         "price": "39.90",
         "vatRate": "0.255",
         "vatAmount": "8.10",
         "currency": "EUR",
         "productType": "ONLINE_COURSE"
      },
      {
         "displayName": "E-kirja: Kirjanpidon muistilistat",
         "description": null,
         "imageUrl": null,
         "originalPrice": "19.90",
         "discountedPrice": null,
         "price": "19.90",
         "vatRate": "0.1",
         "vatAmount": "1.81",
         "currency": "EUR",
         "productType": "E_BOOK"
      }
   ],
   "payments": [
      {
         "stamp": "clw3x8k7t0002s0mn7d4x",
         "transactionId": "4b8f21ce-6d0a-4c19-9a52-1f0e7d3c8b44",
         "provider": "PAYTRAIL",
         "method": "osuuspankki",
         "status": "SUCCESS",
         "processedAt": "2026-02-17T09:41:11.204Z"
      }
   ]
}

Sisällössä käytettävät käytännöt:

  • Kaikki alla luetellut avaimet ovat aina mukana. Nullable-kentät sisältävät arvon null, kun arvoa ei tiedetä.
  • Rahamäärät ja alv-kannat ovat JSON-merkkijonoja, eivät JSON-lukuja.
  • Rahamäärissä on aina kaksi desimaalia, esimerkiksi "39.90" ja "25.00".
  • Hinnat sisältävät arvonlisäveron. vatAmount on price-kentän sisältämä alv-osuus.
  • vatRate on kerroin, ei prosenttiluku. "0.255" tarkoittaa 25,5 %.
  • Aikaleimat ovat ISO 8601 -merkkijonoja UTC-ajassa.
Tilaus
KenttäTyyppiKuvaus
idstringTilauksen tunniste. Pysyy samana myös uudelleensuorituksissa, joten voit käyttää sitä käsittelijäsi idempotenssiavaimena.
statusstringTilauksen tila lähetyshetkellä. Automaattisesti lähetetyissä pyynnöissä PAID. Uudelleensuoritus välittää tilauksen sen hetkisen tilan, joka voi olla myös COMPLETED, PARTIALLY_REFUNDED tai REFUNDED.
localestringTilauksen kieli ISO 639-1 -koodina, esim. fi.
totalPricestring (decimal)Tilauksen maksettu kokonaissumma alv sisältäen.
currencystringJokin arvoista EUR, SEK, USD, PLN, GBP.
taxRegimestringMiten arvonlisävero on käsitelty tilauksella. Jokin arvoista VAT_CHARGED, VAT_REVERSE_CHARGE, VAT_OUT_OF_SCOPE, VAT_NOT_REGISTERED.
updatedAtstring (ISO 8601)Milloin tilausta on viimeksi päivitetty.
billingInformationobjectAsiakkaan antamat laskutustiedot.
productsarrayYksi rivi ostettua tuoteriviä kohden, lisämyynnit mukaan lukien.
paymentsarrayTilaukselle kirjatut onnistuneet maksut.
billingInformation
KenttäTyyppiKuvaus
firstNamestring | nullAsiakkaan etunimi.
lastNamestring | nullAsiakkaan sukunimi.
emailstringAsiakkaan sähköpostiosoite. Kysytään aina kassalla.
countrystringLaskutusmaa ISO 3166-1 alpha-2 -koodina, esim. FI. Kysytään aina kassalla.
regionstring | nullLaskutusosoitteen alue. Kerätään vain maissa, joissa se vaaditaan.
products[]

Jokainen rivi on tilaushetken mukainen tilannekuva tuotteesta tilauksen kielellä.

KenttäTyyppiKuvaus
displayNamestringAsiakkaalle näytettävä tuotteen nimi.
descriptionstring | nullKassalla näytettävä tuotekuvaus.
imageUrlstring | nullTuotekuvan absoluuttinen URL-osoite.
originalPricestring (decimal)Hinta ennen alennuksia, alv sisältäen.
discountedPricestring (decimal) | nullHinta alennuksen jälkeen, alv sisältäen. Null, kun riville ei ole kohdistunut alennusta.
pricestring (decimal)Riviltä tosiasiallisesti veloitettu summa alv sisältäen. Käytä tätä arvoa omassa kirjanpidossasi.
vatRatestring (decimal)Alv-kanta desimaalikertoimena, esim. 0.255.
vatAmountstring (decimal)Kentän price sisältämä alv-osuus.
currencystringJokin arvoista EUR, SEK, USD, PLN, GBP.
productTypestringJokin arvoista ONLINE_COURSE, DIGITAL_CONTENT, E_BOOK, MEMBERSHIP, EVENT_TICKET, SERVICE.
payments[]

Sisältää tilauksen onnistuneet maksut uusin ensin. Jos tilaus ei vaatinut maksua, esimerkiksi kun alennuskoodi kattoi koko hinnan, taulukko on tyhjä.

KenttäTyyppiKuvaus
stampstringVerkkokurssikassan maksutapahtumalle generoima tunniste. Uniikki kaikkien maksujen kesken.
transactionIdstring | nullMaksunvälittäjän antama tunniste. Sen avulla löydät maksun välittäjän omasta raportoinnista.
providerstringJokin arvoista PAYTRAIL, EPASSI, SMARTUM, EDENRED, STRIPE.
methodstring | nullMaksunvälittäjän ilmoittama maksutapa, esim. osuuspankki. Null, jos välittäjä ei ilmoita maksutapaa.
statusstringSisällössä aina SUCCESS.
processedAtstring (ISO 8601) | nullMilloin maksunvälittäjä vahvisti maksun.

Allekirjoituksen varmistaminen

Jokaisessa pyynnössä on Signature-otsake. Laske sen arvo uudelleen omassa päässäsi ja vertaa otsakkeen arvoon:

  1. Lue pyynnön runko (raw body) raakana tekstinä ennen JSON-jäsennystä.
  2. Muodosta allekirjoitettava merkkijono: X-Webhook-Timestamp-otsakkeen arvo, piste ja sen perään lukemasi runko. Esimerkiksi 1771320072883.{"id":"clw3x8k7t0001s0mn2f9qz1ab",...}.
  3. Laske merkkijonosta HMAC-SHA256 käyttäen toiminnon salaisuutta avaimena ja esitä tulos pienaakkosellisena hexinä.
  4. Vertaa tulosta Signature-otsakkeeseen vakioaikaisella vertailulla.

Allekirjoitus kattaa rungon tavu tavulta. Sovelluskehykset, jotka jäsentävät JSONin ja serialisoivat sen uudelleen, tuottavat eri tavut ja vertailu epäonnistuu. Ota siis runko talteen ennen jäsennystä.

import { createHmac, timingSafeEqual } from "node:crypto";

const secret = process.env.WEBHOOK_SECRET;
if (!secret) throw new Error("WEBHOOK_SECRET is not set");

export async function POST(request: Request) {
   const rawBody = await request.text();
   const timestamp = request.headers.get("X-Webhook-Timestamp");
   const signature = request.headers.get("Signature");

   if (!timestamp || !signature) {
      return new Response("Missing signature headers", { status: 400 });
   }

   const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");

   const received = Buffer.from(signature, "hex");
   const computed = Buffer.from(expected, "hex");

   if (received.length !== computed.length || !timingSafeEqual(received, computed)) {
      return new Response("Invalid signature", { status: 401 });
   }

   const order = JSON.parse(rawBody);
   // Handle the order here.

   return new Response(null, { status: 200 });
}

Mitä päätepisteesi kannattaa tehdä

  • Varmista allekirjoitus ennen kuin luotat sisältöön.
  • Vastaa 2xx-statuksella, kun olet käsitellyt pyynnön. Voit tehdä työn suoraan päätepisteessä ja vastata vasta sen onnistuttua, jolloin myös oman järjestelmäsi virheet jäävät näkyviin: muut statukset merkitsevät toiminnon tilaan Epäonnistui tilausnäkymässä, josta ylläpitäjä voi suorittaa sen uudelleen.
  • Vastaa 30 sekunnin kuluessa. Tätä pidempään kestävä pyyntö perutaan ja toiminto merkitään tilaan Epäonnistui, joten siirrä ajassa valmistumaton työ omaan taustaprosessiisi ja vastaa ennen rajaa.
  • Käytä id-kenttää idempotenssiavaimena. Sama tilaus voi tulla päätepisteeseesi useammin kuin kerran, esimerkiksi kun ylläpitäjä suorittaa toiminnon uudelleen.

Lähetä sähköposti

Lähetä asiakkaalle muokattava sähköposti oston jälkeen.

Saatavilla: tuote-, tarjous- ja kassa-tasolla.

Aihe ja sisältö määritellään erikseen jokaiselle kassan tukemalle kielelle.

AsetusKuvaus
NimiSisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa.
AiheSähköpostin aiherivi.
Sähköpostin sisältöSähköpostin runko, joka rakentuu kuva-, rich-text- ja banneri-lohkoista.

Jos tilauksen kielelle ei ole määritetty sisältöä, sähköposti lähetetään kassan ensimmäisellä tuetulla kielellä. Jos sillekään ei ole sisältöä, sähköposti ohitetaan ja toiminto merkitään silti Onnistuneeksi — määritä aihe ja sisältö kaikille tuetuille kielille välttääksesi hiljaiset ohitukset.

Toimintojen tila

Jokaisen toiminnon suoritustila näkyy tilauksen tiedoissa hallintapaneelissa. Mahdolliset tilat:

  • Odottaa — toimintoa ei ole vielä suoritettu
  • Käynnissä — toiminto on suorituksessa
  • Onnistunut — toiminto suoritettiin onnistuneesti
  • Epäonnistunut — toiminnon suoritus epäonnistui

Yksittäisten toimintojen suorittaminen uudelleen

Jokaisen toiminnon vieressä tilauspaneelissa on uudelleensuoritus-painike. Sen avulla voit käynnistää yksittäisen toiminnon uudelleen koskematta muihin — käytännöllistä, kun asiakas tarvitsee uuden kuitin tai kun yksittäinen integraatiokutsu epäonnistui ja haluat yrittää sitä uudelleen asetusten korjauksen jälkeen.

Avaa tilaus, laajenna Oston jälkeiset toiminnot -osio ja klikkaa uudelleensuoritus-kuvaketta haluamasi toiminnon kohdalla. Dialogi näyttää yhteenvedon tulevasta toimenpiteestä.

Käytä uusinta mallia

Dialogissa on Käytä uusinta mallia -valinta. Kun se on käytössä, toiminnon tallennetut asetukset korvataan nykyisellä mallilla ennen uudelleensuoritusta. Tämä on oikea valinta, kun olet korjannut asetuksen (esim. Kajabi-tarjouksen URL:n tai webhook-päätepisteen) ja haluat uudelleensuorituksen käyttävän uutta arvoa.

Kun valinta ei ole käytössä, toiminto suoritetaan alkuperäisen oston aikaisilla asetuksilla.

Asetusten korvaaminen on pysyvää — aiempia arvoja ei voi palauttaa.

Onnistuneen toiminnon uudelleensuoritus

Voit suorittaa uudelleen myös onnistuneita toimintoja. Dialogi näyttää tällöin varoituksen, koska toiminnon sivuvaikutukset toistuvat — esimerkiksi asiakas saa toisen kuittisähköpostin tai webhook-päätepistettä kutsutaan uudelleen. Käytä tätä, kun asiakas pyytää kuittia uudelleen tai kun alempana oleva järjestelmä menetti alkuperäisen toimituksen.

Pakota uudelleensuoritus

Jos toiminto on jumissa Käynnissä-tilassa — tyypillisesti edellisen suorituksen kaaduttua kesken eikä lopputulos ole tallentunut — uudelleensuorituspainike vaihtuu Pakota uudelleensuoritus -toiminnoksi vahvemmalla varoituksella. Pakottaminen ohittaa "käynnissä"-suojauksen. Käytä vain, kun olet varma ettei aiempi suoritus ole enää aktiivinen, sillä kaksi samanaikaista suoritusta voi tuottaa kaksinkertaisia sivuvaikutuksia.

Toiminnon uudelleensuoritus ei muuta tilauksen tilaa itsessään, mutta jos uudelleensuoritus oli viimeinen epäonnistunut osa, tilaus siirtyy automaattisesti Valmis-tilaan.

Sisällysluettelo