mirror of
https://github.com/jlengrand/adyen-node-api-library.git
synced 2026-03-10 08:01:20 +00:00
36
.eslintrc.js
Normal file
36
.eslintrc.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
env: {
|
||||
es6: true,
|
||||
node: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
modules: true
|
||||
},
|
||||
project: path.resolve(__dirname, "./tsconfig.json"),
|
||||
tsconfigRootDir: __dirname
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
rules: {
|
||||
quotes: ["error", "double"],
|
||||
semi: ["error", "always"]
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.ts"],
|
||||
rules: {
|
||||
"no-dupe-class-members": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"modules": true
|
||||
},
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"quotes": ["error", "double"],
|
||||
"semi": ["error", "always"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"no-dupe-class-members": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@adyen/api-library",
|
||||
"version": "2.1.2",
|
||||
"version": "2.1.3",
|
||||
"description": "The Adyen API Library for NodeJS enables you to work with Adyen APIs and Hosted Payment Pages.",
|
||||
"main": "dist/lib/src/index.js",
|
||||
"types": "dist/lib/src/index.d.ts",
|
||||
@@ -47,17 +47,17 @@
|
||||
"@types/jest": "24.0.17",
|
||||
"@types/nock": "10.0.3",
|
||||
"@types/node": "11.13.18",
|
||||
"@typescript-eslint/eslint-plugin": "1.13.0",
|
||||
"@typescript-eslint/parser": "1.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "2.0.0",
|
||||
"@typescript-eslint/parser": "2.0.0",
|
||||
"babel-loader": "8.0.6",
|
||||
"coveralls": "3.0.6",
|
||||
"eslint": "6.1.0",
|
||||
"jest": "24.8.0",
|
||||
"nock": "11.0.0-beta.31",
|
||||
"eslint": "6.2.0",
|
||||
"jest": "24.9.0",
|
||||
"nock": "11.0.0",
|
||||
"ts-loader": "6.0.4",
|
||||
"typescript": "3.5.3",
|
||||
"webpack": "4.39.1",
|
||||
"webpack-cli": "3.3.6"
|
||||
"webpack": "4.39.2",
|
||||
"webpack-cli": "3.3.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "2.2.2"
|
||||
|
||||
@@ -76,7 +76,7 @@ describe("Bin Lookup", function (): void {
|
||||
};
|
||||
|
||||
scope.post("/get3dsAvailability")
|
||||
.reply(403);
|
||||
.reply(403, JSON.stringify({status: 403, message: "fail", errorCode: "171"}));
|
||||
|
||||
try {
|
||||
await binLookup.get3dsAvailability(threeDSAvailabilityRequest as unknown as ThreeDSAvailabilityRequest);
|
||||
|
||||
86
src/__tests__/checkServerIdentity.spec.ts
Normal file
86
src/__tests__/checkServerIdentity.spec.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
import checkServerIdentity from "../helpers/checkServerIdentity";
|
||||
import {PeerCertificate} from "tls";
|
||||
|
||||
const createMockedCertificate = (CN: string): PeerCertificate => ({
|
||||
subjectaltname: "Adyen B.V",
|
||||
infoAccess: {mock: ["any"]},
|
||||
subject:
|
||||
{
|
||||
C: "AB",
|
||||
ST: "AB",
|
||||
L: "City",
|
||||
O: "Company Name",
|
||||
OU: "Mocked Value",
|
||||
CN,
|
||||
},
|
||||
issuer:
|
||||
{
|
||||
C: "AB",
|
||||
ST: "AB",
|
||||
L: "City",
|
||||
O: "Issuer Name",
|
||||
OU: "Issuer",
|
||||
CN: "Issuer CN",
|
||||
},
|
||||
modulus: "ABC123",
|
||||
exponent: "01010101",
|
||||
valid_from: "Nov 19 15:03:32 2018 GMT",
|
||||
valid_to: "Nov 11 15:03:32 2048 GMT",
|
||||
fingerprint: "MOCKED_FINGERPRINT",
|
||||
ext_key_usage: ["1.2.3.4.5.6.7.8"],
|
||||
serialNumber: "1000",
|
||||
raw: Buffer.from("test")
|
||||
});
|
||||
|
||||
describe("Certificate Server Identiy", function () {
|
||||
test.each([
|
||||
["legacy-terminal-certificate", "live"],
|
||||
["legacy-terminal-certificate", "test"],
|
||||
["P400-3123123", "live"],
|
||||
["P400-3123123", "test"],
|
||||
["MODEL-SN", "live"],
|
||||
["MODEL-SN", "test"],
|
||||
])("it should be valid: %s.%s", (prefix, environment) => {
|
||||
const cn = `${prefix}.${environment}.terminal.adyen.com`;
|
||||
const mockedCertificate = createMockedCertificate(cn);
|
||||
expect(checkServerIdentity("any", mockedCertificate)).toBeUndefined();
|
||||
});
|
||||
|
||||
test.each([
|
||||
"INVALID.adyen.com",
|
||||
"terminal.INVALID.com",
|
||||
"terminal.adyen.org",
|
||||
"google.com",
|
||||
"",
|
||||
])("it should fail because invalid domain: %s", (domain) => {
|
||||
const cn = `P400-123123.live.${domain}`;
|
||||
const mockedCertificate = createMockedCertificate(cn);
|
||||
expect(checkServerIdentity("any", mockedCertificate) instanceof Error).toBeTruthy();
|
||||
});
|
||||
|
||||
test.each([
|
||||
"liive", "teest", "lve", "tst", "ANY", "invalid",
|
||||
])("it should fail because invalid environment: %s", (environment) => {
|
||||
const cn = `P400-123123.${environment}.terminal.adyen.com`;
|
||||
const mockedCertificate = createMockedCertificate(cn);
|
||||
expect(checkServerIdentity("any", mockedCertificate) instanceof Error).toBeTruthy();
|
||||
});
|
||||
|
||||
test.each([
|
||||
"legacyy-terminal-certificate", "legacy-terminaal-certificate",
|
||||
"legacy-terminal-certificatee", "P400-", "-123123", "P400--123123", "P400123123",
|
||||
"ANY-ANY-ANY", "ANY", ""
|
||||
])("it should fail because invalid prefix: %s", (prefix) => {
|
||||
const cn = `${prefix}.test.terminal.adyen.com`;
|
||||
const mockedCertificate = createMockedCertificate(cn);
|
||||
expect(checkServerIdentity("any", mockedCertificate) instanceof Error).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should fail if no first part on CN", function () {
|
||||
const cn = "live.terminal.adyen.com";
|
||||
const mockedCertificate = createMockedCertificate(cn);
|
||||
expect(checkServerIdentity("any", mockedCertificate) instanceof Error).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -57,7 +57,7 @@ function createPaymentsDetailsRequest(): DetailsRequest {
|
||||
};
|
||||
}
|
||||
|
||||
function createPaymentsCheckoutRequest(): PaymentRequest {
|
||||
export function createPaymentsCheckoutRequest(): PaymentRequest {
|
||||
const paymentMethodDetails = {
|
||||
cvc: "737",
|
||||
expiryMonth: "10",
|
||||
|
||||
@@ -2,6 +2,7 @@ import nock from "nock";
|
||||
import Client from "../client";
|
||||
import Checkout from "../services/checkout";
|
||||
import ApiException from "../services/exception/apiException";
|
||||
import {createPaymentsCheckoutRequest} from "./checkout.spec";
|
||||
|
||||
beforeEach((): void => {
|
||||
nock.cleanAll();
|
||||
@@ -14,10 +15,10 @@ describe("HTTP Client", function (): void {
|
||||
|
||||
nock(`${client.config.checkoutEndpoint}/${Client.CHECKOUT_API_VERSION}`)
|
||||
.post("/payments")
|
||||
.replyWithError();
|
||||
.replyWithError("error");
|
||||
|
||||
try {
|
||||
await checkout.payments({});
|
||||
await checkout.payments(createPaymentsCheckoutRequest());
|
||||
} catch (e) {
|
||||
expect(e instanceof ApiException);
|
||||
expect(e.message).toContain("x-api-key");
|
||||
@@ -33,7 +34,7 @@ describe("HTTP Client", function (): void {
|
||||
.replyWithError({message: "error_message", statusCode: 500});
|
||||
|
||||
try {
|
||||
await checkout.payments({});
|
||||
await checkout.payments(createPaymentsCheckoutRequest());
|
||||
} catch (e) {
|
||||
expect(e instanceof ApiException);
|
||||
expect(e.message).toEqual("error_message");
|
||||
|
||||
@@ -48,29 +48,29 @@ interface ClientParameters {
|
||||
}
|
||||
|
||||
class Client {
|
||||
public static ENDPOINT_TEST: string = "https://pal-test.adyen.com";
|
||||
public static ENDPOINT_LIVE: string = "https://pal-live.adyen.com";
|
||||
public static ENDPOINT_LIVE_SUFFIX: string = "-pal-live.adyenpayments.com";
|
||||
public static HPP_TEST: string = "https://test.adyen.com/hpp";
|
||||
public static HPP_LIVE: string = "https://live.adyen.com/hpp";
|
||||
public static MARKETPAY_ENDPOINT_TEST: string = "https://cal-test.adyen.com/cal/services";
|
||||
public static MARKETPAY_ENDPOINT_LIVE: string = "https://cal-live.adyen.com/cal/services";
|
||||
public static API_VERSION: string = "v49";
|
||||
public static RECURRING_API_VERSION: string = "v30";
|
||||
public static MARKETPAY_ACCOUNT_API_VERSION: string = "v4";
|
||||
public static MARKETPAY_FUND_API_VERSION: string = "v3";
|
||||
public static MARKETPAY_NOTIFICATION_API_VERSION: string = "v1";
|
||||
public static LIB_NAME: string = "adyen-node-api-library";
|
||||
public static ENDPOINT_TEST = "https://pal-test.adyen.com";
|
||||
public static ENDPOINT_LIVE = "https://pal-live.adyen.com";
|
||||
public static ENDPOINT_LIVE_SUFFIX = "-pal-live.adyenpayments.com";
|
||||
public static HPP_TEST = "https://test.adyen.com/hpp";
|
||||
public static HPP_LIVE = "https://live.adyen.com/hpp";
|
||||
public static MARKETPAY_ENDPOINT_TEST = "https://cal-test.adyen.com/cal/services";
|
||||
public static MARKETPAY_ENDPOINT_LIVE = "https://cal-live.adyen.com/cal/services";
|
||||
public static API_VERSION = "v49";
|
||||
public static RECURRING_API_VERSION = "v30";
|
||||
public static MARKETPAY_ACCOUNT_API_VERSION = "v4";
|
||||
public static MARKETPAY_FUND_API_VERSION = "v3";
|
||||
public static MARKETPAY_NOTIFICATION_API_VERSION = "v1";
|
||||
public static LIB_NAME = "adyen-node-api-library";
|
||||
public static LIB_VERSION: string = version;
|
||||
public static CHECKOUT_ENDPOINT_TEST: string = "https://checkout-test.adyen.com/checkout";
|
||||
public static CHECKOUT_ENDPOINT_LIVE_SUFFIX: string = "-checkout-live.adyenpayments.com/checkout";
|
||||
public static CHECKOUT_API_VERSION: string = "v49";
|
||||
public static CHECKOUT_ENDPOINT_TEST = "https://checkout-test.adyen.com/checkout";
|
||||
public static CHECKOUT_ENDPOINT_LIVE_SUFFIX = "-checkout-live.adyenpayments.com/checkout";
|
||||
public static CHECKOUT_API_VERSION = "v49";
|
||||
public static BIN_LOOKUP_PAL_SUFFIX = "/pal/servlet/BinLookup/";
|
||||
public static BIN_LOOKUP_API_VERSION = "v40";
|
||||
public static CHECKOUT_UTILITY_API_VERSION: string = "v1";
|
||||
public static TERMINAL_API_ENDPOINT_TEST: string = "https://terminal-api-test.adyen.com";
|
||||
public static TERMINAL_API_ENDPOINT_LIVE: string = "https://terminal-api-live.adyen.com";
|
||||
public static ENDPOINT_PROTOCOL: string = "https://";
|
||||
public static CHECKOUT_UTILITY_API_VERSION = "v1";
|
||||
public static TERMINAL_API_ENDPOINT_TEST = "https://terminal-api-test.adyen.com";
|
||||
public static TERMINAL_API_ENDPOINT_LIVE = "https://terminal-api-live.adyen.com";
|
||||
public static ENDPOINT_PROTOCOL = "https://";
|
||||
|
||||
private _httpClient!: ClientInterface;
|
||||
public config: Config;
|
||||
|
||||
10
src/helpers/checkServerIdentity.ts
Normal file
10
src/helpers/checkServerIdentity.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {PeerCertificate} from "tls";
|
||||
|
||||
export default function checkServerIdentity(host: string, cert: PeerCertificate): Error | undefined {
|
||||
const { subject: { CN }} = cert;
|
||||
const re = /^(([a-zA-Z0-9]+-[a-zA-Z0-9]+)|legacy-terminal-certificate)\.(live|test)\.terminal\.adyen\.com$/;
|
||||
const isValid = re.test(CN);
|
||||
const error = new Error("Couldn't verify certificate");
|
||||
|
||||
return isValid ? undefined : error;
|
||||
}
|
||||
@@ -22,15 +22,17 @@
|
||||
import {IncomingHttpHeaders, IncomingMessage} from "http";
|
||||
|
||||
class HttpClientException implements Error {
|
||||
public statusCode: number = 0;
|
||||
public statusCode = 500;
|
||||
public errorCode: string | undefined;
|
||||
public responseHeaders: IncomingHttpHeaders | undefined;
|
||||
public readonly message: string;
|
||||
public readonly name: string;
|
||||
public responseBody: IncomingMessage | undefined;
|
||||
|
||||
public constructor(message: string, statusCode?: number, responseHeaders?: IncomingHttpHeaders, responseBody?: IncomingMessage) {
|
||||
public constructor(message: string, statusCode?: number, errorCode?: string, responseHeaders?: IncomingHttpHeaders, responseBody?: IncomingMessage) {
|
||||
this.name = "HttpClientException";
|
||||
this.message = message;
|
||||
if(errorCode) this.errorCode = errorCode;
|
||||
if(statusCode) this.statusCode = statusCode;
|
||||
if(responseHeaders) this.responseHeaders = responseHeaders;
|
||||
if(responseBody) this.responseBody = responseBody;
|
||||
|
||||
@@ -39,9 +39,11 @@ import {
|
||||
import { RequestOptions } from "../typings/requestOptions";
|
||||
import ClientInterface from "../typings/httpClient/clientInterface";
|
||||
import HttpClientException from "./httpClientException";
|
||||
import checkServerIdentity from "../helpers/checkServerIdentity";
|
||||
import {ApiError} from "../typings/apiError";
|
||||
|
||||
class HttpURLConnectionClient implements ClientInterface {
|
||||
private static CHARSET: string = "utf-8";
|
||||
private static CHARSET = "utf-8";
|
||||
public proxy?: AgentOptions;
|
||||
private agentOptions!: AgentOptions;
|
||||
|
||||
@@ -121,16 +123,19 @@ class HttpURLConnectionClient implements ClientInterface {
|
||||
|
||||
connectionRequest.on("response", (res: IncomingMessage): void => {
|
||||
let resData = "";
|
||||
if (res.statusCode && res.statusCode !== 200) {
|
||||
const exception = new HttpClientException(
|
||||
`HTTP Exception: ${res.statusCode}. ${res.statusMessage}`,
|
||||
res.statusCode,
|
||||
res.headers,
|
||||
res,
|
||||
);
|
||||
reject(exception);
|
||||
}
|
||||
res.on("data", (data): void => {
|
||||
if (res.statusCode && res.statusCode !== 200) {
|
||||
const formattedData: ApiError = JSON.parse(data.toString());
|
||||
const exception = new HttpClientException(
|
||||
`HTTP Exception: ${formattedData.status}. ${res.statusMessage}: ${formattedData.message}`,
|
||||
formattedData.status,
|
||||
formattedData.errorCode,
|
||||
res.headers,
|
||||
res,
|
||||
);
|
||||
return reject(exception);
|
||||
}
|
||||
|
||||
resData += data;
|
||||
});
|
||||
|
||||
@@ -159,9 +164,7 @@ class HttpURLConnectionClient implements ClientInterface {
|
||||
|
||||
this.agentOptions = {
|
||||
ca: certificateInput,
|
||||
checkServerIdentity: (): undefined => {
|
||||
return undefined;
|
||||
},
|
||||
checkServerIdentity,
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import Client from "./client";
|
||||
|
||||
class Service {
|
||||
public apiKeyRequired: boolean = false;
|
||||
public apiKeyRequired = false;
|
||||
public client: Client;
|
||||
|
||||
protected constructor(client: Client) {
|
||||
|
||||
@@ -90,8 +90,8 @@ class Payout extends Service {
|
||||
|
||||
public payout(request: PayoutRequest): Promise<PayoutResponse> {
|
||||
return getJsonResponse<PayoutRequest, PayoutResponse>(
|
||||
this._payout,
|
||||
request
|
||||
this._payout,
|
||||
request
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
"strict": true,
|
||||
"noUnusedLocals": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/__mocks__/*",
|
||||
"**/__tests__/*"
|
||||
]
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user