Hostad kassa — på dina villkor.
Checkout är Octanys moderna kassa. Tre vägar in: en ren URL, en inbäddad varukorg eller ett JS-API. Alla tre landar på samma hostade sida på checkout.octany.com — Octany sköter varukorg, kund, betalsätt, SCA och tack-sidan.
subscription.created-webhooken för att aktivera kontot. Det är standardmönstret. Allt nedan är varianter på det.
§ 02.1 Den hostade URL:en #
Den enklaste integrationen. Bygg en URL på servern och länka eller redirecta dit. Ingen JavaScript krävs.
https://checkout.octany.com/checkout/{account-id}/product/{product-id} ?email=anna@example.com &referenceId=user_42 &successUrl=https://app.example.com/welcome
Octany skapar en checkout-session, lägger produkten i varukorgen och redirectar till /pay/checkout/{account}/{id} där SPA:n tar över. Användaren stannar kvar i Octany tills köpet är klart eller hen hoppar av.
https://checkout.octany.com. Använd den som standard om inte din kontakt på Octany har gett dig en dedikerad miljö.
§ 02.2 Query-parametrar #
Produkten är det enda som måste vara med. Resten är frivilliga förifyllningar eller metadata för spårning.
| Param | Syfte |
|---|---|
| successUrl | Dit Octany skickar användaren efter lyckad betalning. Måste vara HTTPS. |
| referenceId | Använd den här för att korrelera. Ditt eget ID för besökaren, medlemmen, givaren eller ordern. Hamnar på Subscription/Order och på varje webhook för den resursen. |
| referenceName | Mänskligt läsbar etikett som syns bredvid resursen i admin. |
| Förifyller e-postfältet. | |
| firstName, lastName | Förifyller namnfält. |
| companyName | Förifyller företagsfält. |
| role | En roll att tagga användaren med (för CRM-segmentering). |
| roles | Kommaseparerade roller, t.ex. roles=member,donor. |
| amount | Heltal i öre. Skriver över produktens pris för den här sessionen — för "namnge ditt pris"/påfyllningsflöden. |
| methods | Kommaseparerad allow-lista över betalsätt att visa, t.ex. methods=stripe,swish. Användbart när du vill ha ett Swish-först- eller endast-kort-flöde. |
| onlyMethod | Bakåtkompatibel singel-version av methods, t.ex. onlyMethod=swish. Använd methods i nya integrationer. |
| quantity | true/false. Sätt quantity=false för att gömma antal-kontroller och låsa antalet i kassan. |
| locked | Bakåtkompatibel låsflagga. locked=true låser antalet även om quantity=true är satt. Använd quantity=false i nya integrationer. |
| locale | sv, en, osv. Faller tillbaka på kontots språk. Kan ändras senare i UI:t. |
| utm_* | utm_source, utm_medium, utm_campaign, utm_content, utm_term — fångas in på varukorgen och hamnar på ordern. |
Minimalt exempel
<a href="https://checkout.octany.com/checkout/{account}/product/42?referenceId=user_99&email=anna@example.com&successUrl=https://app.example.com/welcome"> Prenumerera — 99 kr/mån </a>
Begränsa betalsätt eller lås antal
Vill du att den hostade kassan bara visar vissa betalsätt skickar du methods på URL:en. Octany matchar värdena mot de betalsätt som faktiskt är aktiva för produkten och kontot, och ignorerar resten.
https://checkout.octany.com/checkout/{account}/product/{product-id}?methods=stripe,swish&quantity=false
Använd quantity=false för att förhindra att antalet ändras i kassan. Äldre integrationer kan fortfarande skicka locked=true eller onlyMethod=swish; båda funkar fortfarande, men nya integrationer bör använda methods och quantity.
§ 02.3 Success → din app #
Lita inte på query-params på successUrl som beslutsunderlag. Octany skickar dit användaren när hen kvitterat betalningen, men det är webhooken som är sanningskällan (order.paid, subscription.created).
Så här gör du det robust:
- Användaren landar på din
successUrl. - Visa en "Tack — vi slutför ditt köp…"-sida.
- Vänta in rätt webhook i din backend (korrelera via
reference_id). - Aktivera sedan användaren och lås upp funktionen.
Reconciliation efter checkout: få in användaren i appen direkt
Webhooken är sanningskällan för "betalningen gick igenom", men den kan släpa några sekunder — och när din successUrl är en deep link tillbaka in i en desktop- eller mobilapp vill du att användaren ska komma in direkt, inte efter en webhook-rundtur. Det finns två varianter; den andra är den vi rekommenderar när latens eller leveransfördröjning är ett bekymmer:
- Polla din egen backend från success-sidan. Funkar, men du får en race condition mot webhooken — om din webhook-handler inte hunnit köra säger din databas fortfarande "ingen prenumeration" och användaren fastnar i spinnern.
- Polla Octany direkt från success-sidan med
GET /subscriptions?filter[reference_id]=<din_user_id>. Hittar prenumerationen så snart den finns, oavsett när webhooken kommer fram. Synka in i din lokala cache och redirecta sedan (webb → nästa sida, native-app → ett custom URL scheme sommyapp://billing/success).
// Success-sida — polla Octany var 2,5 sekund tills prenumerationen är aktiv app.get('/billing/pending/status', async (req, res) => { const subs = await octany.subscriptions.find({ reference_id: req.user.id }) const sub = subs[0] if (sub && ['active', 'trialing'].includes(sub.status)) { await syncSubscriptionLocally(req.user, sub) // samma syncer som webhooken anropar return res.json({ ready: true, redirect: 'myapp://billing/success' }) } res.json({ ready: false }) })
Webhook-handlern bör köra samma syncer så att båda vägarna landar i identiskt lokalt tillstånd — den som hinner först vinner, den andra blir en no-op. Vi har testat mönstret genom att stoppa webhook-leveranserna i tio minuter; användaren kom ändå in i appen inom sekunder från att den pollade prenumerationen dök upp.
§ 02.4 Inbäddningsbar varukorg #
För varukorgar med flera produkter på en innehållssajt. Levererar ett loader-skript som injicerar två iframes — en flytande "fab"-knapp och en utfällbar varukorg — och exponerar JS-API:et window.Octany.
Installera
<!-- 1. Konfigurera --> <script> window.OctanyConfig = { id: '{account-id}', locale: 'sv', // frivilligt; faller tillbaka på kontots språk methods: ['stripe', 'swish'], quantity: false, } </script> <!-- 2. Ladda loadern --> <script src="https://embed.octany.com/loader.js" async></script>
Loadern hämtar internt aktuell version av cart-appen (från app.octany.com/pay/embed-version) och drar matchande app.js/app.css från embed.octany.com, så din sida kan ligga kvar på den stabila loader-URL:en medan Octany släpper nya versioner.
window.OctanyConfig.methods är embed-motsvarigheten till methods-parametern på den hostade kassan: den begränsar vilka betalsätt kassan visar. Sätt quantity: false för att låsa antalet på radobjekt i checkout-sessioner som startas från embed:en.
§ 02.5
window.Octany-API
#
När loadern bootat (lyssna på event:et octany-cart-ready på document om du behöver vänta in det) finns window.Octany tillgängligt.
// Skicka in referens-ID:n / UTM som ska hamna på ordern Octany.set({ referenceId: 'user_42', referenceName: 'Anna Persson', email: 'anna@example.com', }) // Direkt-checkout — samma som URL:en ovan, fast byggd åt dig Octany.checkout(productId, { successUrl: 'https://app.example.com/welcome', amount: 19900, // frivilligt, namnge-ditt-pris i öre }) // Varukorgsdrivet flöde Octany.cart.add(productId) // lägg i den flytande varukorgen Octany.cart.open() // fäll ut varukorgen Octany.cart.close() Octany.cart.hide() // göm den flytande "fab"-knappen Octany.cart.checkout() // gå vidare till kassan med varukorgens innehåll Octany.cart.requestCheckoutUrl() // → Promise<url>, ingen redirect
DOM-event
Varukorgen skickar custom events på document:
| Event | När | event.detail |
|---|---|---|
| octany-cart-updated | Något lagts till, tagits bort eller fått nytt antal | { cart: { items, total, ... } } |
Använd dem för att uppdatera din egen sidchrome (varukorgsbadge etc.) utan att peta i iframen direkt.
Automatisk UTM-fångst
Embed:en läser utm_source/medium/campaign/content/term från värdsidans URL vid laddning och skickar dem genom Octany.set(...) så att de hamnar i utm_parameters på den resulterande ordern. Du behöver inte koppla det manuellt.
Det som inte finns på window.Octany
Det finns medvetet ingen Octany.portal() eller Octany.manage(). Hantering av prenumerationer — byta kort, se fakturor, säga upp — sker via de signerade billing-portal-URL:erna som ligger på själva subscription-resursen: update_card_url, invoices_url och cancel_url. För att skicka en inloggad användare till "hantera prenumeration": hämta hens prenumeration server-side (GET /subscriptions?filter[reference_id]=…) och redirecta sedan till önskad URL. Det finns ingen "ge mig en färsk portal-URL för den här kunden"-endpoint — URL:erna ligger redan i subscription-payloaden.
§ 02.6 Komplett exempel #
Paywall-mönster: hostad kassa + webhook-aktivering.
// 1. Server-side: bygg URL:en när användaren klickar "Prenumerera" app.post('/subscribe', (req, res) => { const params = new URLSearchParams({ referenceId: req.user.id, email: req.user.email, successUrl: 'https://app.example.com/subscribed', }) res.redirect(`https://checkout.octany.com/checkout/${ACCOUNT}/product/${PRODUCT_ID}?${params}`) }) // 2. Webhook-handler aktiverar användaren när betalningen landar app.post('/octany/webhook', /* signaturverifiering — se Webhooks-sidan */ (req, res) => { const event = req.body if (event.name === 'subscription.created' && event.data.reference_id) { activateUser(event.data.reference_id, event.data.id) } res.sendStatus(204) }) // 3. Success-sidan pollar en backend-endpoint som webhooken fyller på app.get('/subscribed', (req, res) => { res.render('thank-you', { pollUrl: '/me/subscription-status' }) })
Success-sidan litar aldrig på själva redirecten — den pollar en backend-endpoint som kollar användarens octany_subscription_id, satt av webhook-handlern. Mönstret klarar stängda flikar, asynkron 3DS och annonsblockerare som plockar bort query-params.