Saved cards: customers & tokens (COFT)
Let returning customers pay with a card they saved earlier (card-on-file tokenisation, COFT). The card is tokenised when the customer pays with save: true in request. Later payments send the token instead of the card number.
Flow
-
Create the customer. (Returns Customer id)
POST /api/v1/customer
{ "mobile_number": "<customer mobile number>", "email_id": "<customer email>", "country_code": "65" }{ "customer_id": "cust-v1-261102112435-aa-vSD11R", "country_code": "65", "mobile_number": "<customer mobile number>", "email_id": "<customer email>", "status": "ACTIVE", "created_at": "2026-11-02T11:24:35.440337118Z", "updated_at": "2026-11-02T11:24:35.440353442Z", "brand_wallet_enabled": false } -
Create the order with the customer. Put customer_id in purchase_details.customer.customer_id. This works for both POST /api/pay/v1/orders (Seamless) and POST /api/checkout/v1/orders (Hosted Checkout).
{ "merchant_order_reference": "order-20001", "order_amount": { "value": 100, "currency": "SGD" }, "integration_mode": "SEAMLESS", "purchase_details": { "customer": { "customer_id": "cust-v1-261102112435-aa-vSD11R", "email_id": "<customer email>", "first_name": "Test", "last_name": "Buyer", "mobile_number": "<customer mobile number>", "country_code": "65" } } } -
First payment, saving the card. POST /api/pay/v1/orders/{order_id}/payments with card_details and "save": true. Complete 3-D Secure via challenge_url, then verify with Get order.
List saved cards. GET /api/v1/customer/{customerId}/tokens returns each token's token_id, status, expiry, acquirer and masked card details.{ "payments": [{ "merchant_payment_reference": "pay-save-0001", "payment_method": "CARD", "payment_amount": { "value": 100, "currency": "SGD" }, "payment_option": { "card_details": { "name": "Test Buyer", "card_number": "<UAT test card>", "cvv": "123", "expiry_month": "12", "expiry_year": "2027", "save": true } } }] } -
List the customer's saved cards
GET /api/v1/customer/{customerId}/tokens
{ "customer_id": "cust-v1-261102131717-aa-lHRqxs", "total_tokens": 1, "tokens": [{ "token_id": "token-v1-261102132209-aa-evR7dh", "merchant_token_reference": "b88bdd60-d47d-4fb1-854f-0bf0d65709c1", "status": "ACTIVE", "expired_at": "2027-05-02T13:22:09.174688Z", "merchant_metadata": { "acquirer": "MPGS" }, "card_data": { "last4_digit": "6000", "network_name": "VISA", "issuer_name": "VISA" } }] }
Note: Show customers only the masked details (network and last 4 digits), and hide tokens whose status isn't ACTIVE or whose expired_at has passed.
-
Pay with a saved card. Create a new order, then create the payment with card_token_details.token_id and token_txn_type: "ACQUIRER_TOKEN". No CVV or expiry is needed. Complete challenge_url if returned, then verify.
{ "payments": [{ "merchant_payment_reference": "pay-coft-0001", "payment_method": "CARD", "payment_amount": { "value": 100, "currency": "SGD" }, "payment_option": { "card_token_details": { "name": "Test Buyer", "last4_digit": "1111", "cvv": "123", "expiry_month": "12", "expiry_year": "2027", "token_id": "token-v1-261102132209-aa-evR7dh", "token_txn_type": "ACQUIRER_TOKEN", "registered_mobile_number": "<customer mobile number>" } } }] }
Updated 5 days ago