SDK
Web
Hosted Card Field

Hosted Card Field

Use this when you want to render your own payment screen but never touch card data. The field is embedded in your page as a cross-origin iframe; the card number and CVC never enter your DOM.

const field = Lunixi.checkout.createCardField({ /* … */ });
await field.mount('#card-field');

It is a variant of the checkout SDK — no separate script, the loader is enough.

Placing it on your page

You provide an empty <div> for the field to render into; the SDK fills it.

<div id="card-field-checkout"></div>
 
<script src="https://cdn.lunixi.com/lunixi-checkout.min.js"></script>
<script>
  const checkout = Lunixi.checkout.createCardField({
    options: {
      token: 'the token from the payment intent response',
      Currency: 'TRY',
      Price: '125.00',
      Locale: 'tr',
      force3Ds: '1',
      cardHolderName: 'Demo User',
    },
  });
 
  checkout.mount('#card-field-checkout');
</script>

mount() accepts either form. It tries getElementById first and falls back to querySelector — so 'card-field-checkout' (a bare id) and '#card-field-checkout' (a CSS selector) do the same thing. You can also pass a DOM element.

⚠️

Leave the target <div> empty. The SDK clears it on mount, so any placeholder content you put inside disappears. If you want a loading indicator, put it outside the div and hide it after mount.

The field's height changes as the card is entered (the instalment table opens). The iframe reports its height with the HEIGHT_CHANGED message and the SDK applies it for you — so do not set a fixed height on the div.

Changing the base URLs

For local development or an on-prem installation:

Lunixi.configure({
  apiBaseUrl: 'https://api-gateway.lunixi.com/',
  cdnBaseUrl: 'https://cdn.lunixi.com/',
});

Call this before createCardField.

Why it matters

Card data lives on an origin your JavaScript cannot reach. That narrows your PCI DSS scope: because the card number is never present on your page, that page is not a component handling card data.

The moment you take a card number into your own <input>, that page enters PCI scope. A hosted field exists precisely to prevent that.

What crosses to the parent, and what does not

From inside the iframe, only these are reported outward:

MessageContents
CARD_VALIDITY_CHANGEDJust true / false — is the field valid
CARD_BRAND_CHANGEDDetected brand (VISA, MASTERCARD …)
CARD_BIN_CHANGEDThe first 6 digits — for bank detection and the instalment table
HEIGHT_CHANGEDThe iframe's pixel height (for resizing)
TOKENIZATION_STARTED / TOKENIZATION_FINISHEDWhen tokenization starts and ends
TOKENIZEDpaymentMethodToken, brand, last4, cardHolderName, cardSave
ERRORcode — see the table below
🚫

The PAN and CVC appear in no message. After tokenization you hold only an opaque paymentMethodToken and last4. Reading the card number from the parent page is not possible — that is the design.

Exposing the BIN (first 6 digits) is deliberate: it is what makes the instalment table and bank campaigns possible, and on its own it does not make a card usable.

What the parent sends inward

INIT · TOKENIZE · FOCUS · RESET · PING

You do not write these by hand; the SDK's methods turn into these messages behind the scenes.

Tokenization

When TOKENIZE arrives, the iframe goes to the platform itself:

POST /api/v1/payments/form/tokenize

It passes the returned cardToken to the parent in the TOKENIZED message. So card data travels iframe → platform and never passes through your server.

You then complete the payment with that token.

Error codes

CodeMeaning
tokenize_not_initializedTOKENIZE was called before INIT arrived
card_invalidThe field is not valid — the user has not finished
tokenize_failedThe platform rejected the tokenization
tokenize_network_errorThe iframe could not reach the platform

The security design

This field's protection comes in layers, and all of them are fail-closed:

  • The parent origin is supplied through the iframe URL. If the iframe does not know the parent origin, or the envelope is not secure, it does not postMessage at all — it simply stops.
  • Strict CSP. default-src 'none', script-src 'self', form-action 'none', object-src 'none', base-uri 'none'; connect-src is open only to the platform.
  • frame-ancestors is enforced as an HTTP header. It is deliberately absent from the meta tag (where it would be ignored anyway); the real rule is applied as a response header at the CDN, so who may frame the field is fixed server-side.
⚠️

To frame the field on your own site, your origin has to be allow-listed. If it is not, the iframe loads but sends no messages — you see an empty area and find no console error. On a first integration, that symptom means: check the origin registration.

When to use this, when the full checkout

NeedChoice
We render the whole payment screenCheckout Form
The screen is mine, only the card field is yoursHosted Card Field (this page)
Card storage without ever touching card dataHosted Card Field plus cardSave

Related