Skip to main content

Secure Fields for card payments

Stitch provides Secure Fields within client SDKs so merchants can collect sensitive card details for payment. Encrypted card details never reach your server unencrypted, while you still control a fully customisable checkout experience.

Key features of Stitch Secure Fields include:

  • Automatic input formatting and validation as customers type
  • Responsive design to fit seamlessly on any screen size
  • Custom styling rules to match the look and feel of your app or site
  • No PCI-DSS compliance requirement for the merchant for handling raw card data (raw PAN and CVC never enter your process)

Usage overview

The Secure Fields SDK returns encrypted details (limiting PCI-DSS scope), which you forward to your backend and use as inputs when creating transactions in:

Platform differences are package install, component APIs, theming, and payload property names (card.number / card.cvc on Web vs card.encryptedNumber / card.encryptedCvc on mobile typed APIs). The client journey is the same on every SDK.

Once-off Card with Secure Fields

Client-side Secure Fields across SDKs: render fields, collect encrypted card data and fingerprint, POST to your backend.

What you send to your backend

Only forward a payload when the form reports isComplete and isValid. Always include encrypted PAN/CVC, expiry, bin, last four, cardholder name (if collected), and fingerprint.

Field mapping (SDK → GraphQL)

When the Secure Fields SDK reports a complete, valid payload, forward the encrypted card data and fingerprint to your backend. Your server maps those SDK fields into GraphQL initiateTransaction (or tokenization) inputs as follows:

ConceptWeb SDK (@stitch-money/react / web)Mobile typed APIs (iOS / Android / Flutter / React Native)GraphQL
Encrypted PANcard.numbercard.encryptedNumberpaymentMethods.card.cardDetails.redacted.redactedCardNumber (encryptedPan)
Encrypted CVCcard.cvccard.encryptedCvcpaymentMethods.card.cardDetails.redacted.redactedSecurityCode (encryptedSecurityCode)
Expiry month / yearcard.expiry.month / card.expiry.yearcard.expiryMonth / card.expiryYearexpiryMonth / expiryYear
BINcard.bincard.binbin
Last 4card.lastFourcard.lastFourlast4
Cardholder namecard.namecard.holderNamecardHolderName
Device fingerprintfingerprintfingerprintdeviceInformation.fingerprint
note

Always forward bin, last4, and fingerprint together with the encrypted PAN and CVC. The once-off and tokenization APIs require bin and last4; fingerprinting is required for many risk and consent flows.

danger

Client apps must not call Stitch GraphQL with secret credentials. Always proxy through your backend.

Platform setup and API reference

Install packages via Cloudsmith using the coordinates and versions from onboarding (see Install via Cloudsmith).

Packages: @stitch-money/react (React) or @stitch-money/web (non-React web components). Registry setup and version pins are covered in Install via Cloudsmith.

# React
npm install @stitch-money/react

# Non-React
npm install @stitch-money/web

The SDK consists of two components:

  • Stitch Secure Form - Contains shared logic and coordinates Stitch Secure Fields composed within the component. It must be placed high enough in your component tree to wrap all instances of <StitchSecureField>.
  • Stitch Secure Field - An individual field for capturing one aspect of the customer's card. You require at least two fields to capture the number and cvc.

Fields render in PCI-compliant iframes; your page never sees the raw PAN or CVC.

Stitch Secure Form

React

This component accepts the following properties:

NameDescription
clientIdThe client ID assigned to you by Stitch that you will be processing your transactions on.
onReadyA function for handling the ready event emitted when all secure fields have loaded successfully.
onChangeA function for handling change events emitted when the customer makes changes to any of the secure fields.
theme(Optional) A custom theme which changes the appearance of the secure fields; supports most CSS attributes.
import { StitchSecureForm } from '@stitch-money/react';

export function PaymentPage() {
const handleSecureFormChange = ({ payload }) => {
console.log(payload);

if (payload.isComplete) {
// Send encrypted card data to your backend, which calls the Stitch API
sendEncryptedCardDataToBackend(payload);
}
};

const handleSecureFormReady = () => {
// Toggle visibility of loaded Secure Field components
console.log('Secure Fields Loaded');
};

const theme = {
styles: {
':root': {
'color-scheme': 'dark',
},
// Style all field inputs
'.field input': {
marginTop: 8,
padding: '8px 12px',
},
},
};

return (
<div>
<StitchSecureForm
clientId="your Stitch client ID"
onReady={handleSecureFormReady}
onChange={handleSecureFormChange}
theme={theme}
>
{/* add child secure field components */}
</StitchSecureForm>
</div>
);
}

Non-React

This component accepts the following properties:

NameDescription
clientIdThe client ID assigned to you by Stitch that you will be processing your transactions on.
theme(Optional) A custom theme which changes the appearance of the secure fields; supports most CSS attributes.

The following are event listeners on the form component (in JavaScript):

NameDescription
stitch-secure-fields-readyFired when all secure fields have loaded successfully.
stitch-secure-fields-changeFired when the customer makes changes to any of the secure fields.
stitch-secure-fields-errorFired when there is an error initializing a field.
<script type="module">
import '@stitch-money/web/secure-fields';
</script>

<stitch-secure-form id="stitch-form" clientId="your Stitch client ID">
</stitch-secure-form>

<script>
const form = document.getElementById('stitch-form');

form.addEventListener('stitch-secure-fields-ready', () => {
// Toggle visibility of the now-loaded Secure Field components
console.log('Secure Fields Loaded');
});

form.addEventListener('stitch-secure-fields-change', (event) => {
const { payload } = event.detail;
console.log('Form payload changed:', payload);

if (payload.isComplete) {
// Send encrypted card data to your backend, which calls the Stitch API
sendEncryptedCardDataToBackend(payload);
}
});
</script>
note

This example shows vanilla JavaScript usage. Adapt it for the JS framework you use on your platform.

Stitch Secure Fields

This component accepts the following properties / attributes:

NameDescription
fieldThe field that should be rendered; one of name, number, expiry, or cvc.
label(Optional) Custom label text for this input.
placeholder(Optional) Custom placeholder text for this input.
invalidErrorMessage(Optional) Custom validation error text for this input.

React

import { StitchSecureField, StitchSecureForm } from '@stitch-money/react';

export function PaymentPage() {
// ... setup from previous step

return (
<div>
<StitchSecureForm
clientId="your Stitch client ID"
onChange={handleSecureFormChange}
theme={theme}
>
<div>
<StitchSecureField field="name" label="Cardholder Name" />
<StitchSecureField field="number" label="Card Number" />
<StitchSecureField field="expiry" label="Expiry Date" />
<StitchSecureField field="cvc" label="CVC" />
</div>
</StitchSecureForm>
</div>
);
}

Non-React

<script type="module">
import '@stitch-money/web/secure-fields';
</script>

<stitch-secure-form id="stitch-form" clientId="your Stitch client ID">
<stitch-secure-field field="name" label="Cardholder Name" />
<stitch-secure-field field="number" label="Card Number" />
<stitch-secure-field field="expiry" label="Expiry Date" />
<stitch-secure-field field="cvc" label="CVC" />
</stitch-secure-form>

The structure of the elements within <StitchSecureForm> is up to you - the only requirement is that all <StitchSecureField> elements are contained by a single <StitchSecureForm> element somewhere up the component tree.

Ready event

React

The onReady handler function is used to determine when all <StitchSecureField> elements have loaded successfully.

const handleSecureFormReady = () => {
// Toggle your loading state to display the form and fields once they have loaded
setIsStitchSecureFormLoading(false);
};

Non-React

The stitch-secure-fields-ready event listener is used to determine when all <StitchSecureField> elements have loaded successfully.

const form = document.getElementById('stitch-form');
form.addEventListener('stitch-secure-fields-ready', function () {
// Toggle your loading state to display the form and fields once they have loaded
console.log('Secure Fields Loaded');
});

Change events

The onChange handler function (or stitch-secure-fields-change event listener) is used to determine when the customer has completed capturing card details and to receive those encrypted details.

The payload supplied to your handler is structured as follows:

NameDescription
card.nameThe card holder's name.
card.numberThe encrypted card number (PAN). Present only if the number is valid.
card.brandThe network that issued the card.
card.lastFourThe last four digits of the card number. Present only if the number is valid.
card.binThe Bank Identification Number (BIN) for the card. Present only if the number is valid.
card.expiry.monthThe month of the card expiration date.
card.expiry.yearThe year of the card expiration date.
card.cvcThe encrypted card CVC.
card.issuerThe financial institution that issued the card. Present only for a subset of issuers.
errorsValidation errors scoped by field.
isValidWhether there are any validation errors on any of the fields.
isCompleteWhether all of the fields have been successfully filled out with valid values.
fingerprintData used to identify the user's device. Forward to your server and include in Stitch API requests.

React

const handleSecureFormChange = ({ payload }) => {
if (!payload.isValid) {
// Values entered are invalid - show validation error
showErrorStateToCustomer(payload.errors.number);
}

if (payload.isComplete) {
// Fields are complete and valid - send to backend for processing
sendEncryptedCardDataToBackend({
name: payload.card.name,
pan: payload.card.number,
expiryMonth: payload.card.expiry.month,
expiryYear: payload.card.expiry.year,
cvc: payload.card.cvc,
bin: payload.card.bin,
last4: payload.card.lastFour,
fingerprint: payload.fingerprint,
});
}
};

Non-React

const form = document.getElementById('stitch-form');
form.addEventListener('stitch-secure-fields-change', (event) => {
const payload = event.detail?.payload;
if (!payload) return;

if (!payload.isValid) {
// Values entered are invalid - show validation error
showErrorStateToCustomer(payload.errors?.number);
}

if (payload.isComplete) {
// Fields are complete and valid - send to backend for processing
sendEncryptedCardDataToBackend({
name: payload.card.name,
pan: payload.card.number,
expiryMonth: payload.card.expiry.month,
expiryYear: payload.card.expiry.year,
cvc: payload.card.cvc,
bin: payload.card.bin,
last4: payload.card.lastFour,
fingerprint: payload.fingerprint,
});
}
});
note

These details must be submitted to your backend, and can be included as inputs for the Once-off or Tokenization API calls.

Custom styling

By default the secure fields render with a simple appearance that should fit into most applications. To customise the appearance of the fields and their associated validation error messages, supply a theme object to the <StitchSecureForm> component.

The keys of the object are CSS selectors and the values are the same values you would use when writing CSS for the respective selector.

Currently only custom fonts from Google Fonts are supported.

Examples

To set styles that affect every part of all fields, use the :root selector:

const theme = {
styles: {
':root': {
'color-scheme': 'dark', // dark mode
},
},
};

To apply styles to all secure field label elements, use the label selector:

const theme = {
styles: {
label: {
fontWeight: 500,
textTransform: 'uppercase',
},
},
};

To apply styles to all secure field input elements, use the .field input selector:

const theme = {
styles: {
'.field input': {
marginTop: 4,
padding: '8px 12px',
},
},
};

To apply styles to the secure field input that currently has focus, use the .field:focus-within input selector:

const theme = {
styles: {
'.field:focus-within input': {
borderColor: '#5F19E2',
},
},
};

To apply styles to the secure field validation messages, use the .error selector:

const theme = {
styles: {
'.error': {
display: 'none', // Hide built-in validation error messages
},
},
};

To apply styles to a specific secure field input, use the .field[ev-name=<field name here>] selector:

const theme = {
styles: {
'.field[ev-name=number]': {
// Style only the card number input
'font-size': '24px',
},
},
};

To load custom fonts, provide the Google Font links in the fonts array and specify the corresponding fontFamily value within the selectors for the target elements:

const theme = {
// This will load the font from Google Fonts
fonts: ['https://fonts.googleapis.com/css2?family=Comic+Neue'],
styles: {
// This will apply the font to the field labels
'.field label': {
fontFamily: "'Comic Neue'",
},
// This will apply the font to the error text
'.error': {
fontFamily: "'Comic Neue'",
},
},
};

Localisation

If your app is localised, pass translated strings to Secure Fields using the label, placeholder, and invalidErrorMessage properties:

React

<StitchSecureField
field="cvc"
label="<'CVC' translated>"
placeholder="<'CVC' translated>"
invalidErrorMessage="<translated feedback indicating the CVC is invalid>"
/>

Non-React

<stitch-secure-field
field="cvc"
label="<'CVC' translated>"
placeholder="<'CVC' translated>"
invalidErrorMessage="<translated feedback indicating the CVC is invalid>"
></stitch-secure-field>

Fingerprinting

Secure Fields change payloads already include a fingerprint when collection succeeds. For flows that need a device fingerprint outside of Secure Fields (for example charging a consent token), use deviceDataManager from the Web SDK.

Full fingerprint APIs, options, and platform coverage are documented on the Fingerprinting with Stitch SDKs page. A short Web example:

React

import { deviceDataManager } from '@stitch-money/react';

const fingerprint = await deviceDataManager.getDeviceData({
clientId: 'your Stitch client ID', // required
payerId: 'unique user ID', // required
});

Non-React

import { deviceDataManager } from '@stitch-money/web/device-data';

const fingerprint = await deviceDataManager.getDeviceData({
clientId: 'your Stitch client ID', // required
payerId: 'unique user ID', // required
});

Submit this value to your backend and include it in deviceInformation when calling Stitch APIs that require a fingerprint.