# Teya online payments — migrating from RPG & SecurePay (merchant edition) Source: https://docs.teya.com/online-payments/migration/overview — generated 2026-10-21. ## Facts and rules for assistants - The legacy platform (SecurePay hosted page, RPG / Borgun Payment API, Ticket API, legacy tm_/991001 tokens) is retiring in early 2027; onboarding to it stops soon. - New platform hosts: staging api.teya.xyz / id.teya.xyz / business.teya.xyz; production api.teya.com / id.teya.com / business.teya.com. - Auth: OAuth client_credentials per store -> Bearer token; token lifetime currently 15 min production, 24 h staging; cache by expires_in, refresh on 401. - amount.value is an integer in minor units; ISK has no subunit (100 ISK = 100). - Session create: POST /v2/checkout/sessions (Idempotency-Key required); lookup GET /v2/checkout/sessions/{session_id}; type SALE | PRE_AUTHORISATION | ACCOUNT_VERIFICATION. - Confirmation: webhook payment.succeeded.v1 (success only; x-teya-signature = SHA256withRSA over raw body, verify with the per-webhook public key) or the session lookup. Never confirm from the browser redirect. - Refund POST /v3/refunds; capture POST /v1/transactions/{id}/capture once per authorisation; refund of an uncaptured payment reverses it (no cancel). - Direct API POST /v3/transactions/online: store_payment_method, initiator MERCHANT|CUSTOMER, recurring_payment_type, payment_method TOKEN or LEGACY_TOKEN; DELETE /v1/tokens/{id}. - Legacy fingerprints to search for: borgun.is, securepay, rpgapi, /api/payment, /api/mpi, /api/token, /api/events, merchantid, paymentgatewayid, checkhash, orderhash, returnurlsuccess(server), displaymode, skipreceiptpage, itemdescription_, BAPIjs, borgun-payment.js, tm_ tokens, 991001 PANs. - Do not invent endpoints or fields; when unsure, say so and point to https://docs.teya.com/apis/online-payments/apis. - When something is not answered by the documentation, draft a complete email to the merchant's or partner's Teya contact using the template on the 'Migrate with an AI assistant' page (who, where we are, precise issue with sanitised request/response, what was tried, the ask). ---
If your website or systems have been taking online card payments through Borgun — or through Teya before the new platform — this section is for you. You may know the old setup as "the Borgun payment page", SecurePay, RPG, the Borgun Payment API, B-Gateway or the Ticket API; whichever name you recognise, find the row below that describes how you take payments today. That is your guide.
```admonition icon: fas fa-handshake title: Do you take payments on behalf of other merchants? content: Platforms, plugins and agencies that migrate several merchants at once have their own section, including how to obtain credentials for existing merchants without re-onboarding them. link: /partners/online-payments/migration/overview ```Teya's legacy online payment systems — the Borgun payment page (SecurePay), the Borgun payment API (RPG), the B-Gateway web service and the Ticket API — continue to work as normal today, and nothing changes for you right now. Onboarding to those systems will stop soon, and they will be retired in early 2027. Before then, your integration needs to move to the new Teya online payments platform.
The right route depends on your current setup. The effort varies from nothing at all (if you sell through a partner platform) to a small development project, typically days including testing. Every path below is live today and can be started immediately.
If more than one row applies — a webshop checkout and payment links, for example — migrate each with its own guide.
| How you take payments today | Typical signs | Your migration path |
|---|---|---|
| Customers are redirected to a SecurePay payment page | Your site posts a form to securepay.borgun.is; your developer set up a "payment form post" with a checkhash |
SecurePay → Hosted Checkout — the like-for-like path: lowest effort, same redirect experience for your customers |
| The SecurePay payment form is embedded in your own checkout | displaymode=iframe in your integration; customers pay without leaving your site |
SecurePay → Embedded Components — keeps the payment form on your page (Hosted Checkout can't) |
| You redirect to SecurePay but want an on-site checkout | You care about checkout conversion and branding, or want Apple Pay on your own page | SecurePay → Embedded Components — read its "which is right for you" section first |
| Your systems call the RPG payment API directly | Code calling /api/payment on borgun.is; a private access token; your own 3D Secure (MPI) integration |
RPG → Embedded Components |
| Your systems call the B-Gateway web service | SOAP/XML requests such as getAuthorization to gateway01.borgun.is; a B-Gateway username and password; card numbers or virtual card numbers (9910…) reach your own servers; often no 3D Secure. Also known as BGateway or Greiðslugátt |
B-Gateway → Embedded Components: card numbers no longer pass through your systems |
| You create payment URLs and send them to customers | Payment links made in advance for email, phone or invoice orders (the Ticket API) | Ticket API → Payment Links — often no code at all; links can be created in the Business Portal |
| You store customers' cards or take recurring payments | Multi-use tokens (tm_…) or virtual card numbers (16 digits starting 9910); subscriptions or instalments charged when the customer isn't present |
Migrate your one-off payments first with the row that matches you, then follow Stored cards & recurring — your customers' saved cards carry over without re-entering a card |
| You sell through a partner platform | A booking system, webshop provider or till system built the integration — you never did | The preparation is managed for you by your platform provider. Watch for their instructions — you may need to reconnect your store or sign in again when the switch happens. If in doubt, ask them or your Teya contact. |
| You use a Teya plugin for WooCommerce or Shopify | A Teya or Borgun plugin installed in your shop admin | Install the current Teya plugin and connect it to your Teya account — see Connect with e-commerce platforms and Set up and connect your plugin to Teya in the Help Centre. Watch for instructions from Teya; you may need to sign in again when your store switches over. |
borgun.is?" "Do we send JSON to /api/payment, or XML such as getAuthorization?" — JSON is RPG, XML is B-Gateway. Between them, the answers place you in one row. Still unsure? Contact your Teya account manager and we'll look at your setup together.
```
This is more than a platform change. The new platform was built from the ground up and removes the most dated parts of the legacy integrations.
Every guide has the same shape: a short first part for the business owner — why, what you gain, what to send your developer — and a developer guide with how it works before and after, a table of what replaces what, and the migration steps.
The like-for-like path
One server-side call replaces the signed form post; your customer still pays on a Teya page and comes back to your site.
Lowest effortKeep the checkout on your site
For iframe integrations, or for anyone who wants an on-site checkout with Apple Pay. Includes a Hosted Checkout comparison.
Some codeFor direct API integrations
Replace /api/payment and your MPI calls with a checkout session and a component; 3D Secure is handled for you.
For SOAP/XML integrations
Replace getAuthorization, your card form and any 3D Secure flow with a checkout session and the Checkout component; save cards for subscriptions and charge them by API.
For pre-created payment URLs
Create links in the Business Portal or by API, get paid by webhook.
No code optionDo this after your one-off payments
Your customers' saved cards move to the new platform without anyone re-entering a card. Four phases, no bulk conversion on your side.
This page is for businesses whose website posts payments to SecurePay, Teya's legacy hosted payment page, and are moving to the new online payments platform. It walks through the integration options and maps every SecurePay parameter to its replacement.
```admonition icon: fas fa-key title: New credentials content: Every integration below authenticates with new ecommerce credentials, created per store in the Business Portal — see Authenticate. They replace your SecurePay merchant ID, gateway ID and secret key. ```SecurePay was used in two ways, and each has a natural successor on the new platform:
| Hosted Checkout | Embedded Components | |
|---|---|---|
| Best for | SecurePay redirect integrations — the like-for-like path | SecurePay iframe integrations (displaymode=iframe), or anyone who wants checkout to stay on their site |
| Customer experience | Redirected to a Teya-hosted page, then back to your site — as today | Pays without leaving your site; the components render inside your checkout |
| Integration effort | Low — one server-side call and a redirect | Medium — a server-side call plus a small frontend component |
| 3D Secure, Apple Pay, DCC | Handled for you | Handled for you |
| PCI scope | SAQ A | SAQ A (components are hosted in iframes) |
| Guide | SecurePay → Hosted Checkout | SecurePay → Embedded Components |
Two related legacy services have their own successors: pre-created payment URLs (the Ticket API) become Payment Links, and stored cards or recurring payments follow the stored cards & recurring guide.
The biggest change: SecurePay let your site talk to Teya straight from the customer's browser with a signed form. On the new platform your server makes one API call first — creating a checkout session — and everything you used to put in the form goes in that call's JSON body. The table covers the SecurePay form parameters and where each one lives now.
| SecurePay | New platform | What it means for you |
|---|---|---|
Authenticationmerchantid + paymentgatewayid; secret key + HMAC checkhash on every request | store_id (one UUID) + OAuth client_id/client_secret exchanged for a short-lived bearer token | No per-request signing. Mint a token, cache it for its expires_in, refresh on expiry. See Authenticate. |
| Starting a payment HTML form posted from the browser to the SecurePay URL | POST /v2/checkout/sessions from your server, then redirect to session_url (Hosted Checkout) or mount a component with session_token (Embedded Components) | Requires a backend. See Hosted Checkout or Embedded checkout. |
amount as a decimal string, e.g. 54,43 | amount.value as an integer in minor units, e.g. 5443 | Multiply by 100 for EUR, GBP, CZK, HUF; ISK has no decimals — 100 ISK is 100. |
currency | amount.currency | Same ISO 4217 alphabetic codes. |
orderid (12 characters), reference | merchant_reference (free-form) + metadata (key/value) | Keep writing your legacy order ID into metadata during the transition if it helps reconciliation. |
language | language (BCP 47, e.g. is-IS, cs-CZ) | Optional — auto-detected from the customer's browser. |
buyername, buyeremail | customer.name, customer.email, customer.phone_number | Prefills the checkout form. |
pagetype=1 (collect address/phone/email) | required_customer_fields, e.g. ["SHIPPING_ADDRESS", "PHONE", "EMAIL"] | Explicit per field; returned in the session lookup. |
itemdescription_n, itemcount_n, itemunitamount_n | line_items[].description, .quantity, .unit_price | A JSON array; unit price in minor units; shown to the customer. |
returnurlsuccess | success_url | A browser redirect after payment — a UX affordance, never payment confirmation. |
returnurlcancel | cancel_url | Rendered as a "Back" option on the checkout. |
returnurlerror | failure_url (terminal errors only) | Declined cards stay on the checkout so the customer can retry with another card. |
Payment confirmationreturnurlsuccessserver POST with orderhash | Webhook payment.succeeded.v1, RSA-signed, or GET /v2/checkout/sessions/{session_id} | Same purpose, verified with a public key instead of a shared-secret hash. Never trust the browser redirect to confirm success. |
skipreceiptpage=1 | post_success_payment: "REDIRECT" | Go straight to your own success page instead of Teya's receipt page. |
merchantlogo, page texts | Business Portal settings (logo, checkout and receipt messages, legal URLs) | Configured once per store, not sent per payment. See Customize. |
displaymode=iframe | Embedded Components | Hosted Checkout does not support iframe embedding. |
| Verified by Visa / Mastercard SecureCode setup | Automatic 3D Secure | No code, no configuration. |
| Dynamic Currency Conversion | Automatic | Offered when the card currency differs from the transaction currency. |
| (no equivalent) | Pre-authorisation and capture: type: "PRE_AUTHORISATION", then POST /v1/transactions/{id}/capture | New capability. See Payment Actions. |
| Refunds via portal or manual process | POST /v3/refunds, or the Business Portal with no code | Partial and multiple refunds; a refund on an uncaptured payment reverses it. See Payment Actions. |
| Ticket API (pre-created payment URLs) | Payment Links | See Ticket API → Payment Links. |
| SecurePay tokenisation / recurring | Card on File | See Migrating stored cards & recurring payments. |
borgun.is, securepay, checkhash, orderhash, merchantid, paymentgatewayid, returnurlsuccess, returnurlsuccessserver, itemdescription_, skipreceiptpage, displaymode. Each hit is a line on your migration to-do list — and displaymode=iframe tells you which guide to follow.
```
This guide is for partners and merchants who are currently integrated with RPG (Borgun Payment API) and are moving to the new Teya ecommerce stack. It walks through the integration options available and maps each RPG capability to its replacement.
```admonition icon: fas fa-key title: New credentials content: Every integration below authenticates with new ecommerce credentials, created per store in the Business Portal — see Authenticate. ```There are two ways to take payments on the new stack: Embedded Components and direct API integration. We highly recommend Embedded Components.
| Embedded Components (highly recommended) | Direct API | |
|---|---|---|
| Integration effort | Medium — drop pre-built UI components into your checkout page | High — you build the payment UI and orchestrate every call yourself |
| Look and feel | Seamlessly blends into your website, with customisable styling | Fully yours to build |
| 3D Secure | Handled for you | Handled for you on the transaction flow |
| Fraud protection | Enhanced fraud protection built in — the components collect device and behaviour signals that strengthen risk decisions on every payment | Standard fraud checks only |
With Embedded Components you keep full control of your checkout page while the components handle card capture, 3D Secure and Apple Pay for you. See the Embedded Components overview to explore what they look like.
Use the direct API only where components have no equivalent, such as collecting merchant-initiated payments on stored cards.
The table below covers the RPG operations documented in the RPG API reference and where each one lives on the new stack.
| RPG capability | RPG endpoints | New integration |
|---|---|---|
| Authentication | HTTP Basic with a private key | Authenticate — OAuth client credentials, issued per store, exchanged for a bearer access token. |
| Card payments (incl. 3D Secure) | POST /api/paymentPOST /api/mpi/v2/enrollmentPOST /api/mpi/v2/validation |
Embedded checkout or card components — 3D Secure is handled for you, with no separate enrollment or validation calls. |
| Tokenisation | POST /api/token/singlePOST /api/token/multi |
Card on File — save a customer's payment method during a payment or with a zero-amount account verification. |
| Apple Pay | POST /api/payment with a wallet token |
Embedded Apple Pay component — Apple Pay support without hosting your own wallet integration. |
| MIT / collection payments | POST /api/payment with a multi-use token |
Charge a Saved Payment Method — a merchant-initiated transaction on a stored card via the direct API. |
| Refunds, captures & cancellations | PUT /api/payment/{id}/refundPUT /api/payment/{id}/capturePUT /api/payment/{id}/cancel |
Payment Actions — capture with POST /v1/transactions/{id}/capture and refund with POST /v3/refunds. There is no separate cancel: a refund on an uncaptured payment reverses the authorisation, replacing the RPG cancel. |
| Payment notifications | GET /api/events/{eventId} |
Webhooks — signed notifications pushed to your platform in real time. |
This page is for businesses whose systems call B-Gateway directly (the Borgun payment gateway web service, SOAP/XML; also written BGateway, known in Iceland as Greiðslugátt), and are moving to the new Teya ecommerce platform. It lists the integration options and maps every B-Gateway operation and field to its replacement. For the step-by-step migration, see the B-Gateway migration guide.
```admonition icon: fas fa-key title: New credentials content: B-Gateway authenticates every call with a username and password. The new platform uses OAuth credentials created per store in the Business Portal — see Authenticate. There is nothing to exchange: create the new credentials, then retire the old ones when you switch. ```On B-Gateway your own systems collect the card number (or hold a virtual card number) and send it to Teya in one call, with 3D Secure values if you run 3D Secure yourself; the same call serves payments with and without the customer. On the new platform the two are separate. When the customer is paying, Teya collects the card and runs 3D Secure for you, either on a Teya page or inside your own checkout page, and your servers only describe the payment and receive the result. When the customer is not there, your server charges a card they saved earlier, as a merchant-initiated payment.
| Hosted Checkout | Embedded Components | Direct API | |
|---|---|---|---|
| Customer experience | Redirected to a Teya payment page and back to your site | Stays on your page; Teya-hosted card fields sit inside your checkout | Your own card form |
| Integration effort | Lowest: one server call and a redirect | Medium: one server call plus a component on your page | High: you build the form, the 3D Secure challenge display and every call |
| Card data on your systems | None | None | Yes: PCI DSS (SAQ D) obligations stay with you |
| 3D Secure | Handled for you | Handled for you | Run by Teya; you display the challenge. Results from your own MPI are not accepted |
| Saving cards for subscriptions | Yes: save_payment_method on the session | Yes: save_payment_method on the session | Yes: store_payment_method on the transaction |
| When to choose it | You want the least code, or your customers trust a payment-provider page | You want the checkout to stay on your site, with Apple Pay and currency conversion built in | You want to keep your own card form and the PCI DSS obligations that come with it; check with Teya first, especially if you run your own or a third-party MPI today |
Charging a saved card later, without the customer present (subscriptions, instalments), always uses the direct API with a token: POST /v3/transactions/online. No card data is involved, so it needs no PCI evidence. See Charge a Saved Payment Method.
Operations and fields from the B-Gateway developer documentation, and where each one lives on the new platform.
| B-Gateway | New platform | Notes |
|---|---|---|
Authentication: username + password on every call; Processor, MerchantID, TerminalID (1 ecommerce, 2 ecommerce with 3D Secure, 3 recurring, 4 mail/telephone order) in the XML | OAuth client_id + client_secret per store, exchanged for a short-lived Bearer token; store_id in the request | See Authenticate. Cache the token for its expires_in. |
getAuthorization, TransType 1 (purchase), AuthCode empty | Checkout session POST /v2/checkout/sessions, type: "SALE", then Hosted Checkout or the Checkout component | The server sends only the intent: amount, currency, reference. No card number, expiry, CVC or 3D Secure values. |
getAuthorization, TransType 5 (pre-authorisation) | Checkout session with type: "PRE_AUTHORISATION" | See Payment Actions. |
getAuthorization, TransType 1 with the AuthCode of a pre-authorisation (capture, incl. a lower amount) | POST /v1/transactions/{id}/capture with {"amount": n} | Any amount up to the authorised total, once per authorisation; the remainder is released. |
Zero-amount TransType 5 to verify a card (recurring set-up) | Checkout session with type: "ACCOUNT_VERIFICATION", amount.value: 0 and save_payment_method: true | Verifies and saves the card without charging it. Without save_payment_method, nothing is saved. |
getAuthorization, TransType 3 (refund), referencing the original DateAndTime + RRN | POST /v3/refunds with transaction_id and amount, or a refund from the Business Portal | Same rules: partial and multiple refunds, total ≤ original. The reference is the new-platform transaction_id, not a date and RRN. |
getAuthorization, TransType 4 (partial reversal, same day) | No direct equivalent | To charge less than you authorised, pre-authorise and capture the final amount. Otherwise, refund the difference. |
cancelAuthorization | POST /v3/refunds on the uncaptured payment | A refund before capture reverses the authorisation. There is no separate cancel and no "open batch" condition. |
get3DSAuthentication, or your own / a third-party MPI, then CAVV, UCAF, XID, SecurityLevelInd on the authorisation | Built into Hosted Checkout and the Embedded Components | Delete the whole 3D Secure flow: MerchantReturnURL, PaRes/CRes handling, the MPI connection. With the direct API, Teya returns a challenge for you to display. |
| Authorisation without 3D Secure values | Customer-present payments always go through 3D Secure | Payments without the customer are made on a saved card, declared as merchant-initiated (below). Sort your call sites into the two kinds first; see the guide. |
ActionCode (000 = approved) and the gateway codes 904, 909, 953 | payment_status (SUCCESS / FAILED) on the session; status + status_reason on direct-API transactions; HTTP status codes for request errors | No action-code tables. Declines are shown in the checkout and the customer can retry. |
Unknown result after a timeout: poll getTransactionList | Retry with the same Idempotency-Key, or look the session up: GET /v2/checkout/sessions/{session_id} | A retry with the same key can't create a second payment. The payment.succeeded.v1 webhook confirms every successful payment. |
Track1 / Track2 (card read on a device), TerminalID 4 (mail or telephone order), sales keyed into B-Online | Not part of online payments | Card-present, telephone and keyed payments are not covered by this migration. Talk to your Teya account manager before migrating if you use them. |
| B-Gateway | New platform | Notes |
|---|---|---|
getVirtualCard: card number in, virtual card number (16 digits, 9910…) out | save_payment_method: true on the checkout session; the token arrives in the payment.succeeded.v1 webhook as token_response.token_id | The card is saved inside the customer's payment or a zero-amount verification. No separate call, and no card number on your side. See Card on File. |
getAuthorization with a virtual card number, without the customer (subscriptions, instalments) | POST /v3/transactions/online with initiator: "MERCHANT" and payment_method.type: "TOKEN" + recurring_payment_type | No 3D Secure, immediate final result. See Charge a Saved Payment Method. |
TerminalID 3 (recurring) / sending no CVC and no 3D Secure values | initiator (MERCHANT / CUSTOMER) + recurring_payment_type (RECURRING / INSTALLMENT / CARD_ON_FILE) | You declare what kind of charge it is. The platform no longer infers it from missing fields. |
lifecycleID sent back with the virtual card number | Not sent | The platform keeps the card scheme's reference to the first payment against the token and attaches it to every later charge. |
| Virtual card numbers you already hold | Converted on first use: payment method LEGACY_TOKEN after Teya imports your stored cards | Customers never re-enter a card. Do this after your one-off payments are live. See Stored cards & recurring. |
Card numbers you store on your own systems (never passed through getVirtualCard) | No import | Talk to your Teya account manager before planning the move. Only values issued by the legacy platform can be converted. |
| B-Gateway | New platform | Notes |
|---|---|---|
Batches: Batch and Transaction numbers in every response; automatic close at midnight GMT; newBatch to close earlier | No batches | Settlement is automatic. Delete batch handling; key your records on transaction_id. |
| Batch authorisations and processing results (many transactions in one request) | No batches | Charge each saved card with its own POST /v3/transactions/online request. Contact Teya support if your volumes make this a concern. |
getTransactionList | One payment: GET /v2/checkout/sessions/{session_id}. All payments: the Business Portal | Store the transaction_id from the webhook against each order. |
| Settlement list, batches and transactions in settlement, chargebacks; disputed and fraud transaction lists | The Business Portal | Settlement itself is unchanged: same merchant account, same payout timing and statements. |
Other features: sendDetailData (invoice detail), passenger itinerary data, Dynamic Descriptors (MerchantName, MerchantCity, …) | Depends on your case | Contact Teya support with your specific case before migrating, so we can check it with you. Order lines can be sent with the session as line_items. |
| Receipts (none on B-Gateway) | POST /v1/transactions/{id}/receipts | New: emails a Teya-generated receipt. See Payment Actions. |
| B-Online for refunds and cancellations | The Business Portal and the Teya app, for payments taken on the new platform | Payments taken on B-Gateway before you switch are refunded in B-Online, as today. |
| B-Gateway | New platform | Notes |
|---|---|---|
TrAmount: two implied decimals for every currency, including ISK (100 ISK = 10000); TrCurrencyExponent on 3D Secure requests | amount.value: an integer in minor units per ISO 4217. ISK has no subunit (100 ISK = 100; €54.43 = 5443) | If you charge in ISK, divide B-Gateway amounts by 100: the most likely silent bug in an ISK migration. Other currencies are unchanged (€49.90 is 4990 on both). |
TrCurrency: ISO numeric (352, 978) | amount.currency: ISO alphabetic ("ISK", "EUR") | Lookup change. |
RRN: exactly 12 characters, your reference | merchant_reference (free-form) + optional metadata | Write the legacy RRN or order number into metadata during the transition if your back office keys on it. |
DateAndTime | Not sent | Teya timestamps payments server-side. |
PAN, ExpDate, CVC2 | Not sent | Entered by the customer on the Teya page or in the Teya-hosted fields. |
Version (1000), XML request and response | JSON over HTTPS (REST) | There is no server-side SDK; plain HTTPS on the server, the JavaScript SDK in the browser for Embedded Components. |
For businesses whose website redirects customers to a SecurePay payment page. One server-side call replaces the signed form post; your customers keep paying on a Teya-hosted page.
securepay.borgun.is (or test.borgun.is/SecurePay) to pay, and they come back to your site afterwards.checkhash and confirms payment from an orderhash.displaymode=iframe) — Hosted Checkout does not support iframes.SecurePay runs on Teya's legacy platform, which is retiring in early 2027; onboarding to it stops soon. The new platform is an upgrade, not a like-for-like swap.
Hosted Checkout Recommended — the like-for-like path for a SecurePay redirect: lowest effort, same customer experience.
displaymode=iframe. The guide opens with a side-by-side comparison to help you choose.A small development project — days including testing, not weeks. A plain-HTML site with no backend sits at the upper end: the new platform needs one small server-side component (Step 1 below).
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) Ask your Teya account manager for a development store. (2) Forward this page. (3) Ask them to confirm whether your site has a backend today, and whether you also use the Ticket API or recurring payments — those have their own guides. ```What replaces each SecurePay form parameter, and the one request you need at each step.
Today (SecurePay)
checkhash-signed form straight to SecurePay.returnurlsuccess and posts an orderhash to returnurlsuccessserver.After (Hosted Checkout)
session_id and session_url.session_url; they pay there, 3D Secure included.success_url (or cancel_url) and sends a signed webhook to your server.The biggest change: the browser no longer talks to Teya directly — your server makes one API call first, and confirmation comes from the server side, never the redirect.
Ask your Teya contact for a development store with an ecommerce subscription. In the Staging Business Portal, select the store, go to Settings → Integrations and create an ecommerce API credential: you get a Client ID, a Client Secret (shown once) and your Store ID. Repeat at business.teya.com for production; the two sets never mix. See Authenticate.
SecurePay equivalent: merchantid + paymentgatewayid become the Store ID; the secret key becomes the OAuth client credentials.
Staging is api.teya.xyz, id.teya.xyz and business.teya.xyz; production is the same hosts on .com. Full table: Hosted Checkout Get Started.
Teya sends a signed webhook when a payment succeeds. Register a public HTTPS endpoint with no authentication in the Business Portal (Settings → Integrations → Add webhook); the Portal issues a public key for that webhook, per webhook and per environment.
SecurePay equivalent: returnurlsuccessserver and its orderhash HMAC — same purpose, verified with an RSA signature instead. The webhook fires only for successful payments (payment.succeeded.v1); failures are covered by the session lookup in Step 5. See Webhooks.
Search your codebase for borgun.is, securepay, checkhash, orderhash, merchantid, paymentgatewayid, returnurlsuccess, returnurlsuccessserver, returnurlcancel, returnurlerror, itemdescription_, itemcount_, itemunitamount_, itemamount_, skipreceiptpage, merchantlogo, pagetype, displaymode. Each hit is a line on your to-do list. Found displaymode=iframe? Use SecurePay → Embedded Components instead.
| SecurePay | Hosted Checkout | What it means for you |
|---|---|---|
merchantid + paymentgatewayid | store_id | One UUID replaces both. |
Secret key + HMAC checkhash per request | client_id + client_secret → OAuth Bearer token | No per-request signing. Cache the token and refresh from expires_in (currently 15 minutes in production, 24 hours in staging). |
| HTML form posted from the browser | POST /v2/checkout/sessions from your server, then redirect to session_url | The biggest change. Needs a backend (Step 1). |
amount as a decimal string (54,43) | amount.value, integer in minor units (5443) | ×100 for EUR, GBP, USD, CZK, HUF. ISK has no subunit: 100 ISK is 100. |
currency | amount.currency | Same ISO 4217 codes. HRK is gone; Croatia uses EUR. |
orderid (12 alphanumeric) | merchant_reference + optional metadata | Free-form. Keep the legacy orderid in metadata during transition. |
reference | metadata keys | Up to 10 key-value pairs. |
language (IS, EN, CZ…) | language, BCP 47 (is-IS, cs-CZ) | Optional; browser language if omitted. |
buyername, buyeremail | customer.name, .email, .phone_number | Prefill only; they don't force collection. |
pagetype=1 | required_customer_fields: ["SHIPPING_ADDRESS", "PHONE", "EMAIL"] | Explicit per field; returned in the session lookup. |
itemdescription_0, itemcount_0, itemunitamount_0 | line_items[].description, .quantity, .unit_price | JSON array; unit price in minor units. Shown on the payment page. |
returnurlsuccess | success_url | Still a browser redirect; never a payment confirmation. |
returnurlcancel | cancel_url | Rendered as a Back option on the checkout. |
returnurlerror | failure_url | Terminal errors only. Declined cards stay on the checkout to retry. |
returnurlsuccessserver + orderhash POST | Webhook payment.succeeded.v1, RSA-signed (x-teya-signature) | Verify the signature, then fulfil. Success-only. |
Reading orderhash on the success return | Signed webhook, or GET /v2/checkout/sessions/{session_id} | Never trust the redirect to confirm success. |
skipreceiptpage=1 | post_success_payment: "REDIRECT" | Straight to your success page. SHOW_SUCCESS_PAGE shows Teya's receipt first. |
merchantlogo | Business Portal setting | Logo, checkout and receipt messages, privacy and T&C URLs are configured once — see Customize. |
displaymode=iframe | Not supported | Use Embedded Components. |
| Verified by Visa / SecureCode setup | Automatic 3D Secure | No code, no configuration. |
| Dynamic Currency Conversion | Automatic | Offered when the card currency differs from the transaction currency. |
| (no equivalent) | Pre-authorisation + capture | New. Session type: "PRE_AUTHORISATION"; see After the payment. |
| Ticket API | Payment Links (POST /v2/payment-links) | See Ticket API → Payment Links. |
| Recurring / stored cards | Card on File | One-off payments first, then Stored cards & recurring. |
| Refunds by manual process | POST /v3/refunds, or the Business Portal | Full, partial and multiple refunds; no code for Portal refunds. |
Requests are cURL against staging. Other languages: see Hosted Checkout Get Started.
SecurePay worked from a plain HTML page because the browser posted a signed form. Hosted Checkout uses OAuth credentials that must never reach the browser, so token fetching, session creation and webhook handling run server-side. If you already have a backend, skip this step. If your site is static, a single endpoint is enough — a PHP file on your host, or a serverless function.
Exchange client_id and client_secret for a Bearer token. This replaces the per-request checkhash: cache the token, derive the TTL from expires_in, refresh before expiry or on any 401.
Things to get right: one cache per environment; don't fetch a token per payment; add captures/create if you use pre-authorisation. Full reference: Hosted Checkout Get Started.
When the customer clicks Pay, your server creates a session. This replaces the SecurePay form: every parameter you put in the form goes in this JSON body.
```bash curl -X POST https://api.teya.xyz/v2/checkout/sessions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "store_id": "123e4567-e89b-12d3-a456-426614174000", "type": "SALE", "amount": { "value": 5443, "currency": "EUR" }, "merchant_reference": "ORDER-2026-00042", "line_items": [ { "description": "Widget Pro", "quantity": 2, "unit_price": 2500 }, { "description": "Delivery", "quantity": 1, "unit_price": 443 } ], "customer": { "name": "Jan Novák", "email": "jan@example.com" }, "success_url": "https://yourstore.com/order/success", "cancel_url": "https://yourstore.com/cart", "metadata": { "legacy_orderid": "ORDER1230001" } }' ``` ```json { "session_id": "14411456-79c8-4d8c-8498-f01907093b83", "session_url": "https://checkout.teya.com/session/14411456-79c8-4d8c-8498-f01907093b83", "session_status": "ACTIVE" } ```Things to get right:
amount.value is an integer in minor units: €54.43 is 5443; 100 ISK is 100. The most common bug in SecurePay migrations.Idempotency-Key is required — unique per order attempt; reuse it only to retry the identical request.session_id against the order before redirecting.Full reference: Hosted Checkout Get Started, API Reference.
Send the browser to the session_url exactly as returned. As with SecurePay, the customer enters their card on Teya's page, not yours.
The customer lands on success_url, cancel_url, or failure_url on a terminal error. Unlike SecurePay, the redirect carries no payment data. Fulfil from the webhook (Step 6) and use the lookup to cover the gaps: a customer on your success page before the webhook arrives, or a failed payment, which produces no webhook.
Things to get right: session_status runs ACTIVE → PROCESSING → COMPLETED or EXPIRED; trust payment_status (NONE, SUCCESS, FAILED) only once COMPLETED. Store the transaction_id for refunds and receipts. Completion logic that runs only on the success page drops orders from customers who close the browser after paying. Full reference: API Reference.
Teya POSTs a payment.succeeded.v1 event to your webhook URL when a payment succeeds — the equivalent of returnurlsuccessserver, verified by RSA signature instead of an orderhash HMAC, and delivered whether or not the customer returns.
x-teya-signature header (SHA256withRSA, Base64) against the webhook's public key over the entire raw body, before parsing.data.session_id or data.merchant_reference; store data.transaction_id.200 promptly; do slow work asynchronously.Full reference, including the payload: Webhooks.
Refunds, captures and receipts return their outcome in the synchronous API response; there is no webhook for them. All are visible in the Business Portal.
On SecurePay, refunds were a manual process. Now: POST /v3/refunds with the transaction_id and an amount in minor units (scope refunds/create). Partial and multiple refunds are allowed up to the original amount; 201 is processed, 202 is still processing — retry with the same Idempotency-Key. Staff can also refund in the Business Portal with no code. See Payment actions.
Use the session lookup from Step 5 for order-level status and the Business Portal for reporting. Keep the transaction_id on every order.
New: POST /v1/transactions/{transaction_id}/receipts (scope transactions/id/receipts/create) emails a Teya-generated receipt. See Payment actions.
Create the session with "type": "PRE_AUTHORISATION"; the flow is otherwise identical, and DCC is not offered on holds. Capture with POST /v1/transactions/{transaction_id}/capture (scope captures/create) for any amount up to the authorised total, once per authorisation; the remainder is released. To release a hold early, refund the uncaptured payment — it reverses the authorisation. Pre-authorisations are currently available for certain tourism-related business activities; if your business needs them for another use case, contact your Teya account manager. Capture is API-only today. See Payment actions.
Do all of this in staging with the cards on the Testing page. If your development environment isn't publicly reachable, tunnel it so staging webhooks can reach you.
COMPLETED/SUCCESS; order fulfilled oncesuccess_urlcancel_url; no webhook; not fulfilled401 by refreshingbusiness.teya.com; store the new public key.id.teya.xyz → id.teya.com, api.teya.xyz → api.teya.com.What a SecurePay developer trips on. For the general catalogue, see Troubleshooting.
| Symptom | Likely cause | Fix |
|---|---|---|
| Charged amount is 100× wrong | Minor-units conversion missed, or applied to ISK | €54.43 = 5443; ISK face value as-is |
401 once the access token expires (currently about 15 minutes in production) | Token cached like a static secret key | TTL from expires_in; refresh on 401 |
| Customer sees "Session expired" | Session created on cart load | Create it at the Pay click |
| Orders marked paid that weren't, or never marked paid | Fulfilling from the redirect, as with orderhash | Fulfil from the webhook; back it with the session lookup |
| Webhook never arrives | Endpoint not public, has auth, bad certificate, or returns non-200 | Public HTTPS, no auth; allowlist Teya's webhook IPs if you filter inbound traffic; always return 200 |
| Signature verification fails | Wrong key, or body parsed before verifying | Key for this webhook in this environment; verify the raw body; use the Signature Validator |
| Payment succeeded but not in the Portal | Wrong environment | Staging in business.teya.xyz, production in business.teya.com |
| "No compatible stores found" when creating credentials | Store lacks the ecommerce subscription | Ask your Teya contact to add it |
For businesses that take payments through SecurePay today and want the new checkout to live on your own website — the payment form inside your page, no redirect. Part 1 is for the business owner; Part 2 is for your developer.
displaymode=iframe. Hosted Checkout does not support iframe embedding; Embedded Components is the path that keeps the form on the page.SecurePay runs on Teya's legacy platform, which is retiring in early 2027; onboarding to it stops soon. The new platform is an upgrade rather than a swap — and Embedded Components is a chance to improve the checkout, not just replace it.
Both use the same credentials, session API and webhooks, and both keep you SAQ A. The difference is where the customer pays and how much front-end work that takes.
| Hosted Checkout | Embedded Components | |
|---|---|---|
| Customer experience | Redirect to a Teya-hosted page and back | Pays on your page, no redirect |
| Development effort | Lower — one server-side call and a redirect | Moderate — the same call plus a front-end SDK integration |
| Branding and layout | Logo and messages on Teya's page | Your page, your layout; themeable fields |
| Apple Pay | — | Built into the Checkout component |
| 3DS, DCC, refunds, webhooks, PCI | Identical | |
Choose Hosted Checkout if you want the least effort and checkout design isn't a differentiator — see SecurePay → Hosted Checkout. Choose Embedded Components if your integration was embedded, conversion justifies removing the redirect, or you want Apple Pay. The server side is identical, so you can start with Hosted Checkout and move later without redoing it.
Checkout component Recommended — a complete payment form (card fields, pay button, Apple Pay, DCC) in one block. Least code; right for most migrations.
A small development project — days including testing, not weeks. It sits a notch above the Hosted Checkout path because of the front-end work. SecurePay keeps working in parallel until you switch it off.
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) A link to this page, pointing at Part 2. (2) Access to a development store — ask your Teya contact. (3) Two questions to answer back: does the integration need pre-authorisations, and does it use stored cards or recurring payments? Recurring is migrated last — see stored cards & recurring. ```Replace the signed SecurePay form (redirect or iframe) with a checkout session created on your server and the Checkout component mounted on your page.
Today (SecurePay):
checkhash to SecurePay — a redirect or an iframe (displaymode=iframe).returnurlsuccess and posts an orderhash to returnurlsuccessserver.orderhash and fulfils the order.After (Embedded Components):
session_token.onSuccess / onError callback fires, and Teya sends a signed webhook to your server — the source of truth for fulfilment.The biggest change: the signed form post, checkhash, orderhash check and any iframe plumbing are deleted — the component replaces them, and there are no return URLs at all.
Ask your Teya contact for a development store. In the Staging Business Portal, select the store, go to Settings → Integrations and create an ecommerce API credential: you receive a Client ID, a Client Secret (shown once) and your Store ID. Repeat in the production Business Portal at go-live; never mix the two.
SecurePay equivalent: merchantid + paymentgatewayid become the Store ID; the checkhash secret key becomes the Client ID and Client Secret.
| Staging | Production | |
|---|---|---|
| API | https://api.teya.xyz | https://api.teya.com |
| OAuth | https://id.teya.xyz/oauth/v2/oauth-token | https://id.teya.com/oauth/v2/oauth-token |
| SDK | developmentMode: true | flag omitted |
Register a public HTTPS endpoint with no authentication in the Business Portal (Settings → Integrations → Add webhook). The Portal issues a public key per webhook, used in Step 5.
SecurePay equivalent: returnurlsuccessserver + orderhash, verified with an RSA signature instead of a shared-secret hash. Only successful payments produce a webhook; failures surface in onError and via session lookup.
Search your codebase for: borgun.is, securepay, checkhash, orderhash, merchantid, paymentgatewayid, returnurlsuccess, returnurlsuccessserver, returnurlcancel, returnurlerror, itemdescription_, itemcount_, itemunitamount_, itemamount_, displaymode, skipreceiptpage, merchantlogo, pagetype. Each hit is a line on your to-do list.
session_tokenonSuccess is for UX; the webhook is the source of truth
| SecurePay (today) | Embedded Components (new) | What it means for you |
|---|---|---|
merchantid + paymentgatewayid | store_id | One UUID replaces both. |
Secret key + HMAC checkhash | client_id + client_secret → OAuth Bearer token | No per-request HMAC. Cache and refresh from expires_in (currently 15 min in production, 24 h in staging). |
| HTML form posted to SecurePay | POST /v2/checkout/sessions → SDK mounts the component with the session_token | The browser never calls Teya's API; it only hosts the component. |
displaymode=iframe + iframe result plumbing | The component is the embedded experience | Teya-hosted iframe fields; your iframe glue code goes. |
amount as a decimal string (54,43) | amount.value — integer in minor units (5443) | ×100 for EUR, GBP, CZK, HUF; ISK has no subunit — 100 ISK is 100. |
currency | amount.currency — ISO 4217 alphabetic | ISK, EUR, CZK, HUF, GBP… |
orderid (max 12 alphanumeric) | merchant_reference + optional metadata | Write the legacy orderid into metadata during transition if it helps reconciliation. |
language | SDK locale option | Set on initTeyaBlocks. |
buyername, buyeremail | customer.name, customer.email on the session | Prefill only. |
itemdescription_0 … | line_items[] on the session | Description, quantity, unit price in minor units. |
returnurlsuccess / returnurlcancel / returnurlerror | onSuccess / onError callbacks | No redirects; your page decides what happens next. |
returnurlsuccessserver + orderhash | Webhook payment.succeeded.v1, RSA-signed | Verify the signature over the raw body, then fulfil. |
skipreceiptpage, merchantlogo, pagetype | Not applicable — there is no Teya page | Theme the component to match your page. |
| Verified by Visa / MC SecureCode | Automatic 3D Secure inside the component | Challenge renders inline. Nothing to build. |
| Dynamic Currency Conversion | Automatic in the Checkout and Card components | Nothing to implement. |
| (no equivalent) | Apple Pay | In the Checkout component; one-off domain verification (Step 3). |
| (no equivalent) | Pre-authorisation + partial capture | See After the payment. |
| Ticket API | Payment links | Ticket API → Payment Links |
| Recurring payments / stored cards | Card on File | Migrate last — stored cards & recurring. |
| Refunds via portal / manual | POST /v3/refunds or the Business Portal | Partial and multiple refunds. |
Steps 1–5 build the Checkout component end to end, in cURL against staging. Other languages, and the interactive quickstart, are on the Embedded Checkout get started page.
Exchange client_id and client_secret for a short-lived Bearer token; this replaces checkhash signing. Add captures/create to the scopes if you use pre-authorisations.
Things to get right: derive the cache lifetime from expires_in (currently 15 min production, 24 h staging); refresh when under a minute remains or on any 401; one cache per environment. Full reference: Authenticate.
When the customer reaches payment, create a session describing the intent — amount, currency, reference. The response carries the session_token your page hands to the SDK. This replaces the signed form; no card data is involved.
Things to get right:
amount.value is an integer in minor units — €54.43 is 5443; ISK face value as-is. The most common migration bug.Idempotency-Key is required and unique per order attempt.session_token is single-use and the session expires — create it when the customer reaches payment, not on cart load; create a fresh one for a retry.session_token to the front end through a small endpoint of your own (e.g. POST /api/teya-session) — never the access token or client secret. Store the session_id against the order first.Full reference: Embedded Checkout · API reference.
Install the SDK (npm install @teyaproduct/teya-blocks-js; React also @teyaproduct/teya-blocks-react), initialise it with the session token from your backend, and mount the component into a container. This replaces the SecurePay form or iframe.
Things to get right:
developmentMode: true is required in staging — without it the SDK targets production and the staging token is rejected. Gate it on environment config; never ship it to production.appearance — presets, CSS variables and fonts on the Customize page.Full reference: SDK Initialization · Embedded Checkout (HTML/JS and React).
For your own form layout and pay button, mount a card element instead and call card.submitPayment() from your button; use its onChange event (event.complete) to enable the button. Session and webhook are unchanged; Apple Pay is a separate element. See Card element.
Use onSuccess / onError for the user experience only; fulfil from the webhook. If the customer closed the page mid-payment or the webhook is delayed, look the session up by ID — this replaces the returnurlsuccess redirect and orderhash check.
Things to get right: session_status runs ACTIVE → PROCESSING → COMPLETED / EXPIRED; trust payment_status only once the session is COMPLETED. Store the transaction_id from the completed session or the webhook — you need it for refunds and receipts. Full reference: API reference.
Teya posts a signed payment.succeeded.v1 event to your webhook URL when a payment succeeds — your fulfilment trigger, replacing returnurlsuccessserver, and one that no longer depends on the customer's browser. Verify the x-teya-signature header (SHA256 with RSA, Base64) against the public key for this webhook, over the entire raw body, before parsing. Match the order on data.merchant_reference or data.session_id.
Things to get right:
200 promptly; do slow work asynchronously.Full reference: Webhooks — payload fields, verification steps and sample code.
Identical to the Hosted Checkout path — the server side doesn't care how the payment was collected. Refunds and captures report their outcome in the synchronous response; there is no webhook for them.
Refunds. Full, partial and multiple refunds up to the original amount — by API or in the Business Portal with no code. 201 is processed; 202 is still processing, retry with the same idempotency key. A DCC payment is refunded in the currency the customer chose.POST /v3/refunds · scope refunds/create · Payment actions
Status lookup. Reconcile by session; store the transaction_id with each order.GET /v2/checkout/sessions/{session_id} · scope checkout/sessions/id/get · API reference
Digital receipts (new, optional). Email the customer a receipt for a completed payment.POST /v1/transactions/{transaction_id}/receipts · scope transactions/id/receipts/create · Payment actions
Pre-authorisations (new). SecurePay never offered holds; the new platform does. Pre-authorisations are currently available for certain tourism-related business activities; if your business needs them for another use case, contact your Teya account manager. Create the session with "type": "PRE_AUTHORISATION"; the component flow is otherwise identical (no DCC on pre-authorisations). Capture once per authorisation, any amount up to the total, with POST /v1/transactions/{transaction_id}/capture (scope captures/create); the remainder is released. To release a hold early, refund the uncaptured payment — that reverses it. See Payment actions.
Test in staging with developmentMode: true and the test cards on the Testing page. If your dev environment isn't publicly reachable, tunnel it so staging webhooks can reach you.
onSuccess fires, webhook verified, session COMPLETED / SUCCESS, order fulfilled once401 by refreshingid.teya.xyz → id.teya.com, api.teya.xyz → api.teya.com) and remove developmentMode: true.| Symptom | Likely cause | Fix |
|---|---|---|
| Staging session token rejected by the SDK | developmentMode: true missing | Add it in staging; remove in production |
| Payments land in the wrong environment | developmentMode shipped to production, or hosts mixed | Gate on environment config; audit hosts |
| Charged amount is 100× wrong | Decimal → minor-units conversion missed | €54.43 is 5443; ISK face value as-is |
| Component won't mount / token error | Session token already used, or session expired | One token per mount; create a fresh session |
401 on API calls | Access token expired | Refresh; derive cache lifetime from expires_in |
403 / 409 | Missing scope / same Idempotency-Key, different body | Add the scope (Step 1) / unique key per order attempt |
| Webhook never arrives | URL unreachable, firewall, or non-200 response | Public HTTPS, no auth; allowlist Teya IPs; return 200 |
| Signature verification fails | Wrong key, or a parsed body | Per-webhook, per-environment key; verify the raw body; use the Signature Validator |
| Apple Pay button doesn't render | Domain not verified in this environment | Verify staging and production separately |
initTeyaBlocks() options and developmentModeFor businesses whose website talks directly to RPG (Borgun Payment API). The first half is for the business owner; the second half is for your developer.
borgun.is, rpgapi or /api/payment, authenticated with a Private Access Token./api/mpi/v2/enrollment and /api/mpi/v2/validation.BAPIjs / borgun-payment.js to create single-use tokens.securepay.borgun.is, payment URLs created in advance, and stored cards charged without the customer present each have their own guide.
link: /online-payments/migration/overview
```
RPG runs on Teya's legacy platform, which is retiring in early 2027; onboarding to it stops soon. The new platform is an upgrade, not a like-for-like swap: it removes the parts of RPG that cost the most to build and maintain.
Checkout component Recommended — the closest match to an RPG checkout. The customer stays on your page; one block provides card fields, pay button, 3D Secure, Apple Pay and currency conversion.
For a one-off-payments RPG integration this is a small development project; pre-authorisations and custom logic sit at the upper end. RPG keeps working in parallel until you switch it off.
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) A link to this page, pointing at the developer guide below. (2) A development store from your Teya account manager, for staging credentials. (3) Two questions to answer back: does the integration use pre-authorisations, and does it use stored cards or recurring payments (multi-use tokens)? Both change the plan. ```How an RPG integration maps onto a checkout session and the Checkout component, with the one request you need at each step.
Today (RPG)
BAPIjs collects the card; card data or a single-use token reaches your server.TermURL callback, validation, MdStatus.POST /api/payment with the ThreeDSecure block and reads ActionCode.PaymentCreate webhook arrives and you call back GET /api/events/{EventId}.After (Embedded Components)
onSuccess fires for the user experience; a signed webhook confirms the payment to your server and drives fulfilment.The biggest change: the whole MPI flow and all card-data or single-use-token handling are deleted — the component replaces both.
Ask your Teya account manager for a development store. In the Staging Business Portal, select the store, go to Integrations and create ecommerce API credentials: a Client ID, a Client Secret (shown once) and your Store ID. Repeat at business.teya.com for production; never mix the two. This replaces your Private and Public Access Tokens. See Authenticate.
Staging is api.teya.xyz / id.teya.xyz / business.teya.xyz with developmentMode: true in the SDK; production is the same hosts on .com without the flag. Full table on Embedded Checkout.
Teya sends a signed payment.succeeded.v1 event to a public HTTPS endpoint with no authentication on it.
Register the URL in the Business Portal next to your credentials; the portal issues a public key per webhook for verifying signatures.
Unlike RPG's PaymentCreate / Refund / Capture events, only successful customer payments produce a webhook today; refunds and captures you trigger return their outcome in the API response. See Webhooks.
Search for borgun.is, rpgapi, /api/payment, /api/token/single, /api/mpi, TransactionType, ActionCode, MdStatus, TermURL, MpiToken, BAPIjs, borgun-payment, PrivateAccessToken, BorgunRPGNet, RPGClient. Each hit is a line on the to-do list. The .NET SDK is a thin wrapper over the same endpoints, so the mapping below applies to its methods one-to-one; the new platform has no server-side SDK — plain HTTPS on the server, the JS SDK in the browser.
The RPG capability reference maps each capability to its replacement. This table goes one level down: the calls and fields an RPG developer has to change.
| RPG today | New platform | What it means for you |
|---|---|---|
| Private Access Token (permanent, Basic auth) | client_id + client_secret → short-lived Bearer token | Cache and refresh from expires_in (currently 15 min in production, 24 h in staging). |
| Public Access Token (browser tokenisation) | Not needed — the session token comes from your backend per payment | Delete with the BAPIjs code. |
POST /api/payment, TransactionType: "Sale" | POST /v2/checkout/sessions, type: "SALE" + the component | The server sends intent only — amount, currency, reference. No card data, no 3DS block. |
POST /api/payment, TransactionType: "PreAuthorization" | type: "PRE_AUTHORISATION" on the same endpoint | See Pre-authorisations below. |
PUT /api/payment/{id}/capture (full only) | POST /v1/transactions/{id}/capture, {"amount": n} | Partial capture gained. Once per authorisation; the remainder is released. |
PUT /api/payment/{id}/cancel | POST /v3/refunds on the uncaptured payment | A refund before capture reverses the authorisation. No separate cancel. |
PUT /api/payment/{id}/refund + PartialAmount | POST /v3/refunds with transaction_id, amount — or the Business Portal | Same semantics: partial, multiple, total ≤ original. |
GET /api/payment/{id} | GET /v2/checkout/sessions/{session_id} | session_status + payment_status instead of TransactionStatus. |
POST /api/token/single + BAPIjs.getToken | Gone — the component's hosted fields do this | No token creation, lifetime management or disable calls. |
/api/mpi/v2/enrollment, /validation, TermURL, MdStatus, MpiToken | Gone — 3D Secure runs inside the component, challenge inline | Delete the whole flow. |
POST /api/payment with raw PAN | Direct API — restricted, PCI SAQ D evidence required | Use the component unless you have no alternative. |
POST /api/token/multi, VerifyCard | Card on File | Separate track — Stored cards & recurring. |
Amount — 2 implied decimals, including ISK (100 ISK = 10000) | amount.value — minor units per ISO 4217; ISK has no subunit (100 ISK = 100, €54.43 = 5443) | Divide RPG ISK amounts by 100. The most likely silent bug in an ISK migration; other currencies are unchanged. |
Currency — ISO numeric ("352") | amount.currency — ISO alphabetic ("ISK") | Lookup change. |
OrderId — exactly 12 chars, A–Z/0–9 | merchant_reference (free-form) + optional metadata | Write the legacy OrderId into metadata during transition if it helps reconciliation. |
TransactionDate | Not sent — Teya timestamps server-side | Delete. |
ActionCode ("000" = approved) + TransactionStatus | payment_status (SUCCESS / FAILED) on the session | No action-code tables; declines show in the component and the customer retries in place. |
DynamicDescriptors | Not exposed on checkout sessions | Tell your Teya contact before migrating if you rely on them. |
PaymentCreate webhook + GET /api/events/{EventId} | payment.succeeded.v1, RSA-signed (x-teya-signature) | Verify by signature instead of calling back. |
| B-Online | Business Portal | Credentials, webhooks, refunds, transaction visibility. |
Steps 1–5 build the Checkout component end to end. Samples are cURL and minimal JavaScript; other languages are on the Embedded Checkout Get Started page.
Exchange client_id + client_secret for a Bearer token. This replaces the permanent Private Access Token: cache the token per environment and refresh it before expires_in runs out or on a 401. Scopes for this guide: checkout/sessions/create, checkout/sessions/id/get, refunds/create, plus captures/create for pre-authorisations.
Full reference: Authenticate.
When the customer clicks Pay, your server describes the payment and gets back a session token for the page. This replaces POST /api/payment, but the body carries no card data and no 3DS block — only the intent.
Things to get right
amount.value is an integer in minor units per ISO 4217: 100 ISK = 100, not RPG's 10000.Idempotency-Key is unique per order attempt; retries with the same key can't create duplicate sessions.session_token to the browser through a small endpoint of your own — never the access token or the Client Secret.Full reference: API Reference.
Install @teyaproduct/teya-blocks-js, initialise the SDK with the session token from your backend, and mount the component. This replaces your card form and BAPIjs.getToken: the fields are Teya-hosted, so card data never enters your DOM.
Things to get right
developmentMode: true; a production token only works without it. Gate the flag on your own environment.MdStatus are no longer your code.Full reference: SDK Initialization and Embedded Checkout.
For your own form layout and pay button, create a card element instead and call card.submitPayment() from your button; the session and everything after are the same. No built-in Apple Pay button. See Card Element.
onSuccess / onError are for the user experience: navigate to a confirmation page, show an error state. The webhook (Step 5) is the source of truth for fulfilment. As a fallback (customer closed the browser, webhook delayed), look the session up by ID. This replaces GET /api/payment/{id} and the "only 000 means approved" rule.
Things to get right
session_status runs ACTIVE → PROCESSING → COMPLETED or EXPIRED; trust payment_status only once COMPLETED.transaction_id from the webhook against the order — it is the key for refunds and receipts.Full reference: API Reference.
Teya POSTs a signed payment.succeeded.v1 event to your registered URL when a payment succeeds — the equivalent of RPG's PaymentCreate, verified by signature instead of a call back to GET /api/events/{EventId}. Verify x-teya-signature (SHA256withRSA, Base64) over the raw request body with the public key issued for this webhook, before parsing anything.
Things to get right
200 promptly and do slow work asynchronously.Full reference: Webhooks.
Each operation returns its outcome in the synchronous API response — there is no webhook for refunds or captures today, so record the response.
Replaces PUT /api/payment/{id}/refund, same rules: partial and multiple refunds, total ≤ original. 201 is processed; 202 is still processing — retry with the same Idempotency-Key. Staff can also refund from the Business Portal. Payments taken on RPG before cutover stay refundable through B-Online for a transition period.
See Payment Actions.
Status: GET /v2/checkout/sessions/{session_id} (Step 4) replaces GET /api/payment/{id}; the Business Portal covers day-to-day visibility. Receipts are new — POST /v1/transactions/{id}/receipts emails a Teya-generated receipt (scope transactions/id/receipts/create). See Payment Actions.
Skip this if you only send TransactionType: "Sale". Create the session with type: "PRE_AUTHORISATION"; the component flow is unchanged and the funds are held (no DCC on pre-auth). Capture with POST /v1/transactions/{id}/capture and {"amount": n} — any amount up to the authorised total, but exactly once: the remainder is released, so capture the final amount. There is no cancel call: refund the uncaptured payment to reverse the authorisation; untouched holds expire on their own. Pre-authorisations are currently available for certain tourism-related business activities; if your business needs them for another use case, contact your Teya account manager. Capture is API-only today; confirm before migrating pre-auth flows. See Payment Actions.
Test in staging with developmentMode: true and the cards on the Testing page — frictionless, challenge and declined scenarios are all there. If your development machine isn't publicly reachable, tunnel it so staging webhooks can reach you.
Definition of done
onSuccess fires, webhook received and verified, session COMPLETED / SUCCESS401 by refreshing the tokenGo live
business.teya.com; store the new public key..xyz → .com) and remove developmentMode.| Symptom | Likely cause | Fix |
|---|---|---|
| Charged amount is 100× too big (ISK) | RPG's two-implied-decimals convention carried over | Divide legacy ISK amounts by 100 |
| Staging session token rejected by the SDK | developmentMode: true missing | Add it in staging, remove in production |
401 on API calls | Access token expired — currently 15 min in production, not permanent like the RPG token | Refresh from expires_in; retry once on 401 |
403 | Missing scope | Add the scope to the token request (Step 1) |
409 | Same Idempotency-Key, different body | Unique key per order attempt; reuse only for retries of the identical request |
422 creating a session | Numeric currency code or non-integer amount | Alphabetic currency; integer amount.value in minor units |
| Webhook never arrives | URL not public, no valid certificate, auth on the endpoint, or non-200 responses | Public HTTPS, no auth, return 200 after processing |
| Signature verification fails | Parsed body re-serialised, or the wrong key | Verify the raw body; use the key issued for this webhook in this environment |
| Payment succeeded but not in the Business Portal | Wrong environment | Staging payments are in business.teya.xyz, production in business.teya.com |
| Waiting for a refund or capture webhook | Only successful customer payments send one | Read the outcome from the API response |
For businesses whose systems send payments to B-Gateway directly: the Borgun payment gateway web service (SOAP/XML), also written BGateway and known in Iceland as Greiðslugátt or simply "the Borgun web service". The first half is for the business owner; the second half is for your developer.
getAuthorization to gateway01.borgun.is, with a B-Gateway username and password.9910…) and send them on to B-Gateway.getAuthorization call for payments where a customer is checking out and for charges you run later without them (subscriptions, instalments, top-ups). You may also run 3D Secure through get3DSAuthentication or your own or a third-party MPI./api/payment (the RPG payment API), redirects to securepay.borgun.is, and payment URLs created in advance each have their own guide.
link: /online-payments/migration/overview
```
B-Gateway runs on Teya's legacy platform, which is retiring in early 2027. The new platform is an upgrade, not a like-for-like swap: it removes the parts of a B-Gateway integration that cost the most to build and maintain.
Checkout component Recommended — the closest match to a B-Gateway checkout with the least to maintain. The customer stays on your page; one block provides card fields, pay button, 3D Secure, Apple Pay and currency conversion. This guide follows it.
This is a small development project, and the result is less code than you have today: the SOAP client, the card form, any 3D Secure flow and batch handling are all removed. B-Gateway keeps working in parallel until you switch it off. If most of your payments are subscriptions or charges on cards you hold, step 5 and the charging of saved cards are the bulk of the work; the one-off checkout still comes first, because it is how new cards get saved from now on. Stored cards move on their own track, without any customer re-entering a card.
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) A link to this page, pointing at the developer guide below. (2) A development store from your Teya account manager, for staging credentials. Card saving (tokenization) is enabled by default, but enrolment can take up to 48 hours, so ask for the store early. (3) Questions to answer back: do you charge cards without the customer present (subscriptions, instalments, top-ups), and are those cards virtual card numbers from B-Gateway or card numbers you store yourself; do you use pre-authorisations; and do you use any other B-Gateway features (telephone or keyed orders, reports pulled from B-Gateway, Dynamic Descriptors, detail or itinerary data)? Each changes the plan. ```How a B-Gateway integration maps onto a checkout session, Hosted Checkout or the Checkout component, and token payments, with the one request you need at each step.
Today (B-Gateway)
get3DSAuthentication or your own MPI, sends the customer to the bank and gets CAVV / UCAF / XID back.getAuthorization (SOAP/XML, username and password, Processor, MerchantID, TransType, RRN) with the card or a virtual card number, plus 3D Secure values if you have them, and reads ActionCode. The same call is used whether or not the customer is present.getVirtualCard and later send getAuthorization with it, without the customer.DateAndTime + RRN; batches close at midnight GMT or on newBatch.After (new platform)
save_payment_method: true.POST /v3/transactions/online, marked as merchant-initiated.transaction_id. There are no batches.The biggest change: card numbers no longer pass through your systems, and each payment is either a checkout session (customer present) or a charge on a saved card (merchant-initiated). The SOAP client, the card form, any 3D Secure flow, getVirtualCard and batch handling are all deleted.
List every place your code calls getAuthorization and decide, for each one, whether a customer is present. On B-Gateway the same request serves both; on the new platform they are different calls:
POST /v3/transactions/online on a saved token, declared merchant-initiated (Step 6). No 3D Secure, immediate result.ACCOUNT_VERIFICATION session with save_payment_method (Step 2).Every item on your list becomes either a session or a token charge. A charge without the customer is only possible on a card they saved with you, so if you hold card numbers that were never saved through Teya, see Stored cards & recurring before you plan Step 6.
Ask your Teya account manager for a development store. In the Staging Business Portal, select the store, go to Integrations and create ecommerce API credentials: a Client ID, a Client Secret (shown once) and your Store ID. Repeat at business.teya.com for production; never mix the two. This replaces your B-Gateway username, password, Processor and MerchantID. See Authenticate.
Card saving (tokenization) is enabled by default on every store, but enrolment can take up to 48 hours after the store is created. Until then, sessions with save_payment_method succeed but return no token. If that persists after 48 hours, contact support.
Staging is api.teya.xyz / id.teya.xyz / business.teya.xyz; production is the same hosts on .com. With Embedded Components, staging also needs developmentMode: true in the SDK. Full table on Hosted Checkout Get Started.
Teya sends a signed payment.succeeded.v1 event to a public HTTPS endpoint with no authentication on it. Register the URL in the Business Portal next to your credentials; the portal issues a public key per webhook for verifying signatures. Only successful customer payments produce a webhook today; refunds, captures and token charges return their outcome in the API response. See Webhooks.
Search for gateway01.borgun.is, borgun.is, getAuthorization, cancelAuthorization, get3DSAuthentication, getVirtualCard, newBatch, getTransactionList, TransType, TrAmount, TrCurrency, RRN, AuthCode, ActionCode, Processor, CAVV, UCAF, XID, SecurityLevelInd, lifecycleID, the XML namespace tempuri.org/getAuthorization.xsd, and stored 16-digit values starting 9910. Code based on the B-Gateway demo clients (BGatewayDemoNet, BGatewayDemoPhp, BGatewayDemoPython) follows the same pattern. Each hit is a line on the to-do list. If you send TerminalID 2 (ecommerce with 3D Secure) or CAVV / UCAF values, you run 3D Secure today: also check the MPI configuration behind it.
The calls and fields a B-Gateway developer has to change. The B-Gateway reference covers every operation, including reporting and the ones with no equivalent.
| B-Gateway today | New platform | What it means for you |
|---|---|---|
| Username + password on every call | client_id + client_secret → short-lived Bearer token | Cache and refresh from expires_in (currently 15 min in production, 24 h in staging). |
Processor, MerchantID, TerminalID (1 ecommerce, 2 ecommerce with 3D Secure, 3 recurring, 4 mail/telephone order) | store_id | One identifier per store, sent in the request body. What the terminal said about the payment is now said by the request itself: a session for customer-present payments, initiator + recurring_payment_type for the rest. |
getAuthorization, TransType 1 | POST /v2/checkout/sessions, type: "SALE" + the Checkout component | The server sends intent only: amount, currency, reference. No card data, no 3D Secure values. |
getAuthorization, TransType 5 | type: "PRE_AUTHORISATION" on the same endpoint | See Pre-authorisations below. |
TransType 1 with a pre-authorisation's AuthCode | POST /v1/transactions/{id}/capture, {"amount": n} | Once per authorisation; the remainder is released. |
TransType 3 by DateAndTime + RRN | POST /v3/refunds with transaction_id, or the Business Portal | Same semantics: partial, multiple, total ≤ original. |
TransType 4, cancelAuthorization | POST /v3/refunds on the uncaptured payment | A refund before capture reverses the authorisation. No partial reversal; no open-batch rule. |
get3DSAuthentication / own MPI; CAVV, UCAF, XID, SecurityLevelInd (if you send them) | Gone: 3D Secure runs inside Hosted Checkout and the component | Delete the whole flow if you have one, including MerchantReturnURL handling. If you don't run 3D Secure today, there is nothing to delete. |
PAN, ExpDate, CVC2; your card form | Gone: entered on the Teya page or in Teya-hosted fields | Delete the card form and every place card data is logged or stored. |
getVirtualCard | save_payment_method: true on the session; token_response.token_id in the webhook | Saving happens inside the payment, or a zero-amount ACCOUNT_VERIFICATION. |
getAuthorization with a virtual card number (or a card number you store), customer absent | POST /v3/transactions/online, initiator: "MERCHANT", payment_method.type: "TOKEN" | See Step 6. Virtual numbers you already hold move later; see Stored cards & recurring. Card numbers you store yourself have no import: talk to your Teya account manager. |
lifecycleID | Not sent | The platform links later charges to the first payment for you. |
TrAmount: 2 implied decimals, including ISK (100 ISK = 10000) | amount.value: minor units per ISO 4217; ISK has no subunit (100 ISK = 100) | If you charge in ISK, divide B-Gateway amounts by 100: the most likely silent bug. Other currencies are unchanged (€49.90 is 4990 on both). |
TrCurrency: ISO numeric (352) | amount.currency: ISO alphabetic ("ISK") | Lookup change. |
RRN (12 characters), DateAndTime | merchant_reference (free-form) + optional metadata; no timestamp | Write the legacy RRN into metadata during the transition if it helps reconciliation. |
ActionCode (000 = approved) | payment_status (SUCCESS / FAILED) on the session | Declines show in the checkout and the customer retries in place. |
Batches, newBatch, Batch / Transaction numbers | None | Delete. Key your records on transaction_id. |
getTransactionList, settlement and dispute lists | GET /v2/checkout/sessions/{session_id} for one payment; the Business Portal for the rest | See the reference for each report. |
| B-Online | Business Portal | Credentials, webhooks, refunds, transaction visibility. |
Steps 1–5 take one-off payments end to end; Step 6 adds charging a saved card. Samples are cURL and minimal JavaScript; other languages are on the Embedded Checkout Get Started page.
Exchange client_id + client_secret for a Bearer token. This replaces sending a username and password on every call: cache the token per environment and refresh it before expires_in runs out or on a 401. Scopes for this guide: checkout/sessions/create, checkout/sessions/id/get, refunds/create. Add captures/create for pre-authorisations and transactions/online/create for charging saved cards.
Full reference: Authenticate.
When the customer clicks Pay, your server describes the payment. This replaces getAuthorization, but the body carries no card data and no 3D Secure values; only the intent. For a subscription sign-up, add save_payment_method: true so the card is saved during this payment.
Things to get right
amount.value is an integer in minor units per ISO 4217: 4,990 ISK is 4990, not B-Gateway's 499000; €49.90 is 4990 on both.Idempotency-Key is unique per order attempt; retries with the same key can't create duplicate sessions. This replaces looking up an unknown result with getTransactionList.type: "ACCOUNT_VERIFICATION" with amount.value: 0 and save_payment_method: true. This replaces the zero-amount pre-authorisation.session_token in the response is what your page needs for the component (Step 3). success_url and cancel_url are only used by Hosted Checkout.Full reference: Embedded Checkout, API Reference.
Install @teyaproduct/teya-blocks-js, initialise the SDK with the session_token from your backend, and mount the component. It replaces your card form and your 3D Secure redirect: the fields are Teya-hosted, so card data never enters your page or your server.
Things to get right
developmentMode: true; a production token only works without it.session_token to the browser through a small endpoint of your own; never the access token or the Client Secret.Using Hosted Checkout instead? Skip the SDK: send the browser to the session_url returned with the session, exactly as returned. The customer pays on Teya's page and lands on your success_url or cancel_url; the redirect carries no payment data, so Steps 4 and 5 are unchanged. See Hosted Checkout Get Started.
Full reference: SDK Initialization and Embedded Checkout.
The webhook (Step 5) is the source of truth for fulfilment. As a fallback (customer closed the browser, webhook delayed, payment failed), look the session up by ID. This replaces reading ActionCode and polling getTransactionList.
Things to get right: session_status runs ACTIVE → PROCESSING → COMPLETED or EXPIRED; trust payment_status only once COMPLETED. Store the transaction_id: it is the key for refunds, replacing DateAndTime + RRN. Full reference: API Reference.
Teya POSTs a signed payment.succeeded.v1 event to your registered URL when a payment succeeds, whether or not the customer comes back to your site. Verify x-teya-signature (SHA256withRSA, Base64) over the raw request body with the public key issued for this webhook, before parsing anything. When the card was saved, the event carries the token.
Things to get right
data.token_response.token_id against the customer. It replaces the virtual card number in your subscription records. token_response is null when no card was saved.200 promptly and do slow work asynchronously.Full reference: Webhooks.
For each subscription or instalment due, charge the token without the customer present. This replaces getAuthorization with a virtual card number and no 3D Secure values. Declare it as merchant-initiated: there is no 3D Secure and the result is immediate and final.
Things to get right
recurring_payment_type: RECURRING for a fixed schedule, INSTALLMENT for a fixed number of payments, CARD_ON_FILE for unscheduled charges.Idempotency-Key per customer per billing period, so a re-run of your billing job can't charge twice.status_reason such as INSUFFICIENT_FUNDS or TEMPORARY_ISSUE_RETRY); the full list is on Charge a Saved Payment Method.Full reference: Charge a Saved Payment Method.
Each operation returns its outcome in the synchronous API response; there is no webhook for refunds or captures today, so record the response.
Replaces TransType 3 and cancelAuthorization: partial and multiple refunds, total ≤ original, referenced by transaction_id instead of DateAndTime + RRN. 201 is processed; 202 is still processing: retry with the same Idempotency-Key. Staff can also refund from the Business Portal. Payments taken on B-Gateway before you switch stay refundable in B-Online.
See Payment Actions.
Skip this if you only send TransType 1 without an AuthCode. Create the session with type: "PRE_AUTHORISATION"; the checkout is unchanged and the funds are held (no currency conversion on pre-authorisations). Capture with POST /v1/transactions/{id}/capture and {"amount": n}: any amount up to the authorised total, but exactly once. The remainder is released, so capture the final amount; this also replaces the partial reversal (TransType 4). To cancel, refund the uncaptured payment; untouched holds expire on their own. See Payment Actions.
Batches, transaction and settlement reports, telephone or keyed orders, Dynamic Descriptors and detail or itinerary data each have their own answer on the new platform, and some depend on your set-up. If you use any of them, contact Teya support with your specific case before you migrate, so we can check it with you. The B-Gateway reference lists every operation and where it lives now.
Test in staging with the cards on the Testing page; frictionless, challenge and declined scenarios are all there. If your development machine isn't publicly reachable, tunnel it so staging webhooks can reach you.
Definition of done
COMPLETED / SUCCESS401 by refreshing the tokensave_payment_method and an ACCOUNT_VERIFICATION both return a token_id in the webhook; a merchant-initiated charge on that token succeedsGo live
business.teya.com; store the new public key. If you save cards, create the production store early: card saving is on by default but enrolment can take up to 48 hours..xyz → .com) and, for Embedded Components, remove developmentMode.getVirtualCard and batch code. Remove stored card numbers and card data from your systems and logs. Teya retires the B-Gateway usernames and passwords for you; there is nothing to request.| Symptom | Likely cause | Fix |
|---|---|---|
| Charged amount is 100× too big (ISK) | B-Gateway's two-implied-decimals convention carried over | Divide legacy ISK amounts by 100 |
422 creating a session | Numeric currency code (352) or a non-integer amount | Alphabetic currency; integer amount.value in minor units |
401 on API calls | Access token expired; unlike a B-Gateway password it is short-lived | Refresh from expires_in; retry once on 401 |
403 | Missing scope, e.g. transactions/online/create for saved-card charges | Add the scope to the token request (Step 1) |
409 | Same Idempotency-Key, different body | Unique key per order attempt or billing period; reuse only for retries of the identical request |
Payment succeeds but no token_id in the webhook | save_payment_method missing, or the store was created less than 48 hours ago and tokenization enrolment is still pending | Send save_payment_method: true; wait for enrolment, then contact support if it persists |
400 charging a 9910… value as TOKEN | A legacy virtual card number sent as a Teya token | Keep charging it on B-Gateway until your stored cards are moved, then use LEGACY_TOKEN. See Stored cards |
| A refund can't find the payment | Legacy DateAndTime + RRN used as the reference | Refund by transaction_id; refund B-Gateway payments in B-Online |
| Webhook never arrives | URL not public, no valid certificate, auth on the endpoint, or non-200 responses | Public HTTPS, no auth, return 200 after processing |
| Signature verification fails | Parsed body re-serialised, or the wrong key | Verify the raw body; use the key issued for this webhook in this environment |
| Payment succeeded but not in the Business Portal | Wrong environment | Staging payments are in business.teya.xyz, production in business.teya.com |
For businesses that create payment URLs in advance with the Ticket API and send them to customers by email, invoice or phone. This guide moves that flow to Pay By Link — with or without code.
The Ticket API runs on Teya's legacy platform, which is retiring in early 2027. Its replacement is Pay By Link: the same idea — a URL you create and share, opening a Teya-hosted payment page — on the new platform.
Business Portal / Teya app Recommended for a handful of links a week — staff enter the amount, a description and your reference, and the Portal emails or texts the link to the customer. Status, resend, cancel and refund are all in the Portal. No developer needed: the migration is a walkthrough of Pay By Link in the Portal.
Pay By Link API — if your systems generate the URLs (invoicing runs, booking confirmations, reminders), your developer replaces each Ticket API call with one Payment Links call and handles a webhook. See the developer guide below.
Both work side by side on the same store.
Portal only: done after a team walkthrough. API: days including testing, not weeks — a fraction of a checkout migration.
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) The link to this page. (2) Which systems create tickets today, and where the paid confirmation needs to land (invoicing, CRM, booking). (3) Whether links should expire, be resent, or accept Apple Pay. ```Replace each Ticket API call with one Pay By Link call, and the legacy paid confirmation with a signed webhook.
Today (Ticket API)
After (Pay By Link)
POST /v2/payment-links. Teya returns a payment_link URL and a payment_link_id.customer.email and let Teya send it.payment.succeeded.v1 webhook carrying payment_link_id, merchant_reference and transaction_id. You mark the invoice paid.Biggest change: confirmation arrives as a signed webhook you verify server-side, not through a legacy callback.
Ask your Teya contact for a development store. In the Staging Business Portal open the store → Integrations and create ecommerce API credentials: you get a Client ID, Client Secret and Store ID. Repeat in production at go-live.
Staging api.teya.xyz / id.teya.xyz / business.teya.xyz; production .com — full table on the Pay By Link API page. Scopes: payment-links/create payment-links/id/get payment-links/id/update, plus refunds/create if you refund by API. Cache tokens by expires_in (currently 15 minutes in production, 24 hours in staging).
Register a webhook URL under the store's Integrations. The Portal issues a public key for that webhook; keep it with the credentials. See Webhooks.
Search for the Ticket API host and paths, the module that creates tickets, and the code that consumes the legacy paid confirmation. Each ticket-creation call site becomes one POST /v2/payment-links; the confirmation consumer becomes your webhook handler.
amount.value is an integer in minor units — €54.43 is 5443; ISK has no subunit, so 100 ISK is 100Idempotency-Key on every create; a retry with the same key returns the same link| Ticket API today | Pay By Link | Notes |
|---|---|---|
| Ticket-creation call | POST /v2/payment-links | Or Create Payment Link in the Portal |
| Legacy credentials / per-request signature | OAuth client_credentials bearer token | Scope payment-links/create |
| Ticket URL | payment_link | Deliver it as today, or set customer.email / phone_number |
| Ticket identifier | payment_link_id | Store it against the invoice or order |
| Amount and currency | amount.value, amount.currency | Minor units |
| Your order / invoice reference | merchant_reference | Returned in the webhook and on lookup |
| Description shown to the customer | line_items | Portal: Description |
| Ticket validity / expiry | expires_at | Extend with PATCH /v2/payment-links/{id} |
| Cancelling a ticket | PATCH with a past expires_at | Portal: Cancel |
| Legacy paid confirmation | payment.succeeded.v1 webhook | Signed; verify with the per-webhook key |
| Checking whether a ticket was paid | GET /v1/payment-links/{id} | Scope payment-links/id/get |
| Refund | POST /v3/refunds | Uses the webhook's transaction_id; or the Portal |
| 3D Secure | Handled on the hosted page | Nothing to build |
cURL only. Other languages: see the Pay By Link API page.
Replaces the legacy per-request credentials. One token serves every call until it expires.
```bash curl -X POST https://id.teya.xyz/oauth/v2/oauth-token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=$TEYA_CLIENT_ID" \ -d "client_secret=$TEYA_CLIENT_SECRET" \ -d "scope=payment-links/create payment-links/id/get payment-links/id/update refunds/create" ``` ```json { "access_token": "eyJhbGciOiJSUzI1NiJ9...", "token_type": "Bearer", "expires_in": 900 } ```expires_in runs out.Full reference: Pay By Link API
Replaces the ticket-creation call. Send the amount, your reference and, if Teya should deliver the link, the customer's details.
```bash curl -X POST https://api.teya.xyz/v2/payment-links \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "amount": { "value": 5443, "currency": "EUR" }, "merchant_reference": "INVOICE-2026-0117", "expires_at": "2026-10-31T23:59:59Z", "line_items": [ { "description": "Invoice 2026-0117", "quantity": 1, "unit_price": 5443 } ], "customer": { "name": "Jan Novák", "email": "jan@example.com" } }' ``` ```json { "payment_link_id": "20eb276c-5b6b-4474-a5a1-49bcd6193148", "payment_link": "https://checkout.teya.com/pbl/20eb276c-5b6b-4474-a5a1-49bcd6193148" } ```payment_link_id against the invoice before you send anything.expires_at to the invoice due date; an expired link shows the customer an error.metadata, receipt emails, supported_payment_methods): see the reference.Full reference: Pay By Link API · API Reference
Replaces the legacy paid confirmation. Verify the x-teya-signature header (SHA256withRSA, Base64) over the raw body with the webhook's public key, then match data.payment_link_id or data.merchant_reference to your invoice and mark it paid.
200 quickly, process asynchronously, and treat a replayed event as already handled.transaction_id: refunds and receipts need it.Full reference: Webhooks — payload fields and the signature validator
Status lookup. GET /v1/payment-links/{payment_link_id} returns the link's status, amount, merchant_reference and expires_at — use it before resending a reminder. The webhook remains the source of truth. To extend or cancel, PATCH /v2/payment-links/{payment_link_id} with a new expires_at (the only updatable field). See Pay By Link API.
Refunds. POST /v3/refunds with the webhook's transaction_id; partial and multiple refunds are allowed up to the original amount (scope refunds/create). Or refund in the Business Portal with no code. See Payment actions.
Receipts. The customer receives a receipt automatically. To send one yourself: POST /v1/transactions/{transaction_id}/receipts.
Pre-authorisation. Not available on payment links — transaction_type is SALE. To reserve funds and capture later, use Hosted Checkout with a PRE_AUTHORISATION session.
Test in staging with the cards on the Testing page. Definition of done:
Go live: production credentials and webhook (new key), swap .xyz hosts for .com, send yourself one real link and pay it, run the Ticket API in parallel for a day or two, then retire the legacy code and ask your Teya contact to decommission the old credentials.
| Symptom | Cause | Fix |
|---|---|---|
403 on create | Token lacks payment-links/create | Request the scopes above when fetching the token |
422, amount rejected | Major units sent (54.43) | Send minor units (5443); ISK at face value |
| Duplicate links after a retry | No Idempotency-Key | One key per invoice attempt, reused on retry |
| Signature never verifies | Body re-serialised, or wrong key | Verify the raw bytes with this webhook's key |
| Invoice never marked paid | Still waiting on the legacy callback | Confirmation now arrives only by webhook — check the endpoint is public HTTPS and returns 200 |
| Customer reports "link expired" | expires_at passed | PATCH a later expires_at, or create a new link |
For businesses that store customers' cards on the legacy platform and charge them again later — subscriptions, instalments or saved cards. This is the second part of a wider migration: first your checkout moves to the new platform using the guide for your integration, then — once that integration is built and live — your customers' stored cards move too, without anyone re-entering a card, in four phases that you control.
Follow this guide if your systems hold token values received when customers saved their cards on the legacy platform, and you charge those tokens again. The stored values look like one of these:
tm_ followed by letters and digits, e.g. tm_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345991001, e.g. 9910012837465190It covers those tokens, the payments you make with them, and how new cards get saved from now on. It does not cover migrating your checkout itself — that is the first part of the project, with its own guide, and it must be finished before the token migration starts.
```admonition icon: fas fa-route title: Where this sits in the migration content: Step 1 — build and go live with your new-platform integration (Hosted Checkout, Embedded Components or the direct API) using the guide for your legacy integration: find it here. Step 2 — this guide: move the stored cards. Everything below assumes step 1 is done; if it isn't yet, start there and come back when your new integration is live. ```The legacy platform, including its token store, is retiring in early 2027. Tokens minted on it cannot be charged on the new platform directly — but they don't need to be re-collected either: Teya imports your stored cards, and each legacy token upgrades itself the first time you charge it.
Four phases, in this order. The order is what makes it safe: at some point Teya takes a copy of your stored cards from the legacy platform, and any token created after that copy would be missing from it. So the first thing to stop is the creation of legacy tokens; the last thing to stop is their use.
Effort: a small development project — one new-card path, a one-line routing rule, and one field change for the cut-over. The phases can be weeks apart; there is no pressure on how long Phase 2 lasts.
```admonition icon: fas fa-paper-plane title: What to send your developer content: (1) This page. (2) The name of your Teya contact, who agrees the phase dates and confirms the import. (3) A list of every system that creates or charges stored cards — every store, every code path, every background job — because Phase 1 has to be complete everywhere before Phase 3 can start. ```The four phases in detail: what to build, the one routing rule, and what happens on the first charge of a legacy token. Token payments themselves are documented on Charge a Saved Payment Method; this page only covers what is specific to migrating.
POST /v3/transactions/online — the direct API used for token payments. Read Card on File and Charge a Saved Payment Method first; the requests below are those pages' requests with one different payment method.Phase 3 can only start after Phase 1 is genuinely done: a legacy token created after the export will not be in the import, and in Phase 4 it fails with an explicit "not migrated" error rather than falling back to the legacy platform. Your Teya contact also provides test-environment legacy-token fixtures and confirms when the legacy-token payment method is enabled for your environment.
Search your codebase for the places that:
/api/token/single, /api/token/multi, SecurePay tokenisation) — Phase 1's scopetm_, 991001) — Phase 2's routing and Phase 4's scopestore_id it was migrated for.token_response.token_id on every successful charge — it is the customer's permanent Teya token.| Legacy platform | New platform | What it means for you |
|---|---|---|
Multi-use token tm_… / virtual card 991001… | Teya token — a UUID in token_response.token_id | Charged with payment method TOKEN. Legacy values are accepted once more, as payment method LEGACY_TOKEN, to convert them (Phase 4). |
POST /api/token/single, /api/token/multi | store_payment_method: true on a SALE or a zero-amount ACCOUNT_VERIFICATION transaction | Tokenisation happens inside the payment; no separate call. See Card on File. |
POST /api/payment with a multi-use token | POST /v3/transactions/online with payment_method.type: "TOKEN" | Same endpoint as every other direct-API payment. See Charge a Saved Payment Method. |
| Recurring flag on the payment | initiator (MERCHANT / CUSTOMER) + recurring_payment_type (RECURRING / INSTALLMENT / CARD_ON_FILE) | MERCHANT = cardholder not present, no 3DS. CUSTOMER = cardholder present, 3DS applies and may challenge. |
| Token deletion | DELETE /v1/tokens/{token_id} → 204 | Delete the token when the customer removes a saved card. |
| Your own token database | Your own token database — now holding a Teya token (UUID) or, temporarily, a legacy value | The routing rule below keys on which one the customer has. |
What: stop creating tokens on the legacy platform. From this day, when a customer saves a card, save it on the new platform and store the Teya token you get back.
Why: every card saved from now on is already migrated, and the export in Phase 3 will be complete.
A token is created by setting store_payment_method: true on a transaction — as part of a sale (type: "SALE"), or without charging the customer (a zero-amount type: "ACCOUNT_VERIFICATION", for an "add a payment method" flow). Either way the response carries a token_response:
Store token_id against the customer. brand and last4 are for what you show them ("Visa ending 1234").
Things to get right:
token_status: "FAILED" with a failure_reason. Treat it as "saving the card didn't work", not as a payment problem.Full reference: Card on File — storing the card.
What: existing legacy tokens keep being charged where they always were — on the legacy platform, unchanged. Customers who saved a card since Phase 1 are charged on the new platform, as an ordinary token payment.
Why: nothing is at risk while the import is prepared, and every card saved during this phase is already on the new platform.
```bash curl -X POST https://api.teya.com/v3/transactions/online \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "store_id": "5bca0d99-3f47-4d91-9372-cf12b8ec50db", "type": "SALE", "initiator": "MERCHANT", "amount": { "value": 10000, "currency": "GBP" }, "merchant_reference_id": "order_ABC123", "payment_method": { "type": "TOKEN", "token": { "token_id": "1e2d4f80-9c3e-4f5b-83aa-9e8d2b6a6c49", "recurring_payment_type": "RECURRING" } } }' ```Your billing engine's routing rule for this whole phase is one line:
``` for each due charge: customer has a Teya token (UUID) → new platform, payment method TOKEN customer has a legacy value → legacy platform, unchanged ```Two fields describe why you are charging a stored card:
| Field | Values | Meaning |
|---|---|---|
initiator | MERCHANT | The cardholder is not present — a recurring charge, an instalment, or a merchant-initiated card-on-file payment. No 3DS/SCA is performed. |
CUSTOMER | The cardholder is present (e.g. clicking "pay with saved card" on your site). 3DS/SCA is performed and may require a challenge. Default if omitted. | |
recurring_payment_type | RECURRING, INSTALLMENT, CARD_ON_FILE | The type of stored-card usage. CUSTOMER-initiated payments support only CARD_ON_FILE; MERCHANT-initiated payments support all three. |
Things to get right: amount.value is in the smallest currency unit (100 ISK = 100, 100 EUR = 10000). Send an Idempotency-Key per charge attempt.
Full reference: Charge a Saved Payment Method.
Once you've confirmed to your Teya contact that no new legacy tokens are being created, Teya takes the file of your stored cards from the legacy platform and imports it into the new one. Your customers never re-enter a card and you never touch card data. Keep the Phase 2 routing unchanged while the import runs; Teya confirms when it is complete.
What: with the import confirmed, retire the "legacy platform" branch of your routing. A legacy token is now charged on the new platform — same endpoint, same fields — with one different payment method:
```json "payment_method": { "type": "LEGACY_TOKEN", "legacy_token": { "token_id": "tm_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345", "recurring_payment_type": "RECURRING" } } ```What happens on first use. The first time each legacy token is charged on the new platform, it is upgraded in-line, inside that payment:
Two properties of this design are worth knowing: the imported card record is never used to pay directly — it exists only to mint the network token, once; and if any step of the conversion fails, the payment fails with an explicit error (below) with nothing half-saved, so a later retry starts clean. Expect the first charge of each token to take a little longer than usual; every later charge skips the conversion entirely.
What comes back — and the one thing you must do with it. A successful charge returns 201, and the response carries the customer's new Teya token:
The same token_response also appears on the transaction lookup and on the payment.succeeded.v1 webhook. Store the token_id, and from this customer's next charge on send it as payment_method.type: "TOKEN". Your routing rule becomes:
Things to get right:
token_id — a lagging deploy or a webhook/response race does no harm.initiator: "CUSTOMER") may return 202 with redirect_html for a 3DS challenge — handle it exactly as on Charge a Saved Payment Method. The conversion happens before the challenge, and the completed payment carries the same token_response.All errors use the API's standard shape — a code and a human-readable description. A payment that fails this way is never sent for authorisation: the customer is not charged and no webhook is emitted.
| HTTP | Description you'll see | Meaning | What to do |
|---|---|---|---|
400 | Legacy token must be either a tm_ prefixed token or a 16-digit virtual card PAN starting with 991001 | The value doesn't match the two valid shapes | Fix the stored value or the lookup that produced it — this was never a valid token. |
400 | Legacy token has not been migrated to a new token. Please contact support. | No imported record for your store + this token | Either the import (Phase 3) hasn't completed for this store, or the token was created after Phase 1 should have ended. Don't retry in a loop; flag it and check with your Teya contact. |
400 | Legacy token payment method is not enabled | The legacy-token flow isn't switched on for your environment yet | Ask your Teya contact about rollout status. |
500 | Error resolving legacy token | The first-use conversion failed part-way | Transient — safe to retry later. Nothing was half-migrated. |
The signal to watch at cut-over is the ratio of legacy-token failures to attempts. A burst of "not migrated" errors on day one almost always means Phases 3 and 4 happened in the wrong order — or that something, somewhere, is still creating legacy tokens.
TOKEN charge. Then a full billing cycle, watching the failure ratio.The questions the migration guides don't answer step by step: what happens if you wait, what changes for your customers and your back office, and what is different on the new platform. For the technical migration itself, start at the overview.
SecurePay, the RPG payment API, the B-Gateway web service and the Ticket API keep working as normal today. Onboarding to them stops soon, and they are retired in early 2027. You will be given notice before anything changes for your customers, and your Teya contact can tell you where you stand at any time.
If your integration is still on the legacy platform when it is retired, your website will no longer be able to take card payments through it. If you sell through a partner platform or use a Teya plugin, the migration preparation is managed for you — but watch for further instructions from your provider or from Teya, because you may need to reconnect your store or sign in again when the switch happens.
We will contact you before your integration is affected — but don't wait for that: the work is the same now as later, and now is calmer.
There is no Teya fee for migrating, and your pricing agreement with Teya is unchanged — it is not tied to which platform your integration uses. The only prerequisite is that Teya online payments are enabled on your store, which your Teya contact arranges.
It depends on the path. A SecurePay redirect integration moving to Hosted Checkout is a small development project — days including testing, not weeks. Embedded Components and RPG integrations are a little more, mainly frontend work. B-Gateway integrations are a small development project too, and end with less code than you have today. Ticket API users often need no development at all. Stored cards and recurring payments are a separate, phased track that runs alongside normal billing cycles — see the stored cards guide.
Your Teya merchant account and settlement stay the same. What changes is how your integration authenticates: instead of a SecurePay secret key, an RPG private key or a B-Gateway username and password, each store gets OAuth API credentials (a Client ID and Client Secret). You create them yourself in the Business Portal under the store's Integrations settings. Your store needs Teya's online payments enabled; if credential creation reports that no compatible store was found, ask your Teya contact.
Ask whoever built your site where the customer types their card number. A Teya/Borgun page means SecurePay redirect; a frame inside your page means SecurePay embedded; your own form means a direct API. For a direct API, check what your server sends to borgun.is: JSON to /api/payment is RPG, XML such as getAuthorization to gateway01.borgun.is is B-Gateway. The overview turns the answers into a guide. If nobody knows, your Teya contact can look up how your payments arrive today.
Three, in increasing order of effort: switch to a Teya plugin if your shop runs on WooCommerce or Shopify; use Payment Links from the Business Portal or Teya app if you mostly invoice customers; or have any web developer or agency follow the Hosted Checkout guide — it is written so a developer who has never seen SecurePay can do it.
Yes, and the guides recommend it: keep the legacy integration live for a day or two after you switch, so rolling back is a configuration change. Both settle to the same merchant account. There is no time limit on parallel running before the retirement date.
Everything is rehearsed in Teya's staging environment with a development store and test cards, including 3D Secure challenges, declines, refunds and webhooks. Nothing reaches your production account until you create production credentials and switch hosts.
On Hosted Checkout the experience is the same shape as SecurePay: click Pay, enter the card on a Teya page, come back. Differences they may notice are improvements — a declined card lets them try another instead of ending the checkout, Apple Pay appears where the device supports it, and foreign cardholders may be offered to pay in their own currency. With Embedded Components the card fields sit inside your own checkout, so the visible change is that they no longer leave your site.
You delete it. Authentication runs automatically inside Hosted Checkout and the Embedded Components — enrollment, the bank challenge and validation are handled for you, with no separate calls and nothing to certify. The only place you still see 3D Secure explicitly is customer-present charges on saved cards through the direct API, where a challenge may be returned for you to display.
This also applies if you run 3D Secure through B-Gateway's get3DSAuthentication or a third-party 3D Secure provider: the connection is no longer needed. On the new platform every customer-present payment goes through 3D Secure, and charges without the customer are made on a card they saved with you, as merchant-initiated payments. See the B-Gateway guide.
Yes, but you need one small server-side component: the new platform uses credentials that must never reach the browser, so a session is created from your server before the customer is redirected. A single endpoint — a PHP file on your host or a serverless function — is enough. See Step 1 of the Hosted Checkout guide. If that is more than you want, Payment Links or a plugin need no backend at all.
Install the current Teya plugin for your platform and connect it to your Teya account — it already uses the new platform. Watch for instructions from Teya, as you may need to sign in again when your store switches over. See Connect with e-commerce platforms and the Help Centre article Set up and connect your plugin to Teya.
Your provider does. Partner platforms migrate their integration once for all their merchants; the preparation is managed for you, but watch for their instructions — you may be asked to reconnect your store or approve a connection to your Teya account when the switch happens. Agencies that built a custom integration for you follow the same guides as your own developer would.
Yes. Refund legacy transactions in B-Online exactly as you do today — it keeps working alongside your new integration and stays available until the legacy platform is retired. Refunds of payments taken on the new platform are made in the Business Portal or by API. Your legacy transactions are also visible in the Business Portal, and stay there.
Yes. Your legacy reporting stays available, read-only, until the legacy platform is retired. Your legacy transactions are also visible in the Teya Business Portal and remain there indefinitely, alongside new-platform payments — which appear in the Portal from your first live transaction.
B-Gateway's transaction list, settlement, chargeback, dispute and fraud reports have no one-to-one API replacement. For a single payment, look up its checkout session by ID; for everything else, use the Business Portal. There are no batches on the new platform, so batch numbers disappear from your records: key them on the transaction_id instead. If a finance or reconciliation system depends on these reports, contact Teya support with your specific case before you migrate so we can check it with you. Each report is listed on the B-Gateway reference.
Yes. Nobody re-enters a card: Teya imports your stored cards, and each legacy token upgrades itself the first time you charge it on the new platform. The order of steps matters — read the stored cards & recurring guide before changing anything about how cards are saved.
This includes virtual card numbers from B-Gateway's getVirtualCard.
Once your parallel-running window is over, remove the legacy code; Teya retires the old SecurePay, RPG or B-Gateway credentials for you — nothing to request. Branding settings do not carry over: logo, checkout and receipt messages and legal URLs are configured once per store in the Business Portal.
Disputes are handled against your merchant account, not the integration, so a chargeback on a payment taken through SecurePay is handled the same way after you migrate.
Evidence for legacy transactions remains retrievable — they stay visible in the Business Portal.
Use merchant_reference for your order number, and keep writing your legacy order ID into metadata during the transition if your back office keys on it. Every new-platform payment has a transaction_id that appears in the webhook, the session lookup and the Business Portal — store it on the order.
Amounts are in the smallest unit of the currency: €54.43 is 5443, £9.99 is 999. ISK has no subunit, so 100 ISK is simply 100. This is the single most common bug in migrations from SecurePay, which took a decimal string.
The platform sends a signed webhook for successful payments (payment.succeeded.v1). Failures and abandoned checkouts are covered by looking the session up by its ID, which also tells you whether a customer who never returned to your site did in fact pay. See Webhooks.
In the Business Portal (and the Teya app), alongside your in-store payments if you have Teya card machines. Refunds can be issued there without a developer.
Settlement does not change: same merchant account, same payout timing and statements. New-platform transactions simply appear in the Business Portal as well.
Refunds are full or partial, and can be repeated up to the original amount. A payment is captured at most once per authorisation, and the remainder is released. There is no separate "cancel": a refund on an uncaptured payment reverses the authorisation. See Payment Actions.
Yes — and if you were on SecurePay this is new. Create the session with type PRE_AUTHORISATION and capture later, including a partial amount. Pre-authorisations are currently available for certain tourism-related business activities; if your business needs them for another use case, contact your Teya account manager.
Yes, automatically: when a cardholder's currency differs from the transaction currency, the checkout offers to pay in their own currency. There is nothing to implement. DCC is not offered on pre-authorisations.
The same ISO currency codes as before (ISK, EUR, GBP, CZK, HUF, USD and others). HRK is no longer used; Croatia settles in EUR. The checkout language is auto-detected from the customer's browser or set per session with a BCP 47 code such as is-IS or cs-CZ.
Checkout languages include English, Icelandic, Czech, Slovak, Croatian and Hungarian, among others.
Card data is entered on Teya-hosted pages or inside Teya-hosted iframes, so your systems never see it and you stay in the lightest PCI category (SAQ A). Per-request signatures (checkhash) are replaced by short-lived access tokens, and payment confirmations are RSA-signed webhooks you verify with a public key rather than a shared secret. Sending raw card data to Teya from your own servers is restricted to merchants with PCI DSS (SAQ D) evidence.
If you use B-Gateway today, card numbers pass through your own servers. Moving to Embedded Components or Hosted Checkout takes them out entirely, which is usually the biggest reduction in compliance work of any migration path.
Some B-Gateway features work differently on the new platform or depend on your set-up: batches (settlement is now automatic), partial reversals (capture the final amount on a pre-authorisation, or refund the difference), Dynamic Descriptors, invoice detail data (sendDetailData), passenger itinerary data, batch authorisations, and telephone, keyed (B-Online) or card-reader payments. If you use any of them, contact Teya support with your specific case before you migrate. The B-Gateway reference lists every operation and where it lives now.
No. Use the official list on the Testing page; it covers frictionless and challenge 3D Secure, declines and specific error cases.
```admonition icon: fas fa-life-ring title: Question not answered here? content: Contact your Teya account manager, or reply to the email that pointed you here. For integration questions, see Support. ```