Skip to content

Commit cc63848

Browse files
authored
Merge pull request #309 from DEFRA/feat/df-634-enhanced-logging
feat/df-634: Additional logging
2 parents 2c18502 + e8caa51 commit cc63848

6 files changed

Lines changed: 173 additions & 30 deletions

File tree

src/server/plugins/engine/components/PaymentField.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export class PaymentField extends FormComponent {
225225
description,
226226
payCallbackUrl,
227227
reference,
228+
isLivePayment,
228229
{ formId, slug }
229230
)
230231

@@ -276,7 +277,10 @@ export class PaymentField extends FormComponent {
276277
/**
277278
* @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle
278279
*/
279-
const status = await paymentService.getPaymentStatus(paymentId)
280+
const status = await paymentService.getPaymentStatus(
281+
paymentId,
282+
isLivePayment
283+
)
280284

281285
PaymentSubmissionError.checkPaymentAmount(
282286
status.amount,

src/server/plugins/engine/routes/payment-helper.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,48 @@ export async function getPaymentContext(request, uuid) {
2828

2929
const apiKey = getPaymentApiKey(isLivePayment, formId)
3030
const paymentService = new PaymentService(apiKey)
31-
const paymentStatus = await paymentService.getPaymentStatus(paymentId)
31+
const paymentStatus = await paymentService.getPaymentStatus(
32+
paymentId,
33+
isLivePayment
34+
)
3235

3336
return { session, sessionKey, paymentStatus }
3437
}
3538

39+
/**
40+
* Builds an object for logging payment information
41+
* @param {string} action
42+
* @param {string} outcome
43+
* @param {string} reason
44+
* @param {boolean} isLivePayment
45+
* @param {string} paymentId
46+
*/
47+
export function buildPaymentInfo(
48+
action,
49+
outcome,
50+
reason,
51+
isLivePayment,
52+
paymentId
53+
) {
54+
return {
55+
event: {
56+
category: 'payment',
57+
action,
58+
outcome,
59+
reason,
60+
type: isLivePayment ? 'live' : 'test',
61+
reference: paymentId
62+
}
63+
}
64+
}
65+
66+
/**
67+
* @param {number} amount
68+
*/
69+
export function convertPenceToPounds(amount) {
70+
return `${amount / 100}`
71+
}
72+
3673
/**
3774
* @import { Request } from '@hapi/hapi'
3875
* @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'

src/server/plugins/engine/routes/payment-helper.test.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { getPaymentContext } from '~/src/server/plugins/engine/routes/payment-helper.js'
1+
import {
2+
buildPaymentInfo,
3+
getPaymentContext
4+
} from '~/src/server/plugins/engine/routes/payment-helper.js'
25
import { get } from '~/src/server/services/httpService.js'
36

47
jest.mock('~/src/server/services/httpService.ts')
@@ -83,6 +86,46 @@ describe('payment helper', () => {
8386
sessionKey: 'payment-5a54c2fe-da49-4202-8cd3-2121eaca03c3'
8487
})
8588
})
89+
90+
it('should create logging info for a test payment', () => {
91+
const res = buildPaymentInfo(
92+
'action1',
93+
'outcome1',
94+
'reason1',
95+
false,
96+
'pay-123'
97+
)
98+
expect(res).toEqual({
99+
event: {
100+
category: 'payment',
101+
action: 'action1',
102+
outcome: 'outcome1',
103+
reason: 'reason1',
104+
type: 'test',
105+
reference: 'pay-123'
106+
}
107+
})
108+
})
109+
110+
it('should create logging info for a live payment', () => {
111+
const res = buildPaymentInfo(
112+
'action2',
113+
'outcome2',
114+
'reason2',
115+
true,
116+
'pay-123'
117+
)
118+
expect(res).toEqual({
119+
event: {
120+
category: 'payment',
121+
action: 'action2',
122+
outcome: 'outcome2',
123+
reason: 'reason2',
124+
type: 'live',
125+
reference: 'pay-123'
126+
}
127+
})
128+
})
86129
})
87130

88131
/**

src/server/plugins/engine/routes/payment.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@ import Boom from '@hapi/boom'
22
import { StatusCodes } from 'http-status-codes'
33
import Joi from 'joi'
44

5+
import { createLogger } from '~/src/server/common/helpers/logging/logger.js'
56
import { EXTERNAL_STATE_APPENDAGE } from '~/src/server/constants.js'
6-
import { getPaymentContext } from '~/src/server/plugins/engine/routes/payment-helper.js'
7+
import {
8+
buildPaymentInfo,
9+
convertPenceToPounds,
10+
getPaymentContext
11+
} from '~/src/server/plugins/engine/routes/payment-helper.js'
712

813
export const PAYMENT_RETURN_PATH = '/payment-callback'
914
export const PAYMENT_SESSION_PREFIX = 'payment-'
1015

16+
const logger = createLogger()
17+
1118
/**
1219
* Flash form component state after successful payment
1320
* @param {Request} request - the request
@@ -48,6 +55,42 @@ export function getRoutes() {
4855
return [getReturnRoute()]
4956
}
5057

58+
/**
59+
* Logs successful payment
60+
* @param {PaymentSessionData} session - the session data
61+
* @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay
62+
*/
63+
function logPaymentSuccess(session, paymentStatus) {
64+
logger.info(
65+
buildPaymentInfo(
66+
'pre-auth',
67+
'success',
68+
`${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,
69+
session.isLivePayment,
70+
paymentStatus.paymentId
71+
),
72+
`[payment] Successful pre-auth for paymentId=${paymentStatus.paymentId}`
73+
)
74+
}
75+
76+
/**
77+
* Logs failed/cancelled payment
78+
* @param {PaymentSessionData} session - the session data
79+
* @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay
80+
*/
81+
function logPaymentFailure(session, paymentStatus) {
82+
logger.info(
83+
buildPaymentInfo(
84+
'pre-auth',
85+
'failed/cancelled',
86+
`${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,
87+
session.isLivePayment,
88+
paymentStatus.paymentId
89+
),
90+
`[payment] Failed/cancelled pre-auth for paymentId=${paymentStatus.paymentId}`
91+
)
92+
}
93+
5194
/**
5295
* Handles successful payment states (capturable/success)
5396
* @param {Request} request - the request
@@ -98,6 +141,7 @@ function getReturnRoute() {
98141
switch (status) {
99142
case 'capturable':
100143
case 'success':
144+
logPaymentSuccess(session, paymentStatus)
101145
return handlePaymentSuccess(
102146
request,
103147
h,
@@ -109,6 +153,7 @@ function getReturnRoute() {
109153
case 'cancelled':
110154
case 'failed':
111155
case 'error':
156+
logPaymentFailure(session, paymentStatus)
112157
return handlePaymentFailure(request, h, session, sessionKey)
113158

114159
case 'created':

src/server/plugins/payment/service.js

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { StatusCodes } from 'http-status-codes'
22

33
import { createLogger } from '~/src/server/common/helpers/logging/logger.js'
4+
import {
5+
buildPaymentInfo,
6+
convertPenceToPounds
7+
} from '~/src/server/plugins/engine/routes/payment-helper.js'
48
import { get, post, postJson } from '~/src/server/services/httpService.js'
59

610
const PAYMENT_BASE_URL = 'https://publicapi.payments.service.gov.uk'
@@ -35,9 +39,17 @@ export class PaymentService {
3539
* @param {string} description
3640
* @param {string} returnUrl
3741
* @param {string} reference
42+
* @param {boolean} isLivePayment
3843
* @param {{ formId: string, slug: string }} metadata
3944
*/
40-
async createPayment(amount, description, returnUrl, reference, metadata) {
45+
async createPayment(
46+
amount,
47+
description,
48+
returnUrl,
49+
reference,
50+
isLivePayment,
51+
metadata
52+
) {
4153
const response = await this.postToPayProvider({
4254
amount,
4355
description,
@@ -48,15 +60,13 @@ export class PaymentService {
4860
})
4961

5062
logger.info(
51-
{
52-
event: {
53-
category: 'payment',
54-
action: 'create-payment',
55-
outcome: 'success',
56-
reason: `amount=${amount}`,
57-
reference: response.payment_id
58-
}
59-
},
63+
buildPaymentInfo(
64+
'create-payment',
65+
'success',
66+
`amount=${convertPenceToPounds(amount)}`,
67+
isLivePayment,
68+
response.payment_id
69+
),
6070
`[payment] Created payment and user taken to enter pre-auth details for paymentId=${response.payment_id}`
6171
)
6272

@@ -68,9 +78,10 @@ export class PaymentService {
6878

6979
/**
7080
* @param {string} paymentId
81+
* @param {boolean} isLivePayment
7182
* @returns {Promise<GetPaymentResponse>}
7283
*/
73-
async getPaymentStatus(paymentId) {
84+
async getPaymentStatus(paymentId, isLivePayment) {
7485
const getByType = /** @type {typeof get<GetPaymentApiResponse>} */ (get)
7586

7687
try {
@@ -92,18 +103,15 @@ export class PaymentService {
92103

93104
const state = response.payload.state
94105
logger.info(
95-
{
96-
event: {
97-
category: 'payment',
98-
action: 'get-payment-status',
99-
outcome:
100-
state.status === 'capturable' || state.status === 'success'
101-
? 'success'
102-
: 'failure',
103-
reason: `status:${state.status} code:${state.code ?? 'N/A'} message:${state.message ?? 'N/A'}`,
104-
reference: paymentId
105-
}
106-
},
106+
buildPaymentInfo(
107+
'get-payment-status',
108+
state.status === 'capturable' || state.status === 'success'
109+
? 'success'
110+
: 'failure',
111+
`status:${state.status} code:${state.code ?? 'N/A'} message:${state.message ?? 'N/A'}`,
112+
isLivePayment,
113+
paymentId
114+
),
107115
`[payment] Got payment status for paymentId=${paymentId}: status=${state.status}`
108116
)
109117

@@ -151,7 +159,7 @@ export class PaymentService {
151159
category: 'payment',
152160
action: 'capture-payment',
153161
outcome: 'success',
154-
reason: `amount=${amount}`,
162+
reason: `amount=${convertPenceToPounds(amount)}`,
155163
reference: paymentId
156164
}
157165
},

src/server/plugins/payment/service.test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('payment service', () => {
4141
'Payment description',
4242
returnUrl,
4343
referenceNumber,
44+
false,
4445
metadata
4546
)
4647
expect(payment.paymentId).toBe('payment-id-12345')
@@ -61,6 +62,7 @@ describe('payment service', () => {
6162
'Payment description',
6263
returnUrl,
6364
referenceNumber,
65+
false,
6466
metadata
6567
)
6668
).rejects.toThrow('internal creation error')
@@ -90,6 +92,7 @@ describe('payment service', () => {
9092
'Payment description',
9193
returnUrl,
9294
referenceNumber,
95+
false,
9396
metadata
9497
)
9598
).rejects.toThrow('Failed to create payment')
@@ -119,7 +122,10 @@ describe('payment service', () => {
119122
error: undefined
120123
})
121124

122-
const paymentStatus = await service.getPaymentStatus('payment-id-12345')
125+
const paymentStatus = await service.getPaymentStatus(
126+
'payment-id-12345',
127+
false
128+
)
123129
expect(paymentStatus.paymentId).toBe('payment-id-12345')
124130
expect(paymentStatus._links.next_url?.href).toBe(
125131
'http://next-url-href/payment'
@@ -137,7 +143,7 @@ describe('payment service', () => {
137143
})
138144

139145
await expect(() =>
140-
service.getPaymentStatus('payment-id-12345')
146+
service.getPaymentStatus('payment-id-12345', false)
141147
).rejects.toThrow('Failed to get payment status: some-error')
142148
})
143149
})

0 commit comments

Comments
 (0)