Merge pull request #81 from Adyen/develop

Release v2.1.3
This commit is contained in:
Ricardo Ambrogi
2019-08-19 14:16:12 +02:00
committed by GitHub
15 changed files with 665 additions and 535 deletions

36
.eslintrc.js Normal file
View 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"
}
}
]
}

View File

@@ -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"
}
}
]
}

View File

@@ -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"

View File

@@ -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);

View 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();
});
});

View File

@@ -57,7 +57,7 @@ function createPaymentsDetailsRequest(): DetailsRequest {
};
}
function createPaymentsCheckoutRequest(): PaymentRequest {
export function createPaymentsCheckoutRequest(): PaymentRequest {
const paymentMethodDetails = {
cvc: "737",
expiryMonth: "10",

View File

@@ -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");

View File

@@ -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;

View 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;
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -22,7 +22,7 @@
import Client from "./client";
class Service {
public apiKeyRequired: boolean = false;
public apiKeyRequired = false;
public client: Client;
protected constructor(client: Client) {

View File

@@ -90,8 +90,8 @@ class Payout extends Service {
public payout(request: PayoutRequest): Promise<PayoutResponse> {
return getJsonResponse<PayoutRequest, PayoutResponse>(
this._payout,
request
this._payout,
request
);
}
}

View File

@@ -19,12 +19,6 @@
"strict": true,
"noUnusedLocals": true
},
"include": [
"src"
],
"exclude": [
"node_modules",
"**/__mocks__/*",
"**/__tests__/*"
]
"include": ["src"],
"exclude": ["node_modules"]
}

921
yarn.lock

File diff suppressed because it is too large Load Diff