Skip to content

Commit b4db0e2

Browse files
authored
fix: updates to make e2e payment flow work (#3668)
* chore(localenv): add CARD_WEBHOOK_SERVICE_URL to cloud nine wallet backend * feat(backend): fetch cardDetails during outgoing payment funded and cancelled * chore(pos): correctly handle response from card service * test(pos): update card service client test * chore(card-service): add paymentRoutes to AppServices * chore(backend): fix withGraphFetched query * test(pos): remove unused test
1 parent 80ff41a commit b4db0e2

File tree

6 files changed

+21
-34
lines changed

6 files changed

+21
-34
lines changed

localenv/cloud-nine-wallet/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ services:
156156
KEY_ID: 7097F83B-CB84-469E-96C6-2141C72E22C0
157157
OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787
158158
CARD_SERVICE_URL: 'http://cloud-nine-wallet-card-service:3007'
159+
CARD_WEBHOOK_SERVICE_URL: 'http://cloud-nine-wallet-card-service:3007/payment-event'
159160
depends_on:
160161
- shared-database
161162
- shared-redis

packages/backend/src/open_payments/payment/outgoing/service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ async function cancelOutgoingPayment(
264264
: {})
265265
}
266266
})
267-
.withGraphFetched('quote')
267+
.withGraphFetched('[quote, cardDetails]')
268268
const asset = await deps.assetService.get(payment.quote.assetId)
269269
if (asset) payment.quote.asset = asset
270270

@@ -719,7 +719,7 @@ async function fundPayment(
719719
tenantId
720720
})
721721
.forUpdate()
722-
.withGraphFetched('quote')
722+
.withGraphFetched('[quote, cardDetails]')
723723
if (!payment) return FundingError.UnknownPayment
724724

725725
const asset = await deps.assetService.get(payment.quote.assetId)

packages/card-service/src/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import cors from '@koa/cors'
99
import { createValidatorMiddleware, HttpMethod } from '@interledger/openapi'
1010
import { PaymentContext } from './payment/types'
1111
import { PaymentEventContext } from './payment/types'
12+
import { PaymentRoutes } from './payment/routes'
1213

1314
export interface AppServices {
1415
logger: Promise<Logger>
1516
config: Promise<IAppConfig>
17+
paymentRoutes: Promise<PaymentRoutes>
1618
}
1719

1820
export type AppContainer = IocContract<AppServices>

packages/point-of-sale/src/card-service-client/client.test.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ describe('CardServiceClient', () => {
2828
expect(nock.isDone()).toBeTruthy()
2929
})
3030

31-
const createPaymentResponse = (result?: Result): PaymentResponse => ({
31+
const createPaymentResponse = (resultCode?: Result): PaymentResponse => ({
3232
requestId: 'requestId',
33-
result: result ?? Result.APPROVED
33+
result: { code: resultCode || Result.APPROVED }
3434
})
3535

3636
const options: SendPaymentArgs = {
@@ -46,20 +46,15 @@ describe('CardServiceClient', () => {
4646
}
4747
}
4848

49-
describe('returns the result', () => {
50-
it.each`
51-
result | code
52-
${Result.APPROVED} | ${HttpStatusCode.Ok}
53-
${Result.CARD_EXPIRED} | ${HttpStatusCode.Unauthorized}
54-
${Result.INVALID_SIGNATURE} | ${HttpStatusCode.Unauthorized}
55-
`('when the result is $result', async (response) => {
56-
nock(CARD_SERVICE_URL)
57-
.post('/payment')
58-
.reply(response.code, createPaymentResponse(response.result))
59-
expect(await client.sendPayment(CARD_SERVICE_URL, options)).toBe(
60-
response.result
61-
)
62-
})
49+
test('returns the result', async () => {
50+
const resultCode = Result.APPROVED
51+
nock(CARD_SERVICE_URL)
52+
.post('/payment')
53+
.reply(201, createPaymentResponse(resultCode))
54+
55+
await expect(client.sendPayment(CARD_SERVICE_URL, options)).resolves.toBe(
56+
resultCode
57+
)
6358
})
6459

6560
test('throws when there is no payload data', async () => {

packages/point-of-sale/src/card-service-client/client.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@ interface ServiceDependencies extends BaseService {
3737

3838
export enum Result {
3939
APPROVED = 'approved',
40-
CARD_EXPIRED = 'card_expired',
4140
INVALID_SIGNATURE = 'invalid_signature'
4241
}
4342

4443
export type PaymentResponse = {
4544
requestId: string
46-
result: Result
45+
result: {
46+
code: Result
47+
}
4748
}
4849

4950
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
@@ -97,14 +98,14 @@ async function sendPayment(
9798
HttpStatusCode.NotFound
9899
)
99100
}
100-
return payment.result
101+
return payment.result.code
101102
} catch (error) {
102103
deps.logger.debug(error)
103104
if (error instanceof CardServiceClientError) throw error
104105

105106
if (error instanceof AxiosError) {
106107
if (isPaymentResponse(error.response?.data)) {
107-
return error.response.data.result
108+
return error.response.data.result.code
108109
}
109110
throw new CardServiceClientError(
110111
error.response?.data ?? 'Unknown Axios error',

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,6 @@ describe('Payment Routes', () => {
6969
expect(webhookWaitMap.get('incoming-payment-url')).toBeUndefined()
7070
})
7171

72-
test('returns 401 with result card_expired or invalid_signature', async () => {
73-
const ctx = createPaymentContext()
74-
mockPaymentService()
75-
jest
76-
.spyOn(cardServiceClient, 'sendPayment')
77-
.mockResolvedValueOnce(Result.CARD_EXPIRED)
78-
79-
await paymentRoutes.payment(ctx)
80-
expect(ctx.response.body).toBe(Result.CARD_EXPIRED)
81-
expect(ctx.status).toBe(401)
82-
})
83-
8472
test('returns cardService error code when thrown', async () => {
8573
const ctx = createPaymentContext()
8674
mockPaymentService()

0 commit comments

Comments
 (0)