Skip to content

Commit

Permalink
PP-13313: Worldpay details index page (#4365)
Browse files Browse the repository at this point in the history
* PP-13313: Worldpay details index page

This commit deals with a moto-enabled gateway account that hasn't configured
the one_off_customer_initiated credentials yet.
  • Loading branch information
oswaldquek authored Dec 3, 2024
1 parent 38c45cf commit d1ab34b
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 3 deletions.
4 changes: 3 additions & 1 deletion app/controllers/simplified-account/settings/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module.exports.serviceName = require('./service-name/service-name.controller')
module.exports.emailNotifications = require('./email-notifications/email-notifications.controller')
module.exports.organisationDetails = require('./organisation-details/organisation-details.controller')
module.exports.serviceName = require('./service-name/service-name.controller')
module.exports.stripeDetails = require('./stripe-details/stripe-details.controller')
module.exports.teamMembers = require('./team-members/team-members.controller')
module.exports.organisationDetails = require('./organisation-details/organisation-details.controller')
module.exports.cardTypes = require('./card-types/card-types.controller')
module.exports.worldpayDetails = require('./worldpay-details/worldpay-details.controller')
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { response } = require('@utils/response')
const { WorldpayTasks } = require('@models/WorldpayTasks.class')

function get (req, res) {
const worldpayTasks = new WorldpayTasks(req.account)

const context = {
tasks: worldpayTasks.tasks,
incompleteTasks: worldpayTasks.incompleteTasks
}
return response(req, res, 'simplified-account/settings/worldpay-details/index', context)
}

module.exports.get = get
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const ControllerTestBuilder = require('@test/test-helpers/simplified-account/controllers/ControllerTestBuilder.class')
const sinon = require('sinon')
const { expect } = require('chai')
const Service = require('@models/Service.class')
const GatewayAccount = require('@models/GatewayAccount.class')

const mockResponse = sinon.spy()

const ACCOUNT_TYPE = 'live'
const SERVICE_ID = 'service-id-123abc'

const { req, res, call } = new ControllerTestBuilder('@controllers/simplified-account/settings/worldpay-details/worldpay-details.controller')
.withService(new Service({
external_id: SERVICE_ID
}))
.withAccountType(ACCOUNT_TYPE)
.withAccount(new GatewayAccount({
type: ACCOUNT_TYPE,
allow_moto: true,
gateway_account_id: 1,
gateway_account_credentials: [{
external_id: 'creds-id',
payment_provider: 'worldpay',
state: 'CREATED',
created_date: '2024-11-29T11:58:36.214Z',
gateway_account_id: 1,
credentials: {}
}]
}))
.withStubs({
'@utils/response': { response: mockResponse }
})
.build()

describe('Controller: settings/worldpay-details', () => {
before(() => {
call('get')
})

describe('get', () => {
it('should call the response method', () => {
expect(mockResponse.called).to.be.true // eslint-disable-line
})

it('should pass req, res and template path to the response method', () => {
expect(mockResponse.args[0][0]).to.deep.equal(req)
expect(mockResponse.args[0][1]).to.deep.equal(res)
expect(mockResponse.args[0][2]).to.equal('simplified-account/settings/worldpay-details/index')
})

it('should pass context data to the response method', () => {
const tasks = [{
href: '#',
id: 'worldpay-credentials',
linkText: 'Link your Worldpay account with GOV.UK Pay',
complete: false
}]
expect(mockResponse.args[0][3]).to.have.property('tasks').to.deep.equal(tasks)
expect(mockResponse.args[0][3]).to.have.property('incompleteTasks').to.equal(true)
})
})
})
9 changes: 8 additions & 1 deletion app/models/GatewayAccount.class.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
const GatewayAccountCredential = require('@models/GatewayAccountCredential.class')
const { GatewayAccountCredential, CREDENTIAL_STATE } = require('@models/GatewayAccountCredential.class')

/**
* @class GatewayAccount
* @property {string} name - The name of the gateway account
* @property {string} id - The id of the gateway account
* @property {string} type - The type of the gateway account (e.g. test/live)
* @property {string} description - The description of the gateway account
* @property {boolean} allowMoto - whether MOTO payments are enabled on the gateway account
* @property {string} analyticsId - Google analyticsId of the gateway account
* @property {boolean} toggle3ds - whether 3DS is enabled or not on this gateway account
* @property {[GatewayAccountCredential]} gatewayAccountCredentials - available credentials for gateway account
* @property {GatewayAccountCredential} [activeCredential] - the active credential for the gateway account
* @property {Object} rawResponse - raw 'gateway account' object
*/
class GatewayAccount {
Expand All @@ -21,6 +23,7 @@ class GatewayAccount {
* @param {string} gatewayAccountData.type - The type of the gateway account
* @param {string} gatewayAccountData.payment_provider - The payment provider of the gateway account
* @param {string} gatewayAccountData.description - The description of the gateway account
* @param {boolean} gatewayAccountData.allow_moto - whether MOTO payments are enabled on the gateway account
* @param {string} gatewayAccountData.analytics_id - Google analytics_id of the gateway account
* @param {boolean} gatewayAccountData.toggle_3ds - whether 3DS is enabled or not on this gateway account
* @param {boolean} gatewayAccountData.provider_switch_enabled - indicates that the gateway is transitioning psp
Expand All @@ -34,13 +37,17 @@ class GatewayAccount {
this.type = gatewayAccountData.type
this.paymentProvider = gatewayAccountData.payment_provider
this.description = gatewayAccountData.description
this.allowMoto = gatewayAccountData.allow_moto
this.analyticsId = gatewayAccountData.analytics_id
this.toggle3ds = gatewayAccountData.toggle_3ds
this.providerSwitchEnabled = gatewayAccountData.provider_switch_enabled
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
}
/** @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 */
Expand Down
11 changes: 10 additions & 1 deletion app/models/GatewayAccountCredential.class.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
const CREDENTIAL_STATE = {
CREATED: 'CREATED',
ENTERED: 'ENTERED',
VERIFIED: 'VERIFIED_WITH_LIVE_PAYMENT',
ACTIVE: 'ACTIVE',
RETIRED: 'RETIRED'
}

class GatewayAccountCredential {
constructor (data) {
this.externalId = data.external_id
Expand All @@ -20,4 +28,5 @@ class Credential {
}
}

module.exports = GatewayAccountCredential
module.exports.GatewayAccountCredential = GatewayAccountCredential
module.exports.CREDENTIAL_STATE = CREDENTIAL_STATE
30 changes: 30 additions & 0 deletions app/models/WorldpayTasks.class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict'

class WorldpayTasks {
/**
* @param {GatewayAccount} gatewayAccount
*/
constructor (gatewayAccount) {
this.tasks = []
this.incompleteTasks = true

const credential = gatewayAccount.activeCredential

if (gatewayAccount.allowMoto) {
const worldpayCredentials = {
href: '#',
id: 'worldpay-credentials',
linkText: 'Link your Worldpay account with GOV.UK Pay',
complete: true
}
if (credential === null || credential.credentials.one_off_customer_initiated === null) {
worldpayCredentials.complete = false
}
this.tasks.push(worldpayCredentials)
}

this.incompleteTasks = this.tasks.filter(t => t.complete === false).length > 0
}
}

module.exports = { WorldpayTasks }
3 changes: 3 additions & 0 deletions app/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ module.exports = {
update: '/settings/stripe-details/organisation-details/update'
}
},
worldpayDetails: {
index: '/settings/worldpay-details'
},
cardPayments: {
index: '/settings/card-payments'
},
Expand Down
3 changes: 3 additions & 0 deletions app/simplified-account-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ simplifiedAccount.post(paths.simplifiedAccount.settings.organisationDetails.edit
// card types
simplifiedAccount.get(paths.simplifiedAccount.settings.cardTypes.index, permission('transactions:read'), serviceSettingsController.cardTypes.get)

// worldpay details
simplifiedAccount.get(paths.simplifiedAccount.settings.worldpayDetails.index, permission('gateway-credentials:read'), serviceSettingsController.worldpayDetails.get)

// stripe details
const stripeDetailsPath = paths.simplifiedAccount.settings.stripeDetails
const stripeDetailsRouter = new Router({ mergeParams: true })
Expand Down
7 changes: 7 additions & 0 deletions app/utils/simplified-account/settings/service-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ module.exports = (account, service, currentUrl, permissions) => {
path: paths.simplifiedAccount.settings.stripeDetails.index,
permission: account.paymentProvider === 'stripe' && account.type === 'live' && Boolean(permissions?.stripe_account_details_update)
})
.category('payment provider')
.add({
id: 'worldpay-details',
name: 'worldpay details',
path: paths.simplifiedAccount.settings.worldpayDetails.index,
permission: account.paymentProvider === 'worldpay' && 'gateway_credentials_read'
})
.category('payments')
.add({
id: 'card-payments',
Expand Down
46 changes: 46 additions & 0 deletions app/views/simplified-account/settings/worldpay-details/index.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends "../settings-layout.njk" %}

{% block settingsPageTitle %}
Worldpay details
{% endblock %}

{% block settingsContent %}
<h1 class="govuk-heading-l">Worldpay details</h1>

{% if incompleteTasks %}
<p class="govuk-body govuk-!-margin-bottom-6">
You need to link your Worldpay account to GOV.UK Pay.
</p>

{% set taskList = [] %}
{% for task in tasks %}
{% set taskList = (taskList.push({
title: {
text: task.linkText
},
href: task.href,
status: {
tag: {
text: "Not yet started",
classes: "govuk-tag--blue"
}
} if task.complete == false else (
{
tag: {
text: "Completed",
classes: "govuk-tag--grey"
}
}
)
}), taskList) %}
{% endfor %}

{{ govukTaskList({
idPrefix: "worldpay-tasks",
items: taskList,
classes: "task-list"
}) }}

{% endif %}

{% endblock %}

0 comments on commit d1ab34b

Please sign in to comment.