Skip to content

Commit e73a86e

Browse files
authored
chore(point-of-sale): update pos service response structure (#3675)
* chore(point-of-sale): update pos service response structure * fix: map cleanup in finally block
1 parent e2d4cde commit e73a86e

File tree

4 files changed

+44
-33
lines changed

4 files changed

+44
-33
lines changed

packages/point-of-sale/src/payments/routes.test.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,34 @@ describe('Payment Routes', () => {
6363
})
6464

6565
await paymentRoutes.payment(ctx)
66-
expect(ctx.response.body).toBe(Result.APPROVED)
66+
expect(ctx.response.body).toEqual({ result: { code: Result.APPROVED } })
6767
expect(ctx.status).toBe(200)
6868

6969
expect(webhookWaitMap.get('incoming-payment-url')).toBeUndefined()
7070
})
7171

72-
test('returns cardService error code when thrown', async () => {
72+
test('returns 200 with invalid_signature result when card service returns invalid signature', async () => {
73+
const ctx = createPaymentContext()
74+
mockPaymentService()
75+
jest
76+
.spyOn(cardServiceClient, 'sendPayment')
77+
.mockResolvedValueOnce(Result.INVALID_SIGNATURE)
78+
await paymentRoutes.payment(ctx)
79+
expect(ctx.response.body).toEqual({
80+
result: { code: Result.INVALID_SIGNATURE }
81+
})
82+
expect(ctx.status).toBe(200)
83+
})
84+
85+
test('returns 400 invalid_request body when an error is thrown', async () => {
7386
const ctx = createPaymentContext()
7487
mockPaymentService()
7588
jest
7689
.spyOn(cardServiceClient, 'sendPayment')
7790
.mockRejectedValue(new CardServiceClientError('Some error', 404))
7891
await paymentRoutes.payment(ctx)
79-
expect(ctx.response.body).toBe('Some error')
80-
expect(ctx.status).toBe(404)
92+
expect(ctx.response.body).toEqual({ error: { code: 'invalid_request' } })
93+
expect(ctx.status).toBe(400)
8194
})
8295

8396
test('returns 400 when there is a paymentService error', async () => {
@@ -86,22 +99,22 @@ describe('Payment Routes', () => {
8699
.spyOn(paymentService, 'getWalletAddress')
87100
.mockRejectedValueOnce(new Error('Wallet address error'))
88101
await paymentRoutes.payment(ctx)
89-
expect(ctx.response.body).toBe('Wallet address error')
102+
expect(ctx.response.body).toEqual({ error: { code: 'invalid_request' } })
90103
expect(ctx.status).toBe(400)
91104
})
92105

93-
test('returns 500 when an unknown error is thrown', async () => {
106+
test('returns 400 when an unknown error is thrown', async () => {
94107
const ctx = createPaymentContext()
95108
jest
96109
.spyOn(paymentService, 'getWalletAddress')
97110
.mockRejectedValueOnce('Unknown error')
98111
await paymentRoutes.payment(ctx)
99-
expect(ctx.response.body).toBe('Unknown error')
100-
expect(ctx.status).toBe(500)
112+
expect(ctx.response.body).toEqual({ error: { code: 'invalid_request' } })
113+
expect(ctx.status).toBe(400)
101114
})
102115

103116
test(
104-
'returns 504 if incoming payment event times out',
117+
'returns 400 if incoming payment event times out',
105118
withConfigOverride(
106119
() => config,
107120
{ webhookTimeoutMs: 1 },
@@ -118,10 +131,10 @@ describe('Payment Routes', () => {
118131
.spyOn(cardServiceClient, 'sendPayment')
119132
.mockResolvedValueOnce(Result.APPROVED)
120133
await paymentRoutes.payment(ctx)
121-
expect(ctx.response.body).toBe(
122-
'Timed out waiting for incoming payment event'
123-
)
124-
expect(ctx.status).toBe(504)
134+
expect(ctx.response.body).toEqual({
135+
error: { code: 'invalid_request' }
136+
})
137+
expect(ctx.status).toBe(400)
125138

126139
expect(deleteSpy).toHaveBeenCalled()
127140
}

packages/point-of-sale/src/payments/routes.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ import { AppContext } from '../app'
22
import { CardServiceClient, Result } from '../card-service-client/client'
33
import { BaseService } from '../shared/baseService'
44
import { PaymentService } from './service'
5-
import { CardServiceClientError } from '../card-service-client/errors'
65
import { Deferred } from '../utils/deferred'
76
import { webhookWaitMap } from '../webhook-handlers/request-map'
87
import { WebhookBody } from '../webhook-handlers/routes'
98
import { IAppConfig } from '../config/app'
109
import {
1110
IncomingPaymentEventTimeoutError,
12-
InvalidCardPaymentError,
13-
PaymentRouteError
11+
InvalidCardPaymentError
1412
} from './errors'
1513

1614
interface ServiceDependencies extends BaseService {
@@ -66,6 +64,7 @@ async function payment(
6664
ctx: PaymentContext
6765
): Promise<void> {
6866
const body = ctx.request.body
67+
let incomingPaymentId: string | undefined
6968
try {
7069
const senderWalletAddress = await deps.paymentService.getWalletAddress(
7170
body.senderWalletAddress.replace(/^https:/, 'http:')
@@ -89,6 +88,7 @@ async function payment(
8988
deferred,
9089
deps.config.webhookTimeoutMs
9190
)
91+
incomingPaymentId = incomingPayment.id
9292
const result = await deps.cardServiceClient.sendPayment(
9393
senderWalletAddress.cardService,
9494
{
@@ -101,20 +101,27 @@ async function payment(
101101
}
102102
)
103103

104+
if (result === Result.INVALID_SIGNATURE) {
105+
ctx.body = { result: { code: Result.INVALID_SIGNATURE } }
106+
ctx.status = 200
107+
return
108+
}
109+
104110
if (result !== Result.APPROVED) throw new InvalidCardPaymentError(result)
105111
const event = await waitForIncomingPaymentEvent(deps.config, deferred)
106-
webhookWaitMap.delete(incomingPayment.id)
107112
if (!event || !event.data.completed)
108113
throw new IncomingPaymentEventTimeoutError(incomingPayment.id)
109-
ctx.body = result
114+
ctx.body = { result: { code: Result.APPROVED } }
110115
ctx.status = 200
111116
} catch (err) {
112117
deps.logger.debug(err)
113-
if (err instanceof IncomingPaymentEventTimeoutError)
114-
webhookWaitMap.delete(err.incomingPaymentId)
115118
const { body, status } = handlePaymentError(err)
116119
ctx.body = body
117120
ctx.status = status
121+
} finally {
122+
if (incomingPaymentId) {
123+
webhookWaitMap.delete(incomingPaymentId)
124+
}
118125
}
119126
}
120127

@@ -130,15 +137,6 @@ async function waitForIncomingPaymentEvent(
130137
])
131138
}
132139

133-
function handlePaymentError(err: unknown) {
134-
if (err instanceof CardServiceClientError) {
135-
return { body: err.message, status: err.status }
136-
}
137-
if (err instanceof PaymentRouteError) {
138-
return { body: err.message, status: err.status }
139-
}
140-
if (err instanceof Error) {
141-
return { body: err.message, status: 400 }
142-
}
143-
return { body: err, status: 500 }
140+
function handlePaymentError(_err: unknown) {
141+
return { body: { error: { code: 'invalid_request' } }, status: 400 }
144142
}

packages/point-of-sale/src/webhook-handlers/routes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('Webhook Handler Routes Tests', (): void => {
5151
)
5252

5353
await webhookHandlerRoutes.handleWebhook(ctx)
54-
expect(ctx.status).toEqual(202)
54+
expect(ctx.status).toEqual(200)
5555
expect(deferredSpy).toHaveBeenCalledWith(ctx.request.body)
5656
})
5757

packages/point-of-sale/src/webhook-handlers/routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async function handleWebhook(
5353
const deferred = webhookWaitMap.get(id)
5454
if (deferred) {
5555
deferred.resolve(ctx.request.body)
56-
ctx.status = 202
56+
ctx.status = 200
5757
} else {
5858
ctx.throw(404, 'Not awaiting webhook for incoming payment id')
5959
}

0 commit comments

Comments
 (0)