diff --git a/package-lock.json b/package-lock.json index 5f13b2f..7228281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,37 @@ "name": "adyen-testing-suite", "version": "1.0.0", "license": "ISC", + "dependencies": { + "uuid": "^9.0.1" + }, "devDependencies": { - "@playwright/test": "^1.37.1" + "@adyen/api-library": "^14.0.0", + "@playwright/test": "^1.37.1", + "dotenv": "^16.3.1" } }, + "node_modules/@adyen/api-library": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@adyen/api-library/-/api-library-14.3.0.tgz", + "integrity": "sha512-DXEIpOEtJEPb+LoZmcx9tDT08Viw75dDsTYK7zDgJa5fhWu0Z+hvBh6niZ/82fJtfGtdq49WpbLKcTOBvyt7Pg==", + "dev": true, + "dependencies": { + "https-proxy-agent": "5.0.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@types/node": "14.18.61" + } + }, + "node_modules/@adyen/api-library/node_modules/@types/node": { + "version": "14.18.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.61.tgz", + "integrity": "sha512-1mFT4DqS4/s9tlZbdkwEB/EnSykA9MDeDLIk3FHApGvIMGY//qgstB2gu9GKGESWyW/qiRUO+jhlLJ9bBJ8j+Q==", + "dev": true, + "optional": true + }, "node_modules/@playwright/test": { "version": "1.37.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz", @@ -37,6 +64,47 @@ "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==", "dev": true }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -51,6 +119,25 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/playwright-core": { "version": "1.37.1", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", @@ -62,9 +149,40 @@ "engines": { "node": ">=16" } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } } }, "dependencies": { + "@adyen/api-library": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@adyen/api-library/-/api-library-14.3.0.tgz", + "integrity": "sha512-DXEIpOEtJEPb+LoZmcx9tDT08Viw75dDsTYK7zDgJa5fhWu0Z+hvBh6niZ/82fJtfGtdq49WpbLKcTOBvyt7Pg==", + "dev": true, + "requires": { + "@types/node": "14.18.61", + "https-proxy-agent": "5.0.1" + }, + "dependencies": { + "@types/node": { + "version": "14.18.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.61.tgz", + "integrity": "sha512-1mFT4DqS4/s9tlZbdkwEB/EnSykA9MDeDLIk3FHApGvIMGY//qgstB2gu9GKGESWyW/qiRUO+jhlLJ9bBJ8j+Q==", + "dev": true, + "optional": true + } + } + }, "@playwright/test": { "version": "1.37.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz", @@ -82,6 +200,30 @@ "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -89,11 +231,32 @@ "dev": true, "optional": true }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "playwright-core": { "version": "1.37.1", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", "dev": true + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" } } } diff --git a/package.json b/package.json index 30d3a3c..d68d57f 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,11 @@ "author": "", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.37.1", "@adyen/api-library": "^14.0.0", + "@playwright/test": "^1.37.1", "dotenv": "^16.3.1" + }, + "dependencies": { + "uuid": "^9.0.1" } } diff --git a/tests/subscription/display-token.spec.js b/tests/subscription/display-token.spec.js new file mode 100644 index 0000000..d0e65fb --- /dev/null +++ b/tests/subscription/display-token.spec.js @@ -0,0 +1,65 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const utilities = require('../utilities'); + +import { v4 as uuidv4 } from 'uuid'; + + +// test RecurringDetailReference is displayed in the Admin panel +test('Display RecurringDetailReference', async ({ request, page }) => { + + var shopperReference = uuidv4(); + var token = uuidv4(); + + // send webhook with RECURRING_CONTRACT event + var notificationRequestItem = { + "eventCode": "RECURRING_CONTRACT", + "eventDate": "2023-06-20T16:09:48+02:00", + "merchantAccountCode": "YOUR_MERCHANT_ACCOUNT", + "merchantReference": "YOUR_PAYMENT_REFERENCE", + "originalReference": "INITIAL_PAYMENT_PSP_REFERENCE", + "originalPsp": "PSP_REFERENCE", + "paymentMethod": "mc", + "amount": { + "value": 0, + "currency": "EUR" + }, + "success": "true", + }; + + // calculate signature from payload + const hmacSignature = await utilities.calculateHmacSignature(notificationRequestItem); + // add 'additionalData' with hmacSignature + notificationRequestItem["additionalData"] = { + "hmacSignature": "" + hmacSignature + "", + "recurring.recurringDetailReference": token, + "recurring.shopperReference": shopperReference, + "shopperReference": "YOUR_SHOPPER_REFERENCE" + } + + // POST webhook + const notifications = await request.post(`/api/webhooks/notifications`, { + data: { + "live": "false", + "notificationItems": [ + { + "NotificationRequestItem": notificationRequestItem + } + ] + } + }); + + // Verify status code + expect(notifications.status()).toEqual(200); + + // Verify token is visible in the Admin panel + await page.goto('/admin'); + + await expect(page).toHaveTitle("Adyen Subscription Admin View"); + await expect(page.locator('text="ADMIN PANEL"')).toBeVisible(); + + await expect(page.locator('text="ShopperReference: ' + shopperReference + '"')).toBeVisible(); + await expect(page.locator('text="RecurringDetailReference: ' + token + '"')).toBeVisible(); + +}); + diff --git a/tests/subscription/webhook-failure.spec.js b/tests/subscription/webhook-failure.spec.js new file mode 100644 index 0000000..fe8959b --- /dev/null +++ b/tests/subscription/webhook-failure.spec.js @@ -0,0 +1,43 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); + +test('Webhook failure', async ({ request }) => { + const notifications = await request.post(`/api/webhooks/notifications`, { + data: { + "live": "false", + "notificationItems": [ + { + "NotificationRequestItem": { + "additionalData": { + "hmacSignature": "INVALID_HMAC_SIGNATURE", + "recurring.recurringDetailReference": "XXXXXXXXX", + "recurring.shopperReference": "UniqueShopperReference" + }, + "eventCode": "AUTHORISATION", + "success": "true", + "eventDate": "2019-06-28T18:03:50+01:00", + "merchantAccountCode": "YOUR_MERCHANT_ACCOUNT", + "pspReference": "7914073381342284", + "merchantReference": "YOUR_REFERENCE", + "amount": { + "value": 0, + "currency": "EUR" + } + } + } + ] + } + }); + + /// Verify notification is not accepted (invalid HMAC) + + // Status code not 404 (verify webhook is found) + expect(notifications.status()).not.toEqual(404); + + // Status code not 200 (verify webhook does not accept the notification ie HMAC invalid) + expect(notifications.status()).not.toEqual(200); + + // Body response does not contain [accepted] + notifications.text() + .then(value => { expect(value).not.toEqual("[accepted]"); }); +}); diff --git a/tests/subscription/webhook.spec.js b/tests/subscription/webhook.spec.js index df1a97b..d60c594 100644 --- a/tests/subscription/webhook.spec.js +++ b/tests/subscription/webhook.spec.js @@ -1,43 +1,49 @@ // @ts-check const { test, expect } = require('@playwright/test'); +const utilities = require('../utilities'); +// test webhook is successfully delivered test('Webhook Notification', async ({ request }) => { + + var notificationRequestItem = { + "eventCode": "AUTHORISATION", + "success": "true", + "eventDate": "2019-06-28T18:03:50+01:00", + "merchantAccountCode": "YOUR_MERCHANT_ACCOUNT", + "pspReference": "7914073381342284", + "merchantReference": "YOUR_REFERENCE", + "amount": { + "value": 0, + "currency": "EUR" + } + }; + + // calculate signature from payload + const hmacSignature = await utilities.calculateHmacSignature(notificationRequestItem); + // add 'additionalData' with hmacSignature + notificationRequestItem["additionalData"] = { + "hmacSignature": "" + hmacSignature + "", + "recurring.recurringDetailReference": "XXXXXXXXX", + "recurring.shopperReference": "UniqueShopperReference" + } + + // POST webhook const notifications = await request.post(`/api/webhooks/notifications`, { data: { "live": "false", - "notificationItems":[ + "notificationItems": [ { - "NotificationRequestItem":{ - "additionalData":{ - "hmacSignature":"+JWKfq4ynALK+FFzGgHnp1jSMQJMBJeb87dlph24sXw=", - "recurring.recurringDetailReference":"XXXXXXXXX", - "recurring.shopperReference": "UniqueShopperReference" - }, - "eventCode":"AUTHORISATION", - "success":"true", - "eventDate":"2019-06-28T18:03:50+01:00", - "merchantAccountCode":"YOUR_MERCHANT_ACCOUNT", - "pspReference": "7914073381342284", - "merchantReference": "YOUR_REFERENCE", - "amount": { - "value":0, - "currency":"EUR" - } - } + "NotificationRequestItem": notificationRequestItem } ] } }); - /// Verify notification is not accepted (invalid HMAC) + // Verify status code + expect(notifications.status()).toEqual(200); - // Status code not 404 (verify webhook is found) - expect(notifications.status()).not.toEqual(404); - - // Status code not 200 (verify webhook does not accept the notification ie HMAC invalid) - expect(notifications.status()).not.toEqual(200); - - // Body response does not contain [accepted] + // Verify body response notifications.text() - .then(value => {expect(value).not.toEqual("[accepted]");} ); + .then(value => { expect(value).toEqual("[accepted]"); }); }); +