diff --git a/app/controllers/simplified-account/settings/worldpay-details/credentials/worldpay-credentials.controller.js b/app/controllers/simplified-account/settings/worldpay-details/credentials/worldpay-credentials.controller.js index b0b425ceb..08e895971 100644 --- a/app/controllers/simplified-account/settings/worldpay-details/credentials/worldpay-credentials.controller.js +++ b/app/controllers/simplified-account/settings/worldpay-details/credentials/worldpay-credentials.controller.js @@ -53,6 +53,14 @@ async function post (req, res) { }) } + await worldpayDetailsService.updateCredentials( + req.service.externalId, + req.account.type, + req.account.getCurrentCredential().externalId, + req.user.externalId, + credential + ) + return res.redirect(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, req.service.externalId, req.account.type)) } diff --git a/app/controllers/simplified-account/settings/worldpay-details/worldpay-details.controller.test.js b/app/controllers/simplified-account/settings/worldpay-details/worldpay-details.controller.test.js index 1fee34c96..8da26de5b 100644 --- a/app/controllers/simplified-account/settings/worldpay-details/worldpay-details.controller.test.js +++ b/app/controllers/simplified-account/settings/worldpay-details/worldpay-details.controller.test.js @@ -51,7 +51,7 @@ describe('Controller: settings/worldpay-details', () => { it('should pass context data to the response method', () => { const tasks = [{ - href: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.credentials, + href: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.oneOffCustomerInitiated, SERVICE_ID, ACCOUNT_TYPE), id: 'worldpay-credentials', linkText: 'Link your Worldpay account with GOV.UK Pay', diff --git a/app/middleware/simplified-account/simplified-account-strategy.middleware.js b/app/middleware/simplified-account/simplified-account-strategy.middleware.js index 4a85a52ff..cec29a110 100644 --- a/app/middleware/simplified-account/simplified-account-strategy.middleware.js +++ b/app/middleware/simplified-account/simplified-account-strategy.middleware.js @@ -28,12 +28,7 @@ async function getGatewayAccount (serviceExternalId, accountType) { serviceExternalId, accountType } - let gatewayAccount = await connectorClient.getAccountByServiceExternalIdAndAccountType(params) - - gatewayAccount = _.extend({}, gatewayAccount, { - supports3ds: ['worldpay', 'stripe'].includes(gatewayAccount.paymentProvider), - disableToggle3ds: gatewayAccount.paymentProvider === 'stripe' - }) + const gatewayAccount = await connectorClient.getAccountByServiceExternalIdAndAccountType(params) const switchingCredential = getSwitchingCredentialIfExists(gatewayAccount) const isSwitchingToStripe = switchingCredential && switchingCredential.payment_provider === 'stripe' diff --git a/app/models/GatewayAccount.class.js b/app/models/GatewayAccount.class.js index 79229f1d6..0f2f603e8 100644 --- a/app/models/GatewayAccount.class.js +++ b/app/models/GatewayAccount.class.js @@ -44,16 +44,35 @@ class GatewayAccount { this.recurringEnabled = gatewayAccountData.recurring_enabled if (gatewayAccountData?.gateway_account_credentials) { this.gatewayAccountCredentials = gatewayAccountData?.gateway_account_credentials - .map(credentialData => new GatewayAccountCredential(credentialData)) - - this.activeCredential = this.gatewayAccountCredentials.filter((credential) => - credential.state === CREDENTIAL_STATE.ACTIVE)[0] || null + .map(credentialData => GatewayAccountCredential.fromJson(credentialData)) } + this.supports3ds = ['worldpay', 'stripe'].includes(gatewayAccountData.payment_provider) + this.disableToggle3ds = gatewayAccountData.payment_provider === 'stripe' /** @deprecated this is a temporary compatability fix! If you find yourself using this for new code * you should instead add any rawResponse data as part of the constructor */ this.rawResponse = gatewayAccountData } + /** + * + * @returns {GatewayAccountCredential} + */ + getCurrentCredential () { + if (this.gatewayAccountCredentials.length === 1) { + return this.gatewayAccountCredentials[0] + } + return this.getActiveCredential() + } + + /** + * + * @returns {GatewayAccountCredential} + */ + getActiveCredential () { + return this.gatewayAccountCredentials + .filter((credential) => credential.state === CREDENTIAL_STATE.ACTIVE)[0] || null + } + /** * @method toJson * @returns {Object} A minimal representation of the gateway account diff --git a/app/models/WorldpayTasks.class.js b/app/models/WorldpayTasks.class.js index 48507e8e1..8fbc302a0 100644 --- a/app/models/WorldpayTasks.class.js +++ b/app/models/WorldpayTasks.class.js @@ -12,11 +12,11 @@ class WorldpayTasks { this.tasks = [] this.incompleteTasks = true - const credential = gatewayAccount.activeCredential + const credential = gatewayAccount.getCurrentCredential() if (gatewayAccount.allowMoto) { const worldpayCredentials = { - href: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.credentials, + href: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.oneOffCustomerInitiated, service.externalId, gatewayAccount.type), id: 'worldpay-credentials', linkText: 'Link your Worldpay account with GOV.UK Pay', diff --git a/app/models/gateway-account-credential/Credential.class.js b/app/models/gateway-account-credential/Credential.class.js index 73114ec0a..8de6d5610 100644 --- a/app/models/gateway-account-credential/Credential.class.js +++ b/app/models/gateway-account-credential/Credential.class.js @@ -7,9 +7,7 @@ class Credential { * @returns {Credential} */ withStripeAccountId (stripeAccountId) { - if (stripeAccountId) { - this.stripeAccountId = stripeAccountId - } + this.stripeAccountId = stripeAccountId return this } @@ -19,9 +17,7 @@ class Credential { * @returns {Credential} */ withOneOffCustomerInitiated (oneOffCustomerInitiated) { - if (oneOffCustomerInitiated) { - this.oneOffCustomerInitiated = oneOffCustomerInitiated - } + this.oneOffCustomerInitiated = oneOffCustomerInitiated return this } @@ -42,10 +38,15 @@ class Credential { } static fromJson (data) { - return new Credential() - .withStripeAccountId(data?.stripe_account_id) - .withOneOffCustomerInitiated(WorldpayCredential.fromJson(data?.one_off_customer_initiated)) + const credential = new Credential() .withRawResponse(data) + if (data?.stripe_account_id) { + credential.withStripeAccountId(data.stripe_account_id) + } + if (data?.one_off_customer_initiated) { + credential.withOneOffCustomerInitiated(WorldpayCredential.fromJson(data.one_off_customer_initiated)) + } + return credential } } diff --git a/app/models/gateway-account-credential/GatewayAccountCredential.class.js b/app/models/gateway-account-credential/GatewayAccountCredential.class.js index f01007234..2809a76b3 100644 --- a/app/models/gateway-account-credential/GatewayAccountCredential.class.js +++ b/app/models/gateway-account-credential/GatewayAccountCredential.class.js @@ -9,15 +9,63 @@ const CREDENTIAL_STATE = { } class GatewayAccountCredential { - constructor (data) { - this.externalId = data.external_id - this.paymentProvider = data.payment_provider - this.credentials = Credential.fromJson(data.credentials) - this.state = data.state - this.createdDate = data.created_date - this.activeStartDate = data.active_start_date - this.activeEndDate = data.active_end_date - this.gatewayAccountId = data.gateway_account_id + withExternalId (externalId) { + this.externalId = externalId + return this + } + + withPaymentProvider (paymentProvider) { + this.paymentProvider = paymentProvider + return this + } + + withCredentials (credentials) { + this.credentials = credentials + return this + } + + withState (state) { + if (state) { + this.state = state + } + return this + } + + withCreatedDate (createdDate) { + this.createdDate = createdDate + return this + } + + withActiveStartDate (activeStartDate) { + this.activeStartDate = activeStartDate + return this + } + + withActiveEndDate (activeEndDate) { + this.activeEndDate = activeEndDate + return this + } + + withGatewayAccountId (gatewayAccountId) { + this.gatewayAccountId = gatewayAccountId + return this + } + + /** + * + * @param data + * @returns {GatewayAccountCredential} + */ + static fromJson (data) { + return new GatewayAccountCredential() + .withExternalId(data?.external_id) + .withPaymentProvider(data?.payment_provider) + .withCredentials(Credential.fromJson(data?.credentials)) + .withState(data?.state) + .withCreatedDate(data?.created_date) + .withActiveStartDate(data?.active_start_date) + .withActiveEndDate(data?.active_end_date) + .withGatewayAccountId(data?.gateway_account_id) } } diff --git a/app/models/gateway-account-credential/GatewayAccountCredentialUpdateRequest.class.js b/app/models/gateway-account-credential/GatewayAccountCredentialUpdateRequest.class.js index 8f068372f..ec82bf48f 100644 --- a/app/models/gateway-account-credential/GatewayAccountCredentialUpdateRequest.class.js +++ b/app/models/gateway-account-credential/GatewayAccountCredentialUpdateRequest.class.js @@ -23,10 +23,15 @@ class GatewayAccountCredentialUpdateRequest { const safeOperation = (op, request) => { return { - credentials: (value) => { - request.updates.push({ op, path: 'credentials', value }) - return request + credentials: () => { + return { + oneOffCustomerInitiated: (value) => { + request.updates.push({ op, path: 'credentials/worldpay/one_off_customer_initiated', value }) + return request + } + } } } } -module.exports = { GatewayAccountCredentialUpdateRequest } + +module.exports = GatewayAccountCredentialUpdateRequest diff --git a/app/models/gateway-account-credential/WorldpayCredential.class.js b/app/models/gateway-account-credential/WorldpayCredential.class.js index a1b3a55b6..64e707224 100644 --- a/app/models/gateway-account-credential/WorldpayCredential.class.js +++ b/app/models/gateway-account-credential/WorldpayCredential.class.js @@ -1,22 +1,16 @@ class WorldpayCredential { withMerchantCode (merchantCode) { - if (merchantCode) { - this.merchantCode = merchantCode - } + this.merchantCode = merchantCode return this } withUsername (username) { - if (username) { - this.username = username - } + this.username = username return this } withPassword (password) { - if (password) { - this.password = password - } + this.password = password return this } @@ -29,9 +23,6 @@ class WorldpayCredential { } static fromJson (data) { - if (!data) { - return undefined - } return new WorldpayCredential() .withMerchantCode(data?.merchant_code) .withUsername(data?.username) diff --git a/app/paths.js b/app/paths.js index acc64cecd..a370acbc0 100644 --- a/app/paths.js +++ b/app/paths.js @@ -215,7 +215,7 @@ module.exports = { }, worldpayDetails: { index: '/settings/worldpay-details', - credentials: '/settings/worldpay-details/credentials' + oneOffCustomerInitiated: '/settings/worldpay-details/one-off-customer-initiated' }, cardPayments: { index: '/settings/card-payments' diff --git a/app/services/clients/connector.client.js b/app/services/clients/connector.client.js index 873b2c6c6..1fdae8440 100644 --- a/app/services/clients/connector.client.js +++ b/app/services/clients/connector.client.js @@ -7,6 +7,7 @@ const StripeAccountSetup = require('../../models/StripeAccountSetup.class') const StripeAccount = require('../../models/StripeAccount.class') const GatewayAccount = require('@models/GatewayAccount.class') const ValidationResult = require('@models/gateway-account-credential/ValidationResult.class') +const { GatewayAccountCredential } = require('@models/gateway-account-credential/GatewayAccountCredential.class') // Constants const SERVICE_NAME = 'connector' @@ -138,18 +139,18 @@ ConnectorClient.prototype = { * @param {String} serviceExternalId * @param {String} accountType * @param {String} credentialsId - * @param {Object} payload + * @param {GatewayAccountCredentialUpdateRequest} patchRequest * @returns {Promise} */ - patchGatewayAccountCredentialsByServiceIdAndAccountType: async function (serviceExternalId, accountType, credentialsId, payload) { + patchGatewayAccountCredentialsByServiceExternalIdAndAccountType: async function (serviceExternalId, accountType, credentialsId, patchRequest) { const url = `${this.connectorUrl}/v1/api/service/{serviceExternalId}/account/{accountType}/credentials/{credentialsId}` .replace('{serviceExternalId}', encodeURIComponent(serviceExternalId)) .replace('{accountType}', encodeURIComponent(accountType)) .replace('{credentialsId}', encodeURIComponent(credentialsId)) configureClient(client, url) - const response = await client.patch(url, payload, 'patch gateway account credentials') - return response.data + const response = await client.patch(url, patchRequest.formatPayload(), 'patch gateway account credentials') + return GatewayAccountCredential.fromJson(response.data) }, patchGooglePayGatewayMerchantId: async function (gatewayAccountId, gatewayAccountCredentialsId, googlePayGatewayMerchantId, userExternalId) { diff --git a/app/services/worldpay-details.service.js b/app/services/worldpay-details.service.js index 67a84f2e4..fb7b1c49b 100644 --- a/app/services/worldpay-details.service.js +++ b/app/services/worldpay-details.service.js @@ -1,4 +1,5 @@ const { ConnectorClient } = require('./clients/connector.client') +const GatewayAccountCredentialUpdateRequest = require('@models/gateway-account-credential/GatewayAccountCredentialUpdateRequest.class') const logger = require('../utils/logger')(__filename) const connectorClient = new ConnectorClient(process.env.CONNECTOR_URL) @@ -8,7 +9,7 @@ const connectorClient = new ConnectorClient(process.env.CONNECTOR_URL) * @param {String} serviceExternalId * @param {String} accountType * @param {WorldpayCredential} credential - * @returns {Promise} + * @returns {Promise} */ async function checkCredential (serviceExternalId, accountType, credential) { const credentialCheck = await connectorClient.postCheckWorldpayCredentialByServiceExternalIdAndAccountType( @@ -25,6 +26,22 @@ async function checkCredential (serviceExternalId, accountType, credential) { return true } +/** + * + * @param {String} serviceExternalId + * @param {String} accountType + * @param {String} credentialId + * @param {String} userExternalId + * @param {WorldpayCredential} credential + * @returns {Promise} + */ +async function updateCredentials (serviceExternalId, accountType, credentialId, userExternalId, credential) { + const patchRequest = new GatewayAccountCredentialUpdateRequest(userExternalId) + .replace().credentials().oneOffCustomerInitiated(credential.toJson()) + return connectorClient.patchGatewayAccountCredentialsByServiceExternalIdAndAccountType(serviceExternalId, accountType, credentialId, patchRequest) +} + module.exports = { - checkCredential + checkCredential, + updateCredentials } diff --git a/app/simplified-account-routes.js b/app/simplified-account-routes.js index 1228fe226..72287f2e1 100644 --- a/app/simplified-account-routes.js +++ b/app/simplified-account-routes.js @@ -66,8 +66,8 @@ simplifiedAccount.get(paths.simplifiedAccount.settings.worldpayDetails.index, pe // worldpay details simplifiedAccount.get(paths.simplifiedAccount.settings.worldpayDetails.index, permission('gateway-credentials:read'), serviceSettingsController.worldpayDetails.get) -simplifiedAccount.get(paths.simplifiedAccount.settings.worldpayDetails.credentials, permission('gateway-credentials:update'), serviceSettingsController.worldpayDetails.worldpayCredentials.get) -simplifiedAccount.post(paths.simplifiedAccount.settings.worldpayDetails.credentials, permission('gateway-credentials:update'), serviceSettingsController.worldpayDetails.worldpayCredentials.post) +simplifiedAccount.get(paths.simplifiedAccount.settings.worldpayDetails.oneOffCustomerInitiated, permission('gateway-credentials:update'), serviceSettingsController.worldpayDetails.worldpayCredentials.get) +simplifiedAccount.post(paths.simplifiedAccount.settings.worldpayDetails.oneOffCustomerInitiated, permission('gateway-credentials:update'), serviceSettingsController.worldpayDetails.worldpayCredentials.post) // card types simplifiedAccount.get(paths.simplifiedAccount.settings.cardTypes.index, permission('transactions:read'), serviceSettingsController.cardTypes.get)