Skip to content

Commit fa44fe8

Browse files
committed
feat(backend): add middleware that handles STREAM KYC/additional data frames (#3706)
* feat(backend): add middleware that handles STREAM KYC/additional data payloads - wip * feat(backend): add wip comment * feat: support arbitrary data on prepare packets - WIP * feat(pay): add controller to handle additional data in STREAM packets * feat: add STREAM connection id in cache key for KYC response, remove unused env vars * chore: address PR comments * chore: add tests for payment decision middleware and interledgerjs changes * fix: update app data controller ti return F99 * feat: update additional data middleware logic, add pay and stream-receiver updates * chore(backend): use updated pay and stream-receiver from npm, update lockfile * feat(backend): refactor partial payment middleware, add handler for partial payments to mock ASE * fix(backend): fix build errors in CI * fix(backend): formatting * fix(backend): formatting * fix(backend): partial payment decision middleware tests * fix(backend): formatting * feat(backend): address review comments * feat(backend): address PR comments * fix(backend): fix failing CI tests * chore: align open-payments-specifications with feature/encrypted-data-exchange * fix: webhooks tests * fix: delete flaky test * fix: backend build * chore: address review comments
1 parent 921a317 commit fa44fe8

File tree

18 files changed

+4824
-2033
lines changed

18 files changed

+4824
-2033
lines changed

localenv/happy-life-bank/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ services:
5252
SIGNATURE_VERSION: 1
5353
SIGNATURE_SECRET: iyIgCprjb9uL8wFckR+pLEkJWMB7FJhgkvqhTQR/964=
5454
IDP_SECRET: 2pEcn2kkCclbOHQiGNEwhJ0rucATZhrA807HTm2rNXE=
55+
DB_ENCRYPTION_SECRET: 'zO9KogehJECHReHgQr+ZWGkmgOD4AYa4ksUxALSwgM8='
5556
DISPLAY_NAME: Happy Life Bank
5657
DISPLAY_ICON: bank-icon.svg
5758
OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d
@@ -114,6 +115,7 @@ services:
114115
OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d
115116
CARD_SERVICE_URL: 'http://happy-life-bank-card-service:4007'
116117
POS_WEBHOOK_SERVICE_URL: 'http://happy-life-bank-point-of-sale:4008/webhook'
118+
DB_ENCRYPTION_SECRET: 'zO9KogehJECHReHgQr+ZWGkmgOD4AYa4ksUxALSwgM8='
117119
WALLET_ADDRESS_NOT_FOUND_POLLING_ENABLED: true
118120
depends_on:
119121
- cloud-nine-backend

localenv/mock-account-servicing-entity/app/lib/webhooks.server.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,62 @@ export async function handleIncomingPaymentCompletedExpired(
186186
return
187187
}
188188

189+
export async function handleIncomingPartialPaymentReceived(
190+
wh: Webhook,
191+
options?: TenantOptions
192+
) {
193+
if (wh.type !== WebhookEventType.IncomingPaymentPartialPaymentReceived) {
194+
throw new Error(
195+
'Invalid event type when handling incoming partial payment webhook'
196+
)
197+
}
198+
199+
const incomingPaymentId = wh.data['id'] as string | undefined
200+
if (!incomingPaymentId) {
201+
throw new Error('No incomingPaymentId found on webhook data')
202+
}
203+
204+
const partialIncomingPaymentId = wh.data['partialIncomingPaymentId'] as
205+
| string
206+
| undefined
207+
if (!partialIncomingPaymentId) {
208+
throw new Error('No partialIncomingPaymentId found on webhook data')
209+
}
210+
211+
const rawDataToTransmit = wh.data['dataToTransmit'] as string | undefined
212+
if (!rawDataToTransmit) {
213+
throw new Error('No dataToTransmit found on webhook data')
214+
}
215+
216+
await generateApolloClient(options)
217+
.mutate({
218+
mutation: gql`
219+
mutation ConfirmPartialIncomingPayment(
220+
$input: ConfirmPartialIncomingPaymentInput!
221+
) {
222+
confirmPartialIncomingPayment(input: $input) {
223+
success
224+
}
225+
}
226+
`,
227+
variables: {
228+
input: {
229+
incomingPaymentId,
230+
partialIncomingPaymentId
231+
}
232+
}
233+
})
234+
.then((query): LiquidityMutationResponse => {
235+
if (query.data) {
236+
return query.data.confirmPartialIncomingPayment
237+
} else {
238+
throw new Error('Data was empty')
239+
}
240+
})
241+
242+
return
243+
}
244+
189245
export async function handleWalletAddressWebMonetization(
190246
wh: Webhook,
191247
options?: TenantOptions

localenv/mock-account-servicing-entity/app/routes/webhooks.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { json } from '@remix-run/node'
33
import {
44
handleLowLiquidity,
55
handleWalletAddressNotFound,
6-
handleWalletAddressWebMonetization
7-
} from '~/lib/webhooks.server'
8-
import {
6+
handleWalletAddressWebMonetization,
97
handleOutgoingPaymentCreated,
108
handleOutgoingPaymentCompletedFailed,
11-
handleIncomingPaymentCompletedExpired
9+
handleIncomingPaymentCompletedExpired,
10+
handleIncomingPartialPaymentReceived
1211
} from '~/lib/webhooks.server'
1312
import { WebhookEventType, Webhook } from 'mock-account-service-lib'
1413
import { getTenantCredentials } from '~/lib/utils'
@@ -36,6 +35,9 @@ export async function action({ request }: ActionFunctionArgs) {
3635
break
3736
case WebhookEventType.IncomingPaymentCreated:
3837
break
38+
case WebhookEventType.IncomingPaymentPartialPaymentReceived:
39+
await handleIncomingPartialPaymentReceived(wh, tenantOptions)
40+
break
3941
case WebhookEventType.IncomingPaymentCompleted:
4042
case WebhookEventType.IncomingPaymentExpired:
4143
await handleIncomingPaymentCompletedExpired(wh, tenantOptions)

packages/backend/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@types/tmp": "^0.2.6",
3333
"@types/uuid": "^9.0.8",
3434
"cross-fetch": "^4.1.0",
35-
"ilp-protocol-stream": "^2.7.2-alpha.2",
35+
"ilp-protocol-stream": "2.7.2-alpha.3",
3636
"jest-environment-node": "^29.7.0",
3737
"jest-openapi": "^0.14.2",
3838
"nock": "14.0.0-beta.19",
@@ -57,8 +57,8 @@
5757
"@interledger/http-signature-utils": "2.0.2",
5858
"@interledger/open-payments": "7.3.0",
5959
"@interledger/openapi": "2.0.2",
60-
"@interledger/pay": "0.4.0-alpha.9",
61-
"@interledger/stream-receiver": "^0.3.3-alpha.3",
60+
"@interledger/pay": "0.4.0-alpha.10",
61+
"@interledger/stream-receiver": "0.3.3-alpha.4",
6262
"@koa/cors": "^5.0.0",
6363
"@koa/router": "^12.0.2",
6464
"@opentelemetry/api": "^1.8.0",

packages/backend/src/config/app.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,18 @@ export const Config = {
212212
),
213213
/** Optional base64-encoded key for encrypting partial-payment payload fields in webhooks. */
214214
dbEncryptionSecret: process.env.DB_ENCRYPTION_SECRET,
215+
enablePartialPaymentDecision: envBool(
216+
'ENABLE_PARTIAL_PAYMENT_DECISION',
217+
false
218+
),
219+
partialPaymentDecisionMaxWaitMs: envInt(
220+
'PARTIAL_PAYMENT_DECISION_MAX_WAIT_MS',
221+
1500
222+
),
223+
partialPaymentDecisionSafetyMarginMs: envInt(
224+
'PARTIAL_PAYMENT_DECISION_SAFETY_MARGIN_MS',
225+
100
226+
),
215227
cardServiceUrl: optional(envString, 'CARD_SERVICE_URL'),
216228
posServiceUrl: optional(envString, 'POS_SERVICE_URL'),
217229
posWebhookServiceUrl: optional(envString, 'POS_WEBHOOK_SERVICE_URL'),

packages/backend/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,13 +377,13 @@ export function initIocContainer(
377377

378378
container.singleton('incomingPaymentService', async (deps) => {
379379
return await createIncomingPaymentService({
380-
redis: await deps.use('redis'),
381380
logger: await deps.use('logger'),
382381
knex: await deps.use('knex'),
383382
accountingService: await deps.use('accountingService'),
384383
walletAddressService: await deps.use('walletAddressService'),
385384
assetService: await deps.use('assetService'),
386-
config: await deps.use('config')
385+
config: await deps.use('config'),
386+
redis: await deps.use('redis')
387387
})
388388
})
389389
container.singleton('remoteIncomingPaymentService', async (deps) => {

0 commit comments

Comments
 (0)