Westpay Agentic Skill is available

Add Westpay API rules, integration flows, examples, and guardrails to your AI coding tool.

EPAS Client Library

Last updated: 2026-05-19

Overview

The EPAS Client Library is the recommended integration layer for ECR applications that communicate directly with Westpay payment terminals. It hides the raw EPAS XML protocol, manages terminal connectivity, sequences messages, exposes callbacks for cashier interaction, and returns typed transaction responses.

Without the library, an ECR must build and parse EPAS XML messages, keep track of protocol state, handle terminal customisations, and maintain the socket connection. With the library, the same purchase can be expressed as a normal API call:

.NET

csharp
ApiResult result = epas.Purchase(59.00m, out TransactionResponse response);

Java

java
Pair<ApiDefinitions.ApiResult, TransactionResponse> result =
    epas.purchase(new ApiDefinitions().new PurchaseTransactionAmounts(59.00));

Swift

swift
let response = try await client.purchase(59.00)

Choose a Library

LibraryPackagePrimary clientNotes
.NETWestpay.EpasEpasClient.NET Framework 4.7.2 class library distributed as EpasClientLibrary.dll.
Javase.westpay.epasEpasClientJava client library distributed as library-1.4.5.jar.
SwiftEpasClientEpasClientSwift Package Manager package for macOS and iPadOS.

Integration Journey

Follow this flow for .NET, Java, and Swift integrations:

  1. Add the library to your ECR project.
  2. Implement the client application callback interface.
  3. Create one client instance for each terminal connection.
  4. Connect to the terminal.
  5. Log in with terminal environment details.
  6. Start a transaction.
  7. Handle callbacks, receipts, and the final transaction response.
  8. Handle disconnections and reconnect by connecting and logging in again.

Step 1 - Add the Library

Add the release artifact to your application and reference it from your ECR project. Use the language tabs below to switch between .NET, Java, and Swift.

.NET

csharp
using Westpay.Epas;

Reference EpasClientLibrary.dll from your .NET Framework 4.7.2 project.

Java

java
import se.westpay.epas.connections.classes.EpasClient;
import se.westpay.epas.services.ApiDefinitions;
import se.westpay.epas.services.IClientApp;
import se.westpay.epas.utils.export.IPEndPoint;

Add library-1.4.5.jar to your application classpath.

Swift

swift
import EpasClient

Add the local or published Swift package to Xcode through Swift Package Manager.

Step 2 - Implement the Client Callbacks

The client library calls your ECR when it needs to display cashier text, print receipts, collect cashier decisions, or report terminal state. Keep callback methods quick; long-running UI work should be marshalled onto your UI thread. The example below shows the same integration shape in each wrapper.

.NET

csharp
public sealed class PosClientApp : IClientApp
{
    public void Display(string text)
    {
        cashierDisplay.Text = text.Replace("\n", Environment.NewLine);
    }

    public void PrintReceipt(ReceiptData receipt)
    {
        receiptStore.Add(receipt);
        printer.Print(receipt.GenerateSimpleReceipt(32));
    }

    public void PrintReport(string report)
    {
        printer.Print(report);
    }

    public bool VerifySignature(string displayText, out bool signatureOk)
    {
        Display(displayText);
        signatureOk = cashierPrompts.Confirm("Signature OK?");
        return true;
    }

    public void Log(LogLevel level, string text)
    {
        appLog.Write(level, text);
    }
}

Java

java
public final class PosClientApp implements IClientApp {
    @Override
    public void Display(String text) {
        cashierDisplay.setText(text.replace("\n", System.lineSeparator()));
    }

    @Override
    public void PrintReceipt(ReceiptData receipt) {
        receiptStore.add(receipt);
        printer.print(receipt.generateSimpleReceipt(32));
    }

    @Override
    public Pair<Boolean, Boolean> VerifySignature(String displayText) {
        Display(displayText);
        boolean accepted = cashierPrompts.confirm("Signature OK?");
        return new Pair<>(true, accepted);
    }

    @Override
    public void Log(ApiDefinitions.LogLevel level, String text) {
        appLog.write(level, text);
    }
}

Swift

swift
import EpasClient

@MainActor
final class PosClientApp: EpasClientDelegate {
    func display(text: String) {
        cashierDisplay.text = text
    }

    func printReceipt(receipt: ReceiptData) {
        receiptStore.append(receipt)
        printer.print(receipt.generateSimpleReceipt(width: 32))
    }

    func printReport(report: String) {
        printer.print(report)
    }

    func verifySignature(displayText: String) async -> BooleanCallbackResult? {
        display(text: displayText)
        return BooleanCallbackResult(handled: true, value: true)
    }

    func log(level: LogLevel, text: String) {
        appLog.write(level: level, text: text)
    }
}

Step 3 - Create the Client

Create one client instance per terminal connection. If your ECR talks to several terminals at the same time, keep a separate EpasClient per terminal.

.NET

csharp
IClientApp clientApp = new PosClientApp();
EpasClient epas = new EpasClient(clientApp);

epas.LinkStatus = linkUp => appLog.Write(LogLevel.Status, $"Link up: {linkUp}");
epas.BusyStatus = busy => appLog.Write(LogLevel.Status, $"Terminal busy: {busy}");
epas.CardStatus = inserted => appLog.Write(LogLevel.Status, $"Card inserted: {inserted}");

Java

java
IClientApp clientApp = new PosClientApp();
EpasClient epas = new EpasClient(clientApp);

epas.complete();

Java status notifications are enabled through the IClientApp notification flags and delivered to methods such as LinkStatusNotification, BusyStatusNotification, CardActionNotification, and CardAcceptedNotification.

Swift

swift
let clientApp = PosClientApp()
let client = EpasClient(delegate: clientApp)

Swift status notifications are delivered through EpasClientDelegate methods such as linkStatusChanged, busyStatusChanged, cardStatusChanged, and epasMessageReceived.

Step 4 - Connect to the Terminal

Network connection details identify the terminal IP and port. A successful connect only means the library established the low-level terminal link; you still need to log in before running transactions.

.NET

csharp
var connection = new TerminalNetworkConnection
{
    TerminalConnectionDetails = new IPEndPoint(IPAddress.Parse("192.168.10.25"), 6001),
    UseKeepAlive = true
};

ApiResult result = epas.ConnectNetwork(connection, forceReconnect: false);
if (result != ApiResult.OK)
{
    throw new InvalidOperationException($"Unable to connect: {result}");
}

Java

java
ApiDefinitions definitions = new ApiDefinitions();
ApiDefinitions.TerminalNetworkConnection connection =
    definitions.new TerminalNetworkConnection();

connection.TerminalConnectionDetails = new IPEndPoint("192.168.10.25", 6001);
connection.UseKeepAlive = true;

ApiDefinitions.ApiResult result = epas.connectNetwork(connection);
if (result != ApiDefinitions.ApiResult.OK) {
    throw new IllegalStateException("Unable to connect: " + result);
}

Swift

swift
let connection = TerminalNetworkConnection(
    terminalConnectionDetails: IPEndPoint("192.168.10.25", 6001),
    useKeepAlive: true
)

try await client.connect(connection)

Step 5 - Log In

Login sends the terminal ID, operator, language, configuration host, and authorisation host to the terminal. The ECR normally logs in once after connecting; there is no need to log in for every transaction.

.NET

csharp
var environment = new TerminalEnvironment
{
    TerminalId = "80000082",
    OperatorId = "cashier_1",
    ISO639_1_LanguageCode = TerminalEnvironment.LangSwedish,
    ConfigurationServer = new IPEndPoint(IPAddress.Parse("185.27.171.42"), 55133),
    AuthorisationServer = new IPEndPoint(IPAddress.Parse("185.27.171.42"), 55144)
};

ApiResult result = epas.Login(environment, out LoginResponse login);
if (result == ApiResult.OK && login.LoggedIn)
{
    appLog.Write(LogLevel.Status, "Terminal is ready for transactions");
}

Java

java
ApiDefinitions.TerminalEnvironment environment =
    definitions.new TerminalEnvironment();

environment.TerminalId = "80000082";
environment.OperatorId = "cashier_1";
environment.SetLanguageCode("sv-SE");
environment.ConfigurationServer = new IPEndPoint("185.27.171.42", 55133);
environment.AuthorisationServer = new IPEndPoint("185.27.171.42", 55144);

Pair<ApiDefinitions.ApiResult, LoginResponse> loginResult = epas.login(environment);
if (loginResult.getKey() == ApiDefinitions.ApiResult.OK
    && loginResult.getValue().isLoggedIn()) {
    appLog.write(ApiDefinitions.LogLevel.Status, "Terminal is ready for transactions");
}

Swift

swift
let environment = TerminalEnvironment(
    terminalId: "80000082",
    workstationId: "pos_1",
    configurationServer: IPEndPoint("185.27.171.42", 55133),
    authorisationServer: IPEndPoint("185.27.171.42", 55144),
    operatorId: "cashier_1",
    iso639_1_LanguageCode: TerminalEnvironment.langSwedish
)

let login = try await client.login(environment)
guard login.loggedIn else {
    throw EpasClientError.invalidState("Terminal login was rejected.")
}

Step 6 - Start a Transaction

Transaction methods return when the terminal has completed the flow, which can take time because the cardholder may need to present a card, enter PIN, or respond to prompts. Run transaction calls away from the UI thread unless your platform integration already uses asynchronous APIs.

.NET

csharp
TransactionResponse response;
ApiResult result = epas.Purchase(100.00m, out response);

if (result == ApiResult.OK)
{
    if (response.Approved)
    {
        completeSale(response.TransactionReference);
    }
    else
    {
        showDecline(response.ErrorCondition, response.AdditionalInformation);
    }
}

Java

java
ApiDefinitions.PurchaseTransactionAmounts amounts =
    definitions.new PurchaseTransactionAmounts(100.00);
Pair<ApiDefinitions.ApiResult, TransactionResponse> result = epas.purchase(amounts);

if (result.getKey() == ApiDefinitions.ApiResult.OK) {
    TransactionResponse response = result.getValue();
    if (response.isApproved()) {
        completeSale(response.getTransactionReference());
    } else {
        showDecline(response.toString());
    }
}

Swift

swift
let response = try await client.purchase(100.00)

if response.approved {
    completeSale(response.transactionReference)
} else {
    showDecline(response.errorCondition, response.additionalInformation)
}

Step 7 - Print Receipts and Store the Result

The terminal supplies receipt data through the receipt callback. Store the receipt data even if you print immediately; it is useful for audit, receipt retry, and transaction reporting.

.NET

csharp
public void PrintReceipt(ReceiptData receipt)
{
    receiptRepository.Save(receipt);
    string text = receipt.GenerateSimpleReceipt(32);
    receiptPrinter.Print(text);
}

Java

java
@Override
public void PrintReceipt(ReceiptData receipt) {
    receiptRepository.save(receipt);
    String text = receipt.generateSimpleReceipt(32);
    receiptPrinter.print(text);
}

Swift

swift
func printReceipt(receipt: ReceiptData) {
    receiptRepository.save(receipt)
    let text = receipt.generateSimpleReceipt(width: 32)
    receiptPrinter.print(text)
}

Step 8 - Handle Disconnects and Aborts

The library does not automatically reconnect. If the link drops, the terminal aborts any active transaction and logs out. Reconnect by calling the connection method again, then log in again.

.NET

csharp
epas.LinkStatus = linkUp =>
{
    if (!linkUp)
    {
        appLog.Write(LogLevel.Error, "Terminal disconnected");
        reconnectWorkflow.Schedule();
    }
};

ApiResult abortResult = epas.RequestTransactionAbort();

Java

java
@Override
public void LinkStatusNotification(boolean linkUp) {
    if (!linkUp) {
        appLog.write(ApiDefinitions.LogLevel.Error, "Terminal disconnected");
        reconnectWorkflow.schedule();
    }
}

ApiDefinitions.ApiResult abortResult = epas.requestTransactionAbort();

Swift

swift
func linkStatusChanged(isConnected: Bool) {
    if !isConnected {
        appLog.write(level: .error, text: "Terminal disconnected")
        reconnectWorkflow.schedule()
    }
}

try await client.abort()

Next Steps

  • Use .NET Library for .NET-specific APIs, callbacks, and transaction examples.
  • Use Java Library for Java-specific packages, callback signatures, and transaction examples.
  • Use Swift Library for Swift Package Manager setup, async APIs, and Apple platform examples.
  • Use Transactions for purchase, refund, reversal, pre-amount, administrative operations, and diagnostic APIs.
  • Use Receipts and Events for receipt data, DCC, card identification, and terminal notifications.