@defra-fish/recurring-payments-job 1.58.0-rc.1 → 1.58.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra-fish/recurring-payments-job",
3
- "version": "1.58.0-rc.1",
3
+ "version": "1.58.0-rc.11",
4
4
  "description": "Rod Licensing Recurring Payments Job",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,10 +36,10 @@
36
36
  "test": "echo \"Error: run tests from root\" && exit 1"
37
37
  },
38
38
  "dependencies": {
39
- "@defra-fish/business-rules-lib": "1.58.0-rc.1",
40
- "@defra-fish/connectors-lib": "1.58.0-rc.1",
39
+ "@defra-fish/business-rules-lib": "1.58.0-rc.11",
40
+ "@defra-fish/connectors-lib": "1.58.0-rc.11",
41
41
  "commander": "^7.2.0",
42
42
  "moment-timezone": "^0.5.34"
43
43
  },
44
- "gitHead": "e00aea1ef5780d2c91cc88c39483fd73bfdbc1fb"
44
+ "gitHead": "07d834ede6126348ee4073360e20fbddac50041c"
45
45
  }
@@ -1,5 +1,6 @@
1
1
  import { salesApi } from '@defra-fish/connectors-lib'
2
2
  import { processRecurringPayments } from '../recurring-payments-processor.js'
3
+ import { sendPayment } from '../services/govuk-pay-service.js'
3
4
 
4
5
  jest.mock('@defra-fish/business-rules-lib')
5
6
  jest.mock('@defra-fish/connectors-lib', () => ({
@@ -8,9 +9,12 @@ jest.mock('@defra-fish/connectors-lib', () => ({
8
9
  preparePermissionDataForRenewal: jest.fn(() => ({
9
10
  licensee: { countryCode: 'GB-ENG' }
10
11
  })),
11
- createTransaction: jest.fn()
12
+ createTransaction: jest.fn(() => ({
13
+ cost: 30
14
+ }))
12
15
  }
13
16
  }))
17
+ jest.mock('../services/govuk-pay-service.js')
14
18
 
15
19
  describe('recurring-payments-processor', () => {
16
20
  beforeEach(() => {
@@ -53,7 +57,7 @@ describe('recurring-payments-processor', () => {
53
57
 
54
58
  it('prepares the data for found recurring payments', async () => {
55
59
  const referenceNumber = Symbol('reference')
56
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber } } } }])
60
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment(referenceNumber)])
57
61
 
58
62
  await processRecurringPayments()
59
63
 
@@ -61,7 +65,7 @@ describe('recurring-payments-processor', () => {
61
65
  })
62
66
 
63
67
  it('creates a transaction with the correct data', async () => {
64
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber: '1' } } } }])
68
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
65
69
 
66
70
  const isLicenceForYou = Symbol('isLicenceForYou')
67
71
  const isRenewal = Symbol('isRenewal')
@@ -108,7 +112,7 @@ describe('recurring-payments-processor', () => {
108
112
  })
109
113
 
110
114
  it('strips the concession name returned by preparePermissionDataForRenewal before passing to createTransaction', async () => {
111
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber: '1' } } } }])
115
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
112
116
 
113
117
  salesApi.preparePermissionDataForRenewal.mockReturnValueOnce({
114
118
  licensee: {
@@ -141,7 +145,7 @@ describe('recurring-payments-processor', () => {
141
145
  })
142
146
 
143
147
  it('assigns the correct startDate when licenceStartTime is present', async () => {
144
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber: '1' } } } }])
148
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
145
149
 
146
150
  salesApi.preparePermissionDataForRenewal.mockReturnValueOnce({
147
151
  licensee: { countryCode: 'GB-ENG' },
@@ -159,7 +163,7 @@ describe('recurring-payments-processor', () => {
159
163
  })
160
164
 
161
165
  it('assigns the correct startDate when licenceStartTime is not present', async () => {
162
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber: '1' } } } }])
166
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
163
167
 
164
168
  salesApi.preparePermissionDataForRenewal.mockReturnValueOnce({
165
169
  licensee: { countryCode: 'GB-ENG' },
@@ -176,7 +180,7 @@ describe('recurring-payments-processor', () => {
176
180
  })
177
181
 
178
182
  it('raises an error if createTransaction fails', async () => {
179
- salesApi.getDueRecurringPayments.mockReturnValueOnce([{ expanded: { activePermission: { entity: { referenceNumber: '1' } } } }])
183
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
180
184
  const error = 'Wuh-oh!'
181
185
  salesApi.createTransaction.mockImplementationOnce(() => {
182
186
  throw new Error(error)
@@ -185,6 +189,34 @@ describe('recurring-payments-processor', () => {
185
189
  await expect(processRecurringPayments()).rejects.toThrowError(error)
186
190
  })
187
191
 
192
+ it('prepares and sends the payment request', async () => {
193
+ const agreementId = Symbol('agreementId')
194
+ const transactionId = Symbol('transactionId')
195
+
196
+ salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment('foo', agreementId)])
197
+
198
+ salesApi.preparePermissionDataForRenewal.mockReturnValueOnce({
199
+ licensee: { countryCode: 'GB-ENG' }
200
+ })
201
+
202
+ salesApi.createTransaction.mockReturnValueOnce({
203
+ cost: 50,
204
+ id: transactionId
205
+ })
206
+
207
+ const expectedData = {
208
+ amount: 5000,
209
+ description: 'The recurring card payment for your rod fishing licence',
210
+ reference: transactionId,
211
+ authorisation_mode: 'agreement',
212
+ agreement_id: agreementId
213
+ }
214
+
215
+ await processRecurringPayments()
216
+
217
+ expect(sendPayment).toHaveBeenCalledWith(expectedData)
218
+ })
219
+
188
220
  describe.each([2, 3, 10])('if there are %d recurring payments', count => {
189
221
  it('prepares the data for each one', async () => {
190
222
  const references = []
@@ -194,7 +226,7 @@ describe('recurring-payments-processor', () => {
194
226
 
195
227
  const mockGetDueRecurringPayments = []
196
228
  references.forEach(reference => {
197
- mockGetDueRecurringPayments.push({ expanded: { activePermission: { entity: { referenceNumber: reference } } } })
229
+ mockGetDueRecurringPayments.push(getMockDueRecurringPayment(reference))
198
230
  })
199
231
  salesApi.getDueRecurringPayments.mockReturnValueOnce(mockGetDueRecurringPayments)
200
232
 
@@ -211,7 +243,7 @@ describe('recurring-payments-processor', () => {
211
243
  it('creates a transaction for each one', async () => {
212
244
  const mockGetDueRecurringPayments = []
213
245
  for (let i = 0; i < count; i++) {
214
- mockGetDueRecurringPayments.push({ expanded: { activePermission: { entity: { referenceNumber: i } } } })
246
+ mockGetDueRecurringPayments.push(getMockDueRecurringPayment(i))
215
247
  }
216
248
  salesApi.getDueRecurringPayments.mockReturnValueOnce(mockGetDueRecurringPayments)
217
249
 
@@ -241,5 +273,53 @@ describe('recurring-payments-processor', () => {
241
273
 
242
274
  expect(salesApi.createTransaction.mock.calls).toEqual(expectedData)
243
275
  })
276
+
277
+ it('sends a payment for each one', async () => {
278
+ const mockGetDueRecurringPayments = []
279
+ const agreementIds = []
280
+ for (let i = 0; i < count; i++) {
281
+ const agreementId = Symbol(`agreementId${1}`)
282
+ agreementIds.push(agreementId)
283
+ mockGetDueRecurringPayments.push(getMockDueRecurringPayment(i, agreementId))
284
+ }
285
+ salesApi.getDueRecurringPayments.mockReturnValueOnce(mockGetDueRecurringPayments)
286
+
287
+ const permits = []
288
+ for (let i = 0; i < count; i++) {
289
+ permits.push(Symbol(`permit${i}`))
290
+ }
291
+
292
+ permits.forEach((permit, i) => {
293
+ salesApi.preparePermissionDataForRenewal.mockReturnValueOnce({
294
+ licensee: { countryCode: 'GB-ENG' }
295
+ })
296
+
297
+ salesApi.createTransaction.mockReturnValueOnce({
298
+ cost: i,
299
+ id: permit
300
+ })
301
+ })
302
+
303
+ const expectedData = []
304
+ permits.forEach((permit, i) => {
305
+ expectedData.push([
306
+ {
307
+ amount: i * 100,
308
+ description: 'The recurring card payment for your rod fishing licence',
309
+ reference: permit,
310
+ authorisation_mode: 'agreement',
311
+ agreement_id: agreementIds[i]
312
+ }
313
+ ])
314
+ })
315
+
316
+ await processRecurringPayments()
317
+ expect(sendPayment.mock.calls).toEqual(expectedData)
318
+ })
244
319
  })
245
320
  })
321
+
322
+ const getMockDueRecurringPayment = (referenceNumber = '123', agreementId = '456') => ({
323
+ entity: { agreementId },
324
+ expanded: { activePermission: { entity: { referenceNumber } } }
325
+ })
@@ -1,6 +1,7 @@
1
1
  import moment from 'moment-timezone'
2
2
  import { SERVICE_LOCAL_TIME } from '@defra-fish/business-rules-lib'
3
3
  import { salesApi } from '@defra-fish/connectors-lib'
4
+ import { sendPayment } from './services/govuk-pay-service.js'
4
5
 
5
6
  export const processRecurringPayments = async () => {
6
7
  if (process.env.RUN_RECURRING_PAYMENTS?.toLowerCase() === 'true') {
@@ -16,17 +17,30 @@ export const processRecurringPayments = async () => {
16
17
 
17
18
  const processRecurringPayment = async record => {
18
19
  const referenceNumber = record.expanded.activePermission.entity.referenceNumber
20
+ const agreementId = record.entity.agreementId
21
+ const transaction = await createNewTransaction(referenceNumber)
22
+ takeRecurringPayment(agreementId, transaction)
23
+ }
24
+
25
+ const createNewTransaction = async referenceNumber => {
19
26
  const transactionData = await processPermissionData(referenceNumber)
20
27
  console.log('Creating new transaction based on', referenceNumber)
21
28
  try {
22
29
  const response = await salesApi.createTransaction(transactionData)
23
30
  console.log('New transaction created:', response)
31
+ return response
24
32
  } catch (e) {
25
33
  console.log('Error creating transaction', JSON.stringify(transactionData))
26
34
  throw e
27
35
  }
28
36
  }
29
37
 
38
+ const takeRecurringPayment = (agreementId, transaction) => {
39
+ const preparedPayment = preparePayment(agreementId, transaction)
40
+ console.log('Requesting payment:', preparedPayment)
41
+ sendPayment(preparedPayment)
42
+ }
43
+
30
44
  const processPermissionData = async referenceNumber => {
31
45
  console.log('Preparing data based on', referenceNumber)
32
46
  const data = await salesApi.preparePermissionDataForRenewal(referenceNumber)
@@ -56,3 +70,15 @@ const prepareStartDate = permission => {
56
70
  .utc()
57
71
  .toISOString()
58
72
  }
73
+
74
+ const preparePayment = (agreementId, transaction) => {
75
+ const result = {
76
+ amount: Math.round(transaction.cost * 100),
77
+ description: 'The recurring card payment for your rod fishing licence',
78
+ reference: transaction.id,
79
+ authorisation_mode: 'agreement',
80
+ agreement_id: agreementId
81
+ }
82
+
83
+ return result
84
+ }
@@ -0,0 +1,23 @@
1
+ import { sendPayment } from '../govuk-pay-service.js'
2
+ import { govUkPayApi } from '@defra-fish/connectors-lib'
3
+
4
+ jest.mock('@defra-fish/connectors-lib')
5
+
6
+ describe('govuk-pay-service', () => {
7
+ describe('sendPayment', () => {
8
+ it('should send provided payload data to Gov.UK Pay', async () => {
9
+ govUkPayApi.createPayment.mockResolvedValue({
10
+ ok: true,
11
+ json: jest.fn().mockResolvedValue({ success: true, paymentId: 'abc123' })
12
+ })
13
+ const unique = Symbol('payload')
14
+ const payload = {
15
+ amount: '100',
16
+ description: 'The recurring card payment for your rod fishing licence',
17
+ reference: unique
18
+ }
19
+ await sendPayment(payload)
20
+ expect(govUkPayApi.createPayment).toHaveBeenCalledWith(payload, true)
21
+ })
22
+ })
23
+ })
@@ -0,0 +1,5 @@
1
+ import { govUkPayApi } from '@defra-fish/connectors-lib'
2
+
3
+ export const sendPayment = preparedPayment => {
4
+ govUkPayApi.createPayment(preparedPayment, true)
5
+ }