Skip to main content
Version: v2.0.4 latest

Enable Tokenization & Save a Card

Saving a customer's card takes four steps:

  1. Enable tokenization on your Merchant Terminal (once).
  2. Create a tokenization request with POST /api/v1/payment.
  3. Redirect the customer to the returned formUrl, where they enter their card and pay.
  4. Receive the token on your webhook or Get Payment Status endpoint and store it.

Step 1 — Enable tokenization on your terminal​

Tokenization is a permission that the Payment Gateway team sets for each Merchant Terminal. Before you send your first tokenization request, contact the team with your terminal ID (the X-Terminal-Id you send on every request) and ask them to enable the capabilities you need:

CapabilityYou need it when
Payment tokensAlways. It lets your terminal issue tokens and accept payments made with a token.
Saving a card without a chargeYou save cards with a 0 IQD tokenization request (nonPaymentOperation: true), as shown in Step 2.
Recurring paymentsYou will charge tokens without the customer present (recurrent: true), for example for subscriptions or installments.

Ask for both your sandbox and your production terminals, because each terminal is configured separately.


Step 2 — Create the tokenization request​

A tokenization request uses the same endpoint as Create Payment. Instead of an amount, it asks the gateway to generate a token, without charging the customer.

API Endpoint​

POST https://uat-sandbox-3ds-api.qi.iq/api/v1/payment

Headers​

NameValueRequired
X-Terminal-IdThe ID of the Merchant Terminal. Provided by the Acquirer.Yes
AuthorizationBasic followed by your Base64-encoded credentials. See Basic Auth.Yes
Content-Typeapplication/jsonYes
Acceptapplication/jsonYes

CURL Example​

curl --location '<API_HOST>/api/v1/payment' \
--header 'X-Terminal-Id: {terminalId}' \
--header 'Authorization: Basic <Base64<username:password>>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"requestId": "<GUID>",
"needPaymentToken": true,
"tokenType": "AUTH",
"nonPaymentOperation": true,
"finishPaymentUrl": "https://www.merchant.com/finish",
"notificationUrl": "https://www.merchant.com/webhook-notification"
}'

Request Body​

Media type: application/json​

{
"requestId": "6f1c2a9e-3b7d-4c55-9a0e-2d8f4b1e7c3a",
"needPaymentToken": true, // ask the gateway to generate a token
"tokenType": "AUTH", // AUTH, NON_RECUR or UNAUTH
"nonPaymentOperation": true, // save the card without charging it (0 IQD)
"finishPaymentUrl": "https://merchant.net/finish",
"notificationUrl": "https://merchant.net/notification"
}

Request body fields​

FieldTypeDescription
requestIdstring requiredA unique identifier generated by the Merchant's side. It must be ensured that the RequestId remains unique within the context of each Merchant Terminal to prevent duplication or conflicts.
needPaymentTokenboolean requiredSet to true to ask the Payment Gateway to generate a payment token (a reusable reference to the payer's card) from this operation. It must be true when nonPaymentOperation is true.
tokenTypestring required The type of token requested, in descending order of capability: AUTH - an authenticated token that can be used for any operation, including recurring payments. NON_RECUR - an authenticated token that can be used for any operation except recurring payments. UNAUTH - an unauthenticated token that can only be used in operations where the payer authenticates. You never receive a more capable token than the one you request, but you can receive a less capable one depending on how the payer was authenticated, so always check the type of the returned token.
nonPaymentOperationboolean requiredSet to true to save (tokenize) the payer's card without charging it. The operation is processed as a 0 IQD payment, so amount must not be sent, and needPaymentToken must be true.
finishPaymentUrlstring requiredThis field specifies the URL where the payer will be redirected after the payment process is completed. The redirection occurs on the client side, and the URL is provided by the merchant. Once the payment is processed, the user is automatically sent to this URL, reflecting the outcome of the transaction, A merchant can use this field to redirect his user back to the merchant side, Maximum characters: 1024
notificationUrlstring required This field is used to provide a webhook URL where the payment gateway will send a POST request to notify the merchant's side of the payment status after the payer completes the transaction. The notification will include in its body the updated data object of the payment (containing the new status: success, pending, or failure), allowing the merchant to track the result of the payment in real time
Don't send an amount

A tokenization request with nonPaymentOperation: true is processed as a 0 IQD operation, so the customer is not charged. Leave amount and currency out of the request.

Not sure which tokenType to request? See Token types.

Variant: save the card during a payment​

To save the card while the customer pays for an order, send a normal Create Payment request with amount and currency, add needPaymentToken and tokenType, and leave out nonPaymentOperation. The customer is charged, and the token is generated from the successful payment:

{
"requestId": "0b5e8d3c-7a14-4f2e-9c61-58d2e4a9f017",
"amount": 25000.0,
"currency": "IQD",
"needPaymentToken": true,
"tokenType": "AUTH",
"finishPaymentUrl": "https://merchant.net/finish",
"notificationUrl": "https://merchant.net/notification"
}

Steps 3 and 4 are the same for both requests.


Step 3 — Redirect the customer to formUrl​

The response is the same Payment object that Create Payment returns (see Create Payment → Response Details). Save the paymentId, then redirect the customer to formUrl.

On the hosted payment page, the customer enters their card details and completes any authentication step, such as a 3-D Secure one-time password. The gateway then redirects them to your finishPaymentUrl.

The redirect does not carry the token

finishPaymentUrl only brings the customer back to your site. The token is delivered to your backend, as described in Step 4.


Step 4 — Receive and store the token​

When the operation finishes, the gateway sends the payment object to your notificationUrl, as for any payment (see Webhook Notifications). For a successful tokenization, the payment object includes a paymentToken object:

{
"requestId": "6f1c2a9e-3b7d-4c55-9a0e-2d8f4b1e7c3a",
"paymentId": "f2bb43a8-488a-4281-977b-5b3418fc3c67",
"status": "SUCCESS",
"canceled": false,
"amount": 0,
"currency": "IQD",
"paymentType": "CARD",
"creationDate": "2025-02-26T17:13:51",
"details": {
"resultCode": "00",
"maskedPan": "521372******8582",
"paymentSystem": "MASTER_CARD"
},
"paymentToken": {
"token": "48b85aa5-e7a6-5ccc-9466-703472aa7541",
"type": "AUTH",
"validTo": "2032-01-31",
"blocked": false
},
"additionalInfo": {}
}
FieldTypeDescription
paymentTokenobjectThe token generated by the operation. It is included in the payment object when the operation was sent with needPaymentToken set to true and completed successfully.
paymentToken.tokenstringThe token value. Store it against your customer and send it as paymentData.paymentToken to charge the card later.
paymentToken.typestringThe type of the issued token: AUTH, NON_RECUR or UNAUTH. It can be lower than the tokenType you requested, so check it before using the token.
paymentToken.validTostringThe date until which the token can be used.
paymentToken.blockedbooleantrue if the token has been blocked and can no longer be used for payments.

You can read the same payment object at any time with Get Payment Status, using the paymentId from Step 3.

Store these values against your customer's account:

  • paymentToken.token — to charge the card later.
  • paymentToken.type — to know what you can use the token for.
  • details.maskedPan and details.paymentSystem — to show the saved card to your customer, for example "Mastercard •••• 8582".
Encrypt the token before you store it

Never store paymentToken.token in plain text. Encrypt it with AES-256-GCM (authenticated encryption) before you write it to your database.

GCM mode provides both confidentiality (encryption) and integrity (authentication), so a stored token can't be tampered with or modified in the database without detection.

  • Generate a new random 12-byte nonce (IV) for every encryption, and store it with the ciphertext and the authentication tag. Never reuse a nonce with the same key.
  • Keep the encryption key out of the database that holds the tokens, for example in a key management service or secrets manager.
  • Reject the token if decryption fails the authentication check.
Check the token type you received

paymentToken.type can be lower than the tokenType you requested. If you requested AUTH for recurring payments but received NON_RECUR or UNAUTH, don't schedule recurring payments with that token. Use it only for payments the customer starts.

Webhook rules still apply

Respond with 200 OK to every webhook, and verify the X-Signature header as described in Message Verification before trusting the token.


Verify it worked​

  1. Use a sandbox terminal that has tokenization enabled (Step 1), and the sandbox host https://uat-sandbox-3ds-api.qi.iq/api/v1.
  2. Send the request from Step 2 and open the returned formUrl in a browser.
  3. Enter the web test card from Sandbox Environment, and the static OTP when the 3-D Secure screen asks for it.
  4. Check that your webhook receives "status": "SUCCESS" with a paymentToken object, or call Get Payment Status with the paymentId.

Next: Pay with a saved token →