Pre-Authorisations
Pre-Authorisations let you place an initial hold on a card. You can later capture all or part of the authorised amount, or release it. Use this flow when the final charge isn’t known upfront - common in hospitality and car rentals.
Note: Every request requires an
X-API-Keyheader with your API key.
Common Enums
enum PaymentMethods {
VISA = 0;
MASTERCARD = 1;
AMEX = 2;
BAN_CONTACT = 3;
CHINA_UNION_PAY = 4;
MAESTRO = 5;
DINERS = 6;
DISCOVER = 7;
JCB = 8;
ALIPAY = 9;
WECHAT = 10;
UNKNOWN_METHOD = -1;
}
enum AuthStatus {
AUTH_STATUS_UNSPECIFIED = 0;
PENDING_AUTHORISATION = 1;
AUTHORISED = 2;
DECLINED = 3;
CANCELLED = 4;
PENDING_CAPTURE = 5;
CAPTURED = 6;
CAPTURE_FAILED = 7;
PENDING_RELEASE = 8;
RELEASED = 9;
RELEASE_FAILED = 10;
PENDING_TOP_UP = 11;
TOP_UP_FAILED = 12;
EXPIRED = 13;
FAILED = 14;
}
enum RecurringProcessingModel {
MODEL_UNSPECIFIED = 0;
CARD_ON_FILE = 1;
SUBSCRIPTION = 2;
UNSCHEDULED_CARD_ON_FILE = 3;
}
enum CardTokenStatus {
PENDING = 0;
FAILED = 1;
READY = 2;
DELETED = 3;
PENDING_DELETE = 4;
UNKNOWN_STATUS = -1;
}
1. Pre-Authorise
Service Call: KodyPreAuthTerminalService.PreAuthorise
Initiates a pre-authorisation on a physical payment terminal. The terminal will prompt the customer to present their card.
Request
rpc PreAuthorise(PreAuthorisationRequest) returns (stream PreAuthorisationResponse);
message PreAuthorisationRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string pre_auth_reference = 2; // Client's unique reference
optional string order_id = 3; // Client's order identifier
string store_id = 4; // Kody Store ID
string terminal_id = 5; // Target payment terminal ID
uint64 amount_minor_units = 6; // Amount in minor units (e.g., pence)
string currency = 7; // ISO 4217 currency code
}
Response
message PreAuthorisationResponse {
string pre_auth_reference = 1; // Echoed from request
string psp_reference = 2; // PSP reference
optional string order_id = 3; // Echoed if provided
string pre_auth_id = 4; // Kody authorisation ID
optional PaymentCard card_details = 5; // Card information
AuthStatus status = 6; // Final status
uint64 authorised_amount_minor_units = 7; // Actual authorised amount
string currency = 8; // Currency
google.protobuf.Timestamp created_at = 9; // Creation timestamp
optional google.protobuf.Timestamp authorised_at = 10; // Authorisation timestamp
optional string auth_code = 11; // PSP auth code
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.AuthStatus;
import com.kodypay.grpc.preauth.v1.KodyPreAuthTerminalServiceGrpc;
import com.kodypay.grpc.preauth.v1.PreAuthorisationRequest;
import com.kodypay.grpc.preauth.v1.PreAuthorisationResponse;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExamplePreAuthorise {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Terminal ID
String terminalId = "TERMINAL ID";
//TODO: Replace with your store operating currency: ISO 4217
String currencyCode = "HKD";
//TODO: Replace with the auth amount in minor units
long amountMinorUnits = 1000;
//TODO: Replace these with your reference ID
String preAuthReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
preAuthorise(storeId, terminalId, amountMinorUnits, currencyCode, preAuthReference, idempotencyUuid);
}
// Example of a pre-authorisation with idempotency and reference IDs
private static void preAuthorise(String storeId, String terminalId, long amountMinorUnits, String currency, String preAuthReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
PreAuthorisationRequest preAuthRequest = PreAuthorisationRequest.newBuilder()
.setStoreId(storeId)
.setTerminalId(terminalId)
.setAmountMinorUnits(amountMinorUnits)
.setCurrency(currency)
.setPreAuthReference(preAuthReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
Iterator<PreAuthorisationResponse> responseIterator = preAuthClient.preAuthorise(preAuthRequest);
while (responseIterator.hasNext()) {
PreAuthorisationResponse preAuthResponse = responseIterator.next();
processPreAuthResponse(preAuthResponse);
}
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process pre-authorisation response
private static void processPreAuthResponse(PreAuthorisationResponse response) {
System.out.println("PreAuth ID: " + response.getPreAuthId() + ", PreAuth Status: " + response.getStatus());
switch (response.getStatus()) {
case AUTHORISED:
System.out.println("Pre-Auth success: " + response.getPreAuthId());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("Auth Code: " + response.getAuthCode());
break;
case PENDING_AUTHORISATION:
System.out.println("Pre-Auth pending: " + response.getPreAuthId());
break;
case FAILED, EXPIRED, CANCELLED, DECLINED:
System.out.println("Pre-Auth failed: " + response.getPreAuthId());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Pre-Auth status unknown: " + response.getPreAuthId());
break;
}
}
}
import grpc
import kody_clientsdk_python.preauth.v1.preauth_pb2 as kody_model
import kody_clientsdk_python.preauth.v1.preauth_pb2_grpc as kody_client
def pre_authorise():
channel = grpc.secure_channel("HOSTNAME:443", grpc.ssl_channel_credentials())
client = kody_client.KodyPreAuthTerminalServiceStub(channel)
metadata = [("x-api-key", "API_KEY")]
request = kody_model.PreAuthorisationRequest(
idempotency_uuid="unique-uuid-123",
pre_auth_reference="PREAUTH-2024-001",
order_id="ORDER-123",
store_id="STORE_ID",
terminal_id="TERMINAL_ID",
amount_minor_units=10000, # £100.00
currency="GBP",
need_tokenisation=True
)
for response in client.PreAuthorise(request, metadata=metadata):
print(f"Status: {response.status}")
print(f"Pre-Auth ID: {response.pre_auth_id}")
if __name__ == "__main__":
pre_authorise()
using Grpc.Core;
using Grpc.Net.Client;
using Com.Kodypay.Grpc.Preauth.V1;
class Program
{
static async Task Main(string[] args)
{
var HOSTNAME = "grpc-staging.kodypay.com";
var API_KEY = "API_KEY";
var channel = GrpcChannel.ForAddress("https://" + HOSTNAME);
var client = new KodyPreAuthTerminalService.KodyPreAuthTerminalServiceClient(channel);
var metadata = new Metadata
{
{ "X-API-Key", API_KEY }
};
var request = new PreAuthorisationRequest
{
IdempotencyUuid = "unique-uuid-123",
PreAuthReference = "PREAUTH-2024-001",
OrderId = "ORDER-123",
StoreId = "STORE_ID",
TerminalId = "TERMINAL_ID",
AmountMinorUnits = 10000, // £100.00
Currency = "GBP",
NeedTokenisation = true
};
using var call = client.PreAuthorise(request, metadata);
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Status: {response.Status}");
Console.WriteLine($"Pre-Auth ID: {response.PreAuthId}");
}
}
}
<?php
require __DIR__ . '/../vendor/autoload.php';
use Com\Kodypay\Grpc\Preauth\V1\KodyPreAuthTerminalServiceClient;
use Com\Kodypay\Grpc\Preauth\V1\PreAuthorisationRequest;
use Grpc\ChannelCredentials;
$HOSTNAME = "grpc-staging.kodypay.com";
$API_KEY = "API_KEY";
$client = new KodyPreAuthTerminalServiceClient($HOSTNAME, [
'credentials' => ChannelCredentials::createSsl()
]);
$metadata = ['X-API-Key' => [$API_KEY]];
$request = new PreAuthorisationRequest();
$request->setIdempotencyUuid('unique-uuid-123');
$request->setPreAuthReference('PREAUTH-2024-001');
$request->setOrderId('ORDER-123');
$request->setStoreId('STORE_ID');
$request->setTerminalId('TERMINAL_ID');
$request->setAmountMinorUnits(10000); // £100.00
$request->setCurrency('GBP');
$request->setNeedTokenisation(true);
$call = $client->PreAuthorise($request, $metadata);
foreach ($call->responses() as $response) {
echo "Status: " . $response->getStatus() . PHP_EOL;
echo "Pre-Auth ID: " . $response->getPreAuthId() . PHP_EOL;
}
require 'kody'
Kody.configure do |config|
config.staging!
config.api_key = 'API_KEY'
config.store_id = 'STORE_ID'
end
stub = Com::Kodypay::Grpc::Preauth::V1::KodyPreAuthTerminalService::Stub.new(
Kody.configuration.endpoint,
GRPC::Core::ChannelCredentials.new
)
request = Com::Kodypay::Grpc::Preauth::V1::PreAuthorisationRequest.new(
idempotency_uuid: 'unique-uuid-123',
pre_auth_reference: 'PREAUTH-2024-001',
order_id: 'ORDER-123',
store_id: Kody.configuration.store_id,
terminal_id: 'TERMINAL_ID',
amount_minor_units: 10000, # £100.00
currency: 'GBP',
need_tokenisation: true
)
begin
stub.pre_authorise(request, metadata: { 'x-api-key' => Kody.configuration.api_key }) do |response|
puts "Status: #{response.status}"
puts "Pre-Auth ID: #{response.pre_auth_id}"
end
rescue GRPC::BadStatus => e
puts "gRPC Error: #{e.code} - #{e.message}"
end
2. Top-Up Authorisation
Service Call: KodyPreAuthTerminalService.TopUpAuthorisation
Increases the authorised amount on an existing pre-authorisation.
Request
rpc TopUpAuthorisation(TopUpAuthorisationRequest) returns (stream TopUpAuthorisationResponse);
message TopUpAuthorisationRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string top_up_reference = 2; // Client's reference for this top-up
optional string order_id = 3; // Original order ID
string store_id = 4; // Kody Store ID
string pre_auth_id = 5; // ID from original PreAuthorise
uint64 top_up_amount_minor_units = 6; // Additional amount to authorise
string currency = 7; // ISO 4217 currency code
}
Response
message TopUpAuthorisationResponse {
string top_up_reference = 1; // Echoed from request
string psp_reference = 2; // PSP reference
optional string order_id = 3; // Echoed if provided
optional uint64 total_authorised_amount_minor_units = 4; // New total authorised amount
optional google.protobuf.Timestamp topped_up_at = 5; // Top-up timestamp
AuthStatus status = 6; // Status of top-up
string topup_id = 7; // Unique identifier for this top-up
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.*;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExampleTopupAuthorisation {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your PreAuth ID returned from the pre-authorisation
String preAuthId = "PRE AUTH ID";
//TODO: Replace with your store operating currency: ISO 4217
String currencyCode = "HKD";
//TODO: Replace with the topup amount in minor units, topup amount is the auth amount after topup
long topUpAmountMinorUnits = 1000;
//TODO: Replace these with your reference ID
String topUpReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
topUpAuthorisation(storeId, preAuthId, topUpAmountMinorUnits, currencyCode, topUpReference, idempotencyUuid);
}
// Example of a topup-authorisation with idempotency and reference IDs
private static void topUpAuthorisation(String storeId, String preAuthId, long topUpAmountMinorUnits, String currency, String topUpReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
TopUpAuthorisationRequest topUpAuthRequest = TopUpAuthorisationRequest.newBuilder()
.setStoreId(storeId)
.setPreAuthId(preAuthId)
.setTopUpAmountMinorUnits(topUpAmountMinorUnits)
.setCurrency(currency)
.setTopUpReference(topUpReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
Iterator<TopUpAuthorisationResponse> responseIterator = preAuthClient.topUpAuthorisation(topUpAuthRequest);
while (responseIterator.hasNext()) {
TopUpAuthorisationResponse topUpAuthResponse = responseIterator.next();
processTopUpAuthResponse(topUpAuthResponse);
}
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process topup-authorisation response
private static void processTopUpAuthResponse(TopUpAuthorisationResponse response) {
System.out.println("TopUp ID: " + response.getTopupId() + ", TopUp Status: " + response.getStatus());
switch (response.getStatus()) {
case AUTHORISED:
System.out.println("TopUp success: " + response.getTopupId());
System.out.println("PspReference: " + response.getPspReference());
break;
case PENDING_TOP_UP:
System.out.println("TopUp pending: " + response.getTopupId());
break;
case TOP_UP_FAILED:
System.out.println("TopUp failed: " + response.getTopupId());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("TopUp status unknown: " + response.getTopupId());
break;
}
}
}
request = kody_model.TopUpAuthorisationRequest(
idempotency_uuid="topup-uuid-789",
top_up_reference="TOPUP-001",
store_id="STORE_ID",
pre_auth_id="preauth_xyz123",
top_up_amount_minor_units=2000, # £20.00
currency="GBP"
)
for response in client.TopUpAuthorisation(request, metadata=metadata):
print(f"New Total: £{response.total_authorised_amount_minor_units / 100}")
var request = new TopUpAuthorisationRequest
{
IdempotencyUuid = "topup-uuid-789",
TopUpReference = "TOPUP-001",
StoreId = "STORE_ID",
PreAuthId = "preauth_xyz123",
TopUpAmountMinorUnits = 2000, // £20.00
Currency = "GBP"
};
using var call = client.TopUpAuthorisation(request, metadata);
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"New Total: £{response.TotalAuthorisedAmountMinorUnits / 100.0}");
}
$request = new TopUpAuthorisationRequest();
$request->setIdempotencyUuid('topup-uuid-789');
$request->setTopUpReference('TOPUP-001');
$request->setStoreId('STORE_ID');
$request->setPreAuthId('preauth_xyz123');
$request->setTopUpAmountMinorUnits(2000); // £20.00
$request->setCurrency('GBP');
$call = $client->TopUpAuthorisation($request, $metadata);
foreach ($call->responses() as $response) {
echo "New Total: £" . ($response->getTotalAuthorisedAmountMinorUnits() / 100) . PHP_EOL;
}
request = Com::Kodypay::Grpc::Preauth::V1::TopUpAuthorisationRequest.new(
idempotency_uuid: 'topup-uuid-789',
top_up_reference: 'TOPUP-001',
store_id: Kody.configuration.store_id,
pre_auth_id: 'preauth_xyz123',
top_up_amount_minor_units: 2000, # £20.00
currency: 'GBP'
)
stub.top_up_authorisation(request, metadata: { 'x-api-key' => Kody.configuration.api_key }) do |response|
puts "New Total: £#{response.total_authorised_amount_minor_units / 100.0}"
end
3. Capture Authorisation
Service Call: KodyPreAuthTerminalService.CaptureAuthorisation
Captures the specified amount from the existing pre-authorisation. Any remaining authorised amount is automatically released back to the cardholder.
Request
rpc CaptureAuthorisation(CaptureAuthorisationRequest) returns (CaptureAuthorisationResponse);
message CaptureAuthorisationRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string capture_reference = 2; // Client's reference for capture
optional string order_id = 3; // Original order ID
string store_id = 4; // Kody Store ID
string pre_auth_id = 5; // ID from original PreAuthorise
uint64 capture_amount_minor_units = 6; // Final amount to capture
string currency = 7; // ISO 4217 currency code
optional string terminal_id = 8; // Required for batch payments
}
Response
message CaptureAuthorisationResponse {
string capture_reference = 1; // Echoed from request
string psp_reference = 2; // PSP reference
optional string order_id = 3; // Echoed if provided
google.protobuf.Timestamp captured_at = 4; // Capture timestamp
string capture_id = 5; // Unique identifier for this capture
AuthStatus status = 6; // Status of capture
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.KodyPreAuthTerminalServiceGrpc;
import com.kodypay.grpc.preauth.v1.CaptureAuthorisationRequest;
import com.kodypay.grpc.preauth.v1.CaptureAuthorisationResponse;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExampleCaptureAuthorisation {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Terminal ID, required for Batch Payments
String terminalId = "TERMINAL ID";
//TODO: Replace this with your PreAuth ID returned from the pre-authorisation
String preAuthId = "PRE AUTH ID";
//TODO: Replace with your store operating currency: ISO 4217
String currencyCode = "HKD";
//TODO: Replace with the auth amount in minor units, the remaining amount will be automatically released
long captureAmountMinorUnits = 1000;
//TODO: Replace these with your reference ID
String captureReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
captureAuthorisation(storeId, terminalId, preAuthId, captureAmountMinorUnits, currencyCode, captureReference, idempotencyUuid);
}
// Example of a capture authorisation with idempotency and reference IDs
private static void captureAuthorisation(String storeId, String terminalId, String preAuthId, long captureAmountMinorUnits, String currency, String captureReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
CaptureAuthorisationRequest captureAuthRequest = CaptureAuthorisationRequest.newBuilder()
.setStoreId(storeId)
.setTerminalId(terminalId)
.setPreAuthId(preAuthId)
.setCaptureAmountMinorUnits(captureAmountMinorUnits)
.setCurrency(currency)
.setCaptureReference(captureReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
CaptureAuthorisationResponse captureAuthResponse = preAuthClient.captureAuthorisation(captureAuthRequest);
processCaptureAuthResponse(captureAuthResponse);
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process capture authorisation response
private static void processCaptureAuthResponse(CaptureAuthorisationResponse response) {
// Capture is processed asynchronously, and the final status can be queried through the GetPreAuthorisation interface.
System.out.println("Capture Ref: " + response.getCaptureReference() + ", Status: " + response.getStatus());
switch (response.getStatus()) {
case CAPTURED:
System.out.println("Capture success: " + response.getCaptureReference());
System.out.println("Capture Id: " + response.getCaptureId());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("CapturedAt: " + response.getCapturedAt());
break;
case PENDING_CAPTURE:
System.out.println("Capture pending: " + response.getCaptureReference());
System.out.println("Capture Id: " + response.getCaptureId());
break;
case CAPTURE_FAILED:
System.out.println("Capture failed: " + response.getCaptureReference());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Capture status unknown: " + response.getCaptureReference());
break;
}
}
}
request = kody_model.CaptureAuthorisationRequest(
idempotency_uuid="capture-uuid-101",
capture_reference="CAPTURE-001",
store_id="STORE_ID",
pre_auth_id="preauth_xyz123",
capture_amount_minor_units=11000, # £110.00
currency="GBP"
)
response = client.CaptureAuthorisation(request, metadata=metadata)
print(f"Capture ID: {response.capture_id}")
var request = new CaptureAuthorisationRequest
{
IdempotencyUuid = "capture-uuid-101",
CaptureReference = "CAPTURE-001",
StoreId = "STORE_ID",
PreAuthId = "preauth_xyz123",
CaptureAmountMinorUnits = 11000, // £110.00
Currency = "GBP"
};
var response = await client.CaptureAuthorisationAsync(request, metadata);
Console.WriteLine($"Capture ID: {response.CaptureId}");
$request = new CaptureAuthorisationRequest();
$request->setIdempotencyUuid('capture-uuid-101');
$request->setCaptureReference('CAPTURE-001');
$request->setStoreId('STORE_ID');
$request->setPreAuthId('preauth_xyz123');
$request->setCaptureAmountMinorUnits(11000); // £110.00
$request->setCurrency('GBP');
list($response, $status) = $client->CaptureAuthorisation($request, $metadata)->wait();
echo "Capture ID: " . $response->getCaptureId() . PHP_EOL;
request = Com::Kodypay::Grpc::Preauth::V1::CaptureAuthorisationRequest.new(
idempotency_uuid: 'capture-uuid-101',
capture_reference: 'CAPTURE-001',
store_id: Kody.configuration.store_id,
pre_auth_id: 'preauth_xyz123',
capture_amount_minor_units: 11000, # £110.00
currency: 'GBP'
)
response = stub.capture_authorisation(request, metadata: { 'x-api-key' => Kody.configuration.api_key })
puts "Capture ID: #{response.capture_id}"
4. Release Authorisation
Service Call: KodyPreAuthTerminalService.ReleaseAuthorisation
Releases the hold on funds from an existing pre-authorisation without capturing.
Request
rpc ReleaseAuthorisation(ReleaseAuthorisationRequest) returns (ReleaseAuthorisationResponse);
message ReleaseAuthorisationRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string release_reference = 2; // Client's reference for release
optional string order_id = 3; // Original order ID
string store_id = 4; // Kody Store ID
string pre_auth_id = 5; // ID from original PreAuthorise
}
Response
message ReleaseAuthorisationResponse {
string release_reference = 1; // Echoed from request
string psp_reference = 2; // PSP reference
optional string order_id = 3; // Echoed if provided
google.protobuf.Timestamp released_at = 4; // Release timestamp
AuthStatus status = 5; // Status of release
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.KodyPreAuthTerminalServiceGrpc;
import com.kodypay.grpc.preauth.v1.ReleaseAuthorisationRequest;
import com.kodypay.grpc.preauth.v1.ReleaseAuthorisationResponse;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExampleReleaseAuthorisation {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your PreAuth ID returned from the pre-authorisation
String preAuthId = "PRE AUTH ID";
//TODO: Replace these with your reference ID
String releaseReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
releaseAuthorisation(storeId, preAuthId, releaseReference, idempotencyUuid);
}
// Example of a release authorisation with idempotency and reference IDs
private static void releaseAuthorisation(String storeId, String preAuthId, String releaseReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
ReleaseAuthorisationRequest releaseAuthRequest = ReleaseAuthorisationRequest.newBuilder()
.setStoreId(storeId)
.setPreAuthId(preAuthId)
.setReleaseReference(releaseReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
ReleaseAuthorisationResponse releaseAuthResponse = preAuthClient.releaseAuthorisation(releaseAuthRequest);
processReleaseAuthResponse(releaseAuthResponse);
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process release authorisation response
private static void processReleaseAuthResponse(ReleaseAuthorisationResponse response) {
// Release is processed asynchronously, and the final status can be queried through the GetPreAuthorisation interface.
System.out.println("Release Ref: " + response.getReleaseReference() + ", Status: " + response.getStatus());
switch (response.getStatus()) {
case RELEASED:
System.out.println("Release success: " + response.getReleaseReference());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("ReleasedAt: " + response.getReleasedAt());
break;
case PENDING_RELEASE:
System.out.println("Release pending: " + response.getReleaseReference());
break;
case RELEASE_FAILED:
System.out.println("Release failed: " + response.getReleaseReference());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Release status unknown: " + response.getReleaseReference());
break;
}
}
}
request = kody_model.ReleaseAuthorisationRequest(
idempotency_uuid="release-uuid-202",
release_reference="RELEASE-001",
store_id="STORE_ID",
pre_auth_id="preauth_xyz123"
)
response = client.ReleaseAuthorisation(request, metadata=metadata)
print(f"Released at: {response.released_at}")
var request = new ReleaseAuthorisationRequest
{
IdempotencyUuid = "release-uuid-202",
ReleaseReference = "RELEASE-001",
StoreId = "STORE_ID",
PreAuthId = "preauth_xyz123"
};
var response = await client.ReleaseAuthorisationAsync(request, metadata);
Console.WriteLine($"Released at: {response.ReleasedAt}");
$request = new ReleaseAuthorisationRequest();
$request->setIdempotencyUuid('release-uuid-202');
$request->setReleaseReference('RELEASE-001');
$request->setStoreId('STORE_ID');
$request->setPreAuthId('preauth_xyz123');
list($response, $status) = $client->ReleaseAuthorisation($request, $metadata)->wait();
echo "Released at: " . $response->getReleasedAt()->toDateTime()->format('Y-m-d H:i:s') . PHP_EOL;
request = Com::Kodypay::Grpc::Preauth::V1::ReleaseAuthorisationRequest.new(
idempotency_uuid: 'release-uuid-202',
release_reference: 'RELEASE-001',
store_id: Kody.configuration.store_id,
pre_auth_id: 'preauth_xyz123'
)
response = stub.release_authorisation(request, metadata: { 'x-api-key' => Kody.configuration.api_key })
puts "Released at: #{response.released_at}"
5. Get Pre-Authorisation
Service Call: KodyPreAuthTerminalService.GetPreAuthorisation
Retrieves the current state and details of an existing pre-authorisation.
Request
rpc GetPreAuthorisation(GetPreAuthorisationRequest) returns (GetPreAuthorisationResponse);
message GetPreAuthorisationRequest {
string store_id = 1; // Kody Store ID
string pre_auth_id = 2; // ID from original PreAuthorise
}
Response
message GetPreAuthorisationResponse {
string pre_auth_reference = 1; // Client's original reference
string pre_auth_id = 2; // Kody authorisation ID
optional string psp_reference = 3; // PSP reference
AuthStatus status = 4; // Current status
uint64 total_authorised_amount_minor_units = 5; // Total authorised amount
optional uint64 captured_amount_minor_units = 6; // Amount captured
string store_currency = 7; // Currency code
PaymentCard card_details = 8; // Card information
google.protobuf.Timestamp created_at = 9; // Creation timestamp
optional google.protobuf.Timestamp last_topped_up_at = 10; // Last top-up timestamp
optional google.protobuf.Timestamp captured_at = 11; // Capture timestamp
optional google.protobuf.Timestamp released_at = 12; // Release timestamp
optional google.protobuf.Timestamp authorised_at = 13; // Authorisation timestamp
repeated Adjustment adjustments = 14; // List of adjustments
repeated Capture captures = 15; // List of captures
}
message Adjustment {
string adjustment_id = 1; // Unique identifier for this adjustment.
optional string adjustment_reference = 2; // Client's reference for the adjustment, if provided.
AdjustmentStatus status = 3; // Current status of the adjustment.
uint64 amount_minor_units = 4; // Amount of the adjustment in minor currency units.
google.protobuf.Timestamp created_at = 5; // Timestamp when the adjustment was created.
google.protobuf.Timestamp updated_at = 6; // Timestamp when the adjustment was last updated.
optional string psp_reference = 7; // PSP reference for this adjustment, if available.
}
enum AdjustmentStatus {
ADJUSTMENT_STATUS_UNSPECIFIED = 0;
ADJUSTMENT_PENDING = 1;
ADJUSTMENT_SUCCESS = 2;
ADJUSTMENT_ERROR = 3;
}
message Capture {
string capture_id = 1;
uint64 amount_minor_units = 2;
CaptureStatus status = 3;
google.protobuf.Timestamp created_at = 4;
optional string psp_reference = 5;
optional string error = 6;
enum CaptureStatus {
CAPTURE_STATUS_UNSPECIFIED = 0;
CAPTURE_PENDING = 1;
CAPTURE_SUCCESS = 2;
CAPTURE_FAILED = 3;
}
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.AuthStatus;
import com.kodypay.grpc.preauth.v1.GetPreAuthorisationRequest;
import com.kodypay.grpc.preauth.v1.GetPreAuthorisationResponse;
import com.kodypay.grpc.preauth.v1.KodyPreAuthTerminalServiceGrpc;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.concurrent.TimeUnit;
public class ExampleGetPreAuthorisation {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your PreAuth ID returned from the pre-authorisation
String preAuthId = "PRE AUTH ID";
getPreAuthorisation(storeId, preAuthId);
}
// Example of a get pre-authorisation with idempotency and reference IDs
private static void getPreAuthorisation(String storeId, String preAuthId) {
var preAuthClient = createKodyPreAuthTerminalClient();
GetPreAuthorisationRequest preAuthRequest = GetPreAuthorisationRequest.newBuilder()
.setStoreId(storeId)
.setPreAuthId(preAuthId)
.build();
GetPreAuthorisationResponse getPreAuthResponse = preAuthClient.getPreAuthorisation(preAuthRequest);
processGetPreAuthResponse(getPreAuthResponse);
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process get pre-authorisation response
private static void processGetPreAuthResponse(GetPreAuthorisationResponse response) {
System.out.println("PreAuth ID: " + response.getPreAuthId() + ", PreAuth Status: " + response.getStatus());
switch (response.getStatus()) {
case AUTHORISED:
System.out.println("Pre-Auth success: " + response.getPreAuthId());
System.out.println("PspReference: " + response.getPspReference());
break;
case PENDING_AUTHORISATION:
System.out.println("Pre-Auth pending: " + response.getPreAuthId());
break;
case FAILED, EXPIRED, CANCELLED, DECLINED:
System.out.println("Pre-Auth failed: " + response.getPreAuthId());
break;
case PENDING_RELEASE:
System.out.println("Pre-Auth pending release: " + response.getPreAuthId());
break;
case RELEASED:
System.out.println("Pre-Auth released: " + response.getPreAuthId());
break;
case PENDING_CAPTURE:
System.out.println("Pre-Auth pending capture: " + response.getPreAuthId());
break;
case CAPTURED:
System.out.println("Pre-Auth captured: " + response.getPreAuthId());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Pre-Auth status unknown: " + response.getPreAuthId());
break;
}
for (var adjustment : response.getAdjustmentsList()) {
System.out.println("Adjustment ID: " + adjustment.getAdjustmentId() + ", Adjustment Amount: " + adjustment.getAmountMinorUnits() + ", Adjustment Status: " + adjustment.getStatus());
switch (adjustment.getStatus()) {
case ADJUSTMENT_SUCCESS -> System.out.println("Adjustment success: " + adjustment.getAdjustmentId());
case ADJUSTMENT_ERROR -> System.out.println("Adjustment failed: " + adjustment.getAdjustmentId());
case ADJUSTMENT_PENDING -> System.out.println("Adjustment pending: " + adjustment.getAdjustmentId());
case ADJUSTMENT_STATUS_UNSPECIFIED, UNRECOGNIZED -> System.out.println("Adjustment status unknown: " + adjustment.getAdjustmentId());
}
}
for (var capture : response.getCapturesList()) {
System.out.println("Capture ID: " + capture.getCaptureId() + ", Capture Amount: " + capture.getAmountMinorUnits() + ", Capture Status: " + capture.getStatus());
switch (capture.getStatus()) {
case CAPTURE_SUCCESS -> System.out.println("Capture success: " + capture.getCaptureId());
case CAPTURE_FAILED -> System.out.println("Capture failed: " + capture.getCaptureId());
case CAPTURE_PENDING -> System.out.println("Capture pending: " + capture.getCaptureId());
case CAPTURE_STATUS_UNSPECIFIED, UNRECOGNIZED -> System.out.println("Capture status unknown: " + capture.getCaptureId());
}
}
}
}
request = kody_model.GetPreAuthorisationRequest(
store_id="STORE_ID",
pre_auth_id="preauth_xyz123"
)
response = client.GetPreAuthorisation(request, metadata=metadata)
print(f"Status: {response.status}")
print(f"Total: £{response.total_authorised_amount_minor_units / 100}")
var request = new GetPreAuthorisationRequest
{
StoreId = "STORE_ID",
PreAuthId = "preauth_xyz123"
};
var response = await client.GetPreAuthorisationAsync(request, metadata);
Console.WriteLine($"Status: {response.Status}");
Console.WriteLine($"Total: £{response.TotalAuthorisedAmountMinorUnits / 100.0}");
$request = new GetPreAuthorisationRequest();
$request->setStoreId('STORE_ID');
$request->setPreAuthId('preauth_xyz123');
list($response, $status) = $client->GetPreAuthorisation($request, $metadata)->wait();
echo "Status: " . $response->getStatus() . PHP_EOL;
echo "Total: £" . ($response->getTotalAuthorisedAmountMinorUnits() / 100) . PHP_EOL;
request = Com::Kodypay::Grpc::Preauth::V1::GetPreAuthorisationRequest.new(
store_id: Kody.configuration.store_id,
pre_auth_id: 'preauth_xyz123'
)
response = stub.get_pre_authorisation(request, metadata: { 'x-api-key' => Kody.configuration.api_key })
puts "Status: #{response.status}"
puts "Total: £#{response.total_authorised_amount_minor_units / 100.0}"
6. Refund Capture
Service Call: KodyPreAuthTerminalService.RefundCapture
Issues a full or partial refund against a previously captured authorisation.
Request
rpc RefundCapture(RefundCaptureRequest) returns (RefundCaptureResponse);
message RefundCaptureRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string refund_reference = 2; // Client's reference for refund
optional string order_id = 3; // Original order ID
string store_id = 4; // Kody Store ID
string pre_auth_id = 5; // ID from original PreAuthorise
string capture_id = 6; // ID from CaptureAuthorisation
uint64 refund_amount_minor_units = 7; // Amount to refund
string currency = 8; // ISO 4217 currency code
}
Response
message RefundCaptureResponse {
string refund_reference = 1; // Echoed from request
optional string psp_reference = 2; // PSP reference
optional string order_id = 3; // Echoed if provided
google.protobuf.Timestamp refunded_at = 4; // Refund timestamp
RefundStatus status = 5; // Status of refund
optional string failure_reason = 6; // Reason if failed
}
enum RefundStatus {
REFUND_STATUS_UNSPECIFIED = 0;
REFUND_PENDING = 1;
REFUND_REQUESTED = 2;
REFUND_FAILED = 3;
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.*;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExampleRefundCapture {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Terminal ID, required for Batch Payments
String terminalId = "TERMINAL ID";
//TODO: Replace this with your PreAuth ID returned from the pre-authorisation
String preAuthId = "PRE AUTH ID";
//TODO: Replace this with your Capture ID returned from the capture authorisation
String captureId = "CAPTURE ID";
//TODO: Replace with your store operating currency: ISO 4217
String currencyCode = "HKD";
//TODO: Replace with the auth amount in minor units, cannot exceed the captured amount
long refundAmountMinorUnits = 1000;
//TODO: Replace these with your reference ID
String refundReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
refundCapture(storeId, preAuthId, captureId, refundAmountMinorUnits, currencyCode, refundReference, idempotencyUuid);
}
// Example of a capture authorisation with idempotency and reference IDs
private static void refundCapture(String storeId, String preAuthId, String captureId, long refundAmountMinorUnits, String currency, String refundReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
RefundCaptureRequest refundCaptureRequest = RefundCaptureRequest.newBuilder()
.setStoreId(storeId)
.setPreAuthId(preAuthId)
.setCaptureId(captureId)
.setRefundAmountMinorUnits(refundAmountMinorUnits)
.setCurrency(currency)
.setRefundReference(refundReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
RefundCaptureResponse refundCaptureResponse = preAuthClient.refundCapture(refundCaptureRequest);
processRefundCaptureResponse(refundCaptureResponse);
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process refund capture response
private static void processRefundCaptureResponse(RefundCaptureResponse response) {
// There is no clear success status for refunds.
// Generally, if REFUND_REQUESTED/REFUND_PENDING is returned, the refund request can be considered successful.
// The actual success is subject to whether the user has received the refund.
System.out.println("Refund Ref: " + response.getRefundReference() + ", Refund Status: " + response.getStatus());
switch (response.getStatus()) {
case REFUND_REQUESTED, REFUND_PENDING:
System.out.println("Refund requested/pending: " + response.getRefundedAt());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("RefundedAt: " + response.getRefundedAt());
break;
case REFUND_FAILED:
System.out.println("Refund failed: " + response.getFailureReason());
break;
case REFUND_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Refund status unknown: " + response.getRefundReference());
break;
}
}
}
request = kody_model.RefundCaptureRequest(
idempotency_uuid="refund-uuid-303",
refund_reference="REFUND-001",
store_id="STORE_ID",
pre_auth_id="preauth_xyz123",
capture_id="capture_abc789",
refund_amount_minor_units=5000, # £50.00
currency="GBP"
)
response = client.RefundCapture(request, metadata=metadata)
print(f"Refund Status: {response.status}")
var request = new RefundCaptureRequest
{
IdempotencyUuid = "refund-uuid-303",
RefundReference = "REFUND-001",
StoreId = "STORE_ID",
PreAuthId = "preauth_xyz123",
CaptureId = "capture_abc789",
RefundAmountMinorUnits = 5000, // £50.00
Currency = "GBP"
};
var response = await client.RefundCaptureAsync(request, metadata);
Console.WriteLine($"Refund Status: {response.Status}");
$request = new RefundCaptureRequest();
$request->setIdempotencyUuid('refund-uuid-303');
$request->setRefundReference('REFUND-001');
$request->setStoreId('STORE_ID');
$request->setPreAuthId('preauth_xyz123');
$request->setCaptureId('capture_abc789');
$request->setRefundAmountMinorUnits(5000); // £50.00
$request->setCurrency('GBP');
list($response, $status) = $client->RefundCapture($request, $metadata)->wait();
echo "Refund Status: " . $response->getStatus() . PHP_EOL;
request = Com::Kodypay::Grpc::Preauth::V1::RefundCaptureRequest.new(
idempotency_uuid: 'refund-uuid-303',
refund_reference: 'REFUND-001',
store_id: Kody.configuration.store_id,
pre_auth_id: 'preauth_xyz123',
capture_id: 'capture_abc789',
refund_amount_minor_units: 5000, # £50.00
currency: 'GBP'
)
response = stub.refund_capture(request, metadata: { 'x-api-key' => Kody.configuration.api_key })
puts "Refund Status: #{response.status}"
7. Create Card Token
Service Call: KodyTerminalTokenService.CreateCardToken
Creates a card token by prompting the customer to present their card at a physical payment terminal. The token can be stored and used for future payments without requiring the physical card.
Request
rpc CreateCardToken(CreateCardTokenRequest) returns (stream TokenDetailsResponse);
message CreateCardTokenRequest {
string store_id = 1; // Your Kody store ID
string terminal_id = 2; // Terminal ID where tokenisation is performed
string idempotency_uuid = 3; // Unique key for idempotency
string token_reference = 4; // Your unique reference for this token
string payer_reference = 5; // Payer identifier (e.g., user ID)
optional RecurringProcessingModel recurring_processing_model = 6; // Recurring model
}
Response
message TokenDetailsResponse {
string token_id = 1; // Kody's unique token identifier
string token_reference = 2; // Echoed from request
optional string payment_token = 3; // PSP token for future payments
string payer_reference = 4; // Echoed from request
optional RecurringProcessingModel recurring_processing_model = 5; // Echoed if provided
CardTokenStatus status = 6; // Token status
optional google.protobuf.Timestamp created_at = 7; // Creation timestamp
optional CardInfo card_info = 8; // Card details
message CardInfo {
PaymentMethods payment_method = 1; // Card brand (e.g., VISA, MC)
string payment_method_variant = 2; // Variant (e.g., visadebit)
string card_last_4_digits = 3; // Last 4 digits
string card_expiry_date = 4; // Expiry in MM/YY format
string pos_entry_mode = 5; // POS entry mode
}
}
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.common.CardTokenStatus;
import com.kodypay.grpc.pay.v1.*;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExampleCreateCardToken {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Terminal ID
String terminalId = "TERMINAL ID";
//TODO: Replace this with your Payer Reference
String payerReference = "Payer-" + System.currentTimeMillis();
createCardToken(storeId, terminalId, payerReference);
//TODO: Optional - Replace these with your reference IDs or leave null
String tokenReference = "REF-" + System.currentTimeMillis();
String idempotencyUuid = UUID.randomUUID().toString();
createCardTokenIdempotently(storeId, terminalId, payerReference, tokenReference, idempotencyUuid);
}
// Example of a card tokenisation
private static void createCardToken(String storeId, String terminalId, String payerReference) {
var tokenClient = createKodyTerminalTokenClient();
CreateCardTokenRequest tokenRequest = CreateCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setTerminalId(terminalId)
.setPayerReference(payerReference)
.build();
Iterator<TokenDetailsResponse> responseIterator = tokenClient.createCardToken(tokenRequest);
while (responseIterator.hasNext()) {
TokenDetailsResponse tokenDetailsResponse = responseIterator.next();
processTokenisationResponse(tokenDetailsResponse);
}
}
// Example of a card tokenisation with idempotency and reference IDs
private static void createCardTokenIdempotently(String storeId, String terminalId, String payerReference, String idempotencyUuid, String tokenReference) {
var tokenClient = createKodyTerminalTokenClient();
CreateCardTokenRequest tokenRequest = CreateCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setTerminalId(terminalId)
.setPayerReference(payerReference)
.setIdempotencyUuid(idempotencyUuid)
.setTokenReference(tokenReference)
.build();
TokenDetailsResponse tokenDetailsResponse = tokenClient.createCardToken(tokenRequest).next();
processTokenisationResponse(tokenDetailsResponse);
}
private static KodyTerminalTokenServiceGrpc.KodyTerminalTokenServiceBlockingStub createKodyTerminalTokenClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyTerminalTokenServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process tokenisation response
private static void processTokenisationResponse(TokenDetailsResponse response) {
System.out.println("Token ID: " + response.getTokenId() + ", Token Status: " + response.getStatus());
switch (response.getStatus()) {
case READY:
System.out.println("Token success: " + response.getTokenId());
System.out.println("Payment Token: " + response.getPaymentToken());
System.out.println("Card Info: " + response.getCardInfo());
break;
case PENDING:
System.out.println("Token pending: " + response.getTokenId());
break;
case FAILED:
System.out.println("Token failed: " + response.getTokenId());
break;
case UNKNOWN_STATUS, UNRECOGNIZED:
System.out.println("TopUp status unknown: " + response.getTokenId());
break;
}
}
}
import grpc
import kody_clientsdk_python.pay.v1.terminal_token_pb2 as token_model
import kody_clientsdk_python.pay.v1.terminal_token_pb2_grpc as token_client
channel = grpc.secure_channel("HOSTNAME:443", grpc.ssl_channel_credentials())
client = token_client.KodyTerminalTokenServiceStub(channel)
metadata = [("x-api-key", "API_KEY")]
request = token_model.CreateCardTokenRequest(
store_id="STORE_ID",
terminal_id="TERMINAL_ID",
idempotency_uuid="token-uuid-001",
token_reference="TOKEN-REF-2024-001",
payer_reference="customer_12345"
)
for response in client.CreateCardToken(request, metadata=metadata):
print(f"Token ID: {response.token_id}")
print(f"Payment Token: {response.payment_token}")
print(f"Status: {response.status}")
using Grpc.Core;
using Com.Kodypay.Grpc.Pay.V1;
var request = new CreateCardTokenRequest
{
StoreId = "STORE_ID",
TerminalId = "TERMINAL_ID",
IdempotencyUuid = "token-uuid-001",
TokenReference = "TOKEN-REF-2024-001",
PayerReference = "customer_12345"
};
using var call = client.CreateCardToken(request, metadata);
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Token ID: {response.TokenId}");
Console.WriteLine($"Payment Token: {response.PaymentToken}");
Console.WriteLine($"Status: {response.Status}");
}
<?php
use Com\Kodypay\Grpc\Pay\V1\KodyTerminalTokenServiceClient;
use Com\Kodypay\Grpc\Pay\V1\CreateCardTokenRequest;
$client = new KodyTerminalTokenServiceClient($HOSTNAME, [
'credentials' => ChannelCredentials::createSsl()
]);
$metadata = ['X-API-Key' => [$API_KEY]];
$request = new CreateCardTokenRequest();
$request->setStoreId('STORE_ID');
$request->setTerminalId('TERMINAL_ID');
$request->setIdempotencyUuid('token-uuid-001');
$request->setTokenReference('TOKEN-REF-2024-001');
$request->setPayerReference('customer_12345');
$call = $client->CreateCardToken($request, $metadata);
foreach ($call->responses() as $response) {
echo "Token ID: " . $response->getTokenId() . PHP_EOL;
echo "Payment Token: " . $response->getPaymentToken() . PHP_EOL;
echo "Status: " . $response->getStatus() . PHP_EOL;
}
require 'kody'
stub = Com::Kodypay::Grpc::Pay::V1::KodyTerminalTokenService::Stub.new(
Kody.configuration.endpoint,
GRPC::Core::ChannelCredentials.new
)
request = Com::Kodypay::Grpc::Pay::V1::CreateCardTokenRequest.new(
store_id: 'STORE_ID',
terminal_id: 'TERMINAL_ID',
idempotency_uuid: 'token-uuid-001',
token_reference: 'TOKEN-REF-2024-001',
payer_reference: 'customer_12345'
)
stub.create_card_token(request, metadata: { 'x-api-key' => Kody.configuration.api_key }) do |response|
puts "Token ID: #{response.token_id}"
puts "Payment Token: #{response.payment_token}"
puts "Status: #{response.status}"
end
8. Get Card Token
Service Call: KodyTerminalTokenService.GetCardToken
Retrieves the details of a previously created card token.
Request
rpc GetCardToken(GetCardTokenRequest) returns (TokenDetailsResponse);
message GetCardTokenRequest {
string store_id = 1; // Your Kody store ID
string token_id = 2; // The unique token identifier created by Kody
}
Response
Same as CreateCardToken response (TokenDetailsResponse).
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.common.CardTokenStatus;
import com.kodypay.grpc.pay.v1.GetCardTokenRequest;
import com.kodypay.grpc.pay.v1.KodyTerminalTokenServiceGrpc;
import com.kodypay.grpc.pay.v1.TokenDetailsResponse;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.concurrent.TimeUnit;
public class ExampleGetCardToken {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Token ID returned from the createCardToken
String tokenId = "TOKEN ID";
getCardToken(storeId, tokenId);
}
// Example of get card token by token ID
private static void getCardToken(String storeId, String tokenId) {
var tokenClient = createKodyTerminalTokenClient();
GetCardTokenRequest tokenRequest = GetCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setTokenId(tokenId)
.build();
TokenDetailsResponse tokenDetailsResponse = tokenClient.getCardToken(tokenRequest);
processTokenisationResponse(tokenDetailsResponse);
}
private static KodyTerminalTokenServiceGrpc.KodyTerminalTokenServiceBlockingStub createKodyTerminalTokenClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyTerminalTokenServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process tokenisation response
private static void processTokenisationResponse(TokenDetailsResponse response) {
System.out.println("Token ID: " + response.getTokenId() + ", Token Status: " + response.getStatus());
switch (response.getStatus()) {
case READY:
System.out.println("Token success: " + response.getTokenId());
System.out.println("Payment Token: " + response.getPaymentToken());
System.out.println("Card Info: " + response.getCardInfo());
break;
case PENDING:
System.out.println("Token pending: " + response.getTokenId());
break;
case FAILED:
System.out.println("Token failed: " + response.getTokenId());
break;
case UNKNOWN_STATUS, UNRECOGNIZED:
System.out.println("TopUp status unknown: " + response.getTokenId());
break;
}
}
}
request = token_model.GetCardTokenRequest(
store_id="STORE_ID",
token_id="token_abc123"
)
response = client.GetCardToken(request, metadata=metadata)
print(f"Token ID: {response.token_id}")
print(f"Payment Token: {response.payment_token}")
print(f"Card Last 4: {response.card_info.card_last_4_digits}")
var request = new GetCardTokenRequest
{
StoreId = "STORE_ID",
TokenId = "token_abc123"
};
var response = await client.GetCardTokenAsync(request, metadata);
Console.WriteLine($"Token ID: {response.TokenId}");
Console.WriteLine($"Payment Token: {response.PaymentToken}");
Console.WriteLine($"Card Last 4: {response.CardInfo.CardLast4Digits}");
$request = new GetCardTokenRequest();
$request->setStoreId('STORE_ID');
$request->setTokenId('token_abc123');
list($response, $status) = $client->GetCardToken($request, $metadata)->wait();
echo "Token ID: " . $response->getTokenId() . PHP_EOL;
echo "Payment Token: " . $response->getPaymentToken() . PHP_EOL;
echo "Card Last 4: " . $response->getCardInfo()->getCardLast4Digits() . PHP_EOL;
request = Com::Kodypay::Grpc::Pay::V1::GetCardTokenRequest.new(
store_id: 'STORE_ID',
token_id: 'token_abc123'
)
response = stub.get_card_token(request, metadata: { 'x-api-key' => Kody.configuration.api_key })
puts "Token ID: #{response.token_id}"
puts "Payment Token: #{response.payment_token}"
puts "Card Last 4: #{response.card_info.card_last_4_digits}"
9. Pre-Authorise With Card Token
Service Call: KodyPreAuthTerminalService.PreAuthoriseWithCardToken
Initiates a pre-authorisation using a previously stored card token (no terminal interaction required).
Request
rpc PreAuthoriseWithCardToken(PreAuthoriseWithCardTokenRequest) returns (stream PreAuthorisationResponse);
message PreAuthoriseWithCardTokenRequest {
string idempotency_uuid = 1; // Unique key for idempotency
string pre_auth_reference = 2; // Client's unique reference
optional string order_id = 3; // Client's order identifier
string store_id = 4; // Kody Store ID
uint64 amount_minor_units = 5; // Amount in minor units
string currency = 6; // ISO 4217 currency code
string payment_token = 7; // Stored card token
string expiry_date = 8; // Card expiry in MM/YY format
}
Response
Same as PreAuthorise response.
Examples
- Java
- Python
- .NET
- PHP
- Ruby
package com.kody;
import com.kodypay.grpc.preauth.v1.*;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ExamplePreAuthoriseWithCardToken {
//TODO: Replace this with the testing or live environment
public static final String HOSTNAME = "grpc-staging.kodypay.com";
public static final String API_KEY = "API KEY";
public static void main(String[] args) {
//TODO: Replace this with your Store ID
String storeId = "STORE ID";
//TODO: Replace this with your Payment Token returned from CreateCardToken
String paymentToken = "Payment Token";
//TODO: Replace this with your Card Expiry Date returned from CreateCardToken
String expiryDate = "03/30"; // format in MM/YY
//TODO: Replace with your store operating currency: ISO 4217
String currencyCode = "HKD";
//TODO: Replace with the auth amount in minor units
long amountMinorUnits = 1000;
//TODO: Replace these with your reference ID
String preAuthReference = "REF-" + System.currentTimeMillis();
//TODO: Replace these with your idempotency UUID
String idempotencyUuid = UUID.randomUUID().toString();
preAuthoriseWithCardToken(storeId, paymentToken, expiryDate, amountMinorUnits, currencyCode, preAuthReference, idempotencyUuid);
}
// Example of a pre-authorisation with idempotency and reference IDs
private static void preAuthoriseWithCardToken(String storeId, String paymentToken, String expiryDate, long amountMinorUnits, String currency, String preAuthReference, String idempotencyUuid) {
var preAuthClient = createKodyPreAuthTerminalClient();
PreAuthoriseWithCardTokenRequest preAuthRequest = PreAuthoriseWithCardTokenRequest.newBuilder()
.setStoreId(storeId)
.setPaymentToken(paymentToken)
.setExpiryDate(expiryDate)
.setAmountMinorUnits(amountMinorUnits)
.setCurrency(currency)
.setPreAuthReference(preAuthReference)
.setIdempotencyUuid(idempotencyUuid)
.build();
Iterator<PreAuthorisationResponse> responseIterator = preAuthClient.preAuthoriseWithCardToken(preAuthRequest);
while (responseIterator.hasNext()) {
PreAuthorisationResponse preAuthResponse = responseIterator.next();
processPreAuthResponse(preAuthResponse);
}
}
private static KodyPreAuthTerminalServiceGrpc.KodyPreAuthTerminalServiceBlockingStub createKodyPreAuthTerminalClient() {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("X-API-Key", Metadata.ASCII_STRING_MARSHALLER), API_KEY);
return KodyPreAuthTerminalServiceGrpc.newBlockingStub(ManagedChannelBuilder
.forAddress(HOSTNAME, 443)
.idleTimeout(3, TimeUnit.MINUTES)
.keepAliveTimeout(3, TimeUnit.MINUTES)
.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
.build());
}
// Helper method to process pre-authorisation response
private static void processPreAuthResponse(PreAuthorisationResponse response) {
System.out.println("PreAuth ID: " + response.getPreAuthId() + ", PreAuth Status: " + response.getStatus());
switch (response.getStatus()) {
case AUTHORISED:
System.out.println("Pre-Auth success: " + response.getPreAuthId());
System.out.println("PspReference: " + response.getPspReference());
System.out.println("Auth Code: " + response.getAuthCode());
break;
case PENDING_AUTHORISATION:
System.out.println("Pre-Auth pending: " + response.getPreAuthId());
break;
case FAILED, EXPIRED, CANCELLED, DECLINED:
System.out.println("Pre-Auth failed: " + response.getPreAuthId());
break;
case AUTH_STATUS_UNSPECIFIED, UNRECOGNIZED:
System.out.println("Pre-Auth status unknown: " + response.getPreAuthId());
break;
}
}
}
request = kody_model.PreAuthoriseWithCardTokenRequest(
idempotency_uuid="unique-uuid-456",
pre_auth_reference="PREAUTH-TOKEN-001",
store_id="STORE_ID",
amount_minor_units=15000, # £150.00
currency="GBP",
payment_token="tok_visa1234",
expiry_date="12/25"
)
for response in client.PreAuthoriseWithCardToken(request, metadata=metadata):
print(f"Pre-Auth ID: {response.pre_auth_id}")
var request = new PreAuthoriseWithCardTokenRequest
{
IdempotencyUuid = "unique-uuid-456",
PreAuthReference = "PREAUTH-TOKEN-001",
StoreId = "STORE_ID",
AmountMinorUnits = 15000, // £150.00
Currency = "GBP",
PaymentToken = "tok_visa1234",
ExpiryDate = "12/25"
};
using var call = client.PreAuthoriseWithCardToken(request, metadata);
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Pre-Auth ID: {response.PreAuthId}");
}
$request = new PreAuthoriseWithCardTokenRequest();
$request->setIdempotencyUuid('unique-uuid-456');
$request->setPreAuthReference('PREAUTH-TOKEN-001');
$request->setStoreId('STORE_ID');
$request->setAmountMinorUnits(15000); // £150.00
$request->setCurrency('GBP');
$request->setPaymentToken('tok_visa1234');
$request->setExpiryDate('12/25');
$call = $client->PreAuthoriseWithCardToken($request, $metadata);
foreach ($call->responses() as $response) {
echo "Pre-Auth ID: " . $response->getPreAuthId() . PHP_EOL;
}
request = Com::Kodypay::Grpc::Preauth::V1::PreAuthoriseWithCardTokenRequest.new(
idempotency_uuid: 'unique-uuid-456',
pre_auth_reference: 'PREAUTH-TOKEN-001',
store_id: Kody.configuration.store_id,
amount_minor_units: 15000, # £150.00
currency: 'GBP',
payment_token: 'tok_visa1234',
expiry_date: '12/25'
)
stub.pre_authorise_with_card_token(request, metadata: { 'x-api-key' => Kody.configuration.api_key }) do |response|
puts "Pre-Auth ID: #{response.pre_auth_id}"
end
Remember to replace the following placeholders with your actual values:
HOSTNAME: Use eithergrpc-staging.kodypay.com(for development and test) orgrpc.kodypay.com(for live).API_KEY: Your API keySTORE_ID: Your store identifierTERMINAL_ID: Your terminal identifier
For further support or more detailed information, please contact our support team.