@defra-fish/payment-mop-up-job 1.76.0 → 1.77.0-rc.1

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/payment-mop-up-job",
3
- "version": "1.76.0",
3
+ "version": "1.77.0-rc.1",
4
4
  "description": "Process incomplete web-sales",
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.76.0",
40
- "@defra-fish/connectors-lib": "1.76.0",
39
+ "@defra-fish/business-rules-lib": "1.77.0-rc.1",
40
+ "@defra-fish/connectors-lib": "1.77.0-rc.1",
41
41
  "bottleneck": "2.19.5",
42
42
  "debug": "4.3.3",
43
43
  "moment": "2.29.1"
44
44
  },
45
- "gitHead": "2287ca0defd4b61d66cbea6f0df84568265b036f"
45
+ "gitHead": "2d2758c633c72fb22cd69fd5c9cbf0046568a9a9"
46
46
  }
@@ -14,30 +14,35 @@ jest.mock('bottleneck', () =>
14
14
  const journalEntries = () => [
15
15
  {
16
16
  id: '4fa393ab-07f4-407e-b233-89be2a6f5f77',
17
+ eligibleForEarlyMopUp: false,
17
18
  paymentStatus: 'In Progress',
18
19
  paymentReference: '05nioqikvvnuu5l8m2qeaj0qap',
19
20
  paymentTimestamp: '2020-06-01T10:35:56.873Z'
20
21
  },
21
22
  {
22
23
  id: 'aaced854-d337-47ee-8d5e-75b26aeb90fb',
24
+ eligibleForEarlyMopUp: false,
23
25
  paymentStatus: 'In Progress',
24
26
  paymentReference: '0f3dr9ugp7u68qq18vt9h8ma85',
25
27
  paymentTimestamp: '2020-06-02T07:17:23.169Z'
26
28
  },
27
29
  {
28
30
  id: 'a0e0e5c3-1004-4271-80ba-d05eda3e8213',
31
+ eligibleForEarlyMopUp: false,
29
32
  paymentStatus: 'In Progress',
30
33
  paymentReference: '7lufvi9sbh077rvrrmnqo63vme',
31
- paymentTimestamp: '2020-06-04T12:04:30.802Z'
34
+ paymentTimestamp: '2020-06-04T11:04:30.802Z'
32
35
  },
33
36
  {
34
37
  id: 'a0e0e5c3-1004-4271-80ba-d05eda3e8214',
38
+ eligibleForEarlyMopUp: true,
35
39
  paymentStatus: 'In Progress',
36
40
  paymentReference: '7lufvi9sbh077rvrrmnqo63vmf',
37
41
  paymentTimestamp: '2020-06-04T12:04:30.802Z'
38
42
  },
39
43
  {
40
44
  id: 'a0e0e5c3-1004-4271-80ba-d05eda3e8215',
45
+ eligibleForEarlyMopUp: false,
41
46
  paymentStatus: 'In Progress',
42
47
  paymentReference: '7lufvi9sbh077rvrrmnqo63vmg',
43
48
  paymentTimestamp: '2020-06-04T12:04:30.802Z'
@@ -128,6 +133,10 @@ const createPaymentEventsEntry = paymentStatus => {
128
133
  describe('processor', () => {
129
134
  beforeEach(jest.clearAllMocks)
130
135
 
136
+ beforeAll(() => {
137
+ salesApi.paymentJournals.getAll.mockReturnValue([])
138
+ })
139
+
131
140
  it('completes normally where there are no journal records retrieved', async () => {
132
141
  salesApi.paymentJournals.getAll.mockReturnValueOnce([])
133
142
  await execute(1, 1)
@@ -166,6 +175,69 @@ describe('processor', () => {
166
175
  })
167
176
  })
168
177
 
178
+ it.each([
179
+ ['2026-09-10T09:00:00.000Z', 1, 1],
180
+ ['2024-02-29T19:00:00.000Z', 14, 5],
181
+ ['2026-09-10T00:01:00.000Z', 28, 37]
182
+ ])(
183
+ "sends expected arguments to query payment journals when it's %s, we're looking for payments over %i minute(s) old and going back %i hour(s)",
184
+ async (now, ageMinutes, scanDurationHours) => {
185
+ jest.useFakeTimers().setSystemTime(new Date(now))
186
+ const expectedFrom = moment(now).subtract({ minutes: ageMinutes, hours: scanDurationHours }).toISOString()
187
+
188
+ await execute(ageMinutes, scanDurationHours)
189
+
190
+ expect(salesApi.paymentJournals.getAll).toHaveBeenCalledWith(
191
+ expect.objectContaining({
192
+ paymentStatus: PAYMENT_JOURNAL_STATUS_CODES.InProgress,
193
+ from: expectedFrom,
194
+ to: now
195
+ })
196
+ )
197
+ jest.useRealTimers()
198
+ }
199
+ )
200
+
201
+ it('only processes payments that are either within the payment window or have eligibleForEarlyMopUp flag', async () => {
202
+ const now = '2020-06-04T12:04:30.802Z'
203
+ jest.useFakeTimers().setSystemTime(new Date(now))
204
+ const sampleJournalEntries = journalEntries()
205
+ salesApi.paymentJournals.getAll.mockResolvedValueOnce(sampleJournalEntries)
206
+ for (let x = 0; x < sampleJournalEntries.length; x++) {
207
+ govUkPayApi.fetchPaymentStatus.mockImplementationOnce(ref => ({ json: async () => ({}) }))
208
+ govUkPayApi.fetchPaymentEvents.mockImplementationOnce(ref => ({
209
+ json: async () => ({
210
+ events: [
211
+ {
212
+ payment_id: ref,
213
+ state: {
214
+ status: 'success',
215
+ finished: true
216
+ },
217
+ amount: 8200
218
+ }
219
+ ]
220
+ })
221
+ }))
222
+ }
223
+
224
+ await execute(1, 1)
225
+
226
+ const expectedIds = [
227
+ '05nioqikvvnuu5l8m2qeaj0qap',
228
+ '0f3dr9ugp7u68qq18vt9h8ma85',
229
+ '7lufvi9sbh077rvrrmnqo63vme',
230
+ '7lufvi9sbh077rvrrmnqo63vmf'
231
+ ]
232
+ const unexpectedId = '7lufvi9sbh077rvrrmnqo63vmg'
233
+ for (const expectedId of expectedIds) {
234
+ expect(govUkPayApi.fetchPaymentStatus).toHaveBeenCalledWith(expectedId, expect.anything())
235
+ }
236
+ expect(govUkPayApi.fetchPaymentStatus).not.toHaveBeenCalledWith(unexpectedId, expect.anything())
237
+
238
+ jest.useRealTimers()
239
+ })
240
+
169
241
  it('calls fetchPaymentStatus with recurring as true since agreementId exists', async () => {
170
242
  salesApi.retrieveStagedTransaction.mockReturnValueOnce({ recurringPayment: { agreementId: '123' } })
171
243
  const paymentReference = '15nioqikvvnuu5l8m2qeaj0qap'
@@ -112,17 +112,19 @@ export const execute = async (ageMinutes, scanDurationHours) => {
112
112
  debug(`Running payment mop up processor with a payment age of ${ageMinutes} minutes and a scan duration of ${scanDurationHours} hours`)
113
113
 
114
114
  const toTimestamp = moment().add(-1 * ageMinutes, 'minutes')
115
+ const now = moment()
115
116
  const fromTimestamp = toTimestamp.clone().add(-1 * scanDurationHours, 'hours')
116
117
 
117
118
  const paymentJournals = await salesApi.paymentJournals.getAll({
118
119
  paymentStatus: PAYMENT_JOURNAL_STATUS_CODES.InProgress,
119
120
  from: fromTimestamp.toISOString(),
120
- to: toTimestamp.toISOString()
121
+ to: now.toISOString()
121
122
  })
123
+ const paymentJournalsToMopUp = paymentJournals.filter(pj => toTimestamp.isAfter(pj.paymentTimestamp) || pj.eligibleForEarlyMopUp)
122
124
 
123
125
  // Get the status for each payment from the GOV.UK Pay API.
124
126
  const journalsWithRecurringPaymentIDs = await Promise.all(
125
- paymentJournals.map(async p => {
127
+ paymentJournalsToMopUp.map(async p => {
126
128
  const transactionRecord = await salesApi.retrieveStagedTransaction(p.id)
127
129
  const paymentJournalWithStatus = {
128
130
  ...p,