diff --git a/bruno/collections/Rafiki/Examples/Open Payments/Create Incoming Payment.bru b/bruno/collections/Rafiki/Examples/Open Payments/Create Incoming Payment.bru index afc6464ee6..093a481bae 100644 --- a/bruno/collections/Rafiki/Examples/Open Payments/Create Incoming Payment.bru +++ b/bruno/collections/Rafiki/Examples/Open Payments/Create Incoming Payment.bru @@ -41,7 +41,7 @@ script:pre-request { script:post-response { const body = res.getBody(); - + if (body?.id) { bru.setEnvVar("incomingPaymentId", body.id.split("/").pop()); } diff --git a/bruno/collections/Rafiki/Examples/Web Monetization/Create Incoming Payment.bru b/bruno/collections/Rafiki/Examples/Web Monetization/Create Incoming Payment.bru index 071e7df0be..d6d4ab3a27 100644 --- a/bruno/collections/Rafiki/Examples/Web Monetization/Create Incoming Payment.bru +++ b/bruno/collections/Rafiki/Examples/Web Monetization/Create Incoming Payment.bru @@ -36,7 +36,7 @@ script:pre-request { script:post-response { const body = res.getBody(); - + if (body?.id) { bru.setEnvVar("incomingPaymentId", body.id.split("/").pop()); bru.setEnvVar("quoteDebitAmount", JSON.stringify({ diff --git a/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/Create Incoming Payment.bru b/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/Create Incoming Payment.bru index c718828e1c..8ed22f916a 100644 --- a/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/Create Incoming Payment.bru +++ b/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/Create Incoming Payment.bru @@ -42,7 +42,7 @@ script:pre-request { script:post-response { const body = res.getBody(); - + if (body?.id) { bru.setEnvVar("incomingPaymentId", body.id.split("/").pop()); } diff --git a/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/List Incoming Payments.bru b/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/List Incoming Payments.bru index 9fd7a87872..18a5fc0038 100644 --- a/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/List Incoming Payments.bru +++ b/bruno/collections/Rafiki/Open Payments APIs/Incoming Payments/List Incoming Payments.bru @@ -10,7 +10,7 @@ get { auth: none } -query { +params:query { first: 10 wallet-address: {{receiverWalletAddress}} ~cursor: ea3bf38f-2719-4473-a0f7-4ba967d1d81b diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Create Incoming Payment.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Create Incoming Payment.bru index 209041819c..4861feaa69 100644 --- a/bruno/collections/Rafiki/Rafiki Admin APIs/Create Incoming Payment.bru +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Create Incoming Payment.bru @@ -45,7 +45,8 @@ body:graphql:vars { }, "incomingAmount": null, "walletAddressId": "{{walletAddressId}}" - } + }, + "tenantId": "{{tenantId}}" } } diff --git a/bruno/collections/Rafiki/Rafiki Admin APIs/Get Incoming Payment By Tenant.bru b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Incoming Payment By Tenant.bru new file mode 100644 index 0000000000..48e6c925a6 --- /dev/null +++ b/bruno/collections/Rafiki/Rafiki Admin APIs/Get Incoming Payment By Tenant.bru @@ -0,0 +1,48 @@ +meta { + name: Get Incoming Payment By Tenant + type: graphql + seq: 54 +} + +post { + url: {{RafikiGraphqlHost}}/{{tenantId}}/graphql + body: graphql + auth: none +} + +body:graphql { + query GetIncomingPayment($id: String!) { + incomingPayment(id: $id) { + id + walletAddressId + client + state + expiresAt + incomingAmount { + value + assetCode + assetScale + } + receivedAmount { + value + assetCode + assetScale + } + metadata + createdAt + } + } +} + +body:graphql:vars { + { + "id": "{{incomingPaymentId}}", + "tenantId": "{{tenantId}}" + } +} + +script:pre-request { + const scripts = require('./scripts'); + + scripts.addApiSignatureHeader(); +} diff --git a/localenv/mock-account-servicing-entity/generated/graphql.ts b/localenv/mock-account-servicing-entity/generated/graphql.ts index 5296c25729..7ce6b6ce2e 100644 --- a/localenv/mock-account-servicing-entity/generated/graphql.ts +++ b/localenv/mock-account-servicing-entity/generated/graphql.ts @@ -600,6 +600,8 @@ export type IncomingPayment = BasePayment & Model & { receivedAmount: Amount; /** State of the incoming payment. */ state: IncomingPaymentState; + /** The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature. */ + tenantId?: Maybe; /** Unique identifier of the wallet address under which the incoming payment was created. */ walletAddressId: Scalars['ID']['output']; }; @@ -2283,6 +2285,7 @@ export type IncomingPaymentResolvers, ParentType, ContextType>; receivedAmount?: Resolver; state?: Resolver; + tenantId?: Resolver, ParentType, ContextType>; walletAddressId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/packages/backend/migrations/20250120101610_add_tenant_to_incoming_payments.js b/packages/backend/migrations/20250120101610_add_tenant_to_incoming_payments.js new file mode 100644 index 0000000000..06c5c0dfd8 --- /dev/null +++ b/packages/backend/migrations/20250120101610_add_tenant_to_incoming_payments.js @@ -0,0 +1,32 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema + .alterTable('incomingPayments', function (table) { + table.uuid('tenantId') + table.foreign('tenantId').references('id').inTable('tenants') + }) + .then(() => { + knex.raw( + `UPDATE "incomingPayments" SET "tenantId" = (SELECT id from "tenants" LIMIT 1)` + ) + }) + .then(() => { + knex.schema.alterTable('incomingPayments', function (table) { + table.uuid('tenantId').notNullable() + }) + }) +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('incomingPayments', function (table) { + table.dropForeign('tenantId') + table.dropColumn('tenantId') + }) +} diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index aa46528a58..701cfad6ce 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -499,7 +499,7 @@ export class App { // POST /incoming-payments // Create incoming payment router.post>( - '/incoming-payments', + '/:tenantId/incoming-payments', createValidatorMiddleware< ContextType> >( @@ -526,7 +526,7 @@ export class App { DefaultState, SignedCollectionContext >( - '/incoming-payments', + '/:tenantId/incoming-payments', createValidatorMiddleware< ContextType> >( @@ -625,7 +625,7 @@ export class App { // GET /incoming-payments/{id} // Read incoming payment router.get( - '/incoming-payments/:id', + '/:tenantId/incoming-payments/:id', createValidatorMiddleware< ContextType >( @@ -650,7 +650,7 @@ export class App { // POST /incoming-payments/{id}/complete // Complete incoming payment router.post( - '/incoming-payments/:id/complete', + '/:tenantId/incoming-payments/:id/complete', createValidatorMiddleware>( resourceServerSpec, { diff --git a/packages/backend/src/graphql/generated/graphql.schema.json b/packages/backend/src/graphql/generated/graphql.schema.json index f441f81038..47c62f1646 100644 --- a/packages/backend/src/graphql/generated/graphql.schema.json +++ b/packages/backend/src/graphql/generated/graphql.schema.json @@ -3511,6 +3511,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "tenantId", + "description": "The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "walletAddressId", "description": "Unique identifier of the wallet address under which the incoming payment was created.", diff --git a/packages/backend/src/graphql/generated/graphql.ts b/packages/backend/src/graphql/generated/graphql.ts index 5296c25729..7ce6b6ce2e 100644 --- a/packages/backend/src/graphql/generated/graphql.ts +++ b/packages/backend/src/graphql/generated/graphql.ts @@ -600,6 +600,8 @@ export type IncomingPayment = BasePayment & Model & { receivedAmount: Amount; /** State of the incoming payment. */ state: IncomingPaymentState; + /** The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature. */ + tenantId?: Maybe; /** Unique identifier of the wallet address under which the incoming payment was created. */ walletAddressId: Scalars['ID']['output']; }; @@ -2283,6 +2285,7 @@ export type IncomingPaymentResolvers, ParentType, ContextType>; receivedAmount?: Resolver; state?: Resolver; + tenantId?: Resolver, ParentType, ContextType>; walletAddressId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/packages/backend/src/graphql/resolvers/combined_payments.test.ts b/packages/backend/src/graphql/resolvers/combined_payments.test.ts index 0f2550a1b9..869aab33d1 100644 --- a/packages/backend/src/graphql/resolvers/combined_payments.test.ts +++ b/packages/backend/src/graphql/resolvers/combined_payments.test.ts @@ -74,7 +74,8 @@ describe('Payment', (): void => { }) const incomingPayment = await createIncomingPayment(deps, { walletAddressId: inWalletAddressId, - client: client + client: client, + tenantId: Config.operatorTenantId }) const query = await appContainer.apolloClient diff --git a/packages/backend/src/graphql/resolvers/incoming_payment.test.ts b/packages/backend/src/graphql/resolvers/incoming_payment.test.ts index 56c0c74ddd..77dfc66d96 100644 --- a/packages/backend/src/graphql/resolvers/incoming_payment.test.ts +++ b/packages/backend/src/graphql/resolvers/incoming_payment.test.ts @@ -38,6 +38,7 @@ describe('Incoming Payment Resolver', (): void => { let incomingPaymentService: IncomingPaymentService let accountingService: AccountingService let asset: Asset + let tenantId: string beforeAll(async (): Promise => { deps = await initIocContainer(Config) @@ -45,6 +46,7 @@ describe('Incoming Payment Resolver', (): void => { incomingPaymentService = await deps.use('incomingPaymentService') accountingService = await deps.use('accountingService') asset = await createAsset(deps) + tenantId = Config.operatorTenantId }) afterAll(async (): Promise => { @@ -78,7 +80,8 @@ describe('Incoming Payment Resolver', (): void => { metadata: { description: `IncomingPayment`, externalRef: '#123' - } + }, + tenantId }), pagedQuery: 'incomingPayments', parent: { @@ -118,7 +121,8 @@ describe('Incoming Payment Resolver', (): void => { client, metadata, expiresAt, - incomingAmount + incomingAmount, + tenantId }) const createSpy = jest @@ -167,7 +171,7 @@ describe('Incoming Payment Resolver', (): void => { query.data?.createIncomingPayment ) - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ ...input, tenantId }) expect(query).toEqual({ __typename: 'IncomingPaymentResponse', payment: { @@ -237,7 +241,7 @@ describe('Incoming Payment Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ ...input, tenantId }) }) test('Internal server error', async (): Promise => { @@ -282,7 +286,10 @@ describe('Incoming Payment Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ + ...input, + tenantId + }) }) }) @@ -299,7 +306,8 @@ describe('Incoming Payment Resolver', (): void => { value: BigInt(56), assetCode: asset.code, assetScale: asset.scale - } + }, + tenantId }) } @@ -475,7 +483,8 @@ describe('Incoming Payment Resolver', (): void => { walletAddressId, metadata, expiresAt, - incomingAmount + incomingAmount, + tenantId }) const input = { id: payment.id, @@ -510,7 +519,7 @@ describe('Incoming Payment Resolver', (): void => { query.data?.updateIncomingPayment ) - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ ...input, tenantId }) expect(query).toEqual({ __typename: 'IncomingPaymentResponse', payment: { @@ -565,7 +574,7 @@ describe('Incoming Payment Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ ...input, tenantId }) }) test('Internal server error', async (): Promise => { @@ -611,7 +620,7 @@ describe('Incoming Payment Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ ...input, tenantId }) }) }) }) diff --git a/packages/backend/src/graphql/resolvers/incoming_payment.ts b/packages/backend/src/graphql/resolvers/incoming_payment.ts index 778654dc2e..eb77bc7691 100644 --- a/packages/backend/src/graphql/resolvers/incoming_payment.ts +++ b/packages/backend/src/graphql/resolvers/incoming_payment.ts @@ -11,19 +11,20 @@ import { errorToCode, errorToMessage } from '../../open_payments/payment/incoming/errors' -import { ApolloContext } from '../../app' +import { ForTenantIdContext, TenantedApolloContext } from '../../app' import { getPageInfo } from '../../shared/pagination' import { Pagination, SortOrder } from '../../shared/baseModel' import { GraphQLError } from 'graphql' import { GraphQLErrorCode } from '../errors' -export const getIncomingPayment: QueryResolvers['incomingPayment'] = +export const getIncomingPayment: QueryResolvers['incomingPayment'] = async (parent, args, ctx): Promise => { const incomingPaymentService = await ctx.container.use( 'incomingPaymentService' ) const payment = await incomingPaymentService.get({ - id: args.id + id: args.id, + tenantId: ctx.isOperator ? undefined : ctx.tenant.id }) if (!payment) { throw new GraphQLError('payment does not exist', { @@ -35,7 +36,7 @@ export const getIncomingPayment: QueryResolvers['incomingPayment' return paymentToGraphql(payment) } -export const getWalletAddressIncomingPayments: WalletAddressResolvers['incomingPayments'] = +export const getWalletAddressIncomingPayments: WalletAddressResolvers['incomingPayments'] = async ( parent, args, @@ -56,14 +57,16 @@ export const getWalletAddressIncomingPayments: WalletAddressResolvers incomingPaymentService.getWalletAddressPage({ walletAddressId: parent.id as string, pagination, - sortOrder + sortOrder, + tenantId: ctx.tenant.id }), page: incomingPayments, sortOrder: order @@ -79,7 +82,7 @@ export const getWalletAddressIncomingPayments: WalletAddressResolvers['createIncomingPayment'] = +export const createIncomingPayment: MutationResolvers['createIncomingPayment'] = async ( parent, args, @@ -88,13 +91,20 @@ export const createIncomingPayment: MutationResolvers['createInco const incomingPaymentService = await ctx.container.use( 'incomingPaymentService' ) + + const tenantId = ctx.forTenantId + if (!tenantId) { + throw new Error('Missing tenant id to create incoming payment') + } + const incomingPaymentOrError = await incomingPaymentService.create({ walletAddressId: args.input.walletAddressId, expiresAt: !args.input.expiresAt ? undefined : new Date(args.input.expiresAt), incomingAmount: args.input.incomingAmount, - metadata: args.input.metadata + metadata: args.input.metadata, + tenantId }) if (isIncomingPaymentError(incomingPaymentOrError)) { throw new GraphQLError(errorToMessage[incomingPaymentOrError], { @@ -108,7 +118,7 @@ export const createIncomingPayment: MutationResolvers['createInco } } -export const updateIncomingPayment: MutationResolvers['updateIncomingPayment'] = +export const updateIncomingPayment: MutationResolvers['updateIncomingPayment'] = async ( parent, args, @@ -117,9 +127,10 @@ export const updateIncomingPayment: MutationResolvers['updateInco const incomingPaymentService = await ctx.container.use( 'incomingPaymentService' ) - const incomingPaymentOrError = await incomingPaymentService.update( - args.input - ) + const incomingPaymentOrError = await incomingPaymentService.update({ + ...args.input, + tenantId: ctx.tenant.id + }) if (isIncomingPaymentError(incomingPaymentOrError)) { throw new GraphQLError(errorToMessage[incomingPaymentOrError], { extensions: { @@ -132,7 +143,7 @@ export const updateIncomingPayment: MutationResolvers['updateInco } } -export const approveIncomingPayment: MutationResolvers['approveIncomingPayment'] = +export const approveIncomingPayment: MutationResolvers['approveIncomingPayment'] = async ( parent, args, @@ -143,7 +154,8 @@ export const approveIncomingPayment: MutationResolvers['approveIn ) const incomingPaymentOrError = await incomingPaymentService.approve( - args.input.id + args.input.id, + ctx.tenant.id ) if (isIncomingPaymentError(incomingPaymentOrError)) { @@ -159,7 +171,7 @@ export const approveIncomingPayment: MutationResolvers['approveIn } } -export const cancelIncomingPayment: MutationResolvers['cancelIncomingPayment'] = +export const cancelIncomingPayment: MutationResolvers['cancelIncomingPayment'] = async ( parent, args, @@ -170,7 +182,8 @@ export const cancelIncomingPayment: MutationResolvers['cancelInco ) const incomingPaymentOrError = await incomingPaymentService.cancel( - args.input.id + args.input.id, + ctx.tenant.id ) if (isIncomingPaymentError(incomingPaymentOrError)) { diff --git a/packages/backend/src/graphql/resolvers/liquidity.test.ts b/packages/backend/src/graphql/resolvers/liquidity.test.ts index 84df10f1fc..fc02751476 100644 --- a/packages/backend/src/graphql/resolvers/liquidity.test.ts +++ b/packages/backend/src/graphql/resolvers/liquidity.test.ts @@ -1759,7 +1759,8 @@ describe('Liquidity Resolvers', (): void => { assetCode: walletAddress.asset.code, assetScale: walletAddress.asset.scale }, - expiresAt: new Date(Date.now() + 60 * 1000) + expiresAt: new Date(Date.now() + 60 * 1000), + tenantId: Config.operatorTenantId }) payment = await createOutgoingPayment(deps, { walletAddressId, @@ -2171,7 +2172,8 @@ describe('Liquidity Resolvers', (): void => { assetCode: walletAddress.asset.code, assetScale: walletAddress.asset.scale }, - expiresAt: new Date(Date.now() + 60 * 1000) + expiresAt: new Date(Date.now() + 60 * 1000), + tenantId: Config.operatorTenantId }) outgoingPayment = await createOutgoingPayment(deps, { walletAddressId, diff --git a/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts b/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts index 00b4e0aa60..27d4aa8730 100644 --- a/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts +++ b/packages/backend/src/graphql/resolvers/outgoing_payment.test.ts @@ -160,7 +160,8 @@ describe('OutgoingPayment Resolvers', (): void => { } const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: firstReceiverWalletAddress.id + walletAddressId: firstReceiverWalletAddress.id, + tenantId: Config.operatorTenantId }) receiver = incomingPayment.getUrl(firstReceiverWalletAddress) firstOutgoingPayment = await createOutgoingPayment(deps, { @@ -171,7 +172,8 @@ describe('OutgoingPayment Resolvers', (): void => { }) const secondIncomingPayment = await createIncomingPayment(deps, { - walletAddressId: secondReceiverWalletAddress.id + walletAddressId: secondReceiverWalletAddress.id, + tenantId: Config.operatorTenantId }) const secondReceiver = secondIncomingPayment.getUrl(secondWalletAddress) secondOutgoingPayment = await createOutgoingPayment(deps, { diff --git a/packages/backend/src/graphql/resolvers/receiver.test.ts b/packages/backend/src/graphql/resolvers/receiver.test.ts index caa2cb32ef..8485a346be 100644 --- a/packages/backend/src/graphql/resolvers/receiver.test.ts +++ b/packages/backend/src/graphql/resolvers/receiver.test.ts @@ -106,7 +106,10 @@ describe('Receiver Resolver', (): void => { }) .then((query): CreateReceiverResponse => query.data?.createReceiver) - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ + ...input, + tenantId: Config.operatorTenantId + }) expect(query).toEqual({ __typename: 'CreateReceiverResponse', receiver: { @@ -187,7 +190,10 @@ describe('Receiver Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ + ...input, + tenantId: Config.operatorTenantId + }) }) test('returns error if error thrown when creating receiver', async (): Promise => { @@ -244,7 +250,10 @@ describe('Receiver Resolver', (): void => { }) ) } - expect(createSpy).toHaveBeenCalledWith(input) + expect(createSpy).toHaveBeenCalledWith({ + ...input, + tenantId: Config.operatorTenantId + }) }) }) diff --git a/packages/backend/src/graphql/resolvers/receiver.ts b/packages/backend/src/graphql/resolvers/receiver.ts index b9002c30f1..5ab7e76ee3 100644 --- a/packages/backend/src/graphql/resolvers/receiver.ts +++ b/packages/backend/src/graphql/resolvers/receiver.ts @@ -4,7 +4,7 @@ import { Receiver as SchemaReceiver, QueryResolvers } from '../generated/graphql' -import { ApolloContext } from '../../app' +import { ApolloContext, TenantedApolloContext } from '../../app' import { Receiver } from '../../open_payments/receiver/model' import { isReceiverError, @@ -32,17 +32,23 @@ export const getReceiver: QueryResolvers['receiver'] = async ( return receiverToGraphql(receiver) } -export const createReceiver: MutationResolvers['createReceiver'] = +export const createReceiver: MutationResolvers['createReceiver'] = async (_, args, ctx): Promise => { const receiverService = await ctx.container.use('receiverService') + const tenantId = ctx.tenant.id + if (!tenantId) { + throw new Error('Tenant id is required to create a receiver') + } + const receiverOrError = await receiverService.create({ walletAddressUrl: args.input.walletAddressUrl, expiresAt: args.input.expiresAt ? new Date(args.input.expiresAt) : undefined, incomingAmount: args.input.incomingAmount, - metadata: args.input.metadata + metadata: args.input.metadata, + tenantId }) if (isReceiverError(receiverOrError)) { diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 78218b01a1..69eab4330e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -922,6 +922,8 @@ type IncomingPayment implements BasePayment & Model { metadata: JSONObject "The date and time that the incoming payment was created." createdAt: String! + "The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature." + tenantId: String } type Receiver { diff --git a/packages/backend/src/open_payments/payment/combined/service.test.ts b/packages/backend/src/open_payments/payment/combined/service.test.ts index d9907f6084..6e6d663eab 100644 --- a/packages/backend/src/open_payments/payment/combined/service.test.ts +++ b/packages/backend/src/open_payments/payment/combined/service.test.ts @@ -64,7 +64,8 @@ describe('Combined Payment Service', (): void => { async function setupPayments(deps: IocContract) { const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: receiveWalletAddress.id + walletAddressId: receiveWalletAddress.id, + tenantId: Config.operatorTenantId }) const receiverUrl = incomingPayment.getUrl(receiveWalletAddress) diff --git a/packages/backend/src/open_payments/payment/incoming/model.test.ts b/packages/backend/src/open_payments/payment/incoming/model.test.ts index e25f524449..a71cf23d4b 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -48,14 +48,15 @@ describe('Models', (): void => { baseUrl = new URL(walletAddress.url).origin incomingPayment = await createIncomingPayment(deps, { walletAddressId: walletAddress.id, - metadata: { description: 'my payment' } + metadata: { description: 'my payment' }, + tenantId: walletAddress.tenantId }) }) describe('toOpenPaymentsType', () => { test('returns incoming payment', async () => { expect(incomingPayment.toOpenPaymentsType(walletAddress)).toEqual({ - id: `${baseUrl}${IncomingPayment.urlPath}/${incomingPayment.id}`, + id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, walletAddress: walletAddress.url, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), @@ -83,7 +84,7 @@ describe('Models', (): void => { streamCredentials ) ).toEqual({ - id: `${baseUrl}${IncomingPayment.urlPath}/${incomingPayment.id}`, + id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, walletAddress: walletAddress.url, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), @@ -108,7 +109,7 @@ describe('Models', (): void => { expect( incomingPayment.toOpenPaymentsTypeWithMethods(walletAddress) ).toEqual({ - id: `${baseUrl}${IncomingPayment.urlPath}/${incomingPayment.id}`, + id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, walletAddress: walletAddress.url, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), @@ -139,7 +140,7 @@ describe('Models', (): void => { streamCredentials ) ).toEqual({ - id: `${baseUrl}${IncomingPayment.urlPath}/${incomingPayment.id}`, + id: `${baseUrl}/${Config.operatorTenantId}${IncomingPayment.urlPath}/${incomingPayment.id}`, walletAddress: walletAddress.url, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index f68a254357..4739efc9dc 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -110,6 +110,7 @@ export class IncomingPayment private incomingAmountValue?: bigint | null private receivedAmountValue?: bigint + public readonly tenantId!: string public get completed(): boolean { return this.state === IncomingPaymentState.Completed @@ -144,7 +145,7 @@ export class IncomingPayment public getUrl(walletAddress: WalletAddress): string { const url = new URL(walletAddress.url) - return `${url.origin}${IncomingPayment.urlPath}/${this.id}` + return `${url.origin}/${walletAddress.tenantId}${IncomingPayment.urlPath}/${this.id}` } public async onCredit({ diff --git a/packages/backend/src/open_payments/payment/incoming/routes.test.ts b/packages/backend/src/open_payments/payment/incoming/routes.test.ts index fe479be640..06b0c69193 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.test.ts @@ -30,6 +30,7 @@ describe('Incoming Payment Routes', (): void => { let config: IAppConfig let incomingPaymentRoutes: IncomingPaymentRoutes let incomingPaymentService: IncomingPaymentService + let tenantId: string beforeAll(async (): Promise => { config = Config @@ -37,6 +38,7 @@ describe('Incoming Payment Routes', (): void => { appContainer = await createTestApp(deps) const { resourceServerSpec } = await deps.use('openApi') jestOpenAPI(resourceServerSpec) + tenantId = Config.operatorTenantId }) let asset: Asset @@ -54,7 +56,7 @@ describe('Incoming Payment Routes', (): void => { expiresAt = new Date(Date.now() + 30_000) asset = await createAsset(deps) walletAddress = await createWalletAddress(deps, { - tenantId: Config.operatorTenantId, + tenantId, assetId: asset.id }) baseUrl = new URL(walletAddress.url).origin @@ -86,7 +88,8 @@ describe('Incoming Payment Routes', (): void => { client, expiresAt, incomingAmount, - metadata + metadata, + tenantId }), get: (ctx) => incomingPaymentRoutes.get(ctx as ReadContextWithAuthenticatedStatus), @@ -129,10 +132,11 @@ describe('Incoming Payment Routes', (): void => { 'returns incoming payment with empty methods if payment state is %s', async (paymentState): Promise => { const walletAddress = await createWalletAddress(deps, { - tenantId: Config.operatorTenantId + tenantId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId }) await incomingPayment.$query().update({ state: paymentState }) @@ -153,7 +157,44 @@ describe('Incoming Payment Routes', (): void => { expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toMatchObject({ methods: [] }) } - ) + ), + test('by tenantId', async () => { + const walletAddress = await createWalletAddress(deps, { + tenantId + }) + const incomingPayment = await createIncomingPayment(deps, { + walletAddressId: walletAddress.id, + tenantId + }) + + const ctx = setup({ + reqOpts: { + headers: { Accept: 'application/json' }, + method: 'GET', + url: `/incoming-payments/${incomingPayment.id}` + }, + params: { + id: incomingPayment.id, + tenantId + }, + walletAddress + }) + + await expect(incomingPaymentRoutes.get(ctx)).resolves.toBeUndefined() + + expect(ctx.response).toSatisfyApiSpec() + expect(ctx.body).toMatchObject({ + methods: [ + { + type: 'ilp', + ilpAddress: expect.stringMatching( + /^test\.rafiki\.[a-zA-Z0-9_-]{95}$/ + ), + sharedSecret: expect.any(String) + } + ] + }) + }) }) describe('create', (): void => { @@ -172,7 +213,10 @@ describe('Incoming Payment Routes', (): void => { async (error): Promise => { const ctx = setup>({ reqOpts: { body: {} }, - walletAddress + walletAddress, + params: { + tenantId + } }) const createSpy = jest .spyOn(incomingPaymentService, 'create') @@ -182,7 +226,8 @@ describe('Incoming Payment Routes', (): void => { status: errorToHTTPCode[error] }) expect(createSpy).toHaveBeenCalledWith({ - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId }) } ) @@ -200,6 +245,9 @@ describe('Incoming Payment Routes', (): void => { expiresAt }): Promise => { const ctx = setup>({ + params: { + tenantId + }, reqOpts: { body: { incomingAmount: incomingAmount ? amount : undefined, @@ -220,7 +268,8 @@ describe('Incoming Payment Routes', (): void => { incomingAmount: incomingAmount ? parseAmount(amount) : undefined, metadata, expiresAt: expiresAt ? new Date(expiresAt) : undefined, - client + client, + tenantId }) expect(ctx.response).toSatisfyApiSpec() const incomingPaymentId = ( @@ -230,7 +279,7 @@ describe('Incoming Payment Routes', (): void => { .pop() expect(ctx.response.body).toEqual({ - id: `${baseUrl}/incoming-payments/${incomingPaymentId}`, + id: `${baseUrl}/${tenantId}/incoming-payments/${incomingPaymentId}`, walletAddress: walletAddress.url, incomingAmount: incomingAmount ? amount : undefined, expiresAt: expiresAt || expect.any(String), @@ -264,7 +313,8 @@ describe('Incoming Payment Routes', (): void => { walletAddressId: walletAddress.id, expiresAt, incomingAmount, - metadata + metadata, + tenantId }) }) test('returns 200 with an updated open payments incoming payment', async (): Promise => { @@ -275,7 +325,8 @@ describe('Incoming Payment Routes', (): void => { url: `/incoming-payments/${incomingPayment.id}/complete` }, params: { - id: incomingPayment.id + id: incomingPayment.id, + tenantId }, walletAddress }) @@ -309,7 +360,8 @@ describe('Incoming Payment Routes', (): void => { walletAddressId: walletAddress.id, expiresAt, incomingAmount, - metadata + metadata, + tenantId }) const ctx = setup({ @@ -319,7 +371,8 @@ describe('Incoming Payment Routes', (): void => { url: `/incoming-payments/${incomingPayment.id}` }, params: { - id: incomingPayment.id + id: incomingPayment.id, + tenantId }, walletAddress }) @@ -328,7 +381,7 @@ describe('Incoming Payment Routes', (): void => { await expect(incomingPaymentRoutes.get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ - authServer: config.authServerGrantUrl + '/' + config.operatorTenantId, // TODO: replace with incoming payment tenant id + authServer: config.authServerGrantUrl + '/' + incomingPayment.tenantId, receivedAmount: { value: '0', assetCode: asset.code, diff --git a/packages/backend/src/open_payments/payment/incoming/routes.ts b/packages/backend/src/open_payments/payment/incoming/routes.ts index 7d20908a55..d0c7b710a2 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.ts @@ -70,7 +70,8 @@ async function getIncomingPaymentPublic( ) { const incomingPayment = await deps.incomingPaymentService.get({ id: ctx.params.id, - client: ctx.accessAction === AccessAction.Read ? ctx.client : undefined + client: ctx.accessAction === AccessAction.Read ? ctx.client : undefined, + tenantId: ctx.params.tenantId }) if (!incomingPayment) { @@ -94,7 +95,8 @@ async function getIncomingPaymentPrivate( ): Promise { const incomingPayment = await deps.incomingPaymentService.get({ id: ctx.params.id, - client: ctx.accessAction === AccessAction.Read ? ctx.client : undefined + client: ctx.accessAction === AccessAction.Read ? ctx.client : undefined, + tenantId: ctx.params.tenantId }) if (!incomingPayment) { @@ -138,7 +140,8 @@ async function createIncomingPayment( client: ctx.client, metadata: body.metadata, expiresAt, - incomingAmount: body.incomingAmount && parseAmount(body.incomingAmount) + incomingAmount: body.incomingAmount && parseAmount(body.incomingAmount), + tenantId: ctx.params.tenantId }) if (isIncomingPaymentError(incomingPaymentOrError)) { @@ -163,7 +166,8 @@ async function completeIncomingPayment( ctx: CompleteContext ): Promise { const incomingPaymentOrError = await deps.incomingPaymentService.complete( - ctx.params.id + ctx.params.id, + ctx.params.tenantId ) if (isIncomingPaymentError(incomingPaymentOrError)) { @@ -182,7 +186,13 @@ async function listIncomingPayments( ): Promise { await listSubresource({ ctx, - getWalletAddressPage: deps.incomingPaymentService.getWalletAddressPage, + getWalletAddressPage: async ({ walletAddressId, pagination, client }) => + deps.incomingPaymentService.getWalletAddressPage({ + walletAddressId, + pagination, + client, + tenantId: ctx.params.tenantId + }), toBody: (payment) => payment.toOpenPaymentsType(ctx.walletAddress) }) } diff --git a/packages/backend/src/open_payments/payment/incoming/service.test.ts b/packages/backend/src/open_payments/payment/incoming/service.test.ts index 6fa47cef0d..afacfd0452 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.test.ts @@ -38,6 +38,7 @@ describe('Incoming Payment Service', (): void => { let accountingService: AccountingService let asset: Asset let config: IAppConfig + let tenantId: string beforeAll(async (): Promise => { deps = initIocContainer({ @@ -49,6 +50,7 @@ describe('Incoming Payment Service', (): void => { knex = appContainer.knex incomingPaymentService = await deps.use('incomingPaymentService') config = await deps.use('config') + tenantId = Config.operatorTenantId }) beforeEach(async (): Promise => { @@ -104,7 +106,8 @@ describe('Incoming Payment Service', (): void => { const options = { client: faker.internet.url({ appendSlash: false }), incomingAmount: true, - expiresAt: new Date(Date.now() + 30_000) + expiresAt: new Date(Date.now() + 30_000), + tenantId } return incomingPaymentService.create({ @@ -174,9 +177,9 @@ describe('Incoming Payment Service', (): void => { describe('approveIncomingPayment', (): void => { it('should return UnknownPayment error if payment does not exist', async (): Promise => { - expect(incomingPaymentService.approve(uuid())).resolves.toBe( - IncomingPaymentError.UnknownPayment - ) + expect( + incomingPaymentService.approve(uuid(), Config.operatorTenantId) + ).resolves.toBe(IncomingPaymentError.UnknownPayment) }) it('should not approve already cancelled incoming payment', async (): Promise => { @@ -188,7 +191,8 @@ describe('Incoming Payment Service', (): void => { .patch({ cancelledAt: new Date() }) const response = await incomingPaymentService.approve( - incomingPayment.id + incomingPayment.id, + Config.operatorTenantId ) expect(response).toBe(IncomingPaymentError.AlreadyActioned) }) @@ -203,7 +207,8 @@ describe('Incoming Payment Service', (): void => { .patch({ approvedAt }) const approvedPayment = await incomingPaymentService.approve( - incomingPayment.id + incomingPayment.id, + Config.operatorTenantId ) assert.ok(!isIncomingPaymentError(approvedPayment)) @@ -221,7 +226,8 @@ describe('Incoming Payment Service', (): void => { .patch({ state: IncomingPaymentState.Pending }) const approvedIncomingPayment = await incomingPaymentService.approve( - incomingPayment.id + incomingPayment.id, + Config.operatorTenantId ) assert.ok(!isIncomingPaymentError(approvedIncomingPayment)) expect(approvedIncomingPayment.id).toBe(incomingPayment.id) @@ -233,9 +239,9 @@ describe('Incoming Payment Service', (): void => { describe('cancelIncomingPayment', (): void => { it('should return UnknownPayment error if payment does not exist', async (): Promise => { - expect(incomingPaymentService.cancel(uuid())).resolves.toBe( - IncomingPaymentError.UnknownPayment - ) + expect( + incomingPaymentService.cancel(uuid(), Config.operatorTenantId) + ).resolves.toBe(IncomingPaymentError.UnknownPayment) }) it('should not cancel already approved incoming payment', async (): Promise => { @@ -246,7 +252,10 @@ describe('Incoming Payment Service', (): void => { .findOne({ id: incomingPayment.id }) .patch({ approvedAt: new Date() }) - const response = await incomingPaymentService.cancel(incomingPayment.id) + const response = await incomingPaymentService.cancel( + incomingPayment.id, + Config.operatorTenantId + ) expect(response).toBe(IncomingPaymentError.AlreadyActioned) }) @@ -260,7 +269,8 @@ describe('Incoming Payment Service', (): void => { .patch({ cancelledAt }) const cancelledPayment = await incomingPaymentService.cancel( - incomingPayment.id + incomingPayment.id, + Config.operatorTenantId ) assert.ok(!isIncomingPaymentError(cancelledPayment)) @@ -278,7 +288,8 @@ describe('Incoming Payment Service', (): void => { .patch({ state: IncomingPaymentState.Pending }) const canceledIncomingPayment = await incomingPaymentService.cancel( - incomingPayment.id + incomingPayment.id, + Config.operatorTenantId ) assert.ok(!isIncomingPaymentError(canceledIncomingPayment)) expect(canceledIncomingPayment.id).toBe(incomingPayment.id) @@ -343,7 +354,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.UnknownWalletAddress) }) @@ -365,7 +377,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidAmount) await expect( @@ -380,7 +393,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidAmount) }) @@ -398,7 +412,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidAmount) await expect( @@ -413,7 +428,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidAmount) }) @@ -431,7 +447,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidExpiry) }) @@ -454,7 +471,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InactiveWalletAddress) }) @@ -474,7 +492,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) ).resolves.toBe(IncomingPaymentError.InvalidExpiry) }) @@ -491,7 +510,8 @@ describe('Incoming Payment Service', (): void => { } payment = (await incomingPaymentService.create({ walletAddressId, - incomingAmount: amount + incomingAmount: amount, + tenantId })) as IncomingPayment assert.ok(!isIncomingPaymentError(payment)) }) @@ -505,6 +525,7 @@ describe('Incoming Payment Service', (): void => { async ({ metadata }): Promise => { const incomingPayment = await incomingPaymentService.update({ id: payment.id, + tenantId: Config.operatorTenantId, metadata }) assert.ok(!isIncomingPaymentError(incomingPayment)) @@ -519,6 +540,7 @@ describe('Incoming Payment Service', (): void => { await expect( incomingPaymentService.update({ id: uuid(), + tenantId: Config.operatorTenantId, metadata: { description: 'Test incoming payment', externalRef: '#123' @@ -543,7 +565,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }), get: (options) => incomingPaymentService.get(options), list: (options) => incomingPaymentService.getWalletAddressPage(options) @@ -565,7 +588,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) assert.ok(!isIncomingPaymentError(incomingPaymentOrError)) incomingPayment = incomingPaymentOrError @@ -628,7 +652,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) assert.ok(!isIncomingPaymentError(incomingPaymentOrError)) const incomingPaymentId = incomingPaymentOrError.id @@ -657,7 +682,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) await expect( accountingService.createDeposit({ @@ -694,7 +720,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) jest.useFakeTimers() jest.setSystemTime(incomingPayment.expiresAt) @@ -731,7 +758,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) await expect( accountingService.createDeposit({ @@ -819,7 +847,8 @@ describe('Incoming Payment Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId }) }) test('updates state of pending incoming payment to complete', async (): Promise => { @@ -827,11 +856,15 @@ describe('Incoming Payment Service', (): void => { jest.useFakeTimers({ now }) await expect( - incomingPaymentService.complete(incomingPayment.id) + incomingPaymentService.complete( + incomingPayment.id, + Config.operatorTenantId + ) ).resolves.toMatchObject({ id: incomingPayment.id, state: IncomingPaymentState.Completed, - processAt: now + processAt: now, + tenantId: Config.operatorTenantId }) await expect( incomingPaymentService.get({ @@ -841,12 +874,21 @@ describe('Incoming Payment Service', (): void => { state: IncomingPaymentState.Completed, processAt: now }) + await expect( + incomingPaymentService.get({ + id: incomingPayment.id, + tenantId: Config.operatorTenantId + }) + ).resolves.toMatchObject({ + state: IncomingPaymentState.Completed, + processAt: now + }) }) test('fails to complete unknown payment', async (): Promise => { - await expect(incomingPaymentService.complete(uuid())).resolves.toEqual( - IncomingPaymentError.UnknownPayment - ) + await expect( + incomingPaymentService.complete(uuid(), Config.operatorTenantId) + ).resolves.toEqual(IncomingPaymentError.UnknownPayment) }) test('updates state of processing incoming payment to complete', async (): Promise => { @@ -864,7 +906,10 @@ describe('Incoming Payment Service', (): void => { state: IncomingPaymentState.Processing }) await expect( - incomingPaymentService.complete(incomingPayment.id) + incomingPaymentService.complete( + incomingPayment.id, + Config.operatorTenantId + ) ).resolves.toMatchObject({ id: incomingPayment.id, state: IncomingPaymentState.Completed, @@ -902,7 +947,10 @@ describe('Incoming Payment Service', (): void => { state: IncomingPaymentState.Expired }) await expect( - incomingPaymentService.complete(incomingPayment.id) + incomingPaymentService.complete( + incomingPayment.id, + Config.operatorTenantId + ) ).resolves.toBe(IncomingPaymentError.WrongState) await expect( incomingPaymentService.get({ @@ -925,7 +973,10 @@ describe('Incoming Payment Service', (): void => { state: IncomingPaymentState.Completed }) await expect( - incomingPaymentService.complete(incomingPayment.id) + incomingPaymentService.complete( + incomingPayment.id, + Config.operatorTenantId + ) ).resolves.toBe(IncomingPaymentError.WrongState) await expect( incomingPaymentService.get({ diff --git a/packages/backend/src/open_payments/payment/incoming/service.ts b/packages/backend/src/open_payments/payment/incoming/service.ts index 6229c613fe..371eaafb41 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.ts @@ -32,11 +32,13 @@ export interface CreateIncomingPaymentOptions { expiresAt?: Date incomingAmount?: Amount metadata?: Record + tenantId: string } export interface UpdateOptions { id: string metadata: Record + tenantId: string } export interface IncomingPaymentService @@ -45,9 +47,18 @@ export interface IncomingPaymentService options: CreateIncomingPaymentOptions, trx?: Knex.Transaction ): Promise - approve(id: string): Promise - cancel(id: string): Promise - complete(id: string): Promise + approve( + id: string, + tenantId: string + ): Promise + cancel( + id: string, + tenantId: string + ): Promise + complete( + id: string, + tenantId: string + ): Promise processNext(): Promise update( options: UpdateOptions @@ -75,9 +86,9 @@ export async function createIncomingPaymentService( return { get: (options) => getIncomingPayment(deps, options), create: (options, trx) => createIncomingPayment(deps, options, trx), - approve: (id) => approveIncomingPayment(deps, id), - cancel: (id) => cancelIncomingPayment(deps, id), - complete: (id) => completeIncomingPayment(deps, id), + approve: (id, tenantId) => approveIncomingPayment(deps, id, tenantId), + cancel: (id, tenantId) => cancelIncomingPayment(deps, id, tenantId), + complete: (id, tenantId) => completeIncomingPayment(deps, id, tenantId), getWalletAddressPage: (options) => getWalletAddressPage(deps, options), processNext: () => processNextIncomingPayment(deps), update: (options) => updateIncomingPayment(deps, options) @@ -107,7 +118,7 @@ async function updateIncomingPayment( ): Promise { const incomingPayment = await IncomingPayment.query( deps.knex - ).patchAndFetchById(options.id, { metadata: options.metadata }) + ).patchAndFetchById(options.id, options) if (incomingPayment) { const asset = await deps.assetService.get(incomingPayment.assetId) if (asset) incomingPayment.asset = asset @@ -129,7 +140,8 @@ async function createIncomingPayment( client, expiresAt, incomingAmount, - metadata + metadata, + tenantId }: CreateIncomingPaymentOptions, trx?: Knex.Transaction ): Promise { @@ -144,7 +156,10 @@ async function createIncomingPayment( if (incomingAmount && incomingAmount.value <= 0) { return IncomingPaymentError.InvalidAmount } - const walletAddress = await deps.walletAddressService.get(walletAddressId) + const walletAddress = await deps.walletAddressService.get( + walletAddressId, + tenantId + ) if (!walletAddress) { return IncomingPaymentError.UnknownWalletAddress } @@ -170,7 +185,8 @@ async function createIncomingPayment( incomingAmount, metadata, state: IncomingPaymentState.Pending, - processAt: expiresAt + processAt: expiresAt, + tenantId }) const asset = await deps.assetService.get(incomingPayment.assetId) @@ -363,7 +379,12 @@ async function getWalletAddressPage( deps: ServiceDependencies, options: ListOptions ): Promise { - const page = await IncomingPayment.query(deps.knex).list(options) + const pageQuery = IncomingPayment.query(deps.knex) + + if (options.tenantId) pageQuery.where('tenantId', options.tenantId) + + const page = await pageQuery.list(options) + for (const payment of page) { const asset = await deps.assetService.get(payment.assetId) if (asset) payment.asset = asset @@ -399,10 +420,13 @@ async function getWalletAddressPage( async function approveIncomingPayment( deps: ServiceDependencies, - id: string + id: string, + tenantId: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx).findById(id).forUpdate() + const payment = await IncomingPayment.query(trx) + .findOne({ id, tenantId }) + .forUpdate() if (!payment) return IncomingPaymentError.UnknownPayment @@ -436,10 +460,13 @@ async function approveIncomingPayment( async function cancelIncomingPayment( deps: ServiceDependencies, - id: string + id: string, + tenantId: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx).findById(id).forUpdate() + const payment = await IncomingPayment.query(trx) + .findOne({ id, tenantId }) + .forUpdate() if (!payment) return IncomingPaymentError.UnknownPayment @@ -473,10 +500,13 @@ async function cancelIncomingPayment( async function completeIncomingPayment( deps: ServiceDependencies, - id: string + id: string, + tenantId: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx).findById(id).forUpdate() + const payment = await IncomingPayment.query(trx) + .findOne({ id, tenantId }) + .forUpdate() if (!payment) return IncomingPaymentError.UnknownPayment const asset = await deps.assetService.get(payment.assetId) diff --git a/packages/backend/src/open_payments/payment/incoming_remote/service.ts b/packages/backend/src/open_payments/payment/incoming_remote/service.ts index bfe4513536..72c830e5c5 100644 --- a/packages/backend/src/open_payments/payment/incoming_remote/service.ts +++ b/packages/backend/src/open_payments/payment/incoming_remote/service.ts @@ -13,7 +13,6 @@ import { BaseService } from '../../../shared/baseService' import { Amount, serializeAmount } from '../../amount' import { RemoteIncomingPaymentError } from './errors' import { isGrantError } from '../../grant/errors' -import { urlWithoutTenantId } from '../../../shared/utils' interface CreateRemoteIncomingPaymentArgs { walletAddressUrl: string @@ -117,7 +116,7 @@ async function createIncomingPayment( try { return await deps.openPaymentsClient.incomingPayment.create( { - url: urlWithoutTenantId(resourceServerUrl), + url: resourceServerUrl, accessToken: grant.accessToken }, { diff --git a/packages/backend/src/open_payments/payment/outgoing/service.test.ts b/packages/backend/src/open_payments/payment/outgoing/service.test.ts index cb60860df1..667009087e 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -292,7 +292,8 @@ describe('OutgoingPaymentService', (): void => { ).resolves.toBeUndefined() incomingPayment = await createIncomingPayment(deps, { - walletAddressId: receiverWalletAddress.id + walletAddressId: receiverWalletAddress.id, + tenantId: Config.operatorTenantId }) receiver = incomingPayment.getUrl(receiverWalletAddress) @@ -419,7 +420,8 @@ describe('OutgoingPaymentService', (): void => { assetId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: receiverWalletAddress.id + walletAddressId: receiverWalletAddress.id, + tenantId: Config.operatorTenantId }) otherReceiver = incomingPayment.getUrl(otherReceiverWalletAddress) @@ -1355,7 +1357,8 @@ describe('OutgoingPaymentService', (): void => { value: receiveAmount.value, assetCode: receiverWalletAddress.asset.code, assetScale: receiverWalletAddress.asset.scale - } + }, + tenantId: Config.operatorTenantId }) assert.ok(incomingPayment.walletAddress) @@ -1436,7 +1439,8 @@ describe('OutgoingPaymentService', (): void => { value: receiveAmount.value * 2n, assetCode: receiverWalletAddress.asset.code, assetScale: receiverWalletAddress.asset.scale - } + }, + tenantId: Config.operatorTenantId }) assert.ok(incomingPayment.walletAddress) @@ -1506,7 +1510,8 @@ describe('OutgoingPaymentService', (): void => { value: receiveAmount.value * 2n, assetCode: receiverWalletAddress.asset.code, assetScale: receiverWalletAddress.asset.scale - } + }, + tenantId: Config.operatorTenantId }) assert.ok(incomingPayment.id) assert.ok(incomingPayment.createdAt) diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index 75e59ec365..7bd4917c11 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -178,7 +178,8 @@ describe('QuoteService', (): void => { beforeEach(async (): Promise => { incomingPayment = await createIncomingPayment(deps, { walletAddressId: receivingWalletAddress.id, - incomingAmount + incomingAmount, + tenantId: Config.operatorTenantId }) options = { walletAddressId: sendingWalletAddress.id, @@ -382,7 +383,8 @@ describe('QuoteService', (): void => { const incomingPayment = await createIncomingPayment(deps, { walletAddressId: receivingWalletAddress.id, incomingAmount, - expiresAt: expiryDate + expiresAt: expiryDate, + tenantId: Config.operatorTenantId }) const options: CreateQuoteOptions = { walletAddressId: sendingWalletAddress.id, @@ -503,7 +505,8 @@ describe('QuoteService', (): void => { 'fails to create $description', async ({ debitAmount, receiveAmount }): Promise => { const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: receivingWalletAddress.id + walletAddressId: receivingWalletAddress.id, + tenantId: Config.operatorTenantId }) const options: CreateQuoteOptions = { walletAddressId: sendingWalletAddress.id, @@ -557,7 +560,8 @@ describe('QuoteService', (): void => { assetCode: asset.code, assetScale: asset.scale, value: incomingAmountValue - } + }, + tenantId: Config.operatorTenantId }) await Fee.query().insertAndFetch({ @@ -599,7 +603,8 @@ describe('QuoteService', (): void => { assetCode: asset.code, assetScale: asset.scale, value: incomingAmountValue - } + }, + tenantId: Config.operatorTenantId }) const mockedQuote = mockQuote({ @@ -750,7 +755,8 @@ describe('QuoteService', (): void => { test('Local receiver uses local payment method', async () => { const incomingPayment = await createIncomingPayment(deps, { walletAddressId: receivingWalletAddress.id, - incomingAmount + incomingAmount, + tenantId: Config.operatorTenantId }) const options: CreateQuoteOptions = { diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index 5976465ad5..5ed02b0144 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -42,7 +42,8 @@ describe('Receiver Model', (): void => { tenantId: Config.operatorTenantId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId: Config.operatorTenantId }) const isLocal = true @@ -88,7 +89,8 @@ describe('Receiver Model', (): void => { tenantId: Config.operatorTenantId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId: Config.operatorTenantId }) incomingPayment.state = IncomingPaymentState.Completed @@ -113,7 +115,8 @@ describe('Receiver Model', (): void => { tenantId: Config.operatorTenantId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId: Config.operatorTenantId }) incomingPayment.expiresAt = new Date(Date.now() - 1) @@ -135,7 +138,8 @@ describe('Receiver Model', (): void => { tenantId: Config.operatorTenantId }) const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId: Config.operatorTenantId }) const streamCredentials = streamCredentialsService.get(incomingPayment) diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index debc5c8a56..0596c7cf3d 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -45,6 +45,7 @@ describe('Receiver Service', (): void => { let streamCredentialsService: StreamCredentialsService let remoteIncomingPaymentService: RemoteIncomingPaymentService let serviceDeps: ServiceDependencies + let tenantId: string beforeAll(async (): Promise => { deps = initIocContainer(Config) @@ -66,6 +67,7 @@ describe('Receiver Service', (): void => { streamCredentialsService, telemetry: await deps.use('telemetry') } + tenantId = Config.operatorTenantId }) afterEach(async (): Promise => { @@ -90,7 +92,8 @@ describe('Receiver Service', (): void => { value: BigInt(5), assetCode: walletAddress.asset.code, assetScale: walletAddress.asset.scale - } + }, + tenantId: Config.operatorTenantId }) await expect( @@ -315,7 +318,8 @@ describe('Receiver Service', (): void => { walletAddressUrl: walletAddress.url, incomingAmount, expiresAt, - metadata + metadata, + tenantId }) assert(receiver instanceof Receiver) @@ -349,7 +353,8 @@ describe('Receiver Service', (): void => { walletAddressId: walletAddress.id, incomingAmount, expiresAt, - metadata + metadata, + tenantId: Config.operatorTenantId }) expect(remoteIncomingPaymentCreateSpy).not.toHaveBeenCalled() } @@ -362,7 +367,8 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: walletAddress.url + walletAddressUrl: walletAddress.url, + tenantId }) ).resolves.toEqual(ReceiverError.InvalidAmount) }) @@ -374,7 +380,8 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: walletAddress.url + walletAddressUrl: walletAddress.url, + tenantId }) ).rejects.toThrow( 'Could not get stream credentials for local incoming payment' @@ -422,7 +429,8 @@ describe('Receiver Service', (): void => { walletAddressUrl: walletAddress.id, incomingAmount, expiresAt, - metadata + metadata, + tenantId }) expect(receiver).toEqual({ @@ -458,7 +466,8 @@ describe('Receiver Service', (): void => { walletAddressUrl: walletAddress.id, incomingAmount, expiresAt, - metadata + metadata, + tenantId }) expect(localIncomingPaymentCreateSpy).not.toHaveBeenCalled() } @@ -473,7 +482,8 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: walletAddress.id + walletAddressUrl: walletAddress.id, + tenantId }) ).resolves.toEqual(ReceiverError.UnknownWalletAddress) }) @@ -493,7 +503,8 @@ describe('Receiver Service', (): void => { await expect( receiverService.create({ - walletAddressUrl: mockedIncomingPayment.walletAddress + walletAddressUrl: mockedIncomingPayment.walletAddress, + tenantId }) ).rejects.toThrow('Could not create receiver from incoming payment') expect(remoteIncomingPaymentServiceCreateSpy).toHaveBeenCalledTimes(1) diff --git a/packages/backend/src/open_payments/receiver/service.ts b/packages/backend/src/open_payments/receiver/service.ts index d93c902fda..622edb104b 100644 --- a/packages/backend/src/open_payments/receiver/service.ts +++ b/packages/backend/src/open_payments/receiver/service.ts @@ -21,6 +21,7 @@ interface CreateReceiverArgs { expiresAt?: Date incomingAmount?: Amount metadata?: Record + tenantId: string } // A receiver is resolved from an incoming payment @@ -97,13 +98,14 @@ async function createLocalIncomingPayment( args: CreateReceiverArgs, walletAddress: WalletAddress ): Promise { - const { expiresAt, incomingAmount, metadata } = args + const { expiresAt, incomingAmount, metadata, tenantId } = args const incomingPaymentOrError = await deps.incomingPaymentService.create({ walletAddressId: walletAddress.id, expiresAt, incomingAmount, - metadata + metadata, + tenantId }) if (isIncomingPaymentError(incomingPaymentOrError)) { diff --git a/packages/backend/src/open_payments/wallet_address/middleware.ts b/packages/backend/src/open_payments/wallet_address/middleware.ts index 6a6601d9f9..2404966244 100644 --- a/packages/backend/src/open_payments/wallet_address/middleware.ts +++ b/packages/backend/src/open_payments/wallet_address/middleware.ts @@ -43,7 +43,8 @@ export async function getWalletAddressUrlFromIncomingPayment( 'incomingPaymentService' ) const incomingPayment = await incomingPaymentService.get({ - id: ctx.params.id + id: ctx.params.id, + tenantId: ctx.params.tenantId }) if (!incomingPayment?.walletAddress) { diff --git a/packages/backend/src/open_payments/wallet_address/model.ts b/packages/backend/src/open_payments/wallet_address/model.ts index c6de194da7..3991c16f53 100644 --- a/packages/backend/src/open_payments/wallet_address/model.ts +++ b/packages/backend/src/open_payments/wallet_address/model.ts @@ -191,6 +191,7 @@ export interface GetOptions { id: string client?: string walletAddressId?: string + tenantId?: string } export interface ListOptions { @@ -198,6 +199,7 @@ export interface ListOptions { client?: string pagination?: Pagination sortOrder?: SortOrder + tenantId?: string } class SubresourceQueryBuilder< diff --git a/packages/backend/src/open_payments/wallet_address/routes.ts b/packages/backend/src/open_payments/wallet_address/routes.ts index 339ef3c789..f3b75c989d 100644 --- a/packages/backend/src/open_payments/wallet_address/routes.ts +++ b/packages/backend/src/open_payments/wallet_address/routes.ts @@ -95,14 +95,16 @@ export const listSubresource = async ({ const page = await getWalletAddressPage({ walletAddressId: ctx.walletAddress.id, pagination, - client + client, + tenantId: ctx.params.tenantId }) const pageInfo = await getPageInfo({ getPage: (pagination) => getWalletAddressPage({ walletAddressId: ctx.walletAddress.id, pagination, - client + client, + tenantId: ctx.params.tenantId }), page, walletAddress: ctx.request.query['wallet-address'] diff --git a/packages/backend/src/open_payments/wallet_address/service.test.ts b/packages/backend/src/open_payments/wallet_address/service.test.ts index cceeaba6bb..5d6f139996 100644 --- a/packages/backend/src/open_payments/wallet_address/service.test.ts +++ b/packages/backend/src/open_payments/wallet_address/service.test.ts @@ -250,7 +250,8 @@ describe('Open Payments Wallet Address Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId: Config.operatorTenantId }) await walletAddressService.update({ @@ -294,7 +295,8 @@ describe('Open Payments Wallet Address Service', (): void => { metadata: { description: 'Test incoming payment', externalRef: '#123' - } + }, + tenantId: Config.operatorTenantId }) await walletAddressService.update({ diff --git a/packages/backend/src/payment-method/ilp/service.test.ts b/packages/backend/src/payment-method/ilp/service.test.ts index bba27b8315..db5dda2be6 100644 --- a/packages/backend/src/payment-method/ilp/service.test.ts +++ b/packages/backend/src/payment-method/ilp/service.test.ts @@ -340,7 +340,8 @@ describe('IlpPaymentService', (): void => { quoteId: uuid(), walletAddress: walletAddressMap['USD'], receiver: await createReceiver(deps, walletAddressMap['USD'], { - incomingAmount + incomingAmount, + tenantId: Config.operatorTenantId }) } @@ -435,7 +436,8 @@ describe('IlpPaymentService', (): void => { assetCode: 'USD', assetScale: 2, value: 100n - } + }, + tenantId: Config.operatorTenantId }) } diff --git a/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts b/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts index 8b479ac893..3092f76491 100644 --- a/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts +++ b/packages/backend/src/payment-method/ilp/stream-credentials/service.test.ts @@ -31,7 +31,8 @@ describe('Stream Credentials Service', (): void => { tenantId: Config.operatorTenantId }) incomingPayment = await createIncomingPayment(deps, { - walletAddressId + walletAddressId, + tenantId: Config.operatorTenantId }) }) diff --git a/packages/backend/src/payment-method/local/service.test.ts b/packages/backend/src/payment-method/local/service.test.ts index c92cb4449d..6af3327f49 100644 --- a/packages/backend/src/payment-method/local/service.test.ts +++ b/packages/backend/src/payment-method/local/service.test.ts @@ -250,7 +250,8 @@ describe('LocalPaymentService', (): void => { const options: StartQuoteOptions = { walletAddress: walletAddressMap['USD'], receiver: await createReceiver(deps, walletAddressMap['USD'], { - incomingAmount + incomingAmount, + tenantId: Config.operatorTenantId }) } diff --git a/packages/backend/src/shared/pagination.test.ts b/packages/backend/src/shared/pagination.test.ts index 6220c37cfa..f2bc6ddd0c 100644 --- a/packages/backend/src/shared/pagination.test.ts +++ b/packages/backend/src/shared/pagination.test.ts @@ -121,7 +121,8 @@ describe('Pagination', (): void => { const paymentIds: string[] = [] for (let i = 0; i < num; i++) { const payment = await createIncomingPayment(deps, { - walletAddressId: defaultWalletAddress.id + walletAddressId: defaultWalletAddress.id, + tenantId: Config.operatorTenantId }) paymentIds.push(payment.id) } diff --git a/packages/backend/src/tests/combinedPayment.ts b/packages/backend/src/tests/combinedPayment.ts index bb258e72a3..b7e96b117a 100644 --- a/packages/backend/src/tests/combinedPayment.ts +++ b/packages/backend/src/tests/combinedPayment.ts @@ -54,7 +54,8 @@ export async function createCombinedPayment( const payment = type === PaymentType.Incoming ? await createIncomingPayment(deps, { - walletAddressId: receiveWalletAddress.id + walletAddressId: receiveWalletAddress.id, + tenantId: receiveWalletAddress.tenantId }) : await createOutgoingPayment(deps, { walletAddressId: sendWalletAddressId, diff --git a/packages/backend/src/tests/incomingPayment.ts b/packages/backend/src/tests/incomingPayment.ts index b37d93d694..618c0b41c8 100644 --- a/packages/backend/src/tests/incomingPayment.ts +++ b/packages/backend/src/tests/incomingPayment.ts @@ -10,8 +10,12 @@ export async function createIncomingPayment( deps: IocContract, options: CreateIncomingPaymentOptions ): Promise { + const config = await deps.use('config') const incomingPaymentService = await deps.use('incomingPaymentService') - const incomingPaymentOrError = await incomingPaymentService.create(options) + const incomingPaymentOrError = await incomingPaymentService.create({ + ...options, + tenantId: options.tenantId ?? config.operatorTenantId + }) if (isIncomingPaymentError(incomingPaymentOrError)) { throw incomingPaymentOrError } diff --git a/packages/backend/src/tests/outgoingPayment.ts b/packages/backend/src/tests/outgoingPayment.ts index 451f33e6bb..c618af026f 100644 --- a/packages/backend/src/tests/outgoingPayment.ts +++ b/packages/backend/src/tests/outgoingPayment.ts @@ -13,6 +13,7 @@ import { CreateIncomingPaymentOptions } from '../open_payments/payment/incoming/ import { IncomingPayment } from '../open_payments/payment/incoming/model' import { createIncomingPayment } from './incomingPayment' import assert from 'assert' +import { Config } from '../config/app' export type CreateTestQuoteAndOutgoingPaymentOptions = Omit< CreateOutgoingPaymentOptions & CreateTestQuoteOptions, @@ -40,15 +41,17 @@ export async function createOutgoingPayment( const walletAddressService = await deps.use('walletAddressService') const streamServer = await deps.use('streamServer') const streamCredentials = streamServer.generateCredentials() - - const incomingPayment = await createIncomingPayment(deps, { - walletAddressId: options.walletAddressId - }) - await incomingPayment.$query().delete() const walletAddress = await walletAddressService.get( options.walletAddressId ) assert(walletAddress) + + const incomingPayment = await createIncomingPayment(deps, { + walletAddressId: options.walletAddressId, + tenantId: walletAddress.tenantId + }) + await incomingPayment.$query().delete() + jest .spyOn(receiverService, 'get') .mockResolvedValueOnce( @@ -115,7 +118,8 @@ export async function createOutgoingPaymentWithReceiver( const incomingPayment = await createIncomingPayment(deps, { ...args.incomingPaymentOptions, - walletAddressId: args.receivingWalletAddress.id + walletAddressId: args.receivingWalletAddress.id, + tenantId: Config.operatorTenantId }) const streamCredentialsService = await deps.use('streamCredentialsService') diff --git a/packages/backend/src/tests/receiver.ts b/packages/backend/src/tests/receiver.ts index 218051e70d..ca72134d61 100644 --- a/packages/backend/src/tests/receiver.ts +++ b/packages/backend/src/tests/receiver.ts @@ -11,9 +11,11 @@ export async function createReceiver( walletAddress: WalletAddress, options?: Omit ): Promise { + const config = await deps.use('config') const incomingPayment = await createIncomingPayment(deps, { ...options, - walletAddressId: walletAddress.id + walletAddressId: walletAddress.id, + tenantId: options?.tenantId ?? config.operatorTenantId }) const streamCredentialsService = await deps.use('streamCredentialsService') diff --git a/packages/backend/src/webhook/service.test.ts b/packages/backend/src/webhook/service.test.ts index a4c878f1dd..c0337a821d 100644 --- a/packages/backend/src/webhook/service.test.ts +++ b/packages/backend/src/webhook/service.test.ts @@ -126,12 +126,14 @@ describe('Webhook Service', (): void => { incomingPaymentIds = [ ( await createIncomingPayment(deps, { - walletAddressId: walletAddressIn.id + walletAddressId: walletAddressIn.id, + tenantId: Config.operatorTenantId }) ).id, ( await createIncomingPayment(deps, { - walletAddressId: walletAddressIn.id + walletAddressId: walletAddressIn.id, + tenantId: Config.operatorTenantId }) ).id ] diff --git a/packages/frontend/app/generated/graphql.ts b/packages/frontend/app/generated/graphql.ts index e7c149d3d2..ab9db6b2b0 100644 --- a/packages/frontend/app/generated/graphql.ts +++ b/packages/frontend/app/generated/graphql.ts @@ -600,6 +600,8 @@ export type IncomingPayment = BasePayment & Model & { receivedAmount: Amount; /** State of the incoming payment. */ state: IncomingPaymentState; + /** The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature. */ + tenantId?: Maybe; /** Unique identifier of the wallet address under which the incoming payment was created. */ walletAddressId: Scalars['ID']['output']; }; @@ -2283,6 +2285,7 @@ export type IncomingPaymentResolvers, ParentType, ContextType>; receivedAmount?: Resolver; state?: Resolver; + tenantId?: Resolver, ParentType, ContextType>; walletAddressId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/packages/mock-account-service-lib/src/generated/graphql.ts b/packages/mock-account-service-lib/src/generated/graphql.ts index 5296c25729..7ce6b6ce2e 100644 --- a/packages/mock-account-service-lib/src/generated/graphql.ts +++ b/packages/mock-account-service-lib/src/generated/graphql.ts @@ -600,6 +600,8 @@ export type IncomingPayment = BasePayment & Model & { receivedAmount: Amount; /** State of the incoming payment. */ state: IncomingPaymentState; + /** The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature. */ + tenantId?: Maybe; /** Unique identifier of the wallet address under which the incoming payment was created. */ walletAddressId: Scalars['ID']['output']; }; @@ -2283,6 +2285,7 @@ export type IncomingPaymentResolvers, ParentType, ContextType>; receivedAmount?: Resolver; state?: Resolver; + tenantId?: Resolver, ParentType, ContextType>; walletAddressId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index d83df3e03f..417a1e703f 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -116,7 +116,7 @@ describe('Integration tests', (): void => { const incomingPayment = await createIncomingPayment( receiverWalletAddress, incomingPaymentGrant.access_token.value, - { amountValueToSend } + { amountValueToSend, tenantId: hlb.config.operatorTenantId } ) const quoteGrant = await grantRequestQuote(senderWalletAddress) const quote = await createQuote( @@ -193,7 +193,7 @@ describe('Integration tests', (): void => { const incomingPayment = await createIncomingPayment( receiverWalletAddress, incomingPaymentGrant.access_token.value, - { amountValueToSend } + { amountValueToSend, tenantId: hlb.config.operatorTenantId } ) const quoteGrant = await grantRequestQuote(senderWalletAddress) const quote = await createQuote( @@ -276,7 +276,8 @@ describe('Integration tests', (): void => { ) const incomingPayment = await createIncomingPayment( receiverWalletAddress, - incomingPaymentGrant.access_token.value + incomingPaymentGrant.access_token.value, + { tenantId: hlb.config.operatorTenantId } ) const outgoingPaymentGrant = await grantRequestOutgoingPayment( diff --git a/test/integration/lib/generated/graphql.ts b/test/integration/lib/generated/graphql.ts index 5296c25729..7ce6b6ce2e 100644 --- a/test/integration/lib/generated/graphql.ts +++ b/test/integration/lib/generated/graphql.ts @@ -600,6 +600,8 @@ export type IncomingPayment = BasePayment & Model & { receivedAmount: Amount; /** State of the incoming payment. */ state: IncomingPaymentState; + /** The tenant UUID associated with the incoming payment. If not provided, it will be obtained from the signature. */ + tenantId?: Maybe; /** Unique identifier of the wallet address under which the incoming payment was created. */ walletAddressId: Scalars['ID']['output']; }; @@ -2283,6 +2285,7 @@ export type IncomingPaymentResolvers, ParentType, ContextType>; receivedAmount?: Resolver; state?: Resolver; + tenantId?: Resolver, ParentType, ContextType>; walletAddressId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/test/integration/lib/test-actions/open-payments.ts b/test/integration/lib/test-actions/open-payments.ts index 5c0cf938b7..567041fe5e 100644 --- a/test/integration/lib/test-actions/open-payments.ts +++ b/test/integration/lib/test-actions/open-payments.ts @@ -123,6 +123,7 @@ async function grantRequestIncomingPayment( type CreateIncomingPaymentOpts = { amountValueToSend?: string + tenantId?: string } async function createIncomingPayment( @@ -158,7 +159,7 @@ async function createIncomingPayment( const incomingPayment = await sendingASE.opClient.incomingPayment.create( { - url: urlWithoutTenantId(receiverWalletAddress.resourceServer), + url: receiverWalletAddress.resourceServer, accessToken }, createInput