ZKRINET Docs
Byggblock

Stripe-betalningar

På Zkrinet: lägg till blocket under Byggblock i panelen — ett klick, din egen kod rörs inte och en ögonblicksbild tas först. Installationsstegen nedan gäller om du kör PocketBase själv, till exempel efter en export.

Engångsköp och prenumerationer via Stripe Checkout, Stripes kundportal och en webhook som håller betalningar och prenumerationer uppdaterade i din databas. Kortuppgifter rör aldrig din server: kunden betalar på Stripes egen sida.

Testat mot PocketBase 0.40.4.

Installation

  1. Kopiera pb_hooks/ och pb_migrations/ till din PocketBase och starta om.
  2. Nycklar — välj ett av två sätt (miljön vinner om båda finns):
    • I adminpanelen: skapa en rad i zk_stripe_installningar med hemlig_nyckel (sk_test_…/sk_live_… eller en begränsad rk_…) och webhook_hemlighet (whsec_…). Fälten är dolda och kan bara läsas av superusers.
    • Som miljövariabler: ZK_STRIPE_HEMLIG_NYCKEL och ZK_STRIPE_WEBHOOK_HEMLIGHET.
  3. Webhook i Stripe (Developers → Webhooks → Add endpoint):
    • Adress: https://din-app.se/api/zk/stripe/webhook
    • Händelser: checkout.session.completed, checkout.session.async_payment_succeeded, checkout.session.async_payment_failed, checkout.session.expired, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted
    • Kopiera Signing secret (whsec_…) till steg 2.
  4. Priser: skapa produkter och priser i Stripe, lägg sedan in varje pris du vill sälja i zk_stripe_priser (pris_id = price_…, namn, lage = engang eller prenumeration, aktiv). Bara priser i den listan kan köpas.
  5. Ligger din webbapp på en annan adress än PocketBase? Lägg dess adresser i tillatna_adresser (kommaseparerat, t.ex. https://min-app.se, https://www.min-app.se). Annars godtas bara appens egen adress (Inställningar → Application URL).

Användning från appen

// Starta köp (användaren måste vara inloggad)
const { url } = await pb.send("/api/zk/stripe/kassa", { method: "POST", body: {
  pris: "price_123",
  lyckad_url: "https://min-app.se/tack?session={CHECKOUT_SESSION_ID}",
  avbruten_url: "https://min-app.se/priser",
}})
location.href = url

// Har användaren en aktiv prenumeration? (användaren ser bara sina egna rader)
const aktiv = await pb.collection("zk_stripe_prenumerationer")
  .getFirstListItem('status = "active" || status = "trialing"').catch(() => null)

// Kundportal: byta kort, säga upp, se kvitton
const { url: portal } = await pb.send("/api/zk/stripe/portal", { method: "POST", body: { retur_url: "https://min-app.se/konto" } })

Prislistan kan läsas utan inloggning: pb.collection("zk_stripe_priser").getFullList() (bara aktiva priser syns).

Lita på webhooken, inte på lyckad_url. Att kunden landar på tack-sidan bevisar ingenting — en betalning är betald först när raden i zk_stripe_betalningar har status = "betald".

Swish & Klarna

Kassan är Stripe Checkout, så Swish och Klarna fungerar så fort de är påslagna hos Stripe. Inställningen betalmetoder i zk_stripe_installningar styr vad kassan skickar:

betalmetoderVad som händer
tomt (rekommenderat)Kassan skickar ingen lista. Stripe visar de metoder du slagit på i Dashboard och som passar köpet (valuta, belopp, engång/prenumeration, kundens land). Du kan slå av och på metoder i Stripe utan att röra appen.
card,swish,klarnaBara de här metoderna erbjuds (skickas som payment_method_types). Använd det bara om en viss kassa måste vara begränsad.

Varför tomt rekommenderas: Stripe rekommenderar själva dynamiska betalmetoder och döljer då automatiskt det som inte passar köpet (Stripe: integration options). Med en fast lista kan Dashboard inte lägga till något, och en metod som inte passar (t.ex. Swish på ett pris i euro) kan få Stripe att vägra hela kassan, i stället för att bara dölja metoden. Det sista har vi inte kunnat mäta mot riktiga Stripe, se Begränsningar.

Det här gäller enligt Stripes dokumentation (läst 7/11 2026):

SwishKlarna
ValutaBara SEK, kunder i Sverige. 3–150 000 kr.SEK, EUR, NOK, DKK, GBP, USD med flera. Beloppsgränser beror på land och betalsätt.
PrenumerationNej. "Checkout doesn't support Swish in subscription mode."Ja (i Sverige: betala direkt och betala senare).
Besked om betalningenDirekt ("immediate notification")Stripe skriver bara "customer-initiated". Om beskedet kan dröja står inte på sidan, så pluginet räknar med att det kan göra det. Du får hela beloppet, och Klarna tar risken om kunden inte betalar.
TvisterFinns inte. Kunden måste kontakta dig.Ja
Källadocs.stripe.com/payments/swishdocs.stripe.com/payments/klarna

Swish i en prenumeration: finns swish i listan och priset är en prenumeration filtrerar kassan bort Swish innan Stripe anropas, svarar med borttagna_betalmetoder: ["swish"] och skriver en varning i PocketBase logg (Logs i adminpanelen). Blir listan tom (t.ex. bara swish) svarar kassan 400 med ett fel som förklarar varför, och Stripe anropas aldrig. Med tomt fält sköter Stripe själv det här.

Det här måste du göra i Stripe:

  1. Settings → Payments → Payment methods: slå på Klarna och Swish.
  2. Swish kräver ett godkännande från Stripe. Dokumentationen har ett formulär för att begära tillgång, och vissa branscher är spärrade (t.ex. alkohol, pantbanker, juridiska tjänster, påfyllning av digitala plånböcker). Klarna har egna regler: inga B2B-köp (Klarna rules).
  3. Priserna måste vara i SEK om du vill att Swish ska synas.
  4. Webhooken måste ta emot checkout.session.async_payment_succeeded och checkout.session.async_payment_failed (de finns redan i listan under Installation).

Asynkrona betalningar: vissa metoder ger checkout.session.completed med payment_status = unpaid och utfallet kommer först senare. Raden i zk_stripe_betalningar går då vantar → betald (kvittot skickas först då) eller vantar → misslyckad. Ett utfall backar aldrig, inte heller om Stripe skickar händelserna i omvänd ordning. Enligt Stripe ger Swish direkt besked, så för Swish lär vantar vara ovanligt. För Klarna anger dokumentationen inte om beskedet kan dröja. Pluginet hanterar båda fallen, oavsett metod.

Samlingar

SamlingVadVem läser
zk_stripe_installningarNycklar (dolda), tillåtna adresser, kvittomejl på/av, betalmetoderBara superusers
zk_stripe_priserVad som får säljasAlla (bara aktiva)
zk_stripe_betalningarEn rad per kassa: skapad → betald / vantar / misslyckad / utgangenAnvändaren sina egna
zk_stripe_prenumerationerStripes status (active, past_due, canceled …), periodslut, om den avslutas vid periodslutAnvändaren sina egna
zk_stripe_handelserVarje mottagen händelse — skyddar mot dubbelhanteringBara superusers

Kvittomejl (valfritt)

Är Mejl-pluginet installerat och kvittomejl påslaget i inställningarna skickas mallen kvitto när en betalning blir betald (med namn, produkt, belopp, datum och referens). Stripe kan också skicka egna kvitton — slå inte på båda om du inte vill att kunden får två.

Säkerhet

Begränsningar

Testa

./bank.py stripe

24 tester mot en låtsas-Stripe (inget når internet): kassa, vitlistor, signatur (fel nyckel, gammal tid, saknat huvud), dubbletter, omkastad ordning, prenumerationens livscykel, behörighet, kundportal, kvittomejl, betalmetoder (dynamiska, fast lista, Swish bortfiltrerad i prenumeration med logg, ogiltiga värden) och asynkrona betalningar (vantar → betald/misslyckad, även omkastat). Säkerhetstesterna är verifierade med mutanter — varje skydd har brutits med flit och fångats.