@defra-fish/recurring-payments-job 1.63.0-rc.17 → 1.63.0-rc.18
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.63.0-rc.
|
|
3
|
+
"version": "1.63.0-rc.18",
|
|
4
4
|
"description": "Rod Licensing Recurring Payments Job",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@defra-fish/business-rules-lib": "1.63.0-rc.
|
|
40
|
-
"@defra-fish/connectors-lib": "1.63.0-rc.
|
|
39
|
+
"@defra-fish/business-rules-lib": "1.63.0-rc.18",
|
|
40
|
+
"@defra-fish/connectors-lib": "1.63.0-rc.18",
|
|
41
41
|
"commander": "^7.2.0",
|
|
42
42
|
"debug": "^4.3.3",
|
|
43
43
|
"moment-timezone": "^0.5.34"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "d21455813e5eddb476706bdfb49a75ea2f1c9404"
|
|
46
46
|
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import commander from 'commander'
|
|
2
|
-
import {
|
|
3
|
-
import { processRecurringPayments } from '../recurring-payments-processor.js'
|
|
2
|
+
import { execute } from '../recurring-payments-processor.js'
|
|
4
3
|
import fs from 'fs'
|
|
5
4
|
|
|
6
5
|
jest.useFakeTimers()
|
|
7
|
-
|
|
8
6
|
jest.mock('../recurring-payments-processor.js')
|
|
9
|
-
jest.mock('
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
jest.mock('fs', () => ({
|
|
8
|
+
readFileSync: jest.fn(),
|
|
9
|
+
promises: {
|
|
10
|
+
readFile: jest.fn()
|
|
13
11
|
}
|
|
14
12
|
}))
|
|
15
13
|
|
|
16
|
-
jest.mock('fs')
|
|
17
14
|
describe('recurring-payments-job', () => {
|
|
18
15
|
beforeAll(() => {
|
|
19
16
|
fs.readFileSync.mockReturnValue(JSON.stringify({ name: 'recurring-payments-test', version: '1.0.0' }))
|
|
@@ -24,81 +21,6 @@ describe('recurring-payments-job', () => {
|
|
|
24
21
|
commander.args = ['test']
|
|
25
22
|
})
|
|
26
23
|
|
|
27
|
-
it('initialises airbrake', () => {
|
|
28
|
-
jest.isolateModules(() => {
|
|
29
|
-
require('../recurring-payments-job.js')
|
|
30
|
-
expect(airbrake.initialise).toHaveBeenCalled()
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('flushes airbrake before script ends', () => {
|
|
35
|
-
jest.isolateModules(() => {
|
|
36
|
-
require('../recurring-payments-job.js')
|
|
37
|
-
expect(airbrake.flush).toHaveBeenCalled()
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it("doesn't flush airbrake before processRecurringPayments has been called", () => {
|
|
42
|
-
jest.isolateModules(() => {
|
|
43
|
-
processRecurringPayments.mockImplementationOnce(() => {
|
|
44
|
-
expect(airbrake.flush).not.toHaveBeenCalled()
|
|
45
|
-
})
|
|
46
|
-
require('../recurring-payments-job.js')
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it.each([
|
|
51
|
-
['SIGINT', 130],
|
|
52
|
-
['SIGTERM', 137]
|
|
53
|
-
])('flushes airbrake on %s signal', (signal, code) => {
|
|
54
|
-
jest.isolateModules(() => {
|
|
55
|
-
// setup a delay so script doesn't call processRecurringPayments and exit naturally
|
|
56
|
-
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '1'
|
|
57
|
-
const signalCallbacks = {}
|
|
58
|
-
jest.spyOn(process, 'on')
|
|
59
|
-
jest.spyOn(process, 'exit')
|
|
60
|
-
process.on.mockImplementation((signalToken, callback) => {
|
|
61
|
-
signalCallbacks[signalToken] = callback
|
|
62
|
-
})
|
|
63
|
-
process.exit.mockImplementation(() => {
|
|
64
|
-
// so we don't crash out of the tests!
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
require('../recurring-payments-job.js')
|
|
68
|
-
signalCallbacks[signal]()
|
|
69
|
-
|
|
70
|
-
expect(airbrake.flush).toHaveBeenCalled()
|
|
71
|
-
process.on.mockRestore()
|
|
72
|
-
process.exit.mockRestore()
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it.each([
|
|
77
|
-
['SIGINT', 130],
|
|
78
|
-
['SIGTERM', 137]
|
|
79
|
-
])('calls process.exit on %s signal with %i code', (signal, code) => {
|
|
80
|
-
jest.isolateModules(() => {
|
|
81
|
-
// setup a delay so script doesn't call processRecurringPayments and exit naturally
|
|
82
|
-
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '1'
|
|
83
|
-
const signalCallbacks = {}
|
|
84
|
-
jest.spyOn(process, 'on')
|
|
85
|
-
jest.spyOn(process, 'exit')
|
|
86
|
-
process.on.mockImplementation((signalToken, callback) => {
|
|
87
|
-
signalCallbacks[signalToken] = callback
|
|
88
|
-
})
|
|
89
|
-
process.exit.mockImplementation(() => {
|
|
90
|
-
// so we don't crash out of the tests!
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
require('../recurring-payments-job.js')
|
|
94
|
-
signalCallbacks[signal]()
|
|
95
|
-
|
|
96
|
-
expect(process.exit).toHaveBeenCalledWith(code)
|
|
97
|
-
process.on.mockRestore()
|
|
98
|
-
process.exit.mockRestore()
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
|
|
102
24
|
it('logs startup details including name and version', () => {
|
|
103
25
|
const mockPkg = { name: 'recurring-payments-test', version: '1.2.3' }
|
|
104
26
|
fs.readFileSync.mockReturnValueOnce(JSON.stringify(mockPkg))
|
|
@@ -116,11 +38,11 @@ describe('recurring-payments-job', () => {
|
|
|
116
38
|
})
|
|
117
39
|
})
|
|
118
40
|
|
|
119
|
-
it('calls
|
|
41
|
+
it('calls execute when no delay', () => {
|
|
120
42
|
jest.isolateModules(() => {
|
|
121
43
|
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '0'
|
|
122
44
|
require('../recurring-payments-job.js')
|
|
123
|
-
expect(
|
|
45
|
+
expect(execute).toHaveBeenCalled()
|
|
124
46
|
})
|
|
125
47
|
})
|
|
126
48
|
|
|
@@ -133,12 +55,12 @@ describe('recurring-payments-job', () => {
|
|
|
133
55
|
})
|
|
134
56
|
})
|
|
135
57
|
|
|
136
|
-
it('calls
|
|
58
|
+
it('calls execute when delay', () => {
|
|
137
59
|
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '5'
|
|
138
60
|
jest.isolateModules(() => {
|
|
139
61
|
require('../recurring-payments-job.js')
|
|
140
62
|
jest.advanceTimersByTime(parseInt(process.env.RECURRING_PAYMENTS_LOCAL_DELAY) * 1000)
|
|
141
|
-
expect(
|
|
63
|
+
expect(execute).toHaveBeenCalled()
|
|
142
64
|
})
|
|
143
65
|
})
|
|
144
66
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { salesApi } from '@defra-fish/connectors-lib'
|
|
1
|
+
import { airbrake, salesApi } from '@defra-fish/connectors-lib'
|
|
2
2
|
import { PAYMENT_STATUS, PAYMENT_JOURNAL_STATUS_CODES } from '@defra-fish/business-rules-lib'
|
|
3
|
-
import {
|
|
3
|
+
import { execute } from '../recurring-payments-processor.js'
|
|
4
4
|
import { getPaymentStatus, isGovPayUp, sendPayment } from '../services/govuk-pay-service.js'
|
|
5
5
|
import db from 'debug'
|
|
6
6
|
|
|
@@ -19,6 +19,10 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
|
|
|
19
19
|
}
|
|
20
20
|
}))
|
|
21
21
|
jest.mock('@defra-fish/connectors-lib', () => ({
|
|
22
|
+
airbrake: {
|
|
23
|
+
initialise: jest.fn(),
|
|
24
|
+
flush: jest.fn()
|
|
25
|
+
},
|
|
22
26
|
salesApi: {
|
|
23
27
|
cancelRecurringPayment: jest.fn(),
|
|
24
28
|
createPaymentJournal: jest.fn(),
|
|
@@ -75,49 +79,120 @@ describe('recurring-payments-processor', () => {
|
|
|
75
79
|
global.setTimeout = jest.fn((cb, ms) => cb())
|
|
76
80
|
})
|
|
77
81
|
|
|
82
|
+
it('initialises airbrake', () => {
|
|
83
|
+
jest.isolateModules(async () => {
|
|
84
|
+
require('../recurring-payments-processor.js')
|
|
85
|
+
await execute()
|
|
86
|
+
expect(airbrake.initialise).toHaveBeenCalled()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('flushes airbrake before script ends', () => {
|
|
91
|
+
jest.isolateModules(async () => {
|
|
92
|
+
const { execute } = require('../recurring-payments-processor.js')
|
|
93
|
+
await execute()
|
|
94
|
+
expect(airbrake.flush).toHaveBeenCalled()
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it("doesn't flush airbrake before execute has been called", () => {
|
|
99
|
+
jest.isolateModules(() => {
|
|
100
|
+
require('../recurring-payments-processor.js')
|
|
101
|
+
expect(airbrake.flush).not.toHaveBeenCalled()
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it.each([
|
|
106
|
+
['SIGINT', 130],
|
|
107
|
+
['SIGTERM', 137]
|
|
108
|
+
])('flushes airbrake on %s signal', (signal, code) => {
|
|
109
|
+
jest.isolateModules(() => {
|
|
110
|
+
// setup a delay so script doesn't call processRecurringPayments and exit naturally
|
|
111
|
+
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '1'
|
|
112
|
+
const signalCallbacks = {}
|
|
113
|
+
jest.spyOn(process, 'on')
|
|
114
|
+
jest.spyOn(process, 'exit')
|
|
115
|
+
process.on.mockImplementation((signalToken, callback) => {
|
|
116
|
+
signalCallbacks[signalToken] = callback
|
|
117
|
+
})
|
|
118
|
+
process.exit.mockImplementation(() => {
|
|
119
|
+
// so we don't crash out of the tests!
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
require('../recurring-payments-processor.js')
|
|
123
|
+
signalCallbacks[signal]()
|
|
124
|
+
|
|
125
|
+
expect(airbrake.flush).toHaveBeenCalled()
|
|
126
|
+
process.on.mockRestore()
|
|
127
|
+
process.exit.mockRestore()
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it.each([
|
|
132
|
+
['SIGINT', 130],
|
|
133
|
+
['SIGTERM', 137]
|
|
134
|
+
])('calls process.exit on %s signal with %i code', (signal, code) => {
|
|
135
|
+
jest.isolateModules(() => {
|
|
136
|
+
const signalCallbacks = {}
|
|
137
|
+
jest.spyOn(process, 'on')
|
|
138
|
+
jest.spyOn(process, 'exit')
|
|
139
|
+
process.on.mockImplementation((signalToken, callback) => {
|
|
140
|
+
signalCallbacks[signalToken] = callback
|
|
141
|
+
})
|
|
142
|
+
process.exit.mockImplementation(() => {
|
|
143
|
+
// so we don't crash out of the tests!
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
require('../recurring-payments-job.js')
|
|
147
|
+
signalCallbacks[signal]()
|
|
148
|
+
|
|
149
|
+
expect(process.exit).toHaveBeenCalledWith(code)
|
|
150
|
+
process.on.mockRestore()
|
|
151
|
+
process.exit.mockRestore()
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
78
155
|
it('debug log displays "Recurring Payments job disabled" when env is false', async () => {
|
|
79
156
|
process.env.RUN_RECURRING_PAYMENTS = 'false'
|
|
80
157
|
|
|
81
|
-
await
|
|
158
|
+
await execute()
|
|
82
159
|
|
|
83
160
|
expect(debugLogger).toHaveBeenCalledWith('Recurring Payments job disabled')
|
|
84
161
|
})
|
|
85
162
|
|
|
86
163
|
it('debug log displays "Recurring Payments job enabled" when env is true', async () => {
|
|
87
|
-
await
|
|
164
|
+
await execute()
|
|
88
165
|
|
|
89
166
|
expect(debugLogger).toHaveBeenCalledWith('Recurring Payments job enabled')
|
|
90
167
|
})
|
|
91
168
|
|
|
92
|
-
it('
|
|
169
|
+
it('logs console error if Gov.UK Pay is not healthy', async () => {
|
|
170
|
+
jest.spyOn(console, 'error')
|
|
93
171
|
isGovPayUp.mockResolvedValueOnce(false)
|
|
94
|
-
await
|
|
172
|
+
await execute()
|
|
173
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
174
|
+
expect.objectContaining({
|
|
175
|
+
message: 'Run aborted, Gov.UK Pay health endpoint is reporting problems.'
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
console.error.mockReset()
|
|
95
179
|
})
|
|
96
180
|
|
|
97
181
|
it('get recurring payments is called when env is true', async () => {
|
|
98
182
|
const date = new Date().toISOString().split('T')[0]
|
|
99
183
|
|
|
100
|
-
await
|
|
184
|
+
await execute()
|
|
101
185
|
|
|
102
186
|
expect(salesApi.getDueRecurringPayments).toHaveBeenCalledWith(date)
|
|
103
187
|
})
|
|
104
188
|
|
|
105
189
|
it('debug log displays "Recurring Payments found:" when env is true', async () => {
|
|
106
|
-
await
|
|
190
|
+
await execute()
|
|
107
191
|
|
|
108
192
|
expect(debugLogger).toHaveBeenNthCalledWith(2, 'Recurring Payments found:', [])
|
|
109
193
|
})
|
|
110
194
|
|
|
111
195
|
describe('When RP fetch throws an error...', () => {
|
|
112
|
-
it('processRecurringPayments re-throws the error', async () => {
|
|
113
|
-
const error = new Error('Test error')
|
|
114
|
-
salesApi.getDueRecurringPayments.mockImplementationOnce(() => {
|
|
115
|
-
throw error
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
await expect(processRecurringPayments()).rejects.toThrowError(error)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
196
|
it('calls console.error with error message', async () => {
|
|
122
197
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn())
|
|
123
198
|
const error = new Error('Test error')
|
|
@@ -126,7 +201,7 @@ describe('recurring-payments-processor', () => {
|
|
|
126
201
|
})
|
|
127
202
|
|
|
128
203
|
try {
|
|
129
|
-
await
|
|
204
|
+
await execute()
|
|
130
205
|
} catch {}
|
|
131
206
|
|
|
132
207
|
expect(errorSpy).toHaveBeenCalledWith('Run aborted. Error fetching due recurring payments:', error)
|
|
@@ -140,7 +215,7 @@ describe('recurring-payments-processor', () => {
|
|
|
140
215
|
sendPayment.mockRejectedValueOnce(oopsie)
|
|
141
216
|
|
|
142
217
|
try {
|
|
143
|
-
await
|
|
218
|
+
await execute()
|
|
144
219
|
} catch {}
|
|
145
220
|
|
|
146
221
|
expect(debugLogger).toHaveBeenCalledWith(expect.any(String), oopsie)
|
|
@@ -180,7 +255,7 @@ describe('recurring-payments-processor', () => {
|
|
|
180
255
|
authorisation_mode: 'agreement'
|
|
181
256
|
}
|
|
182
257
|
|
|
183
|
-
await
|
|
258
|
+
await execute()
|
|
184
259
|
|
|
185
260
|
expect(sendPayment).toHaveBeenCalledTimes(4)
|
|
186
261
|
expect(sendPayment).toHaveBeenNthCalledWith(
|
|
@@ -216,7 +291,7 @@ describe('recurring-payments-processor', () => {
|
|
|
216
291
|
salesApi.createTransaction.mockRejectedValueOnce(errors[1]).mockReturnValueOnce({ cost: 50, id: 'transaction-id-3' })
|
|
217
292
|
sendPayment.mockRejectedValueOnce(errors[2])
|
|
218
293
|
|
|
219
|
-
await
|
|
294
|
+
await execute()
|
|
220
295
|
|
|
221
296
|
expect(debugLogger).toHaveBeenCalledWith(expect.any(String), ...errors)
|
|
222
297
|
})
|
|
@@ -235,7 +310,7 @@ describe('recurring-payments-processor', () => {
|
|
|
235
310
|
}
|
|
236
311
|
salesApi.getDueRecurringPayments.mockReturnValueOnce(dueRecurringPayments)
|
|
237
312
|
|
|
238
|
-
await
|
|
313
|
+
await execute()
|
|
239
314
|
|
|
240
315
|
expect(getPaymentStatus).toHaveBeenCalledTimes(6)
|
|
241
316
|
})
|
|
@@ -248,7 +323,7 @@ describe('recurring-payments-processor', () => {
|
|
|
248
323
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
249
324
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
250
325
|
|
|
251
|
-
await
|
|
326
|
+
await execute()
|
|
252
327
|
|
|
253
328
|
expect(salesApi.preparePermissionDataForRenewal).toHaveBeenCalledWith(referenceNumber)
|
|
254
329
|
})
|
|
@@ -305,7 +380,7 @@ describe('recurring-payments-processor', () => {
|
|
|
305
380
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
306
381
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
307
382
|
|
|
308
|
-
await
|
|
383
|
+
await execute()
|
|
309
384
|
|
|
310
385
|
expect(salesApi.createTransaction).toHaveBeenCalledWith(expectedData)
|
|
311
386
|
})
|
|
@@ -323,7 +398,7 @@ describe('recurring-payments-processor', () => {
|
|
|
323
398
|
sendPayment.mockResolvedValueOnce(samplePayment)
|
|
324
399
|
salesApi.createTransaction.mockResolvedValueOnce(sampleTransaction)
|
|
325
400
|
|
|
326
|
-
await
|
|
401
|
+
await execute()
|
|
327
402
|
|
|
328
403
|
expect(salesApi.createPaymentJournal).toHaveBeenCalledWith(
|
|
329
404
|
sampleTransaction.id,
|
|
@@ -355,7 +430,7 @@ describe('recurring-payments-processor', () => {
|
|
|
355
430
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
356
431
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
357
432
|
|
|
358
|
-
await
|
|
433
|
+
await execute()
|
|
359
434
|
|
|
360
435
|
expect(salesApi.createTransaction).toHaveBeenCalledWith(
|
|
361
436
|
expect.objectContaining({
|
|
@@ -385,7 +460,7 @@ describe('recurring-payments-processor', () => {
|
|
|
385
460
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
386
461
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
387
462
|
|
|
388
|
-
await
|
|
463
|
+
await execute()
|
|
389
464
|
|
|
390
465
|
expect(salesApi.createTransaction).toHaveBeenCalledWith(
|
|
391
466
|
expect.objectContaining({
|
|
@@ -406,7 +481,7 @@ describe('recurring-payments-processor', () => {
|
|
|
406
481
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
407
482
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
408
483
|
|
|
409
|
-
await
|
|
484
|
+
await execute()
|
|
410
485
|
|
|
411
486
|
expect(salesApi.createTransaction).toHaveBeenCalledWith(
|
|
412
487
|
expect.objectContaining({
|
|
@@ -442,7 +517,7 @@ describe('recurring-payments-processor', () => {
|
|
|
442
517
|
agreement_id: agreementId
|
|
443
518
|
}
|
|
444
519
|
|
|
445
|
-
await
|
|
520
|
+
await execute()
|
|
446
521
|
|
|
447
522
|
expect(sendPayment).toHaveBeenCalledWith(expectedData)
|
|
448
523
|
})
|
|
@@ -468,7 +543,7 @@ describe('recurring-payments-processor', () => {
|
|
|
468
543
|
const mockPaymentResponse = { payment_id: 'test-payment-id', agreementId: 'agreement-1' }
|
|
469
544
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
470
545
|
|
|
471
|
-
await
|
|
546
|
+
await execute()
|
|
472
547
|
|
|
473
548
|
expect(getPaymentStatus).toHaveBeenCalledWith('test-payment-id')
|
|
474
549
|
})
|
|
@@ -495,7 +570,7 @@ describe('recurring-payments-processor', () => {
|
|
|
495
570
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
496
571
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
497
572
|
|
|
498
|
-
await
|
|
573
|
+
await execute()
|
|
499
574
|
|
|
500
575
|
console.log(debugLogger.mock.calls)
|
|
501
576
|
expect(debugLogger).toHaveBeenCalledWith(`Payment status for ${mockPaymentId}: ${PAYMENT_STATUS.Success}`)
|
|
@@ -508,7 +583,7 @@ describe('recurring-payments-processor', () => {
|
|
|
508
583
|
throw error
|
|
509
584
|
})
|
|
510
585
|
|
|
511
|
-
await
|
|
586
|
+
await execute()
|
|
512
587
|
|
|
513
588
|
expect(debugLogger).toHaveBeenCalledWith(expect.any(String), error)
|
|
514
589
|
})
|
|
@@ -536,7 +611,7 @@ describe('recurring-payments-processor', () => {
|
|
|
536
611
|
const apiError = { response: { status: statusCode, data: 'boom' } }
|
|
537
612
|
getPaymentStatus.mockRejectedValueOnce(apiError)
|
|
538
613
|
|
|
539
|
-
await
|
|
614
|
+
await execute()
|
|
540
615
|
|
|
541
616
|
expect(debugLogger).toHaveBeenCalledWith(expectedMessage)
|
|
542
617
|
})
|
|
@@ -554,7 +629,7 @@ describe('recurring-payments-processor', () => {
|
|
|
554
629
|
const networkError = new Error('network meltdown')
|
|
555
630
|
getPaymentStatus.mockRejectedValueOnce(networkError)
|
|
556
631
|
|
|
557
|
-
await
|
|
632
|
+
await execute()
|
|
558
633
|
|
|
559
634
|
expect(debugLogger).toHaveBeenCalledWith(`Unexpected error fetching payment status for ${mockPaymentId}.`)
|
|
560
635
|
})
|
|
@@ -568,7 +643,7 @@ describe('recurring-payments-processor', () => {
|
|
|
568
643
|
|
|
569
644
|
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(cb => cb())
|
|
570
645
|
|
|
571
|
-
await
|
|
646
|
+
await execute()
|
|
572
647
|
|
|
573
648
|
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), PAYMENT_STATUS_DELAY)
|
|
574
649
|
})
|
|
@@ -578,7 +653,7 @@ describe('recurring-payments-processor', () => {
|
|
|
578
653
|
|
|
579
654
|
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(cb => cb())
|
|
580
655
|
|
|
581
|
-
await
|
|
656
|
+
await execute()
|
|
582
657
|
|
|
583
658
|
expect(setTimeoutSpy).not.toHaveBeenCalled()
|
|
584
659
|
})
|
|
@@ -595,7 +670,7 @@ describe('recurring-payments-processor', () => {
|
|
|
595
670
|
sendPayment.mockResolvedValueOnce({ payment_id: mockPaymentId, agreementId: 'agreement-1', created_date: mockPaymentCreatedDate })
|
|
596
671
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
597
672
|
|
|
598
|
-
await
|
|
673
|
+
await execute()
|
|
599
674
|
|
|
600
675
|
console.log(salesApi.processRPResult.mock.calls, mockTransactionId, mockPaymentId, mockPaymentCreatedDate)
|
|
601
676
|
expect(salesApi.processRPResult).toHaveBeenCalledWith(mockTransactionId, mockPaymentId, mockPaymentCreatedDate)
|
|
@@ -608,7 +683,7 @@ describe('recurring-payments-processor', () => {
|
|
|
608
683
|
sendPayment.mockResolvedValueOnce({ payment_id: mockPaymentId, agreementId: 'agreement-1' })
|
|
609
684
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusFailure())
|
|
610
685
|
|
|
611
|
-
await
|
|
686
|
+
await execute()
|
|
612
687
|
|
|
613
688
|
expect(salesApi.processRPResult).not.toHaveBeenCalled()
|
|
614
689
|
})
|
|
@@ -629,7 +704,7 @@ describe('recurring-payments-processor', () => {
|
|
|
629
704
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
630
705
|
getPaymentStatus.mockResolvedValueOnce(mockStatus)
|
|
631
706
|
|
|
632
|
-
await
|
|
707
|
+
await execute()
|
|
633
708
|
|
|
634
709
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
635
710
|
`Payment failed. Recurring payment agreement for: ${agreementId} set to be cancelled. Updating payment journal.`
|
|
@@ -656,7 +731,7 @@ describe('recurring-payments-processor', () => {
|
|
|
656
731
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
657
732
|
getPaymentStatus.mockResolvedValueOnce(mockStatus)
|
|
658
733
|
|
|
659
|
-
await
|
|
734
|
+
await execute()
|
|
660
735
|
|
|
661
736
|
expect(salesApi.cancelRecurringPayment).toHaveBeenCalledWith(id)
|
|
662
737
|
})
|
|
@@ -673,7 +748,7 @@ describe('recurring-payments-processor', () => {
|
|
|
673
748
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusFailure())
|
|
674
749
|
salesApi.getPaymentJournal.mockResolvedValueOnce(true)
|
|
675
750
|
|
|
676
|
-
await
|
|
751
|
+
await execute()
|
|
677
752
|
|
|
678
753
|
expect(salesApi.updatePaymentJournal).toHaveBeenCalledWith(transactionId, { paymentStatus: PAYMENT_JOURNAL_STATUS_CODES.Failed })
|
|
679
754
|
})
|
|
@@ -690,7 +765,7 @@ describe('recurring-payments-processor', () => {
|
|
|
690
765
|
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusFailure())
|
|
691
766
|
salesApi.getPaymentJournal.mockResolvedValueOnce(undefined)
|
|
692
767
|
|
|
693
|
-
await
|
|
768
|
+
await execute()
|
|
694
769
|
|
|
695
770
|
expect(salesApi.updatePaymentJournal).not.toHaveBeenCalled()
|
|
696
771
|
})
|
|
@@ -716,7 +791,7 @@ describe('recurring-payments-processor', () => {
|
|
|
716
791
|
expectedData.push([reference])
|
|
717
792
|
})
|
|
718
793
|
|
|
719
|
-
await
|
|
794
|
+
await execute()
|
|
720
795
|
|
|
721
796
|
expect(salesApi.preparePermissionDataForRenewal.mock.calls).toEqual(expectedData)
|
|
722
797
|
})
|
|
@@ -760,7 +835,7 @@ describe('recurring-payments-processor', () => {
|
|
|
760
835
|
])
|
|
761
836
|
})
|
|
762
837
|
|
|
763
|
-
await
|
|
838
|
+
await execute()
|
|
764
839
|
|
|
765
840
|
expect(salesApi.createTransaction.mock.calls).toEqual(expectedData)
|
|
766
841
|
})
|
|
@@ -804,7 +879,7 @@ describe('recurring-payments-processor', () => {
|
|
|
804
879
|
])
|
|
805
880
|
})
|
|
806
881
|
|
|
807
|
-
await
|
|
882
|
+
await execute()
|
|
808
883
|
expect(sendPayment.mock.calls).toEqual(expectedData)
|
|
809
884
|
})
|
|
810
885
|
|
|
@@ -842,7 +917,7 @@ describe('recurring-payments-processor', () => {
|
|
|
842
917
|
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
843
918
|
})
|
|
844
919
|
|
|
845
|
-
await
|
|
920
|
+
await execute()
|
|
846
921
|
expectedData.forEach(paymentId => {
|
|
847
922
|
expect(getPaymentStatus).toHaveBeenCalledWith(paymentId)
|
|
848
923
|
})
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
import recurringPaymentsJob from 'commander'
|
|
3
|
-
import {
|
|
4
|
-
import { airbrake } from '@defra-fish/connectors-lib'
|
|
3
|
+
import { execute } from './recurring-payments-processor.js'
|
|
5
4
|
import path from 'path'
|
|
6
5
|
import fs from 'fs'
|
|
7
|
-
|
|
8
|
-
const SIGINT_CODE = 130
|
|
9
|
-
const SIGTERM_CODE = 137
|
|
10
6
|
const pkgPath = path.join(process.cwd(), 'package.json')
|
|
11
7
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
12
8
|
|
|
13
9
|
console.log('Recurring payments job starting at %s. name: %s. version: %s', new Date().toISOString(), pkg.name, pkg.version)
|
|
14
|
-
airbrake.initialise()
|
|
15
10
|
|
|
16
11
|
const delay = parseInt(process.env.RECURRING_PAYMENTS_LOCAL_DELAY || '0', 10)
|
|
17
12
|
if (delay > 0) {
|
|
@@ -23,16 +18,7 @@ if (delay > 0) {
|
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
function executeRecurringPaymentsJob () {
|
|
26
|
-
recurringPaymentsJob.action(
|
|
27
|
-
airbrake.flush()
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const shutdown = code => {
|
|
31
|
-
airbrake.flush()
|
|
32
|
-
process.exit(code)
|
|
21
|
+
recurringPaymentsJob.action(execute())
|
|
33
22
|
}
|
|
34
23
|
|
|
35
|
-
process.on('SIGINT', () => shutdown(SIGINT_CODE))
|
|
36
|
-
process.on('SIGTERM', () => shutdown(SIGTERM_CODE))
|
|
37
|
-
|
|
38
24
|
export default recurringPaymentsJob
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import moment from 'moment-timezone'
|
|
2
2
|
import { PAYMENT_STATUS, SERVICE_LOCAL_TIME, PAYMENT_JOURNAL_STATUS_CODES } from '@defra-fish/business-rules-lib'
|
|
3
|
-
import { salesApi } from '@defra-fish/connectors-lib'
|
|
3
|
+
import { salesApi, airbrake } from '@defra-fish/connectors-lib'
|
|
4
4
|
import { getPaymentStatus, sendPayment, isGovPayUp } from './services/govuk-pay-service.js'
|
|
5
5
|
import db from 'debug'
|
|
6
6
|
|
|
7
7
|
const debug = db('recurring-payments:processor')
|
|
8
8
|
|
|
9
|
+
const SIGINT_CODE = 130
|
|
10
|
+
const SIGTERM_CODE = 137
|
|
9
11
|
const PAYMENT_STATUS_DELAY = 60000
|
|
10
12
|
const MIN_CLIENT_ERROR = 400
|
|
11
13
|
const MAX_CLIENT_ERROR = 499
|
|
@@ -15,18 +17,18 @@ const MAX_SERVER_ERROR = 599
|
|
|
15
17
|
const isClientError = code => code >= MIN_CLIENT_ERROR && code <= MAX_CLIENT_ERROR
|
|
16
18
|
const isServerError = code => code >= MIN_SERVER_ERROR && code <= MAX_SERVER_ERROR
|
|
17
19
|
|
|
18
|
-
const
|
|
20
|
+
export const execute = async () => {
|
|
21
|
+
airbrake.initialise()
|
|
19
22
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
throw error
|
|
23
|
+
await processRecurringPayments()
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error(e)
|
|
26
|
+
} finally {
|
|
27
|
+
await airbrake.flush()
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
const processRecurringPayments = async () => {
|
|
30
32
|
if (process.env.RUN_RECURRING_PAYMENTS?.toLowerCase() !== 'true') {
|
|
31
33
|
debug('Recurring Payments job disabled')
|
|
32
34
|
return
|
|
@@ -52,6 +54,17 @@ export const processRecurringPayments = async () => {
|
|
|
52
54
|
await Promise.allSettled(payments.map(p => processRecurringPaymentStatus(p)))
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
const fetchDueRecurringPayments = async date => {
|
|
58
|
+
try {
|
|
59
|
+
const duePayments = await salesApi.getDueRecurringPayments(date)
|
|
60
|
+
debug('Recurring Payments found:', duePayments)
|
|
61
|
+
return duePayments
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Run aborted. Error fetching due recurring payments:', error)
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
const requestPayments = async dueRCPayments => {
|
|
56
69
|
const paymentRequestResults = await Promise.allSettled(dueRCPayments.map(processRecurringPayment))
|
|
57
70
|
const payments = paymentRequestResults.filter(prr => prr.status === 'fulfilled').map(p => p.value)
|
|
@@ -168,3 +181,11 @@ const processRecurringPaymentStatus = async payment => {
|
|
|
168
181
|
}
|
|
169
182
|
}
|
|
170
183
|
}
|
|
184
|
+
|
|
185
|
+
const shutdown = code => {
|
|
186
|
+
airbrake.flush()
|
|
187
|
+
process.exit(code)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
process.on('SIGINT', () => shutdown(SIGINT_CODE))
|
|
191
|
+
process.on('SIGTERM', () => shutdown(SIGTERM_CODE))
|