Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PP-13313 Implement save credentials functionality in POST #4391

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love to see it


const switchingCredential = getSwitchingCredentialIfExists(gatewayAccount)
const isSwitchingToStripe = switchingCredential && switchingCredential.payment_provider === 'stripe'
Expand Down
27 changes: 23 additions & 4 deletions app/models/GatewayAccount.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions app/models/WorldpayTasks.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
19 changes: 10 additions & 9 deletions app/models/gateway-account-credential/Credential.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ class Credential {
* @returns {Credential}
*/
withStripeAccountId (stripeAccountId) {
if (stripeAccountId) {
this.stripeAccountId = stripeAccountId
}
this.stripeAccountId = stripeAccountId
return this
}

Expand All @@ -19,9 +17,7 @@ class Credential {
* @returns {Credential}
*/
withOneOffCustomerInitiated (oneOffCustomerInitiated) {
if (oneOffCustomerInitiated) {
this.oneOffCustomerInitiated = oneOffCustomerInitiated
}
this.oneOffCustomerInitiated = oneOffCustomerInitiated
return this
}

Expand All @@ -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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
nlsteers marked this conversation as resolved.
Show resolved Hide resolved
.withState(data?.state)
.withCreatedDate(data?.created_date)
.withActiveStartDate(data?.active_start_date)
.withActiveEndDate(data?.active_end_date)
.withGatewayAccountId(data?.gateway_account_id)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 3 additions & 12 deletions app/models/gateway-account-credential/WorldpayCredential.class.js
Original file line number Diff line number Diff line change
@@ -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
}

Expand All @@ -29,9 +23,6 @@ class WorldpayCredential {
}

static fromJson (data) {
if (!data) {
return undefined
}
return new WorldpayCredential()
.withMerchantCode(data?.merchant_code)
.withUsername(data?.username)
Expand Down
2 changes: 1 addition & 1 deletion app/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
9 changes: 5 additions & 4 deletions app/services/clients/connector.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -138,18 +139,18 @@ ConnectorClient.prototype = {
* @param {String} serviceExternalId
* @param {String} accountType
* @param {String} credentialsId
* @param {Object} payload
* @param {GatewayAccountCredentialUpdateRequest} patchRequest
* @returns {Promise<GatewayAccountCredential>}
*/
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) {
Expand Down
21 changes: 19 additions & 2 deletions app/services/worldpay-details.service.js
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -8,7 +9,7 @@ const connectorClient = new ConnectorClient(process.env.CONNECTOR_URL)
* @param {String} serviceExternalId
* @param {String} accountType
* @param {WorldpayCredential} credential
* @returns {Promise<Object>}
* @returns {Promise<boolean>}
*/
async function checkCredential (serviceExternalId, accountType, credential) {
const credentialCheck = await connectorClient.postCheckWorldpayCredentialByServiceExternalIdAndAccountType(
Expand All @@ -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<GatewayAccountCredential>}
*/
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
}
4 changes: 2 additions & 2 deletions app/simplified-account-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading