feat(backend, mock-ase): encrypted data exchange#3888
Conversation
✅ Deploy Preview for brilliant-pasca-3e80ec canceled.
|
3bea0bd to
e8c8822
Compare
…#3810) * feat: update sender data in incoming partial payment webhook * feat(backend): update partial payment payload in webhook * feat: uuid to id, use args object
* feat: update sender data in incoming partial payment webhook * feat(backend): update partial payment payload in webhook * feat: uuid to id, use args object * feat(backend): add confirmPartialIncomingPayment resolver * feat: move return, revert jest env * chore: rename kyc decision prefix
* feat(backend): add confirmPartialIncomingPayment resolver * feat: move return, revert jest env * feat(backend): reject partial payment gql api * fix: use correct redis key * fix: use const * feat: move redis call out of resolver
… 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
e8c8822 to
fa44fe8
Compare
🚀 Performance Test ResultsTest Configuration:
Test Metrics:
📜 Logs |
njlie
left a comment
There was a problem hiding this comment.
LGTM just needs the merge conflicts fixed, of course
…nto feature/encrypted-data-exchange
ab50e18 to
bdc1dfa
Compare
| expiresAt: | ||
| type: string | ||
| format: date-time | ||
| partialPayment: |
There was a problem hiding this comment.
I think the implementation differs from the spec here. we have partialPayment here but it looks like the implementation is doing partialPaymentId. I think it was originally correct when we implemented here but then something happened: #3810
As far as I know this isnt breaking anything (no spec validation against webhooks I think?) but it should match one way or another.
| input RejectPartialIncomingPaymentInput { | ||
| incomingPaymentId: ID! | ||
| partialIncomingPaymentId: ID! | ||
| "Reason why this incoming payment has been canceled. This value will be publicly visible in the metadata field if this incoming payment is requested through Open Payments." |
There was a problem hiding this comment.
This value will be publicly visible in the metadata field if this incoming payment is requested through Open Payments
I dont think we make good on this promise. rejectPartialIncomingPayment doesnt pass reason through. Should we be putting it on the metadata?
I don't have any context on this, but for whatever its worth, im not sure I like putting this on metadata. Historically it's been a client-controlled field and now we're injecting our own data there. We'd need to be careful in merging the data and it may not be very intuitive for the ASE. Im not sure if the new encrypted data has changed the role of this metadata field or something.
There was a problem hiding this comment.
That is a good point, I can't quite figure if the description of this field is stale and we forgot to update/remove it. CC @njlie
| if (decision?.success) { | ||
| await next() | ||
| return | ||
| } |
There was a problem hiding this comment.
I think this might be too loose. I see in #3686 that it says if value is missing by the deadline → proceed (next()). But if success is undefined (which I believe happens if it did not find anything by deadline), this will not call next.
So I think maybe this should be if (decision?.success !== false) { to ensure its a success. This is just from me tracing though - would be good to have a test for this.
There was a problem hiding this comment.
a test for this case - currently failing
test.each([
['no success field (timeout)', { message: 'No response from ASE' }],
[
'explicit success: undefined',
{ success: undefined, message: 'No response from ASE' }
]
])(
'allows payment when ASE does not respond (%s)',
async (_, decisionResult) => {
const { services } = makeServices()
const ctx = makeContext(undefined, services)
mockIncomingMoneyReply(ctx)
jest
.spyOn(services.incomingPayments, 'processPartialPayment')
.mockResolvedValue(decisionResult)
const next = jest.fn()
await expect(middleware(ctx, next)).resolves.toBeUndefined()
expect(next).toHaveBeenCalledTimes(1)
expect(ctx.response.reply).toBeUndefined()
}
)There was a problem hiding this comment.
Does the reject reason need to get back to the sending ASE? Doesnt look like it does, currently. This is what I see when I setup the mock ase to reject instead of confirm:
cloud-nine-backend-1 | {"level":40,"time":1776363393572,"pid":31,"hostname":"cloud-nine-wallet-backend","service":"OutgoingPaymentService","payment":"db31cbdd-aa28-4c54-a037-908c3d2b41a2","from_state":"SENDING","state":"SENDING","error":"Received error during ILP pay","stateAttempts":1,"errorDescription":"ApplicationError","msg":"payment lifecycle failed"}
cloud-nine-mock-ase-1 | received webhook: {"id":"10038171-7744-492b-90e4-ca09a0a187f4","type":"outgoing_payment.failed","data":{"id":"db31cbdd-aa28-4c54-a037-908c3d2b41a2","walletAddressId":"a74e201e-a81a-4736-a410-b38c0c3bf8b0","client":"https://happy-life-bank-backend/accounts/pfry","state":"FAILED","receiver":"https://happy-life-bank-backend/cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d/incoming-payments/d5b7a04d-0023-42ee-8d73-1e3b043f0df4","debitAmount":{"value":"205","assetCode":"USD","assetScale":2},"receiveAmount":{"value":"100","assetCode":"USD","assetScale":2},"sentAmount":{"value":"0","assetCode":"USD","assetScale":2},"stateAttempts":0,"createdAt":"2026-04-16T18:16:33.045Z","balance":"205","metadata":{"description":"Free Money!"},"error":"Received error during ILP pay","grantId":"b8cd2c65-661f-477c-8616-118304da6141"}}
I set the reason as "KYC checked failed" on the reject request. I was expecting to see this reason on the webhook to the sending ASE. But I just see a generic "Received error during ILP pay". Im not sure if the reject reason is exposed by the pay library currently.
I'm not specifically sure of the requirements but I assumed we needed this. It was briefly noted here although this issue is old and was closed. #3689
There was a problem hiding this comment.
I added the rejection reason on the webhook on the error field of the outgoing_payment.failed webhook in this commit as you suggested. If I am not mistaken #3689 was cancelled because of us not needing a new webhook, but IMO it makes sense to still put the rejection reason on outgoing_payment.failed (CC @mkurapov).
There was a problem hiding this comment.
looks good to me
cloud-nine-backend-1 | {"level":50,"time":1776710737471,"pid":31,"hostname":"cloud-nine-wallet-backend","service":"IlpPaymentService","err":{"type":"PaymentMethodHandlerError","message":"Received error during ILP pay","stack":"PaymentMethodHandlerError: Received error during ILP pay\n at pay (/home/rafiki/packages/backend/src/payment-method/ilp/service.ts:372:17)\n at Object.handleSending (/home/rafiki/packages/backend/src/open_payments/payment/outgoing/lifecycle.ts:117:23)\n at handlePaymentLifecycle (/home/rafiki/packages/backend/src/open_payments/payment/outgoing/worker.ts:106:5)\n at /home/rafiki/packages/backend/src/open_payments/payment/outgoing/worker.ts:32:9","name":"PaymentMethodHandlerError","description":"KYC verification failed: identity could not be confirmed.","retryable":false},"destination":"test.happy-life-bank.mp0E2F-vlTkUM0iCE4KZMMdV7DLouTgqs0dhjqYX-9BqhmPsNK_AfsCW4j1nDtytOkujCm3B_2cbX8eSL1G0u96Sv_seOc0","errorDescription":"KYC verification failed: identity could not be confirmed.","msg":"Received error during ILP pay"}
cloud-nine-backend-1 | {"level":40,"time":1776710737485,"pid":31,"hostname":"cloud-nine-wallet-backend","service":"OutgoingPaymentService","payment":"034979cb-9eaa-4f83-950a-8430ea331eb0","from_state":"SENDING","state":"SENDING","error":"KYC verification failed: identity could not be confirmed.","stateAttempts":1,"errorDescription":"KYC verification failed: identity could not be confirmed.","msg":"payment lifecycle failed"}
cloud-nine-mock-ase-1 | received webhook: {"id":"24e806f5-d330-419c-b3b1-4d1079b6c369","type":"outgoing_payment.failed","data":{"id":"034979cb-9eaa-4f83-950a-8430ea331eb0","walletAddressId":"8a34ee74-78b1-4d7d-8d60-90015cc0dd0e","client":"https://happy-life-bank-backend/accounts/pfry","state":"FAILED","receiver":"https://happy-life-bank-backend/cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d/incoming-payments/f207ab03-615e-4914-9478-8c4f0b996d5c","debitAmount":{"value":"205","assetCode":"USD","assetScale":2},"receiveAmount":{"value":"100","assetCode":"USD","assetScale":2},"sentAmount":{"value":"0","assetCode":"USD","assetScale":2},"stateAttempts":0,"createdAt":"2026-04-20T18:45:37.043Z","balance":"205","metadata":{"description":"Free Money!"},"error":"KYC verification failed: identity could not be confirmed.","grantId":"bf958282-c390-4c56-895b-be8b79e58fd1"}}
…ub.com/interledger/rafiki into feature/encrypted-data-exchange
|
Seems like backend build is failing because of some failing Tigerbeetle tests, but all the other (build) jobs are successful. Currently looking into this issue, might be related to the new lockfile or overrides that I added on this branch. Nothing related to the data exchange logic, though. |
|
I looked at the failing backend tests and why testcontainer couldnt find docker. No resolution yet. Here is what I found: Last working commit: 683096a. After that the lockfile is broken - once its fixed and the tests can be run we see the error. See this on install: I pinned The failure we are seeing, for reference: |
As mentioned in Slack I think it would be best to revert the testcontainer upgrade and handle that in our normal renovate package updating process. The update was just incidental to fixing the lockfile. Should make an issue for it though. |
Changes proposed in this pull request
This PR adds support for sending encrypted additional data over ILP on which the receiving ASE can action upon i.e. approve/reject a specific payment (e.g. if AML/KYC checks were not successful).
Context
Closes Rafiki Encrypted Data Exchange project
Checklist
fixes #numberuser-docslabel (if necessary)