@defra-fish/recurring-payments-job 1.64.0-rc.1 → 1.64.0-rc.3
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.64.0-rc.
|
|
3
|
+
"version": "1.64.0-rc.3",
|
|
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.64.0-rc.
|
|
40
|
-
"@defra-fish/connectors-lib": "1.64.0-rc.
|
|
39
|
+
"@defra-fish/business-rules-lib": "1.64.0-rc.3",
|
|
40
|
+
"@defra-fish/connectors-lib": "1.64.0-rc.3",
|
|
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": "709cc3d3ded64bf33d0c68bfd7484c829a226ab2"
|
|
46
46
|
}
|
|
@@ -70,6 +70,12 @@ const getMockDueRecurringPayment = ({ agreementId = 'test-agreement-id', id = 'a
|
|
|
70
70
|
expanded: { activePermission: { entity: { referenceNumber } } }
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
+
const getMockSendPaymentResponse = ({ payment_id = 'pay-1', agreementId = 'agr-1', created_date = '2025-01-01T00:00:00.000Z' } = {}) => ({
|
|
74
|
+
payment_id,
|
|
75
|
+
agreementId,
|
|
76
|
+
created_date
|
|
77
|
+
})
|
|
78
|
+
|
|
73
79
|
describe('recurring-payments-processor', () => {
|
|
74
80
|
const [{ value: debugLogger }] = db.mock.results
|
|
75
81
|
|
|
@@ -588,6 +594,139 @@ describe('recurring-payments-processor', () => {
|
|
|
588
594
|
expect(debugLogger).toHaveBeenCalledWith(expect.any(String), error)
|
|
589
595
|
})
|
|
590
596
|
|
|
597
|
+
// --- //
|
|
598
|
+
|
|
599
|
+
it('should log errors from await salesApi.processRPResult', async () => {
|
|
600
|
+
salesApi.getDueRecurringPayments.mockResolvedValueOnce([getMockDueRecurringPayment()])
|
|
601
|
+
salesApi.createTransaction.mockResolvedValueOnce({ id: 'trans-1', cost: 30 })
|
|
602
|
+
|
|
603
|
+
const payment = getMockSendPaymentResponse()
|
|
604
|
+
sendPayment.mockResolvedValueOnce(payment)
|
|
605
|
+
|
|
606
|
+
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
607
|
+
|
|
608
|
+
const boom = new Error('boom')
|
|
609
|
+
|
|
610
|
+
salesApi.processRPResult.mockImplementation(transId => (transId === 'trans-1' ? Promise.reject(boom) : Promise.resolve()))
|
|
611
|
+
|
|
612
|
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
613
|
+
|
|
614
|
+
await execute()
|
|
615
|
+
|
|
616
|
+
expect(errorSpy).toHaveBeenCalledWith('Failed to process Recurring Payment for trans-1', boom)
|
|
617
|
+
|
|
618
|
+
errorSpy.mockRestore()
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
describe('handling failures for multiple due payments', () => {
|
|
622
|
+
beforeEach(() => {
|
|
623
|
+
salesApi.getDueRecurringPayments.mockResolvedValueOnce([getMockDueRecurringPayment(), getMockDueRecurringPayment()])
|
|
624
|
+
|
|
625
|
+
salesApi.preparePermissionDataForRenewal.mockResolvedValueOnce({ licensee: { countryCode: 'GB-ENG' } })
|
|
626
|
+
|
|
627
|
+
salesApi.createTransaction.mockResolvedValueOnce({ id: 'trans-1', cost: 30 }).mockResolvedValueOnce({ id: 'trans-2', cost: 30 })
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
it('continues when one sendPayment rejects (Promise.allSettled check)', async () => {
|
|
631
|
+
const secondPayment = getMockSendPaymentResponse({
|
|
632
|
+
payment_id: 'test-payment-second',
|
|
633
|
+
agreementId: 'agr-2',
|
|
634
|
+
created_date: '2025-01-01T00:00:00.000Z'
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
const gatewayDown = new Error('gateway down')
|
|
638
|
+
sendPayment.mockRejectedValueOnce(gatewayDown).mockResolvedValueOnce(secondPayment)
|
|
639
|
+
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
640
|
+
salesApi.processRPResult.mockResolvedValueOnce()
|
|
641
|
+
|
|
642
|
+
await execute()
|
|
643
|
+
|
|
644
|
+
const summary = {
|
|
645
|
+
statusArgs: getPaymentStatus.mock.calls,
|
|
646
|
+
rpResultArgs: salesApi.processRPResult.mock.calls
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
expect(summary).toEqual({
|
|
650
|
+
statusArgs: [[secondPayment.payment_id]],
|
|
651
|
+
rpResultArgs: [['trans-2', secondPayment.payment_id, secondPayment.created_date]]
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
it('continues when processRPResult rejects for one payment', async () => {
|
|
656
|
+
const firstPayment = getMockSendPaymentResponse({
|
|
657
|
+
payment_id: 'pay-1',
|
|
658
|
+
agreementId: 'agr-1',
|
|
659
|
+
created_date: '2025-01-01T00:00:00.000Z'
|
|
660
|
+
})
|
|
661
|
+
const secondPayment = getMockSendPaymentResponse({
|
|
662
|
+
payment_id: 'pay-2',
|
|
663
|
+
agreementId: 'agr-2',
|
|
664
|
+
created_date: '2025-01-01T00:01:00.000Z'
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
sendPayment.mockResolvedValueOnce(firstPayment).mockResolvedValueOnce(secondPayment)
|
|
668
|
+
getPaymentStatus.mockResolvedValueOnce(getPaymentStatusSuccess()).mockResolvedValueOnce(getPaymentStatusSuccess())
|
|
669
|
+
|
|
670
|
+
const boom = new Error('boom')
|
|
671
|
+
salesApi.processRPResult.mockImplementation(transId => (transId === 'trans-1' ? Promise.reject(boom) : Promise.resolve()))
|
|
672
|
+
|
|
673
|
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
674
|
+
|
|
675
|
+
await execute()
|
|
676
|
+
|
|
677
|
+
const summary = {
|
|
678
|
+
rpResultArgs: salesApi.processRPResult.mock.calls,
|
|
679
|
+
rpCount: salesApi.processRPResult.mock.calls.length,
|
|
680
|
+
firstError: errorSpy.mock.calls[0]
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
errorSpy.mockRestore()
|
|
684
|
+
|
|
685
|
+
expect(summary).toEqual({
|
|
686
|
+
rpResultArgs: expect.arrayContaining([
|
|
687
|
+
['trans-1', firstPayment.payment_id, firstPayment.created_date],
|
|
688
|
+
['trans-2', secondPayment.payment_id, secondPayment.created_date]
|
|
689
|
+
]),
|
|
690
|
+
rpCount: 2,
|
|
691
|
+
firstError: ['Failed to process Recurring Payment for trans-1', boom]
|
|
692
|
+
})
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
it('does not abort when getPaymentStatus rejects for one payment (allSettled at status stage)', async () => {
|
|
696
|
+
const p1 = getMockSendPaymentResponse({ payment_id: 'pay-1', created_date: '2025-01-01T00:00:00.000Z' })
|
|
697
|
+
const p2 = getMockSendPaymentResponse({ payment_id: 'pay-2', created_date: '2025-01-01T00:01:00.000Z' })
|
|
698
|
+
|
|
699
|
+
sendPayment.mockResolvedValueOnce(p1).mockResolvedValueOnce(p2)
|
|
700
|
+
|
|
701
|
+
getPaymentStatus.mockImplementation(async id => {
|
|
702
|
+
if (id === p1.payment_id) {
|
|
703
|
+
throw Object.assign(new Error('HTTP 500'), { response: { status: 500, data: 'boom' } })
|
|
704
|
+
}
|
|
705
|
+
return getPaymentStatusSuccess()
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
salesApi.processRPResult.mockResolvedValueOnce()
|
|
709
|
+
|
|
710
|
+
await execute()
|
|
711
|
+
|
|
712
|
+
const summary = {
|
|
713
|
+
statusArgs: getPaymentStatus.mock.calls,
|
|
714
|
+
statusCount: getPaymentStatus.mock.calls.length,
|
|
715
|
+
rpResultArgs: salesApi.processRPResult.mock.calls,
|
|
716
|
+
rpCount: salesApi.processRPResult.mock.calls.length
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
expect(summary).toEqual({
|
|
720
|
+
statusArgs: expect.arrayContaining([[p1.payment_id], [p2.payment_id]]),
|
|
721
|
+
statusCount: 2,
|
|
722
|
+
rpResultArgs: expect.arrayContaining([['trans-2', p2.payment_id, p2.created_date]]),
|
|
723
|
+
rpCount: 1
|
|
724
|
+
})
|
|
725
|
+
})
|
|
726
|
+
})
|
|
727
|
+
|
|
728
|
+
// --- //
|
|
729
|
+
|
|
591
730
|
it.each([
|
|
592
731
|
[400, 'Failed to fetch status for payment test-payment-id, error 400'],
|
|
593
732
|
[486, 'Failed to fetch status for payment test-payment-id, error 486'],
|
|
@@ -155,8 +155,13 @@ const processRecurringPaymentStatus = async payment => {
|
|
|
155
155
|
debug(`Payment status for ${payment.paymentId}: ${status}`)
|
|
156
156
|
|
|
157
157
|
if (status === PAYMENT_STATUS.Success) {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
try {
|
|
159
|
+
await salesApi.processRPResult(payment.transaction.id, payment.paymentId, payment.created_date)
|
|
160
|
+
debug(`Processed Recurring Payment for ${payment.transaction.id}`)
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error(`Failed to process Recurring Payment for ${payment.transaction.id}`, err)
|
|
163
|
+
throw err
|
|
164
|
+
}
|
|
160
165
|
}
|
|
161
166
|
if (status === PAYMENT_STATUS.Failure || status === PAYMENT_STATUS.Error) {
|
|
162
167
|
console.error(
|