Skip to content

Terminal Cloud API async: update return type #1538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,75 @@ const paymentRequest: SaleToPOIRequest = {
// Step 5: Make the request
const terminalApiResponse: terminal.TerminalApiResponse = await terminalLocalAPI.request(paymentRequest);
```
### Using the Cloud Terminal API Integration (async)
If you choose to integrate [Terminal API over Cloud](https://docs.adyen.com/point-of-sale/design-your-integration/choose-your-architecture/cloud/) **asynchronously**, you need to follow similar steps to initialize the client and prepare the request object. However the response will be asynchronous:
* a successful request will return `200` status code and `ok` as response body. Make sure to setup the [event notifications](https://docs.adyen.com/point-of-sale/design-your-integration/notifications/event-notifications/)
* a request that fails will return `200` status code and the `TerminalApiResponse` as response body
``` typescript
// Step 1: Require the parts of the module you want to use
const {Client, TerminalCloudAPI} from "@adyen/api-library";

// Step 2: Initialize the client object
const client = new Client({apiKey: "YOUR_API_KEY", environment: "TEST"});

// Step 3: Initialize the API object
const terminalCloudAPI = new TerminalCloudAPI(client);

// Step 4: Create the request object
const serviceID = "123456789";
const saleID = "POS-SystemID12345";
const POIID = "Your Device Name(eg V400m-123456789)";

// Use a unique transaction for every transaction you perform
const transactionID = "TransactionID";
const paymentRequest: SaleToPOIRequest = {
MessageHeader: {
MessageClass: MessageClassType.Service,
MessageCategory: MessageCategoryType.Payment,
MessageType: MessageType.Request,
ProtocolVersion: "3.0",
ServiceID: serviceID,
SaleID: saleID,
POIID: POIID
},
PaymentRequest: {
SaleData: {
SaleTransactionID: {
TransactionID: transactionID,
TimeStamp: new Date().toISOString()
},

SaleToAcquirerData: {
applicationInfo: {
merchantApplication: {
version: "1",
name: "test",
}
}
}
},
PaymentTransaction: {
AmountsReq: {
Currency: "EUR",
RequestedAmount: 1000
}
}
}
};

// Step 5: Make the request
const response = await terminalCloudAPI.async(paymentRequest);
// handle both `string` and `TerminalApiResponse`
if (typeof response === "string") {
// request was successful
console.log("response:", response); // should be 'ok'
} else {
// request failed: see details in the EventNotification object
console.log("EventToNotify:", response.SaleToPOIRequest?.EventNotification?.EventToNotify);
console.log("EventDetails:", response.SaleToPOIRequest?.EventNotification?.EventDetails);
}
```

## Feedback
We value your input! Help us enhance our API Libraries and improve the integration experience by providing your feedback. Please take a moment to fill out [our feedback form](https://forms.gle/A4EERrR6CWgKWe5r9) to share your thoughts, suggestions or ideas.

Expand Down
22 changes: 22 additions & 0 deletions src/__mocks__/terminalApi/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,25 @@
*/

export const asyncRes = "ok";

export const asyncErrorRes = {
SaleToPOIRequest: {
EventNotification: {
EventToNotify: "Reject",
EventDetails: "message=Did+not+receive+a+response+from+the+POI.",
RejectedMessage: "ewoi...0KfQo=",
TimeStamp: "2020-03-31T10:28:39.515Z"
},
MessageHeader: {
DeviceID: "666568147",
MessageCategory: "Event",
MessageClass: "Event",
MessageType: "Notification",
POIID: "P400Plus-123456789",
ProtocolVersion: "3.0",
SaleID: "saleid-4c32759faaa7",
ServiceID: "31122609"
}
}
};

21 changes: 18 additions & 3 deletions src/__tests__/terminalCloudAPI.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from "nock";
import { createClient, createTerminalAPIPaymentRequest, createTerminalAPIRefundRequest } from "../__mocks__/base";
import { asyncRes } from "../__mocks__/terminalApi/async";
import { asyncRes, asyncErrorRes } from "../__mocks__/terminalApi/async";
import { syncRefund, syncRes, syncResEventNotification, syncResEventNotificationWithAdditionalAttributes, syncResEventNotificationWithUnknownEnum } from "../__mocks__/terminalApi/sync";
import Client from "../client";
import TerminalCloudAPI from "../services/terminalCloudAPI";
Expand Down Expand Up @@ -30,11 +30,27 @@ describe("Terminal Cloud API", (): void => {

const terminalAPIPaymentRequest = createTerminalAPIPaymentRequest();

const requestResponse: string = await terminalCloudAPI.async(terminalAPIPaymentRequest);
const requestResponse = await terminalCloudAPI.async(terminalAPIPaymentRequest);

expect(typeof requestResponse).toBe("string");
expect(requestResponse).toEqual("ok");
});

test("should get an error after async payment request", async (): Promise<void> => {
scope.post("/async").reply(200, asyncErrorRes);

const terminalAPIPaymentRequest = createTerminalAPIPaymentRequest();

const requestResponse = await terminalCloudAPI.async(terminalAPIPaymentRequest);

if (typeof requestResponse === "object") {
expect(requestResponse.SaleToPOIRequest?.EventNotification).toBeDefined();
expect(requestResponse.SaleToPOIRequest?.EventNotification?.EventToNotify).toBe("Reject");
} else {
throw new Error("Expected structured response, but got raw string");
}
});

test("should make a sync payment request", async (): Promise<void> => {
scope.post("/sync").reply(200, syncRes);

Expand Down Expand Up @@ -453,4 +469,3 @@ export const syncTerminalPaymentResponse = {
}
}
};

30 changes: 29 additions & 1 deletion src/helpers/getJsonResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,36 @@

import Resource from "../services/resource";
import { IRequest } from "../typings/requestOptions";
import { TerminalApiResponse } from "../typings/terminal/models";

async function getJsonResponse<T>(resource: Resource, jsonRequest: T | string, requestOptions?: IRequest.Options): Promise<string>;
/**
* Sends a JSON request to the given resource and returns a string or a deserialized `TerminalApiResponse`.
* Used by Terminal API /async method
*
* @template T The request type (usually a model or plain object).
* @param {Resource} resource - The API resource to which the request is sent.
* @param {T | string} jsonRequest - The request payload, either as an object or raw JSON string.
* @param {IRequest.Options} [requestOptions] - Optional HTTP request options.
* @returns {Promise<string | TerminalApiResponse>} A promise resolving to either a raw response string or a `TerminalApiResponse` object.
*
* @example
* const response = await getJsonResponse<TerminalApiRequest>(terminalApiResource, request);
*/
async function getJsonResponse<T>(resource: Resource, jsonRequest: T | string, requestOptions?: IRequest.Options): Promise<string | TerminalApiResponse>;
/**
* Sends a JSON request to the given resource and returns a deserialized response of the expected type.
* Used by all APIs and Terminal API sync method
*
* @template T The request type.
* @template R The expected deserialized response type.
* @param {Resource} resource - The API resource to which the request is sent.
* @param {T | string} jsonRequest - The request payload, either as an object or raw JSON string.
* @param {IRequest.Options} [requestOptions] - Optional HTTP request options.
* @returns {Promise<R>} A promise resolving to the deserialized response object.
*
* @example
* const response = await getJsonResponse<MyRequestType, MyResponseType>(resource, request);
*/
async function getJsonResponse<T, R>(resource: Resource, jsonRequest: T | string, requestOptions?: IRequest.Options): Promise<R>;

/**
Expand Down
23 changes: 17 additions & 6 deletions src/services/terminalCloudAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,34 @@ class TerminalCloudAPI extends Service {
private static setApplicationInfo(request: TerminalApiRequest): TerminalApiRequest {
if (request.SaleToPOIRequest.PaymentRequest) {
const applicationInfo = new ApplicationInfo();
const saleToAcquirerData = {applicationInfo};
const saleData = {saleToAcquirerData};
const paymentRequest = {saleData};
const saleToPOIRequest = {paymentRequest};
const reqWithAppInfo = {saleToPOIRequest};
const saleToAcquirerData = { applicationInfo };
const saleData = { saleToAcquirerData };
const paymentRequest = { saleData };
const saleToPOIRequest = { paymentRequest };
const reqWithAppInfo = { saleToPOIRequest };

mergeDeep(request, reqWithAppInfo);
}

return ObjectSerializer.serialize(request, "TerminalApiRequest");
}

public async(terminalApiRequest: TerminalApiRequest): Promise<string> {
/**
* Send an asynchronous payment request to the Terminal API.
*
* @param terminalApiRequest - The request to send.
* @returns A promise that resolves to "ok" if the request was successful, or a TerminalApiResponse if there is an error.
*/
public async(terminalApiRequest: TerminalApiRequest): Promise<string | TerminalApiResponse> {
const request = TerminalCloudAPI.setApplicationInfo(terminalApiRequest);
return getJsonResponse<TerminalApiRequest>(this.terminalApiAsync, request);
}

/**
* Send a synchronous payment request to the Terminal API.
* @param terminalApiRequest - The request to send.
* @returns A promise that resolves to a TerminalApiResponse.
*/
public async sync(terminalApiRequest: TerminalApiRequest): Promise<TerminalApiResponse> {
const request = TerminalCloudAPI.setApplicationInfo(terminalApiRequest);
const response = await getJsonResponse<TerminalApiRequest, TerminalApiResponse>(
Expand Down