UNPKG

@adyen/api-library

Version:

The Adyen API Library for NodeJS enables you to work with Adyen APIs.

190 lines 10.5 kB
"use strict"; /* * ###### * ###### * ############ ####( ###### #####. ###### ############ ############ * ############# #####( ###### #####. ###### ############# ############# * ###### #####( ###### #####. ###### ##### ###### ##### ###### * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### * ###### ###### #####( ###### #####. ###### ##### ##### ###### * ############# ############# ############# ############# ##### ###### * ############ ############ ############# ############ ##### ###### * ###### * ############# * ############ * Adyen NodeJS API Library * Copyright (c) 2026 Adyen B.V. * This file is open source and available under the MIT license. * See the LICENSE file for more info. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EncryptedCloudDeviceApi = void 0; const getJsonResponse_1 = __importDefault(require("../../helpers/getJsonResponse")); const service_1 = __importDefault(require("../../service")); const resource_1 = __importDefault(require("../resource")); const cloudDeviceApiAsyncResponse_1 = require("../../typings/clouddevice/cloudDeviceApiAsyncResponse"); const nexoSecurityManager_1 = require("../../security/clouddevice/nexoSecurityManager"); const nexoSecurityException_1 = require("../../security/clouddevice/nexoSecurityException"); /** * Cloud Device API service with encrypted payloads. */ class EncryptedCloudDeviceApi extends service_1.default { constructor(client, encryptionCredentialDetails) { super(client); this.API_BASEPATH = "https://device-api-test.adyen.com/v1"; this.baseUrl = this.createBaseUrl(this.API_BASEPATH); this.nexoSecurityManager = new nexoSecurityManager_1.NexoSecurityManager(encryptionCredentialDetails); } /** * @summary Send a Terminal API request with encryption and receive a synchronous decrypted response * @param merchantAccount {@link string } The unique identifier of the merchant account. (required) * @param deviceId {@link string } The unique identifier of the payment device. Must match POIID in the MessageHeader. (required) * @param cloudDeviceApiRequest {@link CloudDeviceApiRequest } * @param requestOptions {@link IRequest.Options } * @return {@link CloudDeviceApiResponse } */ async sync(merchantAccount, deviceId, cloudDeviceApiRequest, requestOptions) { var _a; if (!merchantAccount) { throw new Error("Please provide the merchantAccount path parameter"); } if (!deviceId) { throw new Error("Please provide the deviceId path parameter"); } if (!((_a = cloudDeviceApiRequest.SaleToPOIRequest) === null || _a === void 0 ? void 0 : _a.MessageHeader)) { throw new Error("cloudDeviceApiRequest must contain a SaleToPOIRequest with a MessageHeader"); } cloudDeviceApiRequest.SaleToPOIRequest.MessageHeader.POIID = deviceId; const secured = this.nexoSecurityManager.encrypt(JSON.stringify(cloudDeviceApiRequest), cloudDeviceApiRequest.SaleToPOIRequest.MessageHeader); const securedRequest = { SaleToPOIRequest: secured }; const endpoint = `${this.baseUrl}/merchants/{merchantAccount}/devices/{deviceId}/sync` .replace("{merchantAccount}", encodeURIComponent(merchantAccount)) .replace("{deviceId}", encodeURIComponent(deviceId)); const resource = new resource_1.default(this, endpoint); const response = await (0, getJsonResponse_1.default)(resource, securedRequest, { ...requestOptions, method: "POST" }); const encryptedResponse = response.SaleToPOIResponse; if (!(encryptedResponse === null || encryptedResponse === void 0 ? void 0 : encryptedResponse.NexoBlob)) { // Terminal returned an unencrypted error response (e.g. terminal unreachable). // A genuine terminal outcome is always encrypted, so an unencrypted success is forged. rejectUnencryptedSuccess(response); return response; } return JSON.parse(this.nexoSecurityManager.decrypt(encryptedResponse)); } /** * @summary Send a Terminal API request with encryption and receive an asynchronous response * @param merchantAccount {@link string } The unique identifier of the merchant account. (required) * @param deviceId {@link string } The unique identifier of the payment device. Must match POIID in the MessageHeader. (required) * @param cloudDeviceApiRequest {@link CloudDeviceApiRequest } * @param requestOptions {@link IRequest.Options } * @return {@link CloudDeviceApiAsyncResponse } */ async async(merchantAccount, deviceId, cloudDeviceApiRequest, requestOptions) { var _a; if (!merchantAccount) { throw new Error("Please provide the merchantAccount path parameter"); } if (!deviceId) { throw new Error("Please provide the deviceId path parameter"); } if (!((_a = cloudDeviceApiRequest.SaleToPOIRequest) === null || _a === void 0 ? void 0 : _a.MessageHeader)) { throw new Error("cloudDeviceApiRequest must contain a SaleToPOIRequest with a MessageHeader"); } cloudDeviceApiRequest.SaleToPOIRequest.MessageHeader.POIID = deviceId; const secured = this.nexoSecurityManager.encrypt(JSON.stringify(cloudDeviceApiRequest), cloudDeviceApiRequest.SaleToPOIRequest.MessageHeader); const securedRequest = { SaleToPOIRequest: secured }; const endpoint = `${this.baseUrl}/merchants/{merchantAccount}/devices/{deviceId}/async` .replace("{merchantAccount}", encodeURIComponent(merchantAccount)) .replace("{deviceId}", encodeURIComponent(deviceId)); const resource = new resource_1.default(this, endpoint); const response = await (0, getJsonResponse_1.default)(resource, securedRequest, { ...requestOptions, method: "POST" }); const result = new cloudDeviceApiAsyncResponse_1.CloudDeviceApiAsyncResponse(); if (typeof response === "string" && response.toLowerCase().trim() === "ok") { result.Result = response.trim(); return result; } // Error response: an encrypted EventNotification wrapped in a SaleToPOIRequest envelope. const encryptedError = response; const encryptedErrorRequest = encryptedError === null || encryptedError === void 0 ? void 0 : encryptedError.SaleToPOIRequest; if (!(encryptedErrorRequest === null || encryptedErrorRequest === void 0 ? void 0 : encryptedErrorRequest.NexoBlob)) { // Unencrypted error response (e.g. terminal unreachable). // A genuine terminal outcome is always encrypted, so an unencrypted success is forged. rejectUnencryptedSuccess(response); return response; } const decryptedJson = this.nexoSecurityManager.decrypt(encryptedErrorRequest); const errorResponse = JSON.parse(decryptedJson); result.SaleToPOIRequest = errorResponse.SaleToPOIRequest; return result; } /** * Decrypt an event notification payload. * * @param payload JSON string containing either a SaleToPOIResponse or SaleToPOIRequest envelope. * @return the decrypted payload as a JSON string * @throws NexoSecurityException when decryption fails or the payload is invalid */ decryptNotification(payload) { let parsed; try { parsed = JSON.parse(payload); } catch (_a) { throw new nexoSecurityException_1.NexoSecurityException("Invalid payload"); } if (!parsed || typeof parsed !== "object") { throw new nexoSecurityException_1.NexoSecurityException("Unexpected payload: must be a JSON object"); } if ("SaleToPOIResponse" in parsed) { const securedResponse = parsed; if (!securedResponse.SaleToPOIResponse) { throw new nexoSecurityException_1.NexoSecurityException("Unexpected payload without SaleToPOIResponse or SaleToPOIRequest"); } return this.nexoSecurityManager.decrypt(securedResponse.SaleToPOIResponse); } if ("SaleToPOIRequest" in parsed) { const securedRequest = parsed; return this.nexoSecurityManager.decrypt(securedRequest.SaleToPOIRequest); } throw new nexoSecurityException_1.NexoSecurityException("Unexpected payload without SaleToPOIResponse or SaleToPOIRequest"); } } exports.EncryptedCloudDeviceApi = EncryptedCloudDeviceApi; /** * Rejects an unencrypted response that claims a non-failure outcome. * * A genuine terminal outcome is always encrypted (delivered inside a NexoBlob), so an * unencrypted response carrying a "Success" or "Partial" Result cannot come from the terminal * and must be treated as forged. Legitimate unencrypted responses are gateway-generated errors * (e.g. terminal unreachable, reject event notifications), which always carry "Failure" or no Result. * * @throws NexoSecurityException when a non-failure Result is found in an unencrypted response. */ function rejectUnencryptedSuccess(response) { if (!response || typeof response !== "object") { return; } const envelope = response; for (const message of Object.values(envelope)) { if (!message || typeof message !== "object") { continue; } for (const body of Object.values(message)) { if (!body || typeof body !== "object") { continue; } const inner = body.Response; if (inner && typeof inner === "object") { const result = inner.Result; if (result === "Success" || result === "Partial") { throw new nexoSecurityException_1.NexoSecurityException("Received an unencrypted response with a non-failure result. " + "A genuine terminal outcome must be encrypted."); } } } } } //# sourceMappingURL=encryptedCloudDeviceApi.js.map