Skip to main content

Card Tokenization with Secure Fields

Use the Stitch Secure Fields SDK to securely capture card details and tokenize them for future or recurring payments. Tokenization returns a card ID (token): A reusable reference you can store and use to initiate subsequent transactions, without requiring users to re-enter card details on returning payment journeys.

Capture Card Details

To securely collect card details, integrate the Stitch Secure Fields SDK into your client-side checkout.

At a high level:

  • <StitchSecureForm> wraps one or more <StitchSecureField> components
  • On form completion, the SDK returns all card details
  • Sensitive values (number, cvc) are returned encrypted so their plaintext value never touches your infrastructure
  • You pass these encrypted values to your server to tokenize the card

See the full Secure Fields integration guide here.

Tokenize a Card

You can save a card in two ways, depending on who will initiate future charges:

If...Use this flow
The customer is present and will authenticate each future payment (e.g. "remember my card")Save for Future Customer-Initiated Transactions (CIT)
You need to charge the card later without the customer present (e.g. subscriptions, scheduled billing, etc.)Set Up Merchant-Initiated Transactions (MIT)

Customer-Initiated Transactions (CIT)

Use this flow to store a card for future payments where the customer is present and authenticates each charge. This is the right choice for "remember my card" checkout experiences.

Use the initiateTransaction mutation with storeOnFile: true to verify and tokenize the card (with or without a charge). The response will include a reusable card.id that you can store for future customer-initiated payments.

Authentication Scope

The initiateTransaction mutation requires a client token with the scope transaction_initiate. Use the GraphQL API URL https://api.stitch.money/graphql.

To verify a card, specify a zero amount within the initateTransaction mutation.

Merchant-Initiated Transactions (MIT)

Use this flow when you need to charge the card in the future without the customer present — for example, subscriptions, scheduled billing, or account top-ups.

To set up an MIT mandate, include recurringPayment in your initiateTransaction request with source: "payer". This records the customer's consent during this initial (customer-present) step. The agreement.reference must be unique and remain consistent across all future charges in the recurring series.

The response will include a reusable card.id that you can store for future customer-initiated payments.

Authentication Scope

The initiateTransaction mutation requires a client token with the scope transaction_initiate. Use the GraphQL API URL https://api.stitch.money/graphql.

To verify a card for an MIT mandate, specify a zero amount within the initateTransaction mutation.

API input fields
  • The nonce (4096-character limit) must be unique per request.
  • The externalReference (4096-character limit) can link the request to your order or payment reference.
  • Card details (encryptedPan, encryptedSecurityCode, etc.) come from the Secure Fields SDK.
  • For MIT mandates: recurringPayment.agreement.reference must be unique and consistent across the recurring series; use source: "payer" for this initial customer-initiated step.

Provide Payer information

The payerInformation object should be specified with information of the payer on your system's records. The set of provided information is used to increase the efficacy of fraud risk checks done by Stitch. All possible inputs may be found found in the API reference.

At a minimum, the payerId should always be specified within a request. This may be any internal identifier that always uniquely identifies users across Stitch requests.

Handle 3D Secure (3DS) Authentication

For flows that involve a transaction, the card issuer may require the customer to complete 3D Secure authentication. When this happens, the transaction is returned in a pending state with an interactionUrl. Redirect the customer to this URL to complete 3DS.

Rendering the 3DS Interaction

There are two ways to display the 3DS authentication flow to the user:

  1. Inside an iframe (recommended): This keeps the user on your checkout page and allows you to control the experience while safely handling errors or timeouts.
  2. As a full-page redirect: This navigates the user away from your site to the interaction URL and returns them via a redirect, but offers less control and no built-in recovery if something goes wrong.

Stitch recommends displaying the 3DS interaction inside an iframe, as this keeps the user within your checkout experience and provides safer recovery paths if the issuing bank’s challenge page fails to load or times out.

When using an iframe, the interaction url returned from the Stitch API must be loaded directly as the iframe’s src:

<iframe src="https://3ds.stitch.money/2ce31de7-a30d-4f3b-bfaa-cd06cb30db79"></iframe>

When the user finishes the 3DS flow, the interaction page running inside the iframe will notify your parent window using a postMessage event.

Listen for the message in your hosting page:

window.addEventListener('message', (event) => {
if (event.data?.type === 'finished') {
const { flow, externalReference, id: stitchId } = event.data;

// e.g. close modal, update UI, confirm transaction, etc.
}
});

The following fields are available on the message event:

FieldDescription
typefinished if the 3DS flow is complete
idThe Stitch ID of the transaction
flowThe 3DS interaction type of the transaction: challenge or frictionless
externalReferenceThe external reference of the transaction
statusThe final status of the transaction after the user's interaction: TransactionSuccess or TransactionFailure
statusReasonThe status reason, if applicable to the transaction status

We strongly recommend displaying the 3DS interaction within an overlay modal that sits on top of your checkout page. The modal should include a visible close or cancel button so that users always have a safe way to exit if the issuing bank’s challenge fails or times out. It should present the 3DS page inside its own isolated iframe container to keep the interaction clearly separated from the rest of your UI and it should offer a retry mechanism so users can attempt the authentication again if anything goes wrong. This approach ensures that customers are never left stuck or redirected to a broken page and gives you full control over the overall experience.

As an alternative, you may embed the iframe directly into the body of your page. If you choose this approach, you should still provide a clear retry or recovery option in case the challenge fails and you must ensure that users always have a safe and obvious way to exit the flow. This helps maintain a smooth checkout experience even without a modal.

2. Displaying the 3DS Flow as a full-page redirect

Alternatively, you can redirect the user to the 3DS interaction URL. In this flow, your checkout page is replaced entirely by the 3DS challenge screen and the user completes the authentication directly on that page. To initiate the flow, you simply take the interaction url returned by the Stitch API and perform a standard browser redirect to it (either by setting window.location.href in your frontend or by submitting a form to it).

To redirect the user back to your site once the 3DS flow is completed, you must supply a redirect_uri parameter during the initial redirect. For example, if you want the user returned to https://example.com/payment, you would append the following query string to the interaction URL: ?redirect_uri=https%3A%2F%2Fexample.com%2Fpayment.

The final URL you redirect the user to should look similar to:

https://3ds.stitch.money/2ce31de7-a30d-4f3b-bfaa-cd06cb30db79?redirect_uri=https%3A%2F%2Fexample.payment
danger

The URL specified as the redirect_uri must be secure i.e. an HTTPS URL.

Once the user has successfully completed the interaction and the payment has been processed, they will be redirected back to your specified redirect_uri with the following query parameters.

ParameterDescription
idThe Stitch ID of the transaction
flowThe 3DS interaction type of the transaction: challenge or frictionless
externalReferenceThe external reference of the transaction
statusThe final status of the transaction after the user's interaction: TransactionSuccess or TransactionFailure
statusReasonThe status reason, if applicable to the transaction status

Transaction Statuses

Flows that initiate a transaction can return the following states:

StatusDescription
TransactionPendingThe transaction has been initiated, but an interaction (e.g. 3DS) is required. An associated reason is returned.
TransactionSuccessThe transaction completed successfully. If 3DS was not required, this may be returned immediately. The card.id in the response is the stored token.
TransactionFailureThe transaction failed. An associated reason is returned.

Failure reasons

The TransactionFailure status indicates that the card transaction failed to be initiated, and includes a reason explaining the cause.

Potential failure reasons are detailed below:

ReasonDescription
authorizationFailedThe transaction was declined or blocked.
authorizationNotFinalisedThe transaction could not be processed by the acquirer.
blockedByFraudChecksThe transaction was blocked due to fraud checks.
downstreamProviderErrorThe transaction could not be processed due to downstream error.
exceedsCardWithdrawalLimitThe transaction was declined due to withdrawal limits exceeded.
insufficientFundsThe transaction was declined due to insufficient funds.
internalServerErrorThe transaction could not be processed due to a server error.
invalidCardErrorThe transaction was declined due to an expired card.
invalidConfigurationErrorThe client has invalid or missing configuration.
invalidTransactionErrorThe transaction could not be processed due to invalid data.
secure3dDeclinedThe user has declined 3DS authentication.
secure3dLookupFailed3DS authentication attempt could not be initiated.
secure3dNotCompletedThe user has not completed 3DS authentication.
tokenDecryptionErrorThe payment token could not be decrypted.

Subscribe to Webhooks

Webhooks for transactions can be subscribed to by running the clientWebhookAdd mutation, to receive transaction webhook events. The transaction webhook will include details of the tokenized card in the card object.

If the subscription is successfully created, the body returned by the request will look similar to the sample in the Example Response tab in widget above.

For more information on receiving webhook events, listing active webhook subscriptions, unsubscribing from webhooks and validating signed webhook subscriptions, please visit the Webhooks page.

Webhook Statuses

The transaction webhook will be dispatched for each of the following status updates:

  • TransactionSuccess
  • TransactionFailure
Example Payload
{
"data": {
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"card": {
"bin": "41111111",
"cardHolderName": "Joe Soap",
"expiryMonth": 12,
"expiryYear": 2024,
"first6": "41111111",
"id": "Y2FyZC85YWY4OGE4MS05ZjNhLTRlNDItYWRiYy04ZTA1M2Q1YTM3M2U=",
"issuer": {
"name": "capitec",
"country": "ZA"
},
"last4": "1111",
"maskedPan": "411111******1111",
"network": "Visa",
"type": "Credit"
},
"createdAt": "2023-05-10T12:22:42.865Z",
"eci": "05",
"externalReference": "79261d16-c53b-48eb-9019-dc9cfb6c5126",
"id": "Y2FyZHRyYW5zYWN0aW9uLzQwNDMxRTY5LTNERjctNEIyQS1CNDY0LURFNTQwNDc0QkMxQw==",
"nonce": "abb1c3b7-b39b-4a4c-93cb-3bbb363a3171",
"originalAmount": {
"currency": "ZAR",
"quantity": "1"
},
"paymentRequestId": "RRFyZHRyYW5zYWN0aW9uLzQwNDMxRTY5LTNERjctNEIyQS1CNDY0LURFNTQwNDc0QkMxQw==",
"retrievalReferenceNumber": "508714102541",
"secure3dDecision": "skip",
"secure3dDecisionReason": "clientSpecified",
"status": "SUCCESS",
"statusReason": null,
"type": "CARD",
"updatedAt": "2023-05-10T12:22:42.865Z"
},
"datetime": "2023-05-10T12:22:42.865Z",
"id": "transaction:status:success:40431E69-3DF7-4B2A-B464-DE540474BC1C",
"type": "transaction"
}

Initiating a Transaction

Use the returned card ID (e.g. card.id from initiateTransaction) to initiate transactions for one-off or recurring payments.

Delete a Card Token

When a user unlinks a card from your platform, you should delete the stored token over the Stitch API so that transactions can no longer be initiated with that card.

Initiate a deleteCard mutation request to the GraphQL API URL https://api.stitch.money/graphql to delete a stored card token. The mutation returns a boolean to indicate the result of the operation.

After a successful deletion, the card token can no longer be used to initiate transactions.

Test card numbers

Our sandbox environment allows simulating success and failure cases when initiating a card transaction, without requiring real debit or credit card details.

You may use the below card numbers on the card input screen to trigger the corresponding scenario.

tip

For all cards numbers, any future date for the expiry date (in the format MM/YY), as well as any 3-digit value for the CVC, will process as expected.

ScenarioCard Number
3DS successful and payment completedAny valid card number, such as 4032035421088592
Transaction fails during 3DS verification4004462059871392
Transaction fails during payment authorization4032033425469975
Transaction fails due to insufficient funds4005519200000004
Transaction fails due to exceeding withdrawal limit5284989416854933
Transaction fails due to downstream provider error4787692003020026