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.
| Asetus | Kuvaus |
|---|---|
| Nimi | Sisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa. |
| Webhook-osoite | Kajabi-integraation webhook-URL, joka löytyy Kajabi-hallintapaneelista. |
| Lähetä tarjouksen vahvistussähköposti | Jos aktiivinen, Kajabi lähettää asiakkaalle sähköpostiviestin tarjouksen aktivoimisesta. |
| Piilota sukunimi | Jos 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.
| Asetus | Kuvaus |
|---|---|
| Nimi | Sisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa. |
| Tagit | Oston 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.
| Asetus | Kuvaus |
|---|---|
| Nimi | Sisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa. |
| URL | Osoite, johon webhook-pyyntö lähetetään. |
| Salaisuus | HMAC-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ö
| Ominaisuus | Arvo |
|---|---|
| Metodi | POST |
| Content-Type | application/json |
| Signature | Pyynnön HMAC-SHA256-allekirjoitus pienaakkosellisena hexinä |
| X-Webhook-Timestamp | Allekirjoitushetki 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.
vatAmountonprice-kentän sisältämä alv-osuus. vatRateon kerroin, ei prosenttiluku."0.255"tarkoittaa 25,5 %.- Aikaleimat ovat ISO 8601 -merkkijonoja UTC-ajassa.
Tilaus
| Kenttä | Tyyppi | Kuvaus |
|---|---|---|
| id | string | Tilauksen tunniste. Pysyy samana myös uudelleensuorituksissa, joten voit käyttää sitä käsittelijäsi idempotenssiavaimena. |
| status | string | Tilauksen 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. |
| locale | string | Tilauksen kieli ISO 639-1 -koodina, esim. fi. |
| totalPrice | string (decimal) | Tilauksen maksettu kokonaissumma alv sisältäen. |
| currency | string | Jokin arvoista EUR, SEK, USD, PLN, GBP. |
| taxRegime | string | Miten arvonlisävero on käsitelty tilauksella. Jokin arvoista VAT_CHARGED, VAT_REVERSE_CHARGE, VAT_OUT_OF_SCOPE, VAT_NOT_REGISTERED. |
| updatedAt | string (ISO 8601) | Milloin tilausta on viimeksi päivitetty. |
| billingInformation | object | Asiakkaan antamat laskutustiedot. |
| products | array | Yksi rivi ostettua tuoteriviä kohden, lisämyynnit mukaan lukien. |
| payments | array | Tilaukselle kirjatut onnistuneet maksut. |
billingInformation
| Kenttä | Tyyppi | Kuvaus |
|---|---|---|
| firstName | string | null | Asiakkaan etunimi. |
| lastName | string | null | Asiakkaan sukunimi. |
| string | Asiakkaan sähköpostiosoite. Kysytään aina kassalla. | |
| country | string | Laskutusmaa ISO 3166-1 alpha-2 -koodina, esim. FI. Kysytään aina kassalla. |
| region | string | null | Laskutusosoitteen alue. Kerätään vain maissa, joissa se vaaditaan. |
products[]
Jokainen rivi on tilaushetken mukainen tilannekuva tuotteesta tilauksen kielellä.
| Kenttä | Tyyppi | Kuvaus |
|---|---|---|
| displayName | string | Asiakkaalle näytettävä tuotteen nimi. |
| description | string | null | Kassalla näytettävä tuotekuvaus. |
| imageUrl | string | null | Tuotekuvan absoluuttinen URL-osoite. |
| originalPrice | string (decimal) | Hinta ennen alennuksia, alv sisältäen. |
| discountedPrice | string (decimal) | null | Hinta alennuksen jälkeen, alv sisältäen. Null, kun riville ei ole kohdistunut alennusta. |
| price | string (decimal) | Riviltä tosiasiallisesti veloitettu summa alv sisältäen. Käytä tätä arvoa omassa kirjanpidossasi. |
| vatRate | string (decimal) | Alv-kanta desimaalikertoimena, esim. 0.255. |
| vatAmount | string (decimal) | Kentän price sisältämä alv-osuus. |
| currency | string | Jokin arvoista EUR, SEK, USD, PLN, GBP. |
| productType | string | Jokin 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ä | Tyyppi | Kuvaus |
|---|---|---|
| stamp | string | Verkkokurssikassan maksutapahtumalle generoima tunniste. Uniikki kaikkien maksujen kesken. |
| transactionId | string | null | Maksunvälittäjän antama tunniste. Sen avulla löydät maksun välittäjän omasta raportoinnista. |
| provider | string | Jokin arvoista PAYTRAIL, EPASSI, SMARTUM, EDENRED, STRIPE. |
| method | string | null | Maksunvälittäjän ilmoittama maksutapa, esim. osuuspankki. Null, jos välittäjä ei ilmoita maksutapaa. |
| status | string | Sisällössä aina SUCCESS. |
| processedAt | string (ISO 8601) | null | Milloin 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:
- Lue pyynnön runko (raw body) raakana tekstinä ennen JSON-jäsennystä.
- Muodosta allekirjoitettava merkkijono:
X-Webhook-Timestamp-otsakkeen arvo, piste ja sen perään lukemasi runko. Esimerkiksi1771320072883.{"id":"clw3x8k7t0001s0mn2f9qz1ab",...}. - Laske merkkijonosta HMAC-SHA256 käyttäen toiminnon salaisuutta avaimena ja esitä tulos pienaakkosellisena hexinä.
- 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.
| Asetus | Kuvaus |
|---|---|
| Nimi | Sisäinen nimi toiminnolle. Näkyy vain hallintapaneelissa. |
| Aihe | Sä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.