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:
| Message | Contents |
|---|---|
CARD_VALIDITY_CHANGED | Just true / false — is the field valid |
CARD_BRAND_CHANGED | Detected brand (VISA, MASTERCARD …) |
CARD_BIN_CHANGED | The first 6 digits — for bank detection and the instalment table |
HEIGHT_CHANGED | The iframe's pixel height (for resizing) |
TOKENIZATION_STARTED / TOKENIZATION_FINISHED | When tokenization starts and ends |
TOKENIZED | paymentMethodToken, brand, last4, cardHolderName, cardSave |
ERROR | code — 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/tokenizeIt 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
| Code | Meaning |
|---|---|
tokenize_not_initialized | TOKENIZE was called before INIT arrived |
card_invalid | The field is not valid — the user has not finished |
tokenize_failed | The platform rejected the tokenization |
tokenize_network_error | The 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
postMessageat all — it simply stops. - Strict CSP.
default-src 'none',script-src 'self',form-action 'none',object-src 'none',base-uri 'none';connect-srcis open only to the platform. frame-ancestorsis 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
| Need | Choice |
|---|---|
| We render the whole payment screen | Checkout Form |
| The screen is mine, only the card field is yours | Hosted Card Field (this page) |
| Card storage without ever touching card data | Hosted Card Field plus cardSave |
Related
- Web SDK Overview — loader, modes, authentication models
- Checkout Form — the full payment screen
- Portal SDK — the portal's card form delegates to the same flow