Skip to content

Commit

Permalink
Consume payment responses (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnwatson484 authored Sep 3, 2021
1 parent 9e34d4e commit 6871df5
Show file tree
Hide file tree
Showing 18 changed files with 312 additions and 6 deletions.
18 changes: 16 additions & 2 deletions .snyk
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.22.0
ignore: {}
version: v1.19.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JS-AXIOS-1579269:
- '@azure/identity > axios':
reason: no fix
expires: '2021-10-02T17:50:04.275Z'
- '@azure/identity > @azure/msal-node > axios':
reason: no fix
expires: '2021-10-02T17:50:04.276Z'
- ffc-messaging > @azure/identity > axios:
reason: No fix
expires: '2021-10-02T17:50:04.276Z'
- ffc-messaging > @azure/identity > @azure/msal-node > axios:
reason: No fix
expires: '2021-10-02T17:50:04.276Z'
patch: {}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG PARENT_VERSION=1.2.5-node14.16.1
ARG PARENT_VERSION=1.2.9-node14.17.6
ARG PORT_DEBUG=9229
ARG PORT=3008

Expand Down
9 changes: 9 additions & 0 deletions app/acknowledgement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const db = require('./data')

const updateAcknowledgement = async (acknowledgement) => {
if (acknowledgement.success) {
await db.completedPaymentRequest.update({ acknowledged: acknowledgement.acknowledged }, { where: { invoiceNumber: acknowledgement.invoiceNumber } })
}
}

module.exports = updateAcknowledgement
2 changes: 2 additions & 0 deletions app/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const value = result.value
value.isDev = value.env === 'development'
value.isProd = value.env === 'production'
value.processingSubscription = mqConfig.processingSubscription
value.acknowledgementSubscription = mqConfig.acknowledgementSubscription
value.returnSubscription = mqConfig.returnSubscription
value.submitTopic = mqConfig.submitTopic
value.dbConfig = dbConfig

Expand Down
24 changes: 24 additions & 0 deletions app/config/mq-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ const mqSchema = joi.object({
topic: joi.string(),
numberOfReceivers: joi.number().default(3)
},
acknowledgementSubscription: {
name: joi.string(),
address: joi.string(),
topic: joi.string()
},
returnSubscription: {
name: joi.string(),
address: joi.string(),
topic: joi.string()
},
submitTopic: {
name: joi.string(),
address: joi.string()
Expand All @@ -34,6 +44,16 @@ const mqConfig = {
address: process.env.PROCESSING_SUBSCRIPTION_ADDRESS,
topic: process.env.PROCESSING_TOPIC_ADDRESS
},
acknowledgementSubscription: {
name: process.env.ACKNOWLEDGEMENT_SUBSCRIPTION_NAME,
address: process.env.ACKNOWLEDGEMENT_SUBSCRIPTION_ADDRESS,
topic: process.env.ACKNOWLEDGEMENT_TOPIC_ADDRESS
},
returnSubscription: {
name: process.env.RETURN_SUBSCRIPTION_NAME,
address: process.env.RETURN_SUBSCRIPTION_ADDRESS,
topic: process.env.RETURN_TOPIC_ADDRESS
},
submitTopic: {
name: process.env.PAYMENTSUBMIT_TOPIC_NAME,
address: process.env.PAYMENTSUBMIT_TOPIC_ADDRESS
Expand All @@ -50,9 +70,13 @@ if (mqResult.error) {
}

const processingSubscription = { ...mqResult.value.messageQueue, ...mqResult.value.processingSubscription }
const acknowledgementSubscription = { ...mqResult.value.messageQueue, ...mqResult.value.acknowledgementSubscription }
const returnSubscription = { ...mqResult.value.messageQueue, ...mqResult.value.returnSubscription }
const submitTopic = { ...mqResult.value.messageQueue, ...mqResult.value.submitTopic }

module.exports = {
processingSubscription,
acknowledgementSubscription,
returnSubscription,
submitTopic
}
12 changes: 12 additions & 0 deletions app/messaging/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ const config = require('../config')
const processPaymentMessage = require('./process-payment-message')
const { MessageReceiver } = require('ffc-messaging')
const publishPendingPaymentRequests = require('./publish-pending-payment-requests')
const processAcknowledgementMessage = require('./process-acknowledgement-message')
const processReturnMessage = require('./process-return-message')
const paymentReceivers = []
let acknowledgementReceiver
let returnReceiver

const start = async () => {
for (let i = 0; i < config.processingSubscription.numberOfReceivers; i++) {
Expand All @@ -15,6 +19,14 @@ const start = async () => {
}
setInterval(() => publishPendingPaymentRequests(), config.paymentRequestPublishingInterval)
console.info('Ready to publish payment requests')

const acknowledgementAction = message => processAcknowledgementMessage(message, acknowledgementReceiver)
acknowledgementReceiver = new MessageReceiver(config.acknowledgementSubscription, acknowledgementAction)
await acknowledgementReceiver.subscribe()

const returnAction = message => processReturnMessage(message, returnReceiver)
returnReceiver = new MessageReceiver(config.returnSubscription, returnAction)
await returnReceiver.subscribe()
}

const stop = async () => {
Expand Down
13 changes: 13 additions & 0 deletions app/messaging/process-acknowledgement-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const updateAcknowledgement = require('../acknowledgement')

const processAcknowledgementMessage = async (message, receiver) => {
try {
await updateAcknowledgement(message.body)
await receiver.completeMessage(message)
} catch (err) {
console.error('Unable to process acknowledgement request:', err)
await receiver.deadLetterMessage(message)
}
}

module.exports = processAcknowledgementMessage
13 changes: 13 additions & 0 deletions app/messaging/process-return-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const updateSettlementStatus = require('../settlement')

const processReturnMessage = async (message, receiver) => {
try {
await updateSettlementStatus(message.body)
await receiver.completeMessage(message)
} catch (err) {
console.error('Unable to process return request:', err)
await receiver.deadLetterMessage(message)
}
}

module.exports = processReturnMessage
9 changes: 9 additions & 0 deletions app/settlement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const db = require('./data')

const updateSettlementStatus = async (returnData) => {
if (returnData.settled) {
await db.completedPaymentRequest.update({ settled: returnData.settlementDate }, { where: { invoiceNumber: returnData.invoiceNumber } })
}
}

module.exports = updateSettlementStatus
4 changes: 4 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ services:
MESSAGE_QUEUE_USER: ${MESSAGE_QUEUE_USER:-notset}
PROCESSING_TOPIC_ADDRESS: ${PROCESSING_TOPIC_ADDRESS:-notset}
PROCESSING_SUBSCRIPTION_ADDRESS: ${PROCESSING_SUBSCRIPTION_ADDRESS:-notset}
ACKNOWLEDGEMENT_TOPIC_ADDRESS: ${ACKNOWLEDGEMENT_TOPIC_ADDRESS:-notset}
ACKNOWLEDGEMENT_SUBSCRIPTION_ADDRESS: ${ACKNOWLEDGEMENT_SUBSCRIPTION_ADDRESS:-notset}
RETURN_TOPIC_ADDRESS: ${RETURN_TOPIC_ADDRESS:-notset}
RETURN_SUBSCRIPTION_ADDRESS: ${RETURN_SUBSCRIPTION_ADDRESS:-notset}
PAYMENTSUBMIT_TOPIC_ADDRESS: ${PAYMENTSUBMIT_TOPIC_ADDRESS:-notset}
POSTGRES_DB: ffc_sfi_payments
POSTGRES_HOST: ${POSTGRES_HOST:-ffc-sfi-payments-postgres}
Expand Down
4 changes: 4 additions & 0 deletions helm/ffc-sfi-payments/templates/config-map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ data:
MESSAGE_QUEUE_HOST: {{ quote .Values.container.messageQueueHost }}
PROCESSING_TOPIC_ADDRESS: {{ quote .Values.container.processingTopicAddress }}
PROCESSING_SUBSCRIPTION_ADDRESS: {{ quote .Values.container.processingSubscriptionAddress }}
ACKNOWLEDGEMENT_TOPIC_ADDRESS: {{ quote .Values.container.acknowledgementTopicAddress }}
ACKNOWLEDGEMENT_SUBSCRIPTION_ADDRESS: {{ quote .Values.container.acknowledgementSubscriptionAddress }}
RETURN_TOPIC_ADDRESS: {{ quote .Values.container.returnTopicAddress }}
RETURN_SUBSCRIPTION_ADDRESS: {{ quote .Values.container.returnSubscriptionAddress }}
PAYMENTSUBMIT_TOPIC_ADDRESS: {{ quote .Values.container.paymentSubmitTopicAddress }}
POSTGRES_DB: {{ quote .Values.postgresService.postgresDb }}
POSTGRES_HOST: {{ quote .Values.postgresService.postgresHost }}
Expand Down
6 changes: 5 additions & 1 deletion helm/ffc-sfi-payments/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ container:
requestMemory: 180Mi
requestCpu: 40m
limitMemory: 280Mi
limitCpu: 120m
limitCpu: 300m
port: 3008
messageQueueHost: namespace.servicebus.windows.net
processingTopicAddress: ffc-sfi-payment-processing
processingSubscriptionAddress: ffc-sfi-payment-processing-payments
acknowledgementTopicAddress: ffc-sfi-payment-acknowledgement
acknowledgementSubscriptionAddress: ffc-sfi-payment-acknowledgement-payments
returnTopicAddress: ffc-sfi-payment-return
returnSubscriptionAddress: ffc-sfi-payment-return-payments
paymentSubmitTopicAddress: ffc-sfi-payment-submit

postgresService:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ffc-sfi-payments",
"version": "2.0.3",
"version": "2.1.0",
"description": "FFC SFI payment services",
"homepage": "https://github.com/DEFRA/ffc-sfi-payments",
"main": "app/index.js",
Expand Down
4 changes: 3 additions & 1 deletion provision.azure.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
resources:
topics:
- name: payment
- name: processing
- name: acknowledgement
- name: return
56 changes: 56 additions & 0 deletions test/integration/local/acknowledgement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const db = require('../../../app/data')
const updateAcknowledgement = require('../../../app/acknowledgement')
let scheme
let paymentRequest
let acknowledgement

describe('update settlement status', () => {
beforeEach(async () => {
await db.sequelize.truncate({ cascade: true })

scheme = {
schemeId: 1,
name: 'SFI',
active: true
}

paymentRequest = {
completedPaymentRequestId: 1,
paymentRequestId: 1,
schemeId: 1,
frn: 1234567890,
marketingYear: 2022,
invoiceNumber: 'S12345678A123456V001'
}

acknowledgement = {
invoiceNumber: 'S12345678A123456V001',
acknowledged: new Date(2021, 8, 2),
success: true
}
})

afterAll(async () => {
await db.sequelize.truncate({ cascade: true })
await db.sequelize.close()
})

test('should add acknowledged date if success', async () => {
await db.scheme.create(scheme)
await db.paymentRequest.create(paymentRequest)
await db.completedPaymentRequest.create(paymentRequest)
await updateAcknowledgement(acknowledgement)
const updatedPaymentRequest = await db.completedPaymentRequest.findByPk(paymentRequest.paymentRequestId)
expect(updatedPaymentRequest.acknowledged).toStrictEqual(new Date(2021, 8, 2))
})

test('should not add acknowledged date to failure', async () => {
await db.scheme.create(scheme)
await db.paymentRequest.create(paymentRequest)
await db.completedPaymentRequest.create(paymentRequest)
acknowledgement.success = false
await updateAcknowledgement(acknowledgement)
const updatedPaymentRequest = await db.completedPaymentRequest.findByPk(paymentRequest.paymentRequestId)
expect(updatedPaymentRequest.acknowledged).toBeNull()
})
})
56 changes: 56 additions & 0 deletions test/integration/local/settlement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const db = require('../../../app/data')
const updateSettlementStatus = require('../../../app/settlement')
let scheme
let paymentRequest
let returnData

describe('update settlement status', () => {
beforeEach(async () => {
await db.sequelize.truncate({ cascade: true })

scheme = {
schemeId: 1,
name: 'SFI',
active: true
}

paymentRequest = {
completedPaymentRequestId: 1,
paymentRequestId: 1,
schemeId: 1,
frn: 1234567890,
marketingYear: 2022,
invoiceNumber: 'S12345678A123456V001'
}

returnData = {
invoiceNumber: 'S12345678A123456V001',
settled: true,
settlementDate: new Date(2021, 8, 2)
}
})

afterAll(async () => {
await db.sequelize.truncate({ cascade: true })
await db.sequelize.close()
})

test('should add settlement date to settled', async () => {
await db.scheme.create(scheme)
await db.paymentRequest.create(paymentRequest)
await db.completedPaymentRequest.create(paymentRequest)
await updateSettlementStatus(returnData)
const updatedPaymentRequest = await db.completedPaymentRequest.findByPk(paymentRequest.paymentRequestId)
expect(updatedPaymentRequest.settled).toStrictEqual(new Date(2021, 8, 2))
})

test('should not add settlement date to unsettled', async () => {
await db.scheme.create(scheme)
await db.paymentRequest.create(paymentRequest)
await db.completedPaymentRequest.create(paymentRequest)
returnData.settled = false
await updateSettlementStatus(returnData)
const updatedPaymentRequest = await db.completedPaymentRequest.findByPk(paymentRequest.paymentRequestId)
expect(updatedPaymentRequest.settled).toBeNull()
})
})
42 changes: 42 additions & 0 deletions test/unit/messaging/process-acknowledgement-message.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
jest.mock('ffc-messaging')
jest.mock('../../../app/data')
jest.mock('../../../app/acknowledgement')
const mockUpdateAcknowledgement = require('../../../app/acknowledgement')
const processAcknowledgementMessage = require('../../../app/messaging/process-acknowledgement-message')
let receiver

describe('process acknowledgement message', () => {
beforeEach(() => {
receiver = {
completeMessage: jest.fn(),
deadLetterMessage: jest.fn()
}
})

afterEach(() => {
jest.clearAllMocks()
})

test('completes valid message', async () => {
const message = {
body: {
frn: 1234567890
}
}
await processAcknowledgementMessage(message, receiver)
expect(receiver.completeMessage).toHaveBeenCalledWith(message)
})

test('dead letters invalid message', async () => {
mockUpdateAcknowledgement.mockImplementation(() => {
throw new Error()
})
const message = {
body: {
frn: 1234567890
}
}
await processAcknowledgementMessage(message, receiver)
expect(receiver.deadLetterMessage).toHaveBeenCalledWith(message)
})
})
Loading

0 comments on commit 6871df5

Please sign in to comment.