Hosted checkout
Redirect the payer to a Borga-hosted page that handles cards, wallets, 3-D Secure and bank invoices.
Hosted checkout is the simplest integration and supports every payment method. Your server creates a payment session, you redirect the payer to its url on checkout.borga.is, Borga collects the payment, and the payer is sent back to your return_url. Card details never touch your servers, which keeps you in the lightest PCI scope (SAQ A).
Create the session
Hosted is the default mode.
return_urlandcancel_urlare required.Node.jstsconst session = await borga.paymentSessions.create( { amount: 12900, currency: "ISK", customer_email: "anna@example.is", external_reference: "order_5678", return_url: "https://yoursite.is/order/complete", cancel_url: "https://yoursite.is/cart", enabled_methods: ["card", "apple_pay", "google_pay"], locale: "is", metadata: { order_id: "5678" }, }, // Your own idempotency key makes retries across processes safe too. { idempotencyKey: "order_5678_checkout" }, ); // Send the payer to the hosted page. redirect(session.url!);curlbashcurl https://api.borga.is/v1/payment_sessions \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order_5678_checkout" \ -d '{ "amount": 12900, "currency": "ISK", "customer_email": "anna@example.is", "external_reference": "order_5678", "return_url": "https://yoursite.is/order/complete", "cancel_url": "https://yoursite.is/cart", "enabled_methods": ["card", "apple_pay", "google_pay"], "locale": "is", "metadata": { "order_id": "5678" } }'Sessions stay
openfor 24 hours. Send anIdempotency-Key(the SDK does automatically) so a retried request cannot create two sessions; see Idempotency.Redirect the payer
Send the browser to
session.urlwith an HTTP 303 redirect from your server, or setwindow.location.hrefin the browser. The payer sees your merchant name and logo, the amount, wallet buttons when available, the card form, and a bank-invoice tab if enabled.Handle the return
On success Borga shows a confirmation for about two seconds and then redirects to
return_urlwithsession=ps_…appended as a query parameter. Read the session and branch onpayment_status:Node.jsts// GET /order/complete?session=ps_… (Next.js App Router route handler) export async function GET(request: Request) { const sessionId = new URL(request.url).searchParams.get("session"); if (!sessionId) return new Response("Missing session", { status: 400 }); const session = await borga.paymentSessions.retrieve(sessionId); switch (session.payment_status) { case "succeeded": return Response.redirect(`/thank-you?order=${session.id}`, 303); case "created": case "processing": // Bank invoices and slow 3-D Secure flows land here. Show a // "we're confirming your payment" page and finish on the webhook. return Response.redirect("/order/pending", 303); default: return Response.redirect("/order/failed", 303); } }Treat the return page as a status page. The authoritative signal is the
payment.succeededwebhook, which also covers payers who never make it back to your site.Handle cancellation and failure
If the payer uses the back link, Borga sends them to
cancel_urlunchanged. If a card is declined, the checkout shows the error and lets the payer try again; the session stays open. If the session expires, the page offers a link tocancel_url. In all of these cases no payment succeeded, and there is nothing to clean up on Borga's side.
Session options
The most useful fields. The full list is in the API reference.
| Field | Purpose |
|---|---|
amount, currency | What to charge. currency defaults to ISK. |
return_url, cancel_url | Where the payer lands afterwards. HTTPS on an allowed domain in live mode; http://localhost is fine in test mode. |
external_reference | Your order or invoice number. Copied to the Payment and to webhook payloads so you can match them. |
metadata | Any JSON object. Copied to the Payment. |
customer_email | Pre-fills the email field and locks it. |
customer | Attach the payment to an existing Customer, needed for saved cards and subscriptions. |
enabled_methods | Restrict or extend the methods shown. Default ["card", "apple_pay", "google_pay"]; add "bank_invoice" to offer krafa. |
locale | is (default) or en. Other language tags fall back to English. |
save_payment_method | Save the card for later charges. See Saved cards. |
subscription | Start a subscription with these prices on success. See Subscriptions. |
Allowed redirect domains
Borga only redirects to domains you have registered, so a compromised integration cannot send payers to a phishing page. Add them under Settings → Account → Allowed redirect domains. Matching is on the hostname exactly, so list shop.example.is and www.shop.example.is separately if you use both. Attempts to use other domains fail at session creation with redirect_url_not_allowed.
Branding
The merchant name, logo and primary colour shown on the checkout come from Settings → Account. The page is served in Icelandic by default and in English when locale is en.
Payment links without code
Every session url is a shareable payment link valid for 24 hours. Creating sessions from a small internal tool, or straight from an invoicing workflow, is a common way to take payments by email or chat before building a full checkout.