Skip to main content

Tokenised Payments

The Tokenised Payments API lets you securely store a customer's card and reuse it for future e-commerce payments, giving returning customers a one-tap checkout while keeping you PCI compliant. Use it to create, retrieve, list, charge, and delete card tokens.

Before you begin

Every endpoint on this page is a gRPC call secured with TLS. Authenticate each call by attaching your API key to the request metadata using the X-API-Key header.

Connect to the host closest to your store's region. See the Payments API Overview for the full list of regional hostnames (Asia-Pacific and Europe), authentication, shared conventions, and how to install the client SDK.

RegionDevelopment and TestLive
Asia-Pacificgrpc-staging-ap.kodypay.comgrpc-ap.kodypay.com
Europegrpc-staging-eu.kodypay.comgrpc-eu.kodypay.com

Use the regional staging host that matches your store in the examples below. Replace the following placeholders with your own values:

PlaceholderDescription
HOSTNAMEYour regional gRPC host, e.g. grpc-staging-eu.kodypay.com (staging) or grpc-eu.kodypay.com (live)
API_KEYYour Kody API key
STORE_IDYour Kody store identifier
TOKEN_IDToken-creation request identifier returned by CreateCardToken
PAYER_REFERENCEYour unique, stable identifier for the customer — e.g. a UUID (v4) or your internal customer ID. Reuse the same value for the same customer so their saved cards stay linked.
PAYMENT_TOKENThe reusable card token used to take payments

Endpoints

#EndpointgRPC callDescription
1Create Card TokenCreateCardTokenStart hosted card tokenisation and return a URL.
2Pay With Card TokenPayWithCardTokenCharge a stored card token.
3Get Token Payment DetailsGetTokenPaymentDetailsFetch the status and details of a token payment.
4Get Card TokenGetCardTokenRetrieve a single stored token.
5Get Card TokensGetCardTokensList all stored tokens for a payer.
6Delete Card TokenDeleteCardTokenPermanently delete a stored token.

All calls are methods on KodyEcomPaymentsService.

Tokenised payment flow

Common enums

enum RecurringProcessingModel {
MODEL_UNSPECIFIED = 0; // Default value, not set
CARD_ON_FILE = 1; // Card on file model
SUBSCRIPTION = 2; // Subscription model
UNSCHEDULED_CARD_ON_FILE = 3; // Unscheduled card on file model
}

enum CardTokenStatus {
PENDING = 0;
FAILED = 1;
READY = 2;
DELETED = 3;
PENDING_DELETE = 4; // Token is in the process of being deleted
}

1. Create Card Token

Method: KodyEcomPaymentsService.CreateCardToken

Creates a new card token by initiating a tokenisation process. This returns a URL where the customer can securely enter their card details to create a reusable token.

Request

rpc CreateCardToken(CreateTokenRequest) returns (CreateTokenResponse);

message CreateTokenRequest {
string store_id = 1; // Your Kody store id
string idempotency_uuid = 2; // Idempotency key to ensure the request is processed only once, generated by client.
optional string token_reference = 3; // Your unique reference for this token request, if applicable. This can be used to match the token with your internal systems.
string payer_reference = 4; // The payer for whom the token is being created. This can be a user ID or any unique identifier you use to track users.
optional string metadata = 5; // A data set that can be used to store information about the order and used in the tokenisation process.
string return_url = 6; // The URL that your client application will be redirected to after the tokenisation is authorised. You can include additional query parameters, for example, the user id or order reference.
optional string payer_statement = 7; // The text to be shown on the payer's bank statement. Maximum 22 characters, otherwise banks might truncate the string. If not set it will use the store's terminals receipt printing name. Allowed characters: a-z, A-Z, 0-9, spaces, and special characters . , ' _ - ? + * /
optional string payer_email_address = 8; // We recommend that you provide this data, as it is used in velocity fraud checks. Required for 3D Secure 2 transactions.
optional string payer_phone_number = 9; // We recommend that you provide this data, as it is used in velocity fraud checks. Required for 3D Secure 2 transactions.
optional RecurringProcessingModel recurring_processing_model = 10; // The recurring model to use for the payment, if applicable. Can be 'Subscription', 'UnscheduledCardOnFile' or 'CardOnFile'.
}

Request fields

FieldTypeRequiredDescription
store_idstringYesYour Kody store ID.
idempotency_uuidstringYesClient-generated idempotency key.
token_referencestringNoYour unique reference for this token request.
payer_referencestringYesIdentifier of the payer the token is created for.
metadatastringNoData set stored against the tokenisation process.
return_urlstringYesURL the client is redirected to after authorisation.
payer_statementstringNoText for the payer's bank statement (max 22 chars).
payer_email_addressstringNoPayer email; used in fraud checks, required for 3DS2.
payer_phone_numberstringNoPayer phone; used in fraud checks, required for 3DS2.
recurring_processing_modelRecurringProcessingModelNoRecurring model for the stored card.

Response

message CreateTokenResponse {
oneof result {
Response response = 1;
Error error = 2;
}

message Response {
string token_id = 1; // A unique identifier for the token creation request, used to query the payment token creation result via GetCardToken.
string create_token_url = 2; // The URL to send the customer to for tokenisation
}

message Error {
Type type = 1;
string message = 2;

enum Type {
UNKNOWN = 0;
DUPLICATE_ATTEMPT = 1;
INVALID_REQUEST = 2;
}
}
}

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.CreateTokenRequest;
import com.kodypay.grpc.ecom.v1.CreateTokenResponse;
import com.kodypay.grpc.ecom.v1.RecurringProcessingModel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.UUID;

public class CreateCardTokenExample {
public static final String HOSTNAME = "HOSTNAME";
public static final String API_KEY = "API_KEY";

public static void main(String[] args) {
createCardToken("STORE_ID", "PAYER_REFERENCE");
}

public static void createCardToken(String storeId, String payerReference) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(HOSTNAME, 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);

var client = KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

CreateTokenRequest request = CreateTokenRequest.newBuilder()
.setStoreId(storeId)
.setIdempotencyUuid(UUID.randomUUID().toString())
.setTokenReference("token_ref_" + UUID.randomUUID())
.setPayerReference(payerReference)
.setReturnUrl("https://your-website.com/token-complete")
.setPayerEmailAddress("customer@example.com")
.setRecurringProcessingModel(RecurringProcessingModel.CARD_ON_FILE)
.build();

CreateTokenResponse response = client.createCardToken(request);
if (response.hasResponse()) {
System.out.println("Token ID: " + response.getResponse().getTokenId());
System.out.println("Token Creation URL: " + response.getResponse().getCreateTokenUrl());
System.out.println("Redirect the customer to this URL to complete tokenisation");
} else {
System.err.println("Error: " + response.getError().getMessage());
}
channel.shutdownNow();
}
}

2. Pay With Card Token

Method: KodyEcomPaymentsService.PayWithCardToken

Processes a payment using a previously stored card token. This allows for instant payments without requiring the customer to re-enter their card details.

Request

rpc PayWithCardToken(PayWithCardTokenRequest) returns (PaymentDetailsResponse);

message PayWithCardTokenRequest {
string store_id = 1; // Your Kody store id
string idempotency_uuid = 2; // Idempotency key to ensure the request is processed only once
string payment_token = 3; // The ID of the payment token to be charged
uint64 amount_minor_units = 4; // Amount in minor units. For example, 2000 means GBP 20.00.
string currency = 5; // ISO 4217 three letter currency code
string payment_reference = 6; // Your unique reference for this payment
optional string order_id = 7; // Your identifier for the order
optional string order_metadata = 8; // Optional order details, not yet implemented.
optional string payer_statement = 9; // Optional text for payer's bank statement
optional PaymentInitiationRequest.CaptureOptions capture_options = 10; // Optional capture settings if the charge is an authorisation
}

Request fields

FieldTypeRequiredDescription
store_idstringYesYour Kody store ID.
idempotency_uuidstringYesClient-generated idempotency key.
payment_tokenstringYesThe stored token to charge.
amount_minor_unitsuint64YesAmount in minor units (e.g. 2000 = GBP 20.00).
currencystringYesISO 4217 currency code.
payment_referencestringYesYour unique reference for this payment.
order_idstringNoYour order identifier.
order_metadatastringNoOptional order details (not yet implemented).
payer_statementstringNoText for the payer's bank statement.
capture_optionsCaptureOptionsNoCapture settings if the charge is an authorisation.

Response

The response follows the same structure as the standard PaymentDetailsResponse from the online payments API, containing payment status, transaction details, and card information.

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.PayWithCardTokenRequest;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.UUID;

public class PayWithCardTokenExample {
public static final String HOSTNAME = "HOSTNAME";
public static final String API_KEY = "API_KEY";

public static void main(String[] args) {
payWithCardToken("STORE_ID", "PAYMENT_TOKEN");
}

public static void payWithCardToken(String storeId, String paymentToken) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(HOSTNAME, 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);

var client = KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

PayWithCardTokenRequest request = PayWithCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setIdempotencyUuid(UUID.randomUUID().toString())
.setPaymentToken(paymentToken)
.setAmountMinorUnits(2500) // £25.00
.setCurrency("GBP")
.setPaymentReference("token_pay_" + UUID.randomUUID())
.setOrderId("order_" + UUID.randomUUID()) // optional field
.setPayerStatement("Online Purchase")
.build();

PaymentDetailsResponse response = client.payWithCardToken(request);
if (response.hasResponse()) {
var paymentDetails = response.getResponse();
System.out.println("Payment ID: " + paymentDetails.getPaymentId());
System.out.println("Status: " + paymentDetails.getStatus());

if (paymentDetails.hasPaymentData()) {
var paymentData = paymentDetails.getPaymentData();
System.out.println("Auth Status: " + paymentData.getAuthStatus());
if (paymentData.hasPaymentCard()) {
System.out.println("Card Last 4: " + paymentData.getPaymentCard().getCardLast4Digits());
}
}
} else {
System.err.println("Error: " + response.getError().getMessage());
}
channel.shutdownNow();
}
}

3. Get Token Payment Details

Method: KodyEcomPaymentsService.GetTokenPaymentDetails

Retrieves details of a specific token-payment using either the payment_id or the payment_reference.

Request

The request follows the same structure as the standard PaymentDetailsRequest from the online payments API, containing payment id, payment reference.

Response

The response follows the same structure as the standard PaymentDetailsResponse from the online payments API, containing payment status, transaction details, and card information.

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.PaymentDetailsRequest;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.PaymentDetails;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.PaymentData;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.SaleData;
import com.kodypay.grpc.ecom.v1.PaymentStatus;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.PaymentData.PaymentCard;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.PaymentData.PaymentWallet;
import com.kodypay.grpc.ecom.v1.PaymentDetailsResponse.PaymentData.PaymentMethodDetailsCase;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;

public class GetTokenPaymentDetailsExample {
public static void main(String[] args) throws InterruptedException {
PaymentDetails details = getTokenPaymentDetails("STORE_ID", "PAYMENT_ID");
printPaymentDetails(details);
}

public static PaymentDetails getTokenPaymentDetails(String storeId, String paymentId) throws InterruptedException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("HOSTNAME", 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), "API_KEY");

KodyEcomPaymentsServiceGrpc.KodyEcomPaymentsServiceBlockingStub client =
KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

PaymentDetailsRequest request = PaymentDetailsRequest.newBuilder()
.setStoreId(storeId)
.setPaymentId(paymentId)
.build();

PaymentDetails details;
PaymentStatus status;

do {
PaymentDetailsResponse response = client.getTokenPaymentDetails(request);
if (!response.hasResponse()) {
throw new RuntimeException("Error: " + response.getError().getMessage());
}

details = response.getResponse();
status = details.getStatus();
System.out.println("Current Status: " + status);

if (status == PaymentStatus.PENDING) {
Thread.sleep(2000);
}
} while (status == PaymentStatus.PENDING);

channel.shutdownNow();
return details;
}

public static void printPaymentDetails(PaymentDetails details) {
if (details.hasPaymentData()) {
PaymentData paymentData = details.getPaymentData();
System.out.println("PSP Reference: " + paymentData.getPspReference());
System.out.println("Payment Method: " + paymentData.getPaymentMethod());
System.out.println("Auth Status: " + paymentData.getAuthStatus());

PaymentMethodDetailsCase methodCase = paymentData.getPaymentMethodDetailsCase();
switch (methodCase) {
case PAYMENT_CARD -> {
PaymentCard card = paymentData.getPaymentCard();
System.out.println("Card Last 4: " + card.getCardLast4Digits());
System.out.println("Auth Code: " + card.getAuthCode());
System.out.println("Payment Token: " + card.getPaymentToken());
}
case PAYMENT_WALLET -> {
PaymentWallet wallet = paymentData.getPaymentWallet();
if (wallet.hasCardLast4Digits()) {
System.out.println("Wallet Card Last 4: " + wallet.getCardLast4Digits());
}
System.out.println("Payment Link ID: " + wallet.getPaymentLinkId());
}
default -> System.out.println("Unknown payment method details.");
}
}

if (details.hasSaleData()) {
SaleData sale = details.getSaleData();
System.out.println("Amount: " + sale.getAmountMinorUnits());
System.out.println("Currency: " + sale.getCurrency());
System.out.println("Order ID: " + sale.getOrderId());
System.out.println("Payment Reference: " + sale.getPaymentReference());
if (sale.hasOrderMetadata()) {
System.out.println("Order Metadata: " + sale.getOrderMetadata());
}
}
}
}

4. Get Card Token

Method: KodyEcomPaymentsService.GetCardToken

Retrieves details of a single stored card token by token_id or token_reference.

Request

rpc GetCardToken(GetCardTokenRequest) returns (GetCardTokenResponse);

message GetCardTokenRequest {
string store_id = 1; // Your Kody store id
oneof token_identifier {
string token_id = 2; // The unique identifier for the token creation request, returned from CreateCardToken.
string token_reference = 3; // Your unique payment reference that was set during the initiation
}
}

Request fields

FieldTypeRequiredDescription
store_idstringYesYour Kody store ID.
token_idstringOne ofToken-creation request ID returned by CreateCardToken.
token_referencestringOne ofYour reference set during initiation.

Response

message GetCardTokenResponse {
oneof result {
Response response = 1;
Error error = 2;
}

message Response {
string token_id = 1; // A unique identifier for the token creation request, used to query the payment token creation result via GetCardToken.
optional string token_reference = 2; // the external payment reference associated with the stored payment method, if applicable
string payment_token = 3; // Kody token for subsequent payments or pre-auths.
string payer_reference = 4; // The payer for whom the token is created, e.g. user id or any unique identifier you use to track users
RecurringProcessingModel recurring_processing_model = 5; // Recurring processing model
CardTokenStatus status = 6;
google.protobuf.Timestamp created_at = 7; // Date when the token was created
PaymentMethods payment_method = 8; // Card brand, e.g. mc, visa and so on
string payment_method_variant = 9; // Card variant, e.g. mccredit, mcdebit, visa, visadebit, etc.
string funding_source = 10; // Funding source of the card, e.g. CREDIT, DEBIT, PREPAID, etc. (aligns with PaymentCard.funding_source)
string card_last_4_digits = 11; // Last four digits of the card number (aligns with PaymentCard.card_last_4_digits)
}

message Error {
Type type = 1;
string message = 2;

enum Type {
UNKNOWN = 0;
PENDING_CREATE = 1; // Token not yet created, still in progress
INVALID_REQUEST = 2;
}
}
}

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.GetCardTokenRequest;
import com.kodypay.grpc.ecom.v1.GetCardTokenResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;

public class GetCardTokenExample {
public static final String HOSTNAME = "HOSTNAME";
public static final String API_KEY = "API_KEY";

public static void main(String[] args) {
getCardToken("STORE_ID", "TOKEN_ID");
}

public static void getCardToken(String storeId, String tokenId) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(HOSTNAME, 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);

var client = KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

GetCardTokenRequest request = GetCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setTokenId(tokenId) // or use .setTokenReference() instead
.build();

GetCardTokenResponse response = client.getCardToken(request);
if (response.hasResponse()) {
var tokenInfo = response.getResponse();
System.out.println("Token ID: " + tokenInfo.getTokenId());
System.out.println("Payment Token: " + tokenInfo.getPaymentToken());
System.out.println("Status: " + tokenInfo.getStatus());
System.out.println("Card Last 4: " + tokenInfo.getCardLast4Digits());
System.out.println("Payment Method: " + tokenInfo.getPaymentMethod());
} else {
System.err.println("Error: " + response.getError().getMessage());
}
channel.shutdownNow();
}
}

5. Get Card Tokens

Method: KodyEcomPaymentsService.GetCardTokens

Lists all stored card tokens for a specific payer.

Request

rpc GetCardTokens(GetCardTokensRequest) returns (GetCardTokensResponse);

message GetCardTokensRequest {
string store_id = 1;
string payer_reference = 2; // The customer for whom to list tokens
}

Request fields

FieldTypeRequiredDescription
store_idstringYesYour Kody store ID.
payer_referencestringYesThe payer whose tokens you want to list.

Response

message GetCardTokensResponse {
oneof result {
Response response = 1;
Error error = 2;
}

message Response {
repeated GetCardTokenResponse.Response tokens = 1;
}

message Error {
Type type = 1;
string message = 2;

enum Type {
UNKNOWN = 0;
INVALID_REQUEST = 1; // e.g. missing store_id or payer_reference
}
}
}

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.GetCardTokensRequest;
import com.kodypay.grpc.ecom.v1.GetCardTokensResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;

public class GetCardTokensExample {
public static final String HOSTNAME = "HOSTNAME";
public static final String API_KEY = "API_KEY";

public static void main(String[] args) {
getCardTokens("STORE_ID", "PAYER_REFERENCE");
}

public static void getCardTokens(String storeId, String payerReference) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(HOSTNAME, 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);

var client = KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

GetCardTokensRequest request = GetCardTokensRequest.newBuilder()
.setStoreId(storeId)
.setPayerReference(payerReference)
.build();

GetCardTokensResponse response = client.getCardTokens(request);
if (response.hasResponse()) {
System.out.println("Found " + response.getResponse().getTokensCount() + " tokens:");
for (var token : response.getResponse().getTokensList()) {
System.out.println("Token ID: " + token.getTokenId());
System.out.println("Card Last 4: " + token.getCardLast4Digits());
System.out.println("Payment Method: " + token.getPaymentMethod());
System.out.println("Status: " + token.getStatus());
System.out.println("---");
}
} else {
System.err.println("Error: " + response.getError().getMessage());
}
channel.shutdownNow();
}
}

6. Delete Card Token

Method: KodyEcomPaymentsService.DeleteCardToken

Permanently deletes a stored card token. This action cannot be undone.

Request

rpc DeleteCardToken(DeleteCardTokenRequest) returns (DeleteCardTokenResponse);

message DeleteCardTokenRequest {
string store_id = 1; // Kody store id
oneof token_identifier {
string token_id = 2; // The unique identifier created by Kody
string token_reference = 3; // Your unique payment reference that was set during the initiation
}
}

Request fields

FieldTypeRequiredDescription
store_idstringYesYour Kody store ID.
token_idstringOne ofKody-generated token ID.
token_referencestringOne ofYour reference set during initiation.

Response

message DeleteCardTokenResponse {
oneof result {
Response response = 1;
Error error = 2;
}

message Response {
// Empty response indicates successful deletion
}

message Error {
Type type = 1;
string message = 2;

enum Type {
UNKNOWN = 0;
NOT_FOUND = 1; // Token not found
FAILED = 2; // Deletion failed
INVALID_REQUEST = 3; // Invalid request parameters
}
}
}

Examples

Show code examples (Java · Python · .NET · PHP)
package com.kody;

import com.kodypay.grpc.ecom.v1.KodyEcomPaymentsServiceGrpc;
import com.kodypay.grpc.ecom.v1.DeleteCardTokenRequest;
import com.kodypay.grpc.ecom.v1.DeleteCardTokenResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;

public class DeleteCardTokenExample {
public static final String HOSTNAME = "HOSTNAME";
public static final String API_KEY = "API_KEY";

public static void main(String[] args) {
deleteCardToken("STORE_ID", "TOKEN_ID");
}

public static void deleteCardToken(String storeId, String tokenId) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(HOSTNAME, 443)
.useTransportSecurity()
.build();

Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);

var client = KodyEcomPaymentsServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

DeleteCardTokenRequest request = DeleteCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setTokenId(tokenId) // or use .setTokenReference() instead
.build();

DeleteCardTokenResponse response = client.deleteCardToken(request);
if (response.hasResponse()) {
System.out.println("Card token deleted successfully");
} else {
System.err.println("Error: " + response.getError().getMessage());
}
channel.shutdownNow();
}
}

Best Practices

Security Considerations

  • Use a stable, unique payer_reference for each customer — for example a UUID (v4) or your own internal customer ID. Keep it consistent across sessions so a customer's saved tokens stay linked, and avoid guessable or shared values such as email addresses or phone numbers.
  • Never store actual card details on your servers — use tokens instead
  • Implement proper access controls for token management operations

Token Lifecycle Management

  1. Create tokens only when customers explicitly consent to save their payment method
  2. Monitor token status regularly using GetCardToken to ensure tokens remain valid
  3. Delete tokens when customers request removal or close their accounts
  4. Handle expired tokens gracefully by prompting for new payment method creation

Error Handling

  • Implement retry logic for network failures
  • Handle PENDING_CREATE status when tokens are still being processed
  • Validate input parameters before making API calls
  • Log errors appropriately for debugging and monitoring

Performance Optimisation

  • Use GetCardTokens to batch-load all tokens for a customer
  • Implement caching for frequently accessed token information
  • Consider using connection pooling for high-volume applications

Need help?

For further support or more detailed information, contact the Kody Support team at support@kody.com.