-
Notifications
You must be signed in to change notification settings - Fork 15
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: Validations for Worldpay credentials for a MOTO gateway account #4371
Changes from 2 commits
f6b71fd
9841a0d
f0b394c
05c37a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
const { response } = require('@utils/response') | ||
const formatSimplifiedAccountPathsFor = require('../../../../../utils/simplified-account/format/format-simplified-account-paths-for') | ||
const paths = require('@root/paths') | ||
const { body, validationResult } = require('express-validator') | ||
const formatValidationErrors = require('@utils/simplified-account/format/format-validation-errors') | ||
|
||
function get (req, res) { | ||
return response(req, res, 'simplified-account/settings/worldpay-details/credentials', { | ||
backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, | ||
req.service.externalId, req.account.type) | ||
}) | ||
} | ||
|
||
const worldpayCredentialsValidations = [ | ||
body('merchantCode').not().isEmpty().withMessage('Enter your merchant code').bail() | ||
.custom((value, { req }) => { | ||
const merchantCode = req.body.merchantCode | ||
if (req.account.allowMoto && !merchantCode.endsWith('MOTO') && !merchantCode.endsWith('MOTOGBP')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the |
||
throw new Error('Enter a MOTO merchant code. MOTO payments are enabled for the account') | ||
} | ||
DomBelcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}), | ||
body('username').not().isEmpty().withMessage('Enter your username'), | ||
body('password').not().isEmpty().withMessage('Enter your password') | ||
] | ||
|
||
async function post (req, res) { | ||
await Promise.all(worldpayCredentialsValidations.map(validation => validation.run(req))) | ||
const validationErrors = validationResult(req) | ||
if (!validationErrors.isEmpty()) { | ||
const formattedErrors = formatValidationErrors(validationErrors) | ||
return errorResponse(req, res, { | ||
summary: formattedErrors.errorSummary, | ||
formErrors: formattedErrors.formErrors | ||
}) | ||
} | ||
} | ||
|
||
const errorResponse = (req, res, errors) => { | ||
return response(req, res, 'simplified-account/settings/worldpay-details/credentials', { | ||
errors, | ||
merchantCode: req.body.merchantCode, | ||
username: req.body.username, | ||
password: req.body.password, | ||
backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, | ||
req.service.externalId, req.account.type) | ||
}) | ||
} | ||
|
||
module.exports = { | ||
get, | ||
post | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
const ControllerTestBuilder = require('@test/test-helpers/simplified-account/controllers/ControllerTestBuilder.class') | ||
const Service = require('@models/Service.class') | ||
const GatewayAccount = require('@models/GatewayAccount.class') | ||
const sinon = require('sinon') | ||
const { expect } = require('chai') | ||
const formatSimplifiedAccountPathsFor = require('../../../../../utils/simplified-account/format/format-simplified-account-paths-for') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alias this |
||
const paths = require('@root/paths') | ||
|
||
const ACCOUNT_TYPE = 'live' | ||
const SERVICE_ID = 'service-id-123abc' | ||
|
||
const mockResponse = sinon.spy() | ||
|
||
const { req, res, nextRequest, call } = new ControllerTestBuilder('@controllers/simplified-account/settings/worldpay-details/credentials/worldpay-credentials.controller') | ||
.withService(new Service({ | ||
external_id: SERVICE_ID | ||
})) | ||
.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/credentials', () => { | ||
describe('get', () => { | ||
before(() => { | ||
call('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/credentials') | ||
}) | ||
|
||
it('should pass context data to the response method', () => { | ||
expect(mockResponse.args[0][3]).to.have.property('backLink').to.equal( | ||
formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, SERVICE_ID, ACCOUNT_TYPE) | ||
) | ||
}) | ||
}) | ||
|
||
describe('post', () => { | ||
describe('for MOTO gateway accounts', () => { | ||
describe('when submitting invalid data', () => { | ||
it('should render the form with validation errors when input fields are missing', async () => { | ||
nextRequest({ | ||
body: { | ||
merchantCode: '', | ||
username: '', | ||
password: '' | ||
} | ||
}) | ||
await call('post') | ||
|
||
expect(mockResponse).to.have.been.calledWith( | ||
sinon.match.any, | ||
sinon.match.any, | ||
'simplified-account/settings/worldpay-details/credentials', | ||
{ | ||
errors: { | ||
summary: [ | ||
{ text: 'Enter your username', href: '#username' }, | ||
{ text: 'Enter your password', href: '#password' }, | ||
{ text: 'Enter your merchant code', href: '#merchant-code' } | ||
], | ||
formErrors: { | ||
username: 'Enter your username', | ||
password: 'Enter your password', // pragma: allowlist secret | ||
merchantCode: 'Enter your merchant code' | ||
} | ||
}, | ||
merchantCode: '', | ||
username: '', | ||
password: '', | ||
backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, SERVICE_ID, ACCOUNT_TYPE) | ||
}) | ||
}) | ||
it('should render the form with MOTO validation error when merchant code is invalid', async () => { | ||
nextRequest({ | ||
body: { | ||
merchantCode: 'invalid-merchant-code', | ||
username: 'username', | ||
password: 'password' // pragma: allowlist secret | ||
} | ||
}) | ||
await call('post') | ||
|
||
expect(mockResponse).to.have.been.calledWith( | ||
sinon.match.any, | ||
sinon.match.any, | ||
'simplified-account/settings/worldpay-details/credentials', | ||
{ | ||
errors: { | ||
summary: [ | ||
{ text: 'Enter a MOTO merchant code. MOTO payments are enabled for the account', href: '#merchant-code' } | ||
], | ||
formErrors: { | ||
merchantCode: 'Enter a MOTO merchant code. MOTO payments are enabled for the account' | ||
} | ||
}, | ||
merchantCode: 'invalid-merchant-code', | ||
username: 'username', | ||
password: 'password', // pragma: allowlist secret | ||
backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.index, SERVICE_ID, ACCOUNT_TYPE) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ const sinon = require('sinon') | |
const { expect } = require('chai') | ||
const Service = require('@models/Service.class') | ||
const GatewayAccount = require('@models/GatewayAccount.class') | ||
const formatSimplifiedAccountPathsFor = require('../../../../utils/simplified-account/format/format-simplified-account-paths-for') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alias this |
||
const paths = require('@root/paths') | ||
|
||
const mockResponse = sinon.spy() | ||
|
||
|
@@ -13,7 +15,6 @@ const { req, res, call } = new ControllerTestBuilder('@controllers/simplified-ac | |
.withService(new Service({ | ||
external_id: SERVICE_ID | ||
})) | ||
.withAccountType(ACCOUNT_TYPE) | ||
.withAccount(new GatewayAccount({ | ||
type: ACCOUNT_TYPE, | ||
allow_moto: true, | ||
|
@@ -50,7 +51,8 @@ describe('Controller: settings/worldpay-details', () => { | |
|
||
it('should pass context data to the response method', () => { | ||
const tasks = [{ | ||
href: '#', | ||
href: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.worldpayDetails.credentials, | ||
SERVICE_ID, ACCOUNT_TYPE), | ||
id: 'worldpay-credentials', | ||
linkText: 'Link your Worldpay account with GOV.UK Pay', | ||
complete: false | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
{% extends "../settings-layout.njk" %} | ||
|
||
{% block settingsPageTitle %} | ||
Worldpay details | ||
{% endblock %} | ||
|
||
{% block settingsContent %} | ||
|
||
{{ govukBackLink({ | ||
text: "Back", | ||
href: backLink | ||
}) }} | ||
|
||
{% if errors %} | ||
{{ govukErrorSummary({ | ||
titleText: "There is a problem", | ||
errorList: errors.summary | ||
}) }} | ||
{% endif %} | ||
|
||
<h1 class="govuk-heading-l">Your Worldpay credentials</h1> | ||
|
||
<p class="govuk-body govuk-!-margin-bottom-6 hint-and-body-width"> | ||
Go to your <a class="govuk-link" href="https://secure.worldpay.com/sso/public/auth/login.html">Worldpay account</a> | ||
to get the details you need to enter here or | ||
<a class="govuk-link" href="https://docs.payments.service.gov.uk/switching_to_live/set_up_a_live_worldpay_account/#connect-your-live-account-to-worldpay">read more in our documentation</a>. | ||
</p> | ||
|
||
<form id="credentials-form" method="post" novalidate> | ||
<input id="csrf" name="csrfToken" type="hidden" value="{{csrf}}" /> | ||
|
||
{{ govukInput({ | ||
label: { | ||
text: 'Merchant code' | ||
}, | ||
id: 'merchantCode', | ||
name: 'merchantCode', | ||
classes: 'govuk-input--width-20', | ||
type: 'text', | ||
value: merchantCode, | ||
errorMessage: form.errors.merchantId and { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be |
||
text: form.errors.merchantId | ||
} | ||
}) | ||
}} | ||
|
||
{{ govukInput({ | ||
label: { | ||
text: 'Username' | ||
}, | ||
id: 'username', | ||
name: 'username', | ||
classes: 'govuk-input--width-20', | ||
type: 'text', | ||
value: username, | ||
errorMessage: form.errors.username and { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be |
||
text: form.errors.username | ||
}, | ||
autocomplete: 'off' | ||
}) | ||
}} | ||
|
||
{{ govukInput({ | ||
label: { | ||
text: 'Password' | ||
}, | ||
id: 'password', | ||
name: 'password', | ||
classes: "govuk-input--width-20", | ||
type: 'password', | ||
value: password, | ||
errorMessage: form.errors.password and { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be |
||
text: form.errors.password | ||
}, | ||
autocomplete: 'off' | ||
}) | ||
}} | ||
|
||
{{ | ||
govukButton({ | ||
text: 'Save credentials', | ||
attributes: { | ||
id: 'submitCredentials' | ||
} | ||
}) | ||
}} | ||
</form> | ||
{% endblock %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may as well use the alias for this