From c3ccd8804f6aa3efde9f4133e3833fd8ea0a65f0 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:49:25 -0700 Subject: [PATCH 01/31] feat(open-payments): export the different IncomingPayment types --- .../src/client/incoming-payment.test.ts | 5 +-- .../src/client/incoming-payment.ts | 35 ++++++++++++------- packages/open-payments/src/index.ts | 5 +++ packages/open-payments/src/test/helpers.ts | 23 ++++++++++-- packages/open-payments/src/types.ts | 9 +++-- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/open-payments/src/client/incoming-payment.test.ts b/packages/open-payments/src/client/incoming-payment.test.ts index 90acd2fb42..dcd72c4cc6 100644 --- a/packages/open-payments/src/client/incoming-payment.test.ts +++ b/packages/open-payments/src/client/incoming-payment.test.ts @@ -14,6 +14,7 @@ import { mockILPStreamConnection, mockIncomingPayment, mockIncomingPaymentPaginationResult, + mockIncomingPaymentWithConnection, mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -531,7 +532,7 @@ describe('incoming-payment', (): void => { assetCode: 'CAD' }) - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ incomingAmount: { assetCode: 'USD', assetScale: 2, @@ -556,7 +557,7 @@ describe('incoming-payment', (): void => { assetScale: 1 }) - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ incomingAmount: { assetCode: 'USD', assetScale: 2, diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index 772bd35197..6eeed35d99 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -10,16 +10,23 @@ import { getRSPath, CreateIncomingPaymentArgs, PaginationArgs, - IncomingPaymentPaginationResult + IncomingPaymentPaginationResult, + IncomingPaymentWithConnectionUrl, + IncomingPaymentWithConnection } from '../types' import { get, post } from './requests' +type AnyIncomingPayment = + | IncomingPayment + | IncomingPaymentWithConnection + | IncomingPaymentWithConnectionUrl + export interface IncomingPaymentRoutes { - get(args: ResourceRequestArgs): Promise + get(args: ResourceRequestArgs): Promise create( args: CollectionRequestArgs, createArgs: CreateIncomingPaymentArgs - ): Promise + ): Promise complete(args: ResourceRequestArgs): Promise list( args: CollectionRequestArgs, @@ -33,7 +40,7 @@ export const createIncomingPaymentRoutes = ( const { axiosInstance, openApi, logger } = deps const getIncomingPaymentOpenApiValidator = - openApi.createResponseValidator({ + openApi.createResponseValidator({ path: getRSPath('/incoming-payments/{id}'), method: HttpMethod.GET }) @@ -92,7 +99,7 @@ export const createIncomingPaymentRoutes = ( export const getIncomingPayment = async ( deps: BaseDeps, args: ResourceRequestArgs, - validateOpenApiResponse: ResponseValidator + validateOpenApiResponse: ResponseValidator ) => { const { axiosInstance, logger } = deps const { url } = args @@ -215,9 +222,9 @@ export const listIncomingPayment = async ( return incomingPayments } -export const validateIncomingPayment = ( - payment: IncomingPayment -): IncomingPayment => { +export const validateIncomingPayment = ( + payment: T +): T => { if (payment.incomingAmount) { const { incomingAmount, receivedAmount } = payment if ( @@ -239,10 +246,12 @@ export const validateIncomingPayment = ( } if ( - !payment.ilpStreamConnection || - payment.ilpStreamConnection.assetCode !== + 'ilpStreamConnection' in payment && + typeof payment.ilpStreamConnection === 'object' && + (payment.ilpStreamConnection.assetCode !== payment.receivedAmount.assetCode || - payment.ilpStreamConnection.assetScale !== payment.receivedAmount.assetScale + payment.ilpStreamConnection.assetScale !== + payment.receivedAmount.assetScale) ) { throw new Error( 'Stream connection asset information does not match incoming payment asset information' @@ -253,8 +262,8 @@ export const validateIncomingPayment = ( } export const validateCreatedIncomingPayment = ( - payment: IncomingPayment -): IncomingPayment => { + payment: IncomingPaymentWithConnection +): IncomingPaymentWithConnection => { const { receivedAmount, completed } = payment if (BigInt(receivedAmount.value) !== BigInt(0)) { diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index e4aec97194..1cb9457589 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -2,7 +2,10 @@ export { GrantRequest, GrantContinuationRequest, IncomingPayment, + IncomingPaymentWithConnection, + IncomingPaymentWithConnectionUrl, ILPStreamConnection, + Quote, OutgoingPayment, InteractiveGrant, NonInteractiveGrant, @@ -26,6 +29,8 @@ export { mockILPStreamConnection, mockPaymentPointer, mockIncomingPayment, + mockIncomingPaymentWithConnection, + mockIncomingPaymentWithConnectionUrl, mockOutgoingPayment, mockIncomingPaymentPaginationResult, mockOutgoingPaymentPaginationResult, diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 3376fa4aa6..91f28ca8cb 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -14,7 +14,9 @@ import { JWK, AccessToken, Quote, - IncomingPaymentPaginationResult + IncomingPaymentPaginationResult, + IncomingPaymentWithConnection, + IncomingPaymentWithConnectionUrl } from '../types' import base64url from 'base64url' import { v4 as uuid } from 'uuid' @@ -81,7 +83,7 @@ export const mockPaymentPointer = ( export const mockILPStreamConnection = ( overrides?: Partial ): ILPStreamConnection => ({ - id: uuid(), + id: `https://example.com/.well-known/pay/connections/${uuid()}`, sharedSecret: base64url('sharedSecret'), ilpAddress: 'test.ilpAddress', assetCode: 'USD', @@ -107,10 +109,25 @@ export const mockIncomingPayment = ( }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - ilpStreamConnection: mockILPStreamConnection(), ...overrides }) +export const mockIncomingPaymentWithConnection = ( + overrides?: Partial +): IncomingPaymentWithConnection => ({ + ...mockIncomingPayment(), + ...overrides, + ilpStreamConnection: mockILPStreamConnection() +}) + +export const mockIncomingPaymentWithConnectionUrl = ( + overrides?: Partial +): IncomingPaymentWithConnectionUrl => ({ + ...mockIncomingPayment(), + ...overrides, + ilpStreamConnection: mockILPStreamConnection().id +}) + export const mockIncomingPaymentPaginationResult = ( overrides?: Partial ): IncomingPaymentPaginationResult => { diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 37d522a983..473a534039 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -12,11 +12,16 @@ import { export const getRSPath =

(path: P): string => path as string -export type IncomingPayment = + +export type IncomingPayment = RSComponents['schemas']['incoming-payment'] +export type IncomingPaymentWithConnection = RSComponents['schemas']['incoming-payment-with-connection'] +export type IncomingPaymentWithConnectionUrl = + RSComponents['schemas']['incoming-payment-with-connection-url'] export type CreateIncomingPaymentArgs = RSOperations['create-incoming-payment']['requestBody']['content']['application/json'] -export type IncomingPaymentPaginationResult = PaginationResult +export type IncomingPaymentPaginationResult = + PaginationResult export type ILPStreamConnection = RSComponents['schemas']['ilp-stream-connection'] export type OutgoingPayment = RSComponents['schemas']['outgoing-payment'] From 3415f5f64ac7d733766ce4d528bac756ead1ceb5 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:49:58 -0700 Subject: [PATCH 02/31] feat(backend): use open-payment types for IncomingPayment --- .../open_payments/payment/incoming/model.ts | 56 ++++++++++--------- .../open_payments/payment/incoming/routes.ts | 44 ++++++--------- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index b75f4c5c04..69a96991b8 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -2,7 +2,7 @@ import { Model, ModelOptions, Pojo, QueryContext } from 'objection' import { v4 as uuid } from 'uuid' import { Amount, AmountJSON, serializeAmount } from '../../amount' -import { Connection, ConnectionJSON } from '../../connection/model' +import { Connection } from '../../connection/model' import { PaymentPointer, PaymentPointerSubresource @@ -11,7 +11,11 @@ import { Asset } from '../../../asset/model' import { LiquidityAccount, OnCreditOptions } from '../../../accounting/service' import { ConnectorAccount } from '../../../connector/core/rafiki' import { WebhookEvent } from '../../../webhook/model' -import { IncomingPayment as OpenPaymentsIncomingPayment } from 'open-payments' +import { + IncomingPayment as OpenPaymentsIncomingPayment, + IncomingPaymentWithConnection as OpenPaymentsIncomingPaymentWithConnection, + IncomingPaymentWithConnectionUrl as OpenPaymentsIncomingPaymentWithConnectionUrl +} from 'open-payments' export enum IncomingPaymentEventType { IncomingPaymentExpired = 'incoming_payment.expired', @@ -239,12 +243,13 @@ export class IncomingPayment return payment } - public toOpenPaymentsType({ - ilpStreamConnection - }: { - ilpStreamConnection: Connection - }): OpenPaymentsIncomingPayment { - return { + public toOpenPaymentsType( + ilpStreamConnection: Connection | string | undefined + ): + | OpenPaymentsIncomingPayment + | OpenPaymentsIncomingPaymentWithConnection + | OpenPaymentsIncomingPaymentWithConnectionUrl { + const baseIncomingPayment: OpenPaymentsIncomingPayment = { id: this.url, paymentPointer: this.paymentPointer.url, incomingAmount: this.incomingAmount @@ -254,24 +259,23 @@ export class IncomingPayment completed: this.completed, createdAt: this.createdAt.toISOString(), updatedAt: this.updatedAt.toISOString(), - expiresAt: this.expiresAt.toISOString(), - ilpStreamConnection: ilpStreamConnection.toOpenPaymentsType() + expiresAt: this.expiresAt.toISOString() } - } -} -// TODO: disallow undefined -// https://github.com/interledger/rafiki/issues/594 -export type IncomingPaymentJSON = { - id: string - paymentPointer: string - incomingAmount?: AmountJSON - receivedAmount: AmountJSON - completed: boolean - description?: string - externalRef?: string - createdAt: string - updatedAt: string - expiresAt?: string - ilpStreamConnection?: ConnectionJSON | string + if (!ilpStreamConnection) { + return baseIncomingPayment + } + + if (typeof ilpStreamConnection === 'string') { + return { + ...baseIncomingPayment, + ilpStreamConnection + } + } + + return { + ...baseIncomingPayment, + ilpStreamConnection: ilpStreamConnection.toOpenPaymentsType().id + } + } } diff --git a/packages/backend/src/open_payments/payment/incoming/routes.ts b/packages/backend/src/open_payments/payment/incoming/routes.ts index 00464d2da5..e22cd7c17d 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.ts @@ -7,7 +7,7 @@ import { } from '../../../app' import { IAppConfig } from '../../../config/app' import { IncomingPaymentService } from './service' -import { IncomingPayment, IncomingPaymentJSON } from './model' +import { IncomingPayment } from './model' import { errorToCode, errorToMessage, @@ -16,8 +16,13 @@ import { } from './errors' import { AmountJSON, parseAmount } from '../../amount' import { listSubresource } from '../../payment_pointer/routes' -import { ConnectionJSON } from '../../connection/model' +import { Connection } from '../../connection/model' import { ConnectionService } from '../../connection/service' +import { + IncomingPayment as OpenPaymentsIncomingPayment, + IncomingPaymentWithConnection as OpenPaymentsIncomingPaymentWithConnection, + IncomingPaymentWithConnectionUrl as OpenPaymentsIncomingPaymentWithConnectionUrl +} from 'open-payments' // Don't allow creating an incoming payment too far out. Incoming payments with no payments before they expire are cleaned up, since incoming payments creation is unauthenticated. // TODO what is a good default value for this? @@ -69,7 +74,7 @@ async function getIncomingPayment( } if (!incomingPayment) return ctx.throw(404) const connection = deps.connectionService.get(incomingPayment) - ctx.body = incomingPaymentToBody(deps, incomingPayment, connection?.toJSON()) + ctx.body = incomingPaymentToBody(incomingPayment, connection) } export type CreateBody = { @@ -110,11 +115,7 @@ async function createIncomingPayment( ctx.status = 201 const connection = deps.connectionService.get(incomingPaymentOrError) - ctx.body = incomingPaymentToBody( - deps, - incomingPaymentOrError, - connection?.toJSON() - ) + ctx.body = incomingPaymentToBody(incomingPaymentOrError, connection) } async function completeIncomingPayment( @@ -136,7 +137,7 @@ async function completeIncomingPayment( errorToMessage[incomingPaymentOrError] ) } - ctx.body = incomingPaymentToBody(deps, incomingPaymentOrError) + ctx.body = incomingPaymentToBody(incomingPaymentOrError) } async function listIncomingPayments( @@ -148,29 +149,18 @@ async function listIncomingPayments( ctx, getPaymentPointerPage: deps.incomingPaymentService.getPaymentPointerPage, toBody: (payment) => - incomingPaymentToBody( - deps, - payment, - deps.connectionService.getUrl(payment) - ) + incomingPaymentToBody(payment, deps.connectionService.getUrl(payment)) }) } catch (_) { ctx.throw(500, 'Error trying to list incoming payments') } } - function incomingPaymentToBody( - deps: ServiceDependencies, incomingPayment: IncomingPayment, - ilpStreamConnection?: ConnectionJSON | string -): IncomingPaymentJSON { - const body = { - ...incomingPayment.toJSON(), - id: incomingPayment.url, - paymentPointer: incomingPayment.paymentPointer.url - } as unknown as IncomingPaymentJSON - if (ilpStreamConnection) { - body.ilpStreamConnection = ilpStreamConnection - } - return body + ilpStreamConnection?: Connection | string +): + | OpenPaymentsIncomingPayment + | OpenPaymentsIncomingPaymentWithConnection + | OpenPaymentsIncomingPaymentWithConnectionUrl { + return incomingPayment.toOpenPaymentsType(ilpStreamConnection) } From 4693a79c48b42b0cae0dfe1790ed724e7e25addc Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:50:04 -0700 Subject: [PATCH 03/31] feat(backend): use open-payment types for Connection --- .../src/open_payments/connection/model.ts | 18 ------------------ .../src/open_payments/connection/routes.ts | 2 +- .../open_payments/connection/service.test.ts | 2 +- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/open_payments/connection/model.ts b/packages/backend/src/open_payments/connection/model.ts index 4dc8c726a0..0fc2eafaf7 100644 --- a/packages/backend/src/open_payments/connection/model.ts +++ b/packages/backend/src/open_payments/connection/model.ts @@ -4,14 +4,6 @@ import { IlpAddress } from 'ilp-packet' import { ILPStreamConnection } from 'open-payments' import { IncomingPayment } from '../payment/incoming/model' -export type ConnectionJSON = { - id: string - ilpAddress: IlpAddress - sharedSecret: string - assetCode: string - assetScale: number -} - export abstract class ConnectionBase { protected constructor( public readonly ilpAddress: IlpAddress, @@ -55,16 +47,6 @@ export class Connection extends ConnectionBase { return `${this.openPaymentsUrl}/connections/${this.id}` } - public toJSON(): ConnectionJSON { - return { - id: this.url, - ilpAddress: this.ilpAddress, - sharedSecret: base64url(this.sharedSecret), - assetCode: this.assetCode, - assetScale: this.assetScale - } - } - public toOpenPaymentsType(): ILPStreamConnection { return { id: this.url, diff --git a/packages/backend/src/open_payments/connection/routes.ts b/packages/backend/src/open_payments/connection/routes.ts index 1d7492c628..bb3c74f74a 100644 --- a/packages/backend/src/open_payments/connection/routes.ts +++ b/packages/backend/src/open_payments/connection/routes.ts @@ -36,5 +36,5 @@ async function getConnection( const connection = deps.connectionService.get(incomingPayment) if (!connection) return ctx.throw(404) - ctx.body = connection.toJSON() + ctx.body = connection.toOpenPaymentsType() } diff --git a/packages/backend/src/open_payments/connection/service.test.ts b/packages/backend/src/open_payments/connection/service.test.ts index ca30596049..3497964dd5 100644 --- a/packages/backend/src/open_payments/connection/service.test.ts +++ b/packages/backend/src/open_payments/connection/service.test.ts @@ -57,7 +57,7 @@ describe('Connection Service', (): void => { expect(connection.url).toEqual( `${Config.openPaymentsUrl}/connections/${incomingPayment.connectionId}` ) - expect(connection.toJSON()).toEqual({ + expect(connection.toOpenPaymentsType()).toEqual({ id: connection.url, ilpAddress: connection.ilpAddress, sharedSecret: base64url(connection.sharedSecret || ''), From c633b3016dbeb12b92744d9421ef229a67d0c100 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:50:12 -0700 Subject: [PATCH 04/31] feat(backend): use open-payment types for OutgoingPayment --- .../open_payments/payment/outgoing/model.ts | 41 ++++++++++++------- .../open_payments/payment/outgoing/routes.ts | 29 ++++--------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 8dcd358db1..91db0b9a71 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -6,8 +6,9 @@ import { Asset } from '../../../asset/model' import { ConnectorAccount } from '../../../connector/core/rafiki' import { PaymentPointerSubresource } from '../../payment_pointer/model' import { Quote } from '../../quote/model' -import { Amount, AmountJSON } from '../../amount' +import { Amount, AmountJSON, serializeAmount } from '../../amount' import { WebhookEvent } from '../../../webhook/model' +import { OutgoingPayment as OpenPaymentsOutgoingPayment } from 'open-payments' export class OutgoingPaymentGrant extends DbErrors(Model) { public static get modelPaths(): string[] { @@ -72,6 +73,14 @@ export class OutgoingPayment return this.quote.asset } + public get url(): string { + return `${this.paymentPointerId}${OutgoingPayment.urlPath}/${this.id}` + } + + public get failed(): boolean { + return this.state === OutgoingPaymentState.Failed + } + // Outgoing peer public peerId?: string @@ -168,6 +177,23 @@ export class OutgoingPayment updatedAt: json.updatedAt } } + + public toOpenPaymentsType(): OpenPaymentsOutgoingPayment { + return { + id: this.url, + paymentPointer: this.paymentPointerId, + quoteId: this.quote.id, + receiveAmount: serializeAmount(this.receiveAmount), + sendAmount: serializeAmount(this.sendAmount), + sentAmount: serializeAmount(this.sentAmount), + receiver: this.receiver, + failed: this.failed, + externalRef: this.externalRef, + description: this.description, + createdAt: this.createdAt.toISOString(), + updatedAt: this.updatedAt.toISOString(), + } + } } export enum OutgoingPaymentState { @@ -236,16 +262,3 @@ export class PaymentEvent extends WebhookEvent { public type!: PaymentEventType public data!: PaymentData } - -export type OutgoingPaymentJSON = { - id: string - paymentPointer: string - receiver: string - sendAmount: AmountJSON - sentAmount: AmountJSON - receiveAmount: AmountJSON - description: string | null - externalRef: string | null - createdAt: string - updatedAt: string -} diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.ts b/packages/backend/src/open_payments/payment/outgoing/routes.ts index fcf817c06e..b77752e640 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.ts @@ -3,9 +3,9 @@ import { ReadContext, CreateContext, ListContext } from '../../../app' import { IAppConfig } from '../../../config/app' import { OutgoingPaymentService } from './service' import { isOutgoingPaymentError, errorToCode, errorToMessage } from './errors' -import { OutgoingPayment, OutgoingPaymentState } from './model' +import { OutgoingPayment } from './model' import { listSubresource } from '../../payment_pointer/routes' -import { PaymentPointer } from '../../payment_pointer/model' +import { OutgoingPayment as OpenPaymentsOutgoingPayment } from 'open-payments' interface ServiceDependencies { config: IAppConfig @@ -49,8 +49,7 @@ async function getOutgoingPayment( ctx.throw(500, 'Error trying to get outgoing payment') } if (!outgoingPayment) return ctx.throw(404) - const body = outgoingPaymentToBody(deps, outgoingPayment, ctx.paymentPointer) - ctx.body = body + ctx.body = outgoingPaymentToBody(outgoingPayment) } export type CreateBody = { @@ -84,8 +83,7 @@ async function createOutgoingPayment( return ctx.throw(errorToCode[paymentOrErr], errorToMessage[paymentOrErr]) } ctx.status = 201 - const res = outgoingPaymentToBody(deps, paymentOrErr, ctx.paymentPointer) - ctx.body = res + ctx.body = outgoingPaymentToBody(paymentOrErr) } async function listOutgoingPayments( @@ -96,8 +94,7 @@ async function listOutgoingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.outgoingPaymentService.getPaymentPointerPage, - toBody: (payment) => - outgoingPaymentToBody(deps, payment, ctx.paymentPointer) + toBody: (payment) => outgoingPaymentToBody(payment) }) } catch (_) { ctx.throw(500, 'Error trying to list outgoing payments') @@ -105,17 +102,7 @@ async function listOutgoingPayments( } function outgoingPaymentToBody( - deps: ServiceDependencies, - outgoingPayment: OutgoingPayment, - paymentPointer: PaymentPointer -) { - return Object.fromEntries( - Object.entries({ - ...outgoingPayment.toJSON(), - id: `${paymentPointer.url}/outgoing-payments/${outgoingPayment.id}`, - paymentPointer: paymentPointer.url, - state: null, - failed: outgoingPayment.state === OutgoingPaymentState.Failed - }).filter(([_, v]) => v != null) - ) + outgoingPayment: OutgoingPayment +): OpenPaymentsOutgoingPayment { + return outgoingPayment.toOpenPaymentsType() } From 909462a5e0dd41c965e4d9a04000593613d0463e Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:50:19 -0700 Subject: [PATCH 05/31] feat(backend): use open-payment types for Quote --- .../backend/src/open_payments/quote/model.ts | 19 ++++++++++++++- .../backend/src/open_payments/quote/routes.ts | 23 ++++--------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index 1efc525e82..c7b39b07e4 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -1,9 +1,10 @@ import { Model, Pojo } from 'objection' import * as Pay from '@interledger/pay' -import { Amount, AmountJSON } from '../amount' +import { Amount, AmountJSON, serializeAmount } from '../amount' import { PaymentPointerSubresource } from '../payment_pointer/model' import { Asset } from '../../asset/model' +import { Quote as OpenPaymentsQuote } from 'open-payments' export class Quote extends PaymentPointerSubresource { public static readonly tableName = 'quotes' @@ -43,6 +44,10 @@ export class Quote extends PaymentPointerSubresource { private sendAmountValue!: bigint + public get url(): string { + return `${this.paymentPointerId}${Quote.urlPath}/${this.id}` + } + public get sendAmount(): Amount { return { value: this.sendAmountValue, @@ -148,6 +153,18 @@ export class Quote extends PaymentPointerSubresource { expiresAt: json.expiresAt.toISOString() } } + + public toOpenPaymentsType(): OpenPaymentsQuote { + return { + id: this.url, + paymentPointer: this.paymentPointerId, + receiveAmount: serializeAmount(this.receiveAmount), + sendAmount: serializeAmount(this.sendAmount), + receiver: this.receiver, + expiresAt: this.expiresAt.toISOString(), + createdAt: this.createdAt.toISOString(), + } + } } export type QuoteJSON = { diff --git a/packages/backend/src/open_payments/quote/routes.ts b/packages/backend/src/open_payments/quote/routes.ts index 6e578a79dc..00e0237550 100644 --- a/packages/backend/src/open_payments/quote/routes.ts +++ b/packages/backend/src/open_payments/quote/routes.ts @@ -5,7 +5,7 @@ import { CreateQuoteOptions, QuoteService } from './service' import { isQuoteError, errorToCode, errorToMessage } from './errors' import { Quote } from './model' import { AmountJSON, parseAmount } from '../amount' -import { PaymentPointer } from '../payment_pointer/model' +import { Quote as OpenPaymentsQuote } from 'open-payments' interface ServiceDependencies { config: IAppConfig @@ -39,8 +39,7 @@ async function getQuote( paymentPointerId: ctx.paymentPointer.id }) if (!quote) return ctx.throw(404) - const body = quoteToBody(deps, quote, ctx.paymentPointer) - ctx.body = body + ctx.body = quoteToBody(quote) } interface CreateBodyBase { @@ -80,8 +79,7 @@ async function createQuote( } ctx.status = 201 - const res = quoteToBody(deps, quoteOrErr, ctx.paymentPointer) - ctx.body = res + ctx.body = quoteToBody(quoteOrErr) } catch (err) { if (isQuoteError(err)) { return ctx.throw(errorToCode[err], errorToMessage[err]) @@ -91,17 +89,6 @@ async function createQuote( } } -function quoteToBody( - deps: ServiceDependencies, - quote: Quote, - paymentPointer: PaymentPointer -) { - return Object.fromEntries( - Object.entries({ - ...quote.toJSON(), - id: `${paymentPointer.url}/quotes/${quote.id}`, - paymentPointer: paymentPointer.url, - paymentPointerId: undefined - }).filter(([_, v]) => v != null) - ) +function quoteToBody(quote: Quote): OpenPaymentsQuote { + return quote.toOpenPaymentsType() } From 8feaacb6fc0cfdcb62608aa3cce260dbe45fce2a Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:55:12 -0700 Subject: [PATCH 06/31] chore(backend): fix IncomingPayments.toOpenPaymentsType --- packages/backend/src/open_payments/payment/incoming/model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 69a96991b8..ada45d224e 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -275,7 +275,7 @@ export class IncomingPayment return { ...baseIncomingPayment, - ilpStreamConnection: ilpStreamConnection.toOpenPaymentsType().id + ilpStreamConnection: ilpStreamConnection.toOpenPaymentsType() } } } From 5300073946f87a00fba4315a5eb9fea114389c22 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 15:58:14 -0700 Subject: [PATCH 07/31] chore(open-payments): update mocks --- packages/open-payments/src/test/helpers.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 91f28ca8cb..c8e1cd1ac9 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -95,7 +95,7 @@ export const mockIncomingPayment = ( overrides?: Partial ): IncomingPayment => ({ id: `https://example.com/.well-known/pay/incoming-payments/${uuid()}`, - paymentPointer: 'paymentPointer', + paymentPointer: 'https://example.com/.well-known/pay', completed: false, incomingAmount: { assetCode: 'USD', @@ -151,8 +151,8 @@ export const mockIncomingPaymentPaginationResult = ( export const mockOutgoingPayment = ( overrides?: Partial ): OutgoingPayment => ({ - id: uuid(), - paymentPointer: 'paymentPointer', + id: `https://example.com/.well-known/pay/outgoing-payments/${uuid()}`, + paymentPointer: 'https://example.com/.well-known/pay', failed: false, sendAmount: { assetCode: 'USD', @@ -286,9 +286,9 @@ export const mockAccessToken = ( }) export const mockQuote = (overrides?: Partial): Quote => ({ - id: uuid(), - receiver: `receiver`, - paymentPointer: 'paymentPointer', + id: `https://example.com/.well-known/pay/quotes/${uuid()}`, + receiver: 'https://example.com/.well-known/peer', + paymentPointer: 'https://example.com/.well-known/pay', sendAmount: { value: '100', assetCode: 'USD', From 01a0ba0b2be8f9c4ade08fbe41090acea055a9ae Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 24 Jan 2023 16:02:15 -0700 Subject: [PATCH 08/31] chore(backend): formatting --- packages/backend/src/open_payments/payment/outgoing/model.ts | 2 +- packages/backend/src/open_payments/quote/model.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 91db0b9a71..c1182ecd71 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -191,7 +191,7 @@ export class OutgoingPayment externalRef: this.externalRef, description: this.description, createdAt: this.createdAt.toISOString(), - updatedAt: this.updatedAt.toISOString(), + updatedAt: this.updatedAt.toISOString() } } } diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index c7b39b07e4..afa0a08817 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -162,7 +162,7 @@ export class Quote extends PaymentPointerSubresource { sendAmount: serializeAmount(this.sendAmount), receiver: this.receiver, expiresAt: this.expiresAt.toISOString(), - createdAt: this.createdAt.toISOString(), + createdAt: this.createdAt.toISOString() } } } From 36d8b8eed57f587fb4d59b5645729cb3cb8b46a9 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 15:44:01 -0700 Subject: [PATCH 09/31] chore(backend): add better typing for incomingPayment.toOpenPaymentsType --- .../src/graphql/resolvers/receiver.test.ts | 7 +++++-- .../src/open_payments/payment/incoming/model.ts | 9 ++++++++- .../src/open_payments/receiver/model.test.ts | 16 ++++------------ .../backend/src/open_payments/receiver/model.ts | 6 +++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/graphql/resolvers/receiver.test.ts b/packages/backend/src/graphql/resolvers/receiver.test.ts index 86355f2486..6f13a8ff75 100644 --- a/packages/backend/src/graphql/resolvers/receiver.test.ts +++ b/packages/backend/src/graphql/resolvers/receiver.test.ts @@ -6,7 +6,10 @@ import { AppServices } from '../../app' import { initIocContainer } from '../..' import { Config } from '../../config/app' import { Amount, serializeAmount } from '../../open_payments/amount' -import { mockIncomingPayment, mockPaymentPointer } from 'open-payments' +import { + mockIncomingPaymentWithConnection, + mockPaymentPointer +} from 'open-payments' import { CreateReceiverResponse } from '../generated/graphql' import { ReceiverService } from '../../open_payments/receiver/service' import { Receiver } from '../../open_payments/receiver/model' @@ -49,7 +52,7 @@ describe('Receiver Resolver', (): void => { incomingAmount }): Promise => { const receiver = Receiver.fromIncomingPayment( - mockIncomingPayment({ + mockIncomingPaymentWithConnection({ id: `${paymentPointer.id}/incoming-payments/${uuid()}`, paymentPointer: paymentPointer.id, description, diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index ada45d224e..dcd7ca6030 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -243,8 +243,15 @@ export class IncomingPayment return payment } + public toOpenPaymentsType(): OpenPaymentsIncomingPayment public toOpenPaymentsType( - ilpStreamConnection: Connection | string | undefined + ilpStreamConnection: Connection + ): OpenPaymentsIncomingPaymentWithConnection + public toOpenPaymentsType( + ilpStreamConnection: string + ): OpenPaymentsIncomingPaymentWithConnectionUrl + public toOpenPaymentsType( + ilpStreamConnection?: Connection | string ): | OpenPaymentsIncomingPayment | OpenPaymentsIncomingPaymentWithConnection diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index d2cbcf70d0..f840502d52 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -44,9 +44,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) const receiver = Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType({ - ilpStreamConnection: connection - }) + incomingPayment.toOpenPaymentsType(connection) ) expect(receiver).toEqual({ @@ -79,9 +77,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) expect(() => Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType({ - ilpStreamConnection: connection - }) + incomingPayment.toOpenPaymentsType(connection) ) ).toThrow('Cannot create receiver from completed incoming payment') }) @@ -98,9 +94,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) expect(() => Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType({ - ilpStreamConnection: connection - }) + incomingPayment.toOpenPaymentsType(connection) ) ).toThrow('Cannot create receiver from expired incoming payment') }) @@ -117,9 +111,7 @@ describe('Receiver Model', (): void => { expect(() => Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType({ - ilpStreamConnection: connection - }) + incomingPayment.toOpenPaymentsType(connection) ) ).toThrow('Invalid ILP address on stream connection') }) diff --git a/packages/backend/src/open_payments/receiver/model.ts b/packages/backend/src/open_payments/receiver/model.ts index f183f0b0fd..541f9efdee 100644 --- a/packages/backend/src/open_payments/receiver/model.ts +++ b/packages/backend/src/open_payments/receiver/model.ts @@ -4,7 +4,7 @@ import base64url from 'base64url' import { Amount, parseAmount } from '../amount' import { AssetOptions } from '../../asset/service' import { - IncomingPayment as OpenPaymentsIncomingPayment, + IncomingPaymentWithConnection as OpenPaymentsIncomingPaymentWithConnection, ILPStreamConnection as OpenPaymentsConnection } from 'open-payments' import { ConnectionBase } from '../connection/model' @@ -17,7 +17,7 @@ interface OpenPaymentsConnectionWithIlpAddress type ReceiverIncomingPayment = Readonly< Omit< - OpenPaymentsIncomingPayment, + OpenPaymentsIncomingPaymentWithConnection, | 'ilpStreamConnection' | 'expiresAt' | 'receivedAmount' @@ -51,7 +51,7 @@ export class Receiver extends ConnectionBase { } static fromIncomingPayment( - incomingPayment: OpenPaymentsIncomingPayment + incomingPayment: OpenPaymentsIncomingPaymentWithConnection ): Receiver { if (!incomingPayment.ilpStreamConnection) { throw new Error('Missing stream connection on incoming payment') From d04f994cf75023555a992aa406580686599e5bdd Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 15:49:03 -0700 Subject: [PATCH 10/31] chore(backend): remove QuoteJSON --- packages/backend/src/open_payments/quote/model.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index afa0a08817..828b7a9708 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -1,7 +1,7 @@ import { Model, Pojo } from 'objection' import * as Pay from '@interledger/pay' -import { Amount, AmountJSON, serializeAmount } from '../amount' +import { Amount, serializeAmount } from '../amount' import { PaymentPointerSubresource } from '../payment_pointer/model' import { Asset } from '../../asset/model' import { Quote as OpenPaymentsQuote } from 'open-payments' @@ -166,13 +166,3 @@ export class Quote extends PaymentPointerSubresource { } } } - -export type QuoteJSON = { - id: string - paymentPointerId: string - receiver: string - sendAmount: AmountJSON - receiveAmount: AmountJSON - createdAt: string - expiresAt: string -} From 11fa5423fb5c963473b7db6f3afcd8d6d70482cb Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 15:53:12 -0700 Subject: [PATCH 11/31] chore(open-payments): fix mocks --- packages/open-payments/src/test/helpers.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index c8e1cd1ac9..d4b0dde2b6 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -116,16 +116,16 @@ export const mockIncomingPaymentWithConnection = ( overrides?: Partial ): IncomingPaymentWithConnection => ({ ...mockIncomingPayment(), - ...overrides, - ilpStreamConnection: mockILPStreamConnection() + ilpStreamConnection: mockILPStreamConnection(), + ...overrides }) export const mockIncomingPaymentWithConnectionUrl = ( overrides?: Partial ): IncomingPaymentWithConnectionUrl => ({ ...mockIncomingPayment(), - ...overrides, - ilpStreamConnection: mockILPStreamConnection().id + ilpStreamConnection: mockILPStreamConnection().id, + ...overrides }) export const mockIncomingPaymentPaginationResult = ( From 27cfddefa7852cc90e6683dc87d6bda273681aa4 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 16:04:15 -0700 Subject: [PATCH 12/31] chore(backend): fix IncomingPayment types --- .../open_payments/payment/incoming_remote/service.test.ts | 8 ++++---- .../backend/src/open_payments/receiver/service.test.ts | 8 ++++---- packages/backend/src/open_payments/receiver/service.ts | 6 ++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts b/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts index 67f1635484..dd78e6e9ef 100644 --- a/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming_remote/service.test.ts @@ -11,10 +11,10 @@ import { AuthenticatedClient as OpenPaymentsClient, AccessAction, AccessType, - mockIncomingPayment, mockInteractiveGrant, mockNonInteractiveGrant, - mockPaymentPointer + mockPaymentPointer, + mockIncomingPaymentWithConnection } from 'open-payments' import { GrantService } from '../../grant/service' import { RemoteIncomingPaymentError } from './errors' @@ -90,7 +90,7 @@ describe('Remote Incoming Payment Service', (): void => { ${undefined} | ${undefined} | ${undefined} | ${undefined} ${amount} | ${new Date(Date.now() + 30_000)} | ${'Test incoming payment'} | ${'#123'} `('creates remote incoming payment ($#)', async (args): Promise => { - const mockedIncomingPayment = mockIncomingPayment({ + const mockedIncomingPayment = mockIncomingPaymentWithConnection({ ...args, paymentPointerUrl: paymentPointer.id }) @@ -178,7 +178,7 @@ describe('Remote Incoming Payment Service', (): void => { ${undefined} | ${undefined} | ${undefined} | ${undefined} ${amount} | ${new Date(Date.now() + 30_000)} | ${'Test incoming payment'} | ${'#123'} `('creates remote incoming payment ($#)', async (args): Promise => { - const mockedIncomingPayment = mockIncomingPayment({ + const mockedIncomingPayment = mockIncomingPaymentWithConnection({ ...args, paymentPointerUrl: paymentPointer.id }) diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index dd2908890d..140d2791a1 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -7,10 +7,10 @@ import { AccessAction, IncomingPayment as OpenPaymentsIncomingPayment, PaymentPointer as OpenPaymentsPaymentPointer, - mockIncomingPayment, mockPaymentPointer, NonInteractiveGrant, - GrantRequest + GrantRequest, + mockIncomingPaymentWithConnection } from 'open-payments' import { URL } from 'url' import { v4 as uuid } from 'uuid' @@ -263,7 +263,7 @@ describe('Receiver Service', (): void => { paymentPointer = mockPaymentPointer({ authServer }) - incomingPayment = mockIncomingPayment({ + incomingPayment = mockIncomingPaymentWithConnection({ id: `${paymentPointer.id}/incoming-payments/${uuid()}`, paymentPointer: paymentPointer.id }) @@ -455,7 +455,7 @@ describe('Receiver Service', (): void => { expiresAt, incomingAmount }): Promise => { - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ description, externalRef, expiresAt, diff --git a/packages/backend/src/open_payments/receiver/service.ts b/packages/backend/src/open_payments/receiver/service.ts index 3de999b5a1..b413b10bd0 100644 --- a/packages/backend/src/open_payments/receiver/service.ts +++ b/packages/backend/src/open_payments/receiver/service.ts @@ -131,9 +131,7 @@ async function createLocalIncomingPayment( throw new Error(errorMessage) } - return incomingPaymentOrError.toOpenPaymentsType({ - ilpStreamConnection: connection - }) + return incomingPaymentOrError.toOpenPaymentsType(connection) } async function getReceiver( @@ -276,7 +274,7 @@ async function getLocalIncomingPayment({ return undefined } - return incomingPayment.toOpenPaymentsType({ ilpStreamConnection: connection }) + return incomingPayment.toOpenPaymentsType(connection) } async function getIncomingPaymentGrant( From 4efddb3c697e24edda57e58eb91269ef56394658 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 16:04:28 -0700 Subject: [PATCH 13/31] chore(open-payments): fix incoming payment types --- .../src/client/incoming-payment.test.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/open-payments/src/client/incoming-payment.test.ts b/packages/open-payments/src/client/incoming-payment.test.ts index dcd72c4cc6..a9cd22c98e 100644 --- a/packages/open-payments/src/client/incoming-payment.test.ts +++ b/packages/open-payments/src/client/incoming-payment.test.ts @@ -15,6 +15,7 @@ import { mockIncomingPayment, mockIncomingPaymentPaginationResult, mockIncomingPaymentWithConnection, + mockIncomingPaymentWithConnectionUrl, mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -49,7 +50,7 @@ describe('incoming-payment', (): void => { describe('getIncomingPayment', (): void => { test('returns incoming payment if passes validation', async (): Promise => { - const incomingPayment = mockIncomingPayment() + const incomingPayment = mockIncomingPaymentWithConnection() nock(paymentPointer) .get('/incoming-payments/1') @@ -67,7 +68,7 @@ describe('incoming-payment', (): void => { }) test('throws if incoming payment does not pass validation', async (): Promise => { - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ incomingAmount: { assetCode: 'USD', assetScale: 2, @@ -100,7 +101,7 @@ describe('incoming-payment', (): void => { }) test('throws if incoming payment does not pass open api validation', async (): Promise => { - const incomingPayment = mockIncomingPayment() + const incomingPayment = mockIncomingPaymentWithConnection() nock(paymentPointer) .get('/incoming-payments/1') @@ -135,7 +136,7 @@ describe('incoming-payment', (): void => { description, externalRef }): Promise => { - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ incomingAmount, expiresAt, description, @@ -170,7 +171,7 @@ describe('incoming-payment', (): void => { value: '10' } - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnection({ incomingAmount: amount, receivedAmount: amount, completed: false @@ -192,7 +193,7 @@ describe('incoming-payment', (): void => { }) test('throws if the created incoming payment does not pass open api validation', async (): Promise => { - const incomingPayment = mockIncomingPayment() + const incomingPayment = mockIncomingPaymentWithConnection() const scope = nock(paymentPointer) .post('/incoming-payments') @@ -293,7 +294,7 @@ describe('incoming-payment', (): void => { async ({ first, cursor }): Promise => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: Array(first).fill(mockIncomingPayment()) + result: Array(first).fill(mockIncomingPaymentWithConnectionUrl()) }) const scope = nock(paymentPointer) @@ -336,7 +337,7 @@ describe('incoming-payment', (): void => { async ({ last, cursor }): Promise => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: Array(last).fill(mockIncomingPayment()) + result: Array(last).fill(mockIncomingPaymentWithConnectionUrl()) }) const scope = nock(paymentPointer) @@ -370,7 +371,7 @@ describe('incoming-payment', (): void => { }) test('throws if an incoming payment does not pass validation', async (): Promise => { - const incomingPayment = mockIncomingPayment({ + const incomingPayment = mockIncomingPaymentWithConnectionUrl({ incomingAmount: { assetCode: 'USD', assetScale: 2, @@ -659,7 +660,7 @@ describe('incoming-payment', (): void => { const getSpy = jest .spyOn(requestors, 'get') - .mockResolvedValueOnce(mockIncomingPayment()) + .mockResolvedValueOnce(mockIncomingPaymentWithConnection()) await createIncomingPaymentRoutes({ openApi, @@ -685,7 +686,7 @@ describe('incoming-payment', (): void => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: [mockIncomingPayment()] + result: [mockIncomingPaymentWithConnectionUrl()] }) const url = `${paymentPointer}${getRSPath('/incoming-payments')}` @@ -733,7 +734,9 @@ describe('incoming-payment', (): void => { const postSpy = jest .spyOn(requestors, 'post') - .mockResolvedValueOnce(mockIncomingPayment(incomingPaymentCreateArgs)) + .mockResolvedValueOnce( + mockIncomingPaymentWithConnection(incomingPaymentCreateArgs) + ) await createIncomingPaymentRoutes({ openApi, From dc051ec97a20ad12fdfe9b677e3d87dfc97ac2e5 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 25 Jan 2023 16:58:01 -0700 Subject: [PATCH 14/31] chore(backend): fix incoming payment method --- packages/backend/src/open_payments/payment/incoming/model.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index dcd7ca6030..8460da0443 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -264,6 +264,8 @@ export class IncomingPayment : undefined, receivedAmount: serializeAmount(this.receivedAmount), completed: this.completed, + description: this.description, + externalRef: this.externalRef, createdAt: this.createdAt.toISOString(), updatedAt: this.updatedAt.toISOString(), expiresAt: this.expiresAt.toISOString() From 0fddf9e2c3d2ef0c102d4ea0da88791c225b8c29 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 19:37:14 -0700 Subject: [PATCH 15/31] chore(backend): make toOpenPaymentsType async --- .../open_payments/payment/incoming/model.ts | 34 +++++++----- .../payment/incoming/routes.test.ts | 54 ++++++++++--------- .../open_payments/payment/outgoing/model.ts | 21 +++++--- .../open_payments/payment/outgoing/routes.ts | 10 ++-- .../payment/outgoing/service.test.ts | 6 ++- .../payment_pointer/model.test.ts | 7 ++- .../open_payments/payment_pointer/routes.ts | 8 +-- .../backend/src/open_payments/quote/model.ts | 24 ++++++--- .../backend/src/open_payments/quote/routes.ts | 4 +- .../src/open_payments/quote/service.test.ts | 12 ++--- 10 files changed, 107 insertions(+), 73 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 8460da0443..0f10580b34 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -84,7 +84,6 @@ export class IncomingPayment } } - public paymentPointer!: PaymentPointer public description?: string public expiresAt!: Date public state!: IncomingPaymentState @@ -131,8 +130,15 @@ export class IncomingPayment this.receivedAmountValue = amount.value } - public get url(): string { - return `${this.paymentPointer.url}${IncomingPayment.urlPath}/${this.id}` + public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { + const paymentPointer = + fetchedPaymentPointer || (await this.getPaymentPointer()) + + return `${paymentPointer.url}${IncomingPayment.urlPath}/${this.id}` + } + + public async getPaymentPointer(): Promise { + return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) } public async onCredit({ @@ -243,22 +249,24 @@ export class IncomingPayment return payment } - public toOpenPaymentsType(): OpenPaymentsIncomingPayment - public toOpenPaymentsType( + public async toOpenPaymentsType(): Promise + public async toOpenPaymentsType( ilpStreamConnection: Connection - ): OpenPaymentsIncomingPaymentWithConnection - public toOpenPaymentsType( + ): Promise + public async toOpenPaymentsType( ilpStreamConnection: string - ): OpenPaymentsIncomingPaymentWithConnectionUrl - public toOpenPaymentsType( + ): Promise + public async toOpenPaymentsType( ilpStreamConnection?: Connection | string - ): + ): Promise< | OpenPaymentsIncomingPayment | OpenPaymentsIncomingPaymentWithConnection - | OpenPaymentsIncomingPaymentWithConnectionUrl { + | OpenPaymentsIncomingPaymentWithConnectionUrl + > { + const paymentPointer = await this.getPaymentPointer() const baseIncomingPayment: OpenPaymentsIncomingPayment = { - id: this.url, - paymentPointer: this.paymentPointer.url, + id: await this.getUrl(paymentPointer), + paymentPointer: paymentPointer.url, incomingAmount: this.incomingAmount ? serializeAmount(this.incomingAmount) : undefined, 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 40410ea4b5..81544b4538 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.test.ts @@ -82,31 +82,33 @@ describe('Incoming Payment Routes', (): void => { externalRef }), get: (ctx) => incomingPaymentRoutes.get(ctx), - getBody: (incomingPayment, list) => ({ - id: incomingPayment.url, - paymentPointer: paymentPointer.url, - completed: false, - incomingAmount: - incomingPayment.incomingAmount && - serializeAmount(incomingPayment.incomingAmount), - description: incomingPayment.description, - expiresAt: incomingPayment.expiresAt.toISOString(), - createdAt: incomingPayment.createdAt.toISOString(), - updatedAt: incomingPayment.updatedAt.toISOString(), - receivedAmount: serializeAmount(incomingPayment.receivedAmount), - externalRef: '#123', - ilpStreamConnection: list - ? `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}` - : { - id: `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}`, - ilpAddress: expect.stringMatching( - /^test\.rafiki\.[a-zA-Z0-9_-]{95}$/ - ), - sharedSecret: expect.stringMatching(/^[a-zA-Z0-9-_]{43}$/), - assetCode: incomingPayment.receivedAmount.assetCode, - assetScale: incomingPayment.receivedAmount.assetScale - } - }), + getBody: async (incomingPayment, list) => { + return { + id: await incomingPayment.getUrl(), + paymentPointer: paymentPointer.url, + completed: false, + incomingAmount: + incomingPayment.incomingAmount && + serializeAmount(incomingPayment.incomingAmount), + description: incomingPayment.description, + expiresAt: incomingPayment.expiresAt.toISOString(), + createdAt: incomingPayment.createdAt.toISOString(), + updatedAt: incomingPayment.updatedAt.toISOString(), + receivedAmount: serializeAmount(incomingPayment.receivedAmount), + externalRef: '#123', + ilpStreamConnection: list + ? `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}` + : { + id: `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}`, + ilpAddress: expect.stringMatching( + /^test\.rafiki\.[a-zA-Z0-9_-]{95}$/ + ), + sharedSecret: expect.stringMatching(/^[a-zA-Z0-9-_]{43}$/), + assetCode: incomingPayment.receivedAmount.assetCode, + assetScale: incomingPayment.receivedAmount.assetScale + } + } + }, list: (ctx) => incomingPaymentRoutes.list(ctx), urlPath: IncomingPayment.urlPath }) @@ -263,7 +265,7 @@ describe('Incoming Payment Routes', (): void => { await expect(incomingPaymentRoutes.complete(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ - id: incomingPayment.url, + id: await incomingPayment.getUrl(), paymentPointer: paymentPointer.url, incomingAmount: { value: '123', diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index c1182ecd71..8528d8239e 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -4,7 +4,10 @@ import { DbErrors } from 'objection-db-errors' import { LiquidityAccount } from '../../../accounting/service' import { Asset } from '../../../asset/model' import { ConnectorAccount } from '../../../connector/core/rafiki' -import { PaymentPointerSubresource } from '../../payment_pointer/model' +import { + PaymentPointerSubresource, + PaymentPointer +} from '../../payment_pointer/model' import { Quote } from '../../quote/model' import { Amount, AmountJSON, serializeAmount } from '../../amount' import { WebhookEvent } from '../../../webhook/model' @@ -73,10 +76,6 @@ export class OutgoingPayment return this.quote.asset } - public get url(): string { - return `${this.paymentPointerId}${OutgoingPayment.urlPath}/${this.id}` - } - public get failed(): boolean { return this.state === OutgoingPaymentState.Failed } @@ -178,10 +177,16 @@ export class OutgoingPayment } } - public toOpenPaymentsType(): OpenPaymentsOutgoingPayment { + public async getPaymentPointer(): Promise { + return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) + } + + public async toOpenPaymentsType(): Promise { + const paymentPointer = await this.getPaymentPointer() + return { - id: this.url, - paymentPointer: this.paymentPointerId, + id: `${paymentPointer.url}${OutgoingPayment.urlPath}/${this.id}`, + paymentPointer: paymentPointer.id, quoteId: this.quote.id, receiveAmount: serializeAmount(this.receiveAmount), sendAmount: serializeAmount(this.sendAmount), diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.ts b/packages/backend/src/open_payments/payment/outgoing/routes.ts index aaccbacb3d..191ee79af1 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.ts @@ -50,7 +50,7 @@ async function getOutgoingPayment( ctx.throw(500, 'Error trying to get outgoing payment') } if (!outgoingPayment) return ctx.throw(404) - ctx.body = outgoingPaymentToBody(outgoingPayment) + ctx.body = await outgoingPaymentToBody(outgoingPayment) } export type CreateBody = { @@ -84,7 +84,7 @@ async function createOutgoingPayment( return ctx.throw(errorToCode[paymentOrErr], errorToMessage[paymentOrErr]) } ctx.status = 201 - ctx.body = outgoingPaymentToBody(paymentOrErr) + ctx.body = await outgoingPaymentToBody(paymentOrErr) } async function listOutgoingPayments( @@ -95,15 +95,15 @@ async function listOutgoingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.outgoingPaymentService.getPaymentPointerPage, - toBody: (payment) => outgoingPaymentToBody(payment) + toBody: outgoingPaymentToBody }) } catch (_) { ctx.throw(500, 'Error trying to list outgoing payments') } } -function outgoingPaymentToBody( +async function outgoingPaymentToBody( outgoingPayment: OutgoingPayment -): OpenPaymentsOutgoingPayment { +): Promise { return outgoingPayment.toOpenPaymentsType() } 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 6fa087e86d..93d02ab80f 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -266,7 +266,7 @@ describe('OutgoingPaymentService', (): void => { incomingPayment = await createIncomingPayment(deps, { paymentPointerId: receiverPaymentPointer.id }) - receiver = incomingPayment.url + receiver = await incomingPayment.getUrl() amtDelivered = BigInt(0) }) @@ -858,7 +858,9 @@ describe('OutgoingPaymentService', (): void => { const fetchedReceiver = connectionService.getUrl(incomingPayment) assert.ok(fetchedReceiver) const paymentId = await setup({ - receiver: toConnection ? fetchedReceiver : incomingPayment.url, + receiver: toConnection + ? fetchedReceiver + : await incomingPayment.getUrl(), receiveAmount }) diff --git a/packages/backend/src/open_payments/payment_pointer/model.test.ts b/packages/backend/src/open_payments/payment_pointer/model.test.ts index 5ae2ae0bd1..90db619ee4 100644 --- a/packages/backend/src/open_payments/payment_pointer/model.test.ts +++ b/packages/backend/src/open_payments/payment_pointer/model.test.ts @@ -194,7 +194,10 @@ type RouteTestsOptions = Omit< > & { getPaymentPointer: () => Promise get: (ctx: ReadContext) => Promise - getBody: (model: M, list?: boolean) => Record + getBody: ( + model: M, + list?: boolean + ) => Record | Promise> list?: (ctx: ListContext) => Promise urlPath: string } @@ -230,7 +233,7 @@ export const getRouteTests = ({ expect(ctx.response).toSatisfyApiSpec() } expect(ctx.body).toEqual({ - result: expectedMatch ? [getBody(expectedMatch, true)] : [], + result: expectedMatch ? [await getBody(expectedMatch, true)] : [], pagination: { hasPreviousPage: false, hasNextPage: false, diff --git a/packages/backend/src/open_payments/payment_pointer/routes.ts b/packages/backend/src/open_payments/payment_pointer/routes.ts index e095e9a016..93758c5022 100644 --- a/packages/backend/src/open_payments/payment_pointer/routes.ts +++ b/packages/backend/src/open_payments/payment_pointer/routes.ts @@ -40,7 +40,9 @@ export async function getPaymentPointer( interface ListSubresourceOptions { ctx: ListContext getPaymentPointerPage: PaymentPointerSubresourceService['getPaymentPointerPage'] - toBody: (model: M) => Record + toBody: + | ((model: M) => Record) + | ((model: M) => Promise>) } export const listSubresource = async ({ @@ -66,9 +68,9 @@ export const listSubresource = async ({ ) const result = { pagination: pageInfo, - result: page.map((item: M) => { + result: page.map(async (item: M) => { item.paymentPointer = ctx.paymentPointer - return toBody(item) + return await toBody(item) }) } ctx.body = result diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index 828b7a9708..7684dacd09 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -2,7 +2,10 @@ import { Model, Pojo } from 'objection' import * as Pay from '@interledger/pay' import { Amount, serializeAmount } from '../amount' -import { PaymentPointerSubresource } from '../payment_pointer/model' +import { + PaymentPointer, + PaymentPointerSubresource +} from '../payment_pointer/model' import { Asset } from '../../asset/model' import { Quote as OpenPaymentsQuote } from 'open-payments' @@ -44,8 +47,15 @@ export class Quote extends PaymentPointerSubresource { private sendAmountValue!: bigint - public get url(): string { - return `${this.paymentPointerId}${Quote.urlPath}/${this.id}` + public async getPaymentPointer(): Promise { + return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) + } + + public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { + const paymentPointer = + fetchedPaymentPointer || (await this.getPaymentPointer()) + + return `${paymentPointer.url}${Quote.urlPath}/${this.id}` } public get sendAmount(): Amount { @@ -154,10 +164,12 @@ export class Quote extends PaymentPointerSubresource { } } - public toOpenPaymentsType(): OpenPaymentsQuote { + public async toOpenPaymentsType(): Promise { + const paymentPointer = await this.getPaymentPointer() + return { - id: this.url, - paymentPointer: this.paymentPointerId, + id: await this.getUrl(paymentPointer), + paymentPointer: paymentPointer.url, receiveAmount: serializeAmount(this.receiveAmount), sendAmount: serializeAmount(this.sendAmount), receiver: this.receiver, diff --git a/packages/backend/src/open_payments/quote/routes.ts b/packages/backend/src/open_payments/quote/routes.ts index 4619b013f9..808a84525a 100644 --- a/packages/backend/src/open_payments/quote/routes.ts +++ b/packages/backend/src/open_payments/quote/routes.ts @@ -80,7 +80,7 @@ async function createQuote( } ctx.status = 201 - ctx.body = quoteToBody(quoteOrErr) + ctx.body = await quoteToBody(quoteOrErr) } catch (err) { if (isQuoteError(err)) { return ctx.throw(errorToCode[err], errorToMessage[err]) @@ -90,6 +90,6 @@ async function createQuote( } } -function quoteToBody(quote: Quote): OpenPaymentsQuote { +async function quoteToBody(quote: Quote): Promise { return quote.toOpenPaymentsType() } diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index 0eadf95b66..e3376b0a91 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -247,7 +247,7 @@ describe('QuoteService', (): void => { paymentPointerId, receiver: toConnection ? connectionService.getUrl(incomingPayment) - : incomingPayment.url + : await incomingPayment.getUrl() } if (sendAmount) options.sendAmount = sendAmount if (receiveAmount) options.receiveAmount = receiveAmount @@ -575,7 +575,7 @@ describe('QuoteService', (): void => { }) const options: CreateQuoteOptions = { paymentPointerId, - receiver: incomingPayment.url, + receiver: await incomingPayment.getUrl(), receiveAmount } const expected: ExpectedQuote = { @@ -653,11 +653,11 @@ describe('QuoteService', (): void => { async ({ sendAmount, receiveAmount }): Promise => { const options: CreateQuoteOptions = { paymentPointerId, - receiver: ( + receiver: await ( await createIncomingPayment(deps, { paymentPointerId: receivingPaymentPointer.id }) - ).url + ).getUrl() } if (sendAmount) options.sendAmount = sendAmount if (receiveAmount) options.receiveAmount = receiveAmount @@ -675,11 +675,11 @@ describe('QuoteService', (): void => { await expect( quoteService.create({ paymentPointerId, - receiver: ( + receiver: await ( await createIncomingPayment(deps, { paymentPointerId: receivingPaymentPointer.id }) - ).url, + ).getUrl(), sendAmount }) ).rejects.toThrow('missing prices') From ad570482c828211af3b88d1265e2fbe53b2c502a Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 19:40:50 -0700 Subject: [PATCH 16/31] chore(backend): getUrl for OutgoingPayment --- .../backend/src/open_payments/payment/outgoing/model.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 8528d8239e..694a80b9d0 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -72,6 +72,13 @@ export class OutgoingPayment return this.quote.assetId } + public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { + const paymentPointer = + fetchedPaymentPointer || (await this.getPaymentPointer()) + + return `${paymentPointer.url}${OutgoingPayment.urlPath}/${this.id}` + } + public get asset(): Asset { return this.quote.asset } @@ -185,7 +192,7 @@ export class OutgoingPayment const paymentPointer = await this.getPaymentPointer() return { - id: `${paymentPointer.url}${OutgoingPayment.urlPath}/${this.id}`, + id: await this.getUrl(paymentPointer), paymentPointer: paymentPointer.id, quoteId: this.quote.id, receiveAmount: serializeAmount(this.receiveAmount), From d48dc0af433c2ea47cd3bc21bfbcd1d483faf61b Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 19:49:06 -0700 Subject: [PATCH 17/31] chore(backend): fix tests with async toOpenPaymentsType --- .../open_payments/payment/incoming/model.ts | 6 +++ .../open_payments/payment/incoming/routes.ts | 9 +++-- .../src/open_payments/receiver/model.test.ts | 26 +++++++------ .../open_payments/receiver/service.test.ts | 38 +++++++++---------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 0f10580b34..39fbc89781 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -256,6 +256,12 @@ export class IncomingPayment public async toOpenPaymentsType( ilpStreamConnection: string ): Promise + public async toOpenPaymentsType( + ilpStreamConnection?: Connection | string + ): Promise< + | OpenPaymentsIncomingPaymentWithConnection + | OpenPaymentsIncomingPaymentWithConnectionUrl + > public async toOpenPaymentsType( ilpStreamConnection?: Connection | string ): Promise< diff --git a/packages/backend/src/open_payments/payment/incoming/routes.ts b/packages/backend/src/open_payments/payment/incoming/routes.ts index e6afb2280d..6bbfd9aaa5 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.ts @@ -1,4 +1,3 @@ -import { AccessAction } from 'open-payments' import { Logger } from 'pino' import { ReadContext, @@ -20,6 +19,7 @@ import { listSubresource } from '../../payment_pointer/routes' import { Connection } from '../../connection/model' import { ConnectionService } from '../../connection/service' import { + AccessAction, IncomingPayment as OpenPaymentsIncomingPayment, IncomingPaymentWithConnection as OpenPaymentsIncomingPaymentWithConnection, IncomingPaymentWithConnectionUrl as OpenPaymentsIncomingPaymentWithConnectionUrl @@ -156,12 +156,13 @@ async function listIncomingPayments( ctx.throw(500, 'Error trying to list incoming payments') } } -function incomingPaymentToBody( +async function incomingPaymentToBody( incomingPayment: IncomingPayment, ilpStreamConnection?: Connection | string -): +): Promise< | OpenPaymentsIncomingPayment | OpenPaymentsIncomingPaymentWithConnection - | OpenPaymentsIncomingPaymentWithConnectionUrl { + | OpenPaymentsIncomingPaymentWithConnectionUrl +> { return incomingPayment.toOpenPaymentsType(ilpStreamConnection) } diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index f840502d52..def750565a 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -44,7 +44,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) const receiver = Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(connection) ) expect(receiver).toEqual({ @@ -53,8 +53,8 @@ describe('Receiver Model', (): void => { ilpAddress: expect.any(String), sharedSecret: expect.any(Buffer), incomingPayment: { - id: incomingPayment.url, - paymentPointer: incomingPayment.paymentPointer.url, + id: await incomingPayment.getUrl(), + paymentPointer: (await incomingPayment.getPaymentPointer()).url, updatedAt: incomingPayment.updatedAt, createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, @@ -75,11 +75,13 @@ describe('Receiver Model', (): void => { const connection = connectionService.get(incomingPayment) assert(connection instanceof Connection) - expect(() => + await expect( Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(connection) ) - ).toThrow('Cannot create receiver from completed incoming payment') + ).rejects.toThrow( + 'Cannot create receiver from completed incoming payment' + ) }) test('throws if incoming payment is expired', async () => { @@ -92,11 +94,11 @@ describe('Receiver Model', (): void => { const connection = connectionService.get(incomingPayment) assert(connection instanceof Connection) - expect(() => + await expect( Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(connection) ) - ).toThrow('Cannot create receiver from expired incoming payment') + ).rejects.toThrow('Cannot create receiver from expired incoming payment') }) test('throws if stream connection has invalid ILP address', async () => { @@ -109,11 +111,11 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) ;(connection.ilpAddress as string) = 'not base 64 encoded' - expect(() => + await expect( Receiver.fromIncomingPayment( - incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(connection) ) - ).toThrow('Invalid ILP address on stream connection') + ).rejects.toThrow('Invalid ILP address on stream connection') }) }) diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index 140d2791a1..d03cbb089f 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -193,26 +193,26 @@ describe('Receiver Service', (): void => { 'get' ) - await expect(receiverService.get(incomingPayment.url)).resolves.toEqual( - { - assetCode: incomingPayment.receivedAmount.assetCode, - assetScale: incomingPayment.receivedAmount.assetScale, - ilpAddress: expect.any(String), - sharedSecret: expect.any(Buffer), - incomingPayment: { - id: incomingPayment.url, - paymentPointer: incomingPayment.paymentPointer.url, - completed: incomingPayment.completed, - receivedAmount: incomingPayment.receivedAmount, - incomingAmount: incomingPayment.incomingAmount, - description: incomingPayment.description || undefined, - externalRef: incomingPayment.externalRef || undefined, - expiresAt: incomingPayment.expiresAt, - updatedAt: new Date(incomingPayment.updatedAt), - createdAt: new Date(incomingPayment.createdAt) - } + await expect( + receiverService.get(await incomingPayment.getUrl()) + ).resolves.toEqual({ + assetCode: incomingPayment.receivedAmount.assetCode, + assetScale: incomingPayment.receivedAmount.assetScale, + ilpAddress: expect.any(String), + sharedSecret: expect.any(Buffer), + incomingPayment: { + id: await incomingPayment.getUrl(), + paymentPointer: (await incomingPayment.getPaymentPointer()).url, + completed: incomingPayment.completed, + receivedAmount: incomingPayment.receivedAmount, + incomingAmount: incomingPayment.incomingAmount, + description: incomingPayment.description || undefined, + externalRef: incomingPayment.externalRef || undefined, + expiresAt: incomingPayment.expiresAt, + updatedAt: new Date(incomingPayment.updatedAt), + createdAt: new Date(incomingPayment.createdAt) } - ) + }) expect(clientGetIncomingPaymentSpy).not.toHaveBeenCalled() }) From 9302bad1e54cebd7ce4f81ebaf9524fecd63e429 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 21:58:30 -0700 Subject: [PATCH 18/31] chore(backend): return undefined instead of null --- packages/backend/src/open_payments/payment/incoming/model.ts | 4 ++-- packages/backend/src/open_payments/payment/outgoing/model.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 39fbc89781..b8c0a6ac54 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -278,8 +278,8 @@ export class IncomingPayment : undefined, receivedAmount: serializeAmount(this.receivedAmount), completed: this.completed, - description: this.description, - externalRef: this.externalRef, + description: this.description ?? undefined, + externalRef: this.externalRef ?? undefined, createdAt: this.createdAt.toISOString(), updatedAt: this.updatedAt.toISOString(), expiresAt: this.expiresAt.toISOString() diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 694a80b9d0..08b0b2f5c5 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -200,8 +200,8 @@ export class OutgoingPayment sentAmount: serializeAmount(this.sentAmount), receiver: this.receiver, failed: this.failed, - externalRef: this.externalRef, - description: this.description, + externalRef: this.externalRef ?? undefined, + description: this.description ?? undefined, createdAt: this.createdAt.toISOString(), updatedAt: this.updatedAt.toISOString() } From 5e5d4e89c19e2d3535caecfc2eacf552b0975729 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 22:18:34 -0700 Subject: [PATCH 19/31] chore(backend): add awaits where necessary --- .../src/open_payments/payment/incoming/routes.ts | 13 ++++++++----- .../open_payments/payment/outgoing/routes.test.ts | 1 + .../src/open_payments/payment/outgoing/routes.ts | 8 +++++--- packages/backend/src/open_payments/quote/routes.ts | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/routes.ts b/packages/backend/src/open_payments/payment/incoming/routes.ts index 6bbfd9aaa5..0820e4858f 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.ts @@ -75,7 +75,7 @@ async function getIncomingPayment( } if (!incomingPayment) return ctx.throw(404) const connection = deps.connectionService.get(incomingPayment) - ctx.body = incomingPaymentToBody(incomingPayment, connection) + ctx.body = await incomingPaymentToBody(incomingPayment, connection) } export type CreateBody = { @@ -116,7 +116,7 @@ async function createIncomingPayment( ctx.status = 201 const connection = deps.connectionService.get(incomingPaymentOrError) - ctx.body = incomingPaymentToBody(incomingPaymentOrError, connection) + ctx.body = await incomingPaymentToBody(incomingPaymentOrError, connection) } async function completeIncomingPayment( @@ -138,7 +138,7 @@ async function completeIncomingPayment( errorToMessage[incomingPaymentOrError] ) } - ctx.body = incomingPaymentToBody(incomingPaymentOrError) + ctx.body = await incomingPaymentToBody(incomingPaymentOrError) } async function listIncomingPayments( @@ -149,8 +149,11 @@ async function listIncomingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.incomingPaymentService.getPaymentPointerPage, - toBody: (payment) => - incomingPaymentToBody(payment, deps.connectionService.getUrl(payment)) + toBody: async (payment) => + await incomingPaymentToBody( + payment, + deps.connectionService.getUrl(payment) + ) }) } catch (_) { ctx.throw(500, 'Error trying to list incoming payments') diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index a7f154b55b..1fc3634e0e 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -201,6 +201,7 @@ describe('Outgoing Payment Routes', (): void => { id: `${paymentPointer.url}/outgoing-payments/${outgoingPaymentId}`, paymentPointer: paymentPointer.url, receiver: payment.receiver, + quoteId: options.quoteId, sendAmount: { ...payment.sendAmount, value: payment.sendAmount.value.toString() diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.ts b/packages/backend/src/open_payments/payment/outgoing/routes.ts index 191ee79af1..5c0d2d15e7 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.ts @@ -1,4 +1,3 @@ -import { AccessAction } from 'open-payments' import { Logger } from 'pino' import { ReadContext, CreateContext, ListContext } from '../../../app' import { IAppConfig } from '../../../config/app' @@ -6,7 +5,10 @@ import { OutgoingPaymentService } from './service' import { isOutgoingPaymentError, errorToCode, errorToMessage } from './errors' import { OutgoingPayment } from './model' import { listSubresource } from '../../payment_pointer/routes' -import { OutgoingPayment as OpenPaymentsOutgoingPayment } from 'open-payments' +import { + AccessAction, + OutgoingPayment as OpenPaymentsOutgoingPayment +} from 'open-payments' interface ServiceDependencies { config: IAppConfig @@ -95,7 +97,7 @@ async function listOutgoingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.outgoingPaymentService.getPaymentPointerPage, - toBody: outgoingPaymentToBody + toBody: async (payment) => await outgoingPaymentToBody(payment) }) } catch (_) { ctx.throw(500, 'Error trying to list outgoing payments') diff --git a/packages/backend/src/open_payments/quote/routes.ts b/packages/backend/src/open_payments/quote/routes.ts index 808a84525a..efa12167e8 100644 --- a/packages/backend/src/open_payments/quote/routes.ts +++ b/packages/backend/src/open_payments/quote/routes.ts @@ -40,7 +40,7 @@ async function getQuote( paymentPointerId: ctx.paymentPointer.id }) if (!quote) return ctx.throw(404) - ctx.body = quoteToBody(quote) + ctx.body = await quoteToBody(quote) } interface CreateBodyBase { From 25972d3cbef588aff6471806252017809fc4b78e Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 22:47:36 -0700 Subject: [PATCH 20/31] chore(open-payments): make getBody async --- .../payment/outgoing/routes.test.ts | 28 ++++++++++--------- .../payment_pointer/model.test.ts | 9 ++---- .../src/open_payments/quote/routes.test.ts | 20 +++++++------ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index 1fc3634e0e..bac7b7f297 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -100,19 +100,21 @@ describe('Outgoing Payment Routes', (): void => { return outgoingPayment }, get: (ctx) => outgoingPaymentRoutes.get(ctx), - getBody: (outgoingPayment) => ({ - id: `${paymentPointer.url}/outgoing-payments/${outgoingPayment.id}`, - paymentPointer: paymentPointer.url, - receiver: outgoingPayment.receiver, - sendAmount: serializeAmount(outgoingPayment.sendAmount), - sentAmount: serializeAmount(outgoingPayment.sentAmount), - receiveAmount: serializeAmount(outgoingPayment.receiveAmount), - description: outgoingPayment.description, - externalRef: outgoingPayment.externalRef, - failed, - createdAt: outgoingPayment.createdAt.toISOString(), - updatedAt: outgoingPayment.updatedAt.toISOString() - }), + getBody: async (outgoingPayment) => { + return { + id: `${paymentPointer.url}/outgoing-payments/${outgoingPayment.id}`, + paymentPointer: paymentPointer.url, + receiver: outgoingPayment.receiver, + sendAmount: serializeAmount(outgoingPayment.sendAmount), + sentAmount: serializeAmount(outgoingPayment.sentAmount), + receiveAmount: serializeAmount(outgoingPayment.receiveAmount), + description: outgoingPayment.description, + externalRef: outgoingPayment.externalRef, + failed, + createdAt: outgoingPayment.createdAt.toISOString(), + updatedAt: outgoingPayment.updatedAt.toISOString() + } + }, list: (ctx) => outgoingPaymentRoutes.list(ctx), urlPath: OutgoingPayment.urlPath }) diff --git a/packages/backend/src/open_payments/payment_pointer/model.test.ts b/packages/backend/src/open_payments/payment_pointer/model.test.ts index 90db619ee4..dcc7ceabed 100644 --- a/packages/backend/src/open_payments/payment_pointer/model.test.ts +++ b/packages/backend/src/open_payments/payment_pointer/model.test.ts @@ -194,10 +194,7 @@ type RouteTestsOptions = Omit< > & { getPaymentPointer: () => Promise get: (ctx: ReadContext) => Promise - getBody: ( - model: M, - list?: boolean - ) => Record | Promise> + getBody: (model: M, list?: boolean) => Promise> list?: (ctx: ListContext) => Promise urlPath: string } @@ -264,7 +261,7 @@ export const getRouteTests = ({ if (expectedMatch) { await expect(get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() - expect(ctx.body).toEqual(getBody(expectedMatch)) + expect(ctx.body).toEqual(await getBody(expectedMatch)) } else { await expect(get(ctx)).rejects.toMatchObject({ status: 404, @@ -324,7 +321,7 @@ export const getRouteTests = ({ pagination, result: models .slice(startIndex, endIndex + 1) - .map((model) => getBody(model, true)) + .map(async (model) => await getBody(model, true)) }) } ) diff --git a/packages/backend/src/open_payments/quote/routes.test.ts b/packages/backend/src/open_payments/quote/routes.test.ts index 4088916bbe..269581aafa 100644 --- a/packages/backend/src/open_payments/quote/routes.test.ts +++ b/packages/backend/src/open_payments/quote/routes.test.ts @@ -96,15 +96,17 @@ describe('Quote Routes', (): void => { client }), get: (ctx) => quoteRoutes.get(ctx), - getBody: (quote) => ({ - id: `${paymentPointer.url}/quotes/${quote.id}`, - paymentPointer: paymentPointer.url, - receiver: quote.receiver, - sendAmount: serializeAmount(quote.sendAmount), - receiveAmount: serializeAmount(quote.receiveAmount), - createdAt: quote.createdAt.toISOString(), - expiresAt: quote.expiresAt.toISOString() - }), + getBody: async (quote) => { + return { + id: `${paymentPointer.url}/quotes/${quote.id}`, + paymentPointer: paymentPointer.url, + receiver: quote.receiver, + sendAmount: serializeAmount(quote.sendAmount), + receiveAmount: serializeAmount(quote.receiveAmount), + createdAt: quote.createdAt.toISOString(), + expiresAt: quote.expiresAt.toISOString() + } + }, urlPath: Quote.urlPath }) }) From 4800c808a2c8575475f82bcef4f399602dd5e5ef Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Fri, 27 Jan 2023 23:47:19 -0700 Subject: [PATCH 21/31] chore(backend): fix outgoingPayment model method --- packages/backend/src/open_payments/payment/outgoing/model.ts | 4 ++-- .../backend/src/open_payments/payment/outgoing/routes.test.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 08b0b2f5c5..77463c455d 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -193,8 +193,8 @@ export class OutgoingPayment return { id: await this.getUrl(paymentPointer), - paymentPointer: paymentPointer.id, - quoteId: this.quote.id, + paymentPointer: paymentPointer.url, + quoteId: (await this.quote?.getUrl()) ?? undefined, receiveAmount: serializeAmount(this.receiveAmount), sendAmount: serializeAmount(this.sendAmount), sentAmount: serializeAmount(this.sentAmount), diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index bac7b7f297..0d802f2590 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -105,6 +105,7 @@ describe('Outgoing Payment Routes', (): void => { id: `${paymentPointer.url}/outgoing-payments/${outgoingPayment.id}`, paymentPointer: paymentPointer.url, receiver: outgoingPayment.receiver, + quoteId: await outgoingPayment.quote.getUrl(), sendAmount: serializeAmount(outgoingPayment.sendAmount), sentAmount: serializeAmount(outgoingPayment.sentAmount), receiveAmount: serializeAmount(outgoingPayment.receiveAmount), From cf9541013722cd06372063cfe5687bcd14cd3dbf Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Sat, 28 Jan 2023 00:16:08 -0700 Subject: [PATCH 22/31] chore(backend): fix list tests --- .../src/open_payments/payment_pointer/model.test.ts | 8 +++++--- .../backend/src/open_payments/payment_pointer/routes.ts | 5 +---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/open_payments/payment_pointer/model.test.ts b/packages/backend/src/open_payments/payment_pointer/model.test.ts index dcc7ceabed..c3a87565a4 100644 --- a/packages/backend/src/open_payments/payment_pointer/model.test.ts +++ b/packages/backend/src/open_payments/payment_pointer/model.test.ts @@ -319,9 +319,11 @@ export const getRouteTests = ({ expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ pagination, - result: models - .slice(startIndex, endIndex + 1) - .map(async (model) => await getBody(model, true)) + result: await Promise.all( + models + .slice(startIndex, endIndex + 1) + .map(async (model) => await getBody(model, true)) + ) }) } ) diff --git a/packages/backend/src/open_payments/payment_pointer/routes.ts b/packages/backend/src/open_payments/payment_pointer/routes.ts index 93758c5022..92a0ba606d 100644 --- a/packages/backend/src/open_payments/payment_pointer/routes.ts +++ b/packages/backend/src/open_payments/payment_pointer/routes.ts @@ -68,10 +68,7 @@ export const listSubresource = async ({ ) const result = { pagination: pageInfo, - result: page.map(async (item: M) => { - item.paymentPointer = ctx.paymentPointer - return await toBody(item) - }) + result: await Promise.all(page.map(async (item: M) => await toBody(item))) } ctx.body = result } From 9a967f10befd16765215138cbcebf4c9d1032dd7 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Sat, 28 Jan 2023 00:30:02 -0700 Subject: [PATCH 23/31] chore(backend): update receiver tests --- .../src/open_payments/receiver/model.test.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index def750565a..e8a74a3a1e 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -54,7 +54,7 @@ describe('Receiver Model', (): void => { sharedSecret: expect.any(Buffer), incomingPayment: { id: await incomingPayment.getUrl(), - paymentPointer: (await incomingPayment.getPaymentPointer()).url, + paymentPointer: paymentPointer.url, updatedAt: incomingPayment.updatedAt, createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, @@ -75,13 +75,12 @@ describe('Receiver Model', (): void => { const connection = connectionService.get(incomingPayment) assert(connection instanceof Connection) - await expect( - Receiver.fromIncomingPayment( - await incomingPayment.toOpenPaymentsType(connection) - ) - ).rejects.toThrow( - 'Cannot create receiver from completed incoming payment' - ) + const openPaymentsIncomingPayment = + await incomingPayment.toOpenPaymentsType(connection) + + expect(() => + Receiver.fromIncomingPayment(openPaymentsIncomingPayment) + ).toThrow('Cannot create receiver from completed incoming payment') }) test('throws if incoming payment is expired', async () => { @@ -94,11 +93,12 @@ describe('Receiver Model', (): void => { const connection = connectionService.get(incomingPayment) assert(connection instanceof Connection) - await expect( - Receiver.fromIncomingPayment( - await incomingPayment.toOpenPaymentsType(connection) - ) - ).rejects.toThrow('Cannot create receiver from expired incoming payment') + const openPaymentsIncomingPayment = + await incomingPayment.toOpenPaymentsType(connection) + + expect(() => + Receiver.fromIncomingPayment(openPaymentsIncomingPayment) + ).toThrow('Cannot create receiver from expired incoming payment') }) test('throws if stream connection has invalid ILP address', async () => { @@ -111,11 +111,12 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) ;(connection.ilpAddress as string) = 'not base 64 encoded' - await expect( - Receiver.fromIncomingPayment( - await incomingPayment.toOpenPaymentsType(connection) - ) - ).rejects.toThrow('Invalid ILP address on stream connection') + const openPaymentsIncomingPayment = + await incomingPayment.toOpenPaymentsType(connection) + + expect(() => + Receiver.fromIncomingPayment(openPaymentsIncomingPayment) + ).toThrow('Invalid ILP address on stream connection') }) }) From 57332242b6ece32c634e4c87690a58f4790b150a Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 1 Feb 2023 10:15:08 -0700 Subject: [PATCH 24/31] chore(backend): make toOpenPaymentType sync (#1056) * chore(backend): make toOpenPaymentType sync * chore(backend): make quote.toOpenPaymentsType sync * chore(backend): convert tests to sync * chore(backend): update tests --- .../open_payments/payment/incoming/model.ts | 41 +++++++++---------- .../payment/incoming/routes.test.ts | 6 +-- .../open_payments/payment/incoming/routes.ts | 30 +++++++++----- .../open_payments/payment/outgoing/model.ts | 15 +++---- .../payment/outgoing/routes.test.ts | 4 +- .../open_payments/payment/outgoing/routes.ts | 14 ++++--- .../payment/outgoing/service.test.ts | 15 +++---- .../payment_pointer/model.test.ts | 14 +++---- .../open_payments/payment_pointer/routes.ts | 6 +-- .../backend/src/open_payments/quote/model.ts | 15 ++----- .../src/open_payments/quote/routes.test.ts | 2 +- .../backend/src/open_payments/quote/routes.ts | 12 ++++-- .../src/open_payments/quote/service.test.ts | 23 +++++------ .../src/open_payments/receiver/model.test.ts | 10 ++--- .../open_payments/receiver/service.test.ts | 6 +-- .../src/open_payments/receiver/service.ts | 4 +- 16 files changed, 107 insertions(+), 110 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index b8c0a6ac54..45d947de0b 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -130,17 +130,10 @@ export class IncomingPayment this.receivedAmountValue = amount.value } - public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { - const paymentPointer = - fetchedPaymentPointer || (await this.getPaymentPointer()) - + public getUrl(paymentPointer: PaymentPointer): string { return `${paymentPointer.url}${IncomingPayment.urlPath}/${this.id}` } - public async getPaymentPointer(): Promise { - return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) - } - public async onCredit({ totalReceived }: OnCreditOptions): Promise { @@ -249,29 +242,33 @@ export class IncomingPayment return payment } - public async toOpenPaymentsType(): Promise - public async toOpenPaymentsType( + public toOpenPaymentsType( + paymentPointer: PaymentPointer + ): OpenPaymentsIncomingPayment + public toOpenPaymentsType( + paymentPointer: PaymentPointer, ilpStreamConnection: Connection - ): Promise - public async toOpenPaymentsType( + ): OpenPaymentsIncomingPaymentWithConnection + public toOpenPaymentsType( + paymentPointer: PaymentPointer, ilpStreamConnection: string - ): Promise - public async toOpenPaymentsType( + ): OpenPaymentsIncomingPaymentWithConnectionUrl + public toOpenPaymentsType( + paymentPointer: PaymentPointer, ilpStreamConnection?: Connection | string - ): Promise< + ): | OpenPaymentsIncomingPaymentWithConnection | OpenPaymentsIncomingPaymentWithConnectionUrl - > - public async toOpenPaymentsType( + + public toOpenPaymentsType( + paymentPointer: PaymentPointer, ilpStreamConnection?: Connection | string - ): Promise< + ): | OpenPaymentsIncomingPayment | OpenPaymentsIncomingPaymentWithConnection - | OpenPaymentsIncomingPaymentWithConnectionUrl - > { - const paymentPointer = await this.getPaymentPointer() + | OpenPaymentsIncomingPaymentWithConnectionUrl { const baseIncomingPayment: OpenPaymentsIncomingPayment = { - id: await this.getUrl(paymentPointer), + id: this.getUrl(paymentPointer), paymentPointer: paymentPointer.url, incomingAmount: this.incomingAmount ? serializeAmount(this.incomingAmount) 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 81544b4538..644b0726ac 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.test.ts @@ -82,9 +82,9 @@ describe('Incoming Payment Routes', (): void => { externalRef }), get: (ctx) => incomingPaymentRoutes.get(ctx), - getBody: async (incomingPayment, list) => { + getBody: (incomingPayment, list) => { return { - id: await incomingPayment.getUrl(), + id: incomingPayment.getUrl(paymentPointer), paymentPointer: paymentPointer.url, completed: false, incomingAmount: @@ -265,7 +265,7 @@ describe('Incoming Payment Routes', (): void => { await expect(incomingPaymentRoutes.complete(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ - id: await incomingPayment.getUrl(), + id: incomingPayment.getUrl(paymentPointer), paymentPointer: paymentPointer.url, incomingAmount: { value: '123', diff --git a/packages/backend/src/open_payments/payment/incoming/routes.ts b/packages/backend/src/open_payments/payment/incoming/routes.ts index 0820e4858f..8175670f7b 100644 --- a/packages/backend/src/open_payments/payment/incoming/routes.ts +++ b/packages/backend/src/open_payments/payment/incoming/routes.ts @@ -24,6 +24,7 @@ import { IncomingPaymentWithConnection as OpenPaymentsIncomingPaymentWithConnection, IncomingPaymentWithConnectionUrl as OpenPaymentsIncomingPaymentWithConnectionUrl } from 'open-payments' +import { PaymentPointer } from '../../payment_pointer/model' // Don't allow creating an incoming payment too far out. Incoming payments with no payments before they expire are cleaned up, since incoming payments creation is unauthenticated. // TODO what is a good default value for this? @@ -75,7 +76,11 @@ async function getIncomingPayment( } if (!incomingPayment) return ctx.throw(404) const connection = deps.connectionService.get(incomingPayment) - ctx.body = await incomingPaymentToBody(incomingPayment, connection) + ctx.body = incomingPaymentToBody( + ctx.paymentPointer, + incomingPayment, + connection + ) } export type CreateBody = { @@ -116,7 +121,11 @@ async function createIncomingPayment( ctx.status = 201 const connection = deps.connectionService.get(incomingPaymentOrError) - ctx.body = await incomingPaymentToBody(incomingPaymentOrError, connection) + ctx.body = incomingPaymentToBody( + ctx.paymentPointer, + incomingPaymentOrError, + connection + ) } async function completeIncomingPayment( @@ -138,7 +147,7 @@ async function completeIncomingPayment( errorToMessage[incomingPaymentOrError] ) } - ctx.body = await incomingPaymentToBody(incomingPaymentOrError) + ctx.body = incomingPaymentToBody(ctx.paymentPointer, incomingPaymentOrError) } async function listIncomingPayments( @@ -149,8 +158,9 @@ async function listIncomingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.incomingPaymentService.getPaymentPointerPage, - toBody: async (payment) => - await incomingPaymentToBody( + toBody: (payment) => + incomingPaymentToBody( + ctx.paymentPointer, payment, deps.connectionService.getUrl(payment) ) @@ -159,13 +169,13 @@ async function listIncomingPayments( ctx.throw(500, 'Error trying to list incoming payments') } } -async function incomingPaymentToBody( +function incomingPaymentToBody( + paymentPointer: PaymentPointer, incomingPayment: IncomingPayment, ilpStreamConnection?: Connection | string -): Promise< +): | OpenPaymentsIncomingPayment | OpenPaymentsIncomingPaymentWithConnection - | OpenPaymentsIncomingPaymentWithConnectionUrl -> { - return incomingPayment.toOpenPaymentsType(ilpStreamConnection) + | OpenPaymentsIncomingPaymentWithConnectionUrl { + return incomingPayment.toOpenPaymentsType(paymentPointer, ilpStreamConnection) } diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 77463c455d..736cca0950 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -72,10 +72,7 @@ export class OutgoingPayment return this.quote.assetId } - public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { - const paymentPointer = - fetchedPaymentPointer || (await this.getPaymentPointer()) - + public getUrl(paymentPointer: PaymentPointer): string { return `${paymentPointer.url}${OutgoingPayment.urlPath}/${this.id}` } @@ -188,13 +185,13 @@ export class OutgoingPayment return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) } - public async toOpenPaymentsType(): Promise { - const paymentPointer = await this.getPaymentPointer() - + public toOpenPaymentsType( + paymentPointer: PaymentPointer + ): OpenPaymentsOutgoingPayment { return { - id: await this.getUrl(paymentPointer), + id: this.getUrl(paymentPointer), paymentPointer: paymentPointer.url, - quoteId: (await this.quote?.getUrl()) ?? undefined, + quoteId: this.quote?.getUrl(paymentPointer) ?? undefined, receiveAmount: serializeAmount(this.receiveAmount), sendAmount: serializeAmount(this.sendAmount), sentAmount: serializeAmount(this.sentAmount), diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts index 0d802f2590..2b949c5a16 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.test.ts @@ -100,12 +100,12 @@ describe('Outgoing Payment Routes', (): void => { return outgoingPayment }, get: (ctx) => outgoingPaymentRoutes.get(ctx), - getBody: async (outgoingPayment) => { + getBody: (outgoingPayment) => { return { id: `${paymentPointer.url}/outgoing-payments/${outgoingPayment.id}`, paymentPointer: paymentPointer.url, receiver: outgoingPayment.receiver, - quoteId: await outgoingPayment.quote.getUrl(), + quoteId: outgoingPayment.quote.getUrl(paymentPointer), sendAmount: serializeAmount(outgoingPayment.sendAmount), sentAmount: serializeAmount(outgoingPayment.sentAmount), receiveAmount: serializeAmount(outgoingPayment.receiveAmount), diff --git a/packages/backend/src/open_payments/payment/outgoing/routes.ts b/packages/backend/src/open_payments/payment/outgoing/routes.ts index 5c0d2d15e7..9884292cab 100644 --- a/packages/backend/src/open_payments/payment/outgoing/routes.ts +++ b/packages/backend/src/open_payments/payment/outgoing/routes.ts @@ -9,6 +9,7 @@ import { AccessAction, OutgoingPayment as OpenPaymentsOutgoingPayment } from 'open-payments' +import { PaymentPointer } from '../../payment_pointer/model' interface ServiceDependencies { config: IAppConfig @@ -52,7 +53,7 @@ async function getOutgoingPayment( ctx.throw(500, 'Error trying to get outgoing payment') } if (!outgoingPayment) return ctx.throw(404) - ctx.body = await outgoingPaymentToBody(outgoingPayment) + ctx.body = outgoingPaymentToBody(ctx.paymentPointer, outgoingPayment) } export type CreateBody = { @@ -86,7 +87,7 @@ async function createOutgoingPayment( return ctx.throw(errorToCode[paymentOrErr], errorToMessage[paymentOrErr]) } ctx.status = 201 - ctx.body = await outgoingPaymentToBody(paymentOrErr) + ctx.body = outgoingPaymentToBody(ctx.paymentPointer, paymentOrErr) } async function listOutgoingPayments( @@ -97,15 +98,16 @@ async function listOutgoingPayments( await listSubresource({ ctx, getPaymentPointerPage: deps.outgoingPaymentService.getPaymentPointerPage, - toBody: async (payment) => await outgoingPaymentToBody(payment) + toBody: (payment) => outgoingPaymentToBody(ctx.paymentPointer, payment) }) } catch (_) { ctx.throw(500, 'Error trying to list outgoing payments') } } -async function outgoingPaymentToBody( +function outgoingPaymentToBody( + paymentPointer: PaymentPointer, outgoingPayment: OutgoingPayment -): Promise { - return outgoingPayment.toOpenPaymentsType() +): OpenPaymentsOutgoingPayment { + return outgoingPayment.toOpenPaymentsType(paymentPointer) } 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 93d02ab80f..b2827bdd11 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -245,11 +245,10 @@ describe('OutgoingPaymentService', (): void => { beforeEach(async (): Promise => { const { id: sendAssetId } = await createAsset(deps, asset) - paymentPointerId = ( - await createPaymentPointer(deps, { - assetId: sendAssetId - }) - ).id + const paymentPointer = await createPaymentPointer(deps, { + assetId: sendAssetId + }) + paymentPointerId = paymentPointer.id const { id: destinationAssetId } = await createAsset(deps, destinationAsset) receiverPaymentPointer = await createPaymentPointer(deps, { assetId: destinationAssetId, @@ -266,7 +265,7 @@ describe('OutgoingPaymentService', (): void => { incomingPayment = await createIncomingPayment(deps, { paymentPointerId: receiverPaymentPointer.id }) - receiver = await incomingPayment.getUrl() + receiver = incomingPayment.getUrl(receiverPaymentPointer) amtDelivered = BigInt(0) }) @@ -855,12 +854,14 @@ describe('OutgoingPaymentService', (): void => { assetScale: receiverPaymentPointer.asset.scale } }) + const fetchedReceiver = connectionService.getUrl(incomingPayment) assert.ok(fetchedReceiver) + assert.ok(incomingPayment.paymentPointer) const paymentId = await setup({ receiver: toConnection ? fetchedReceiver - : await incomingPayment.getUrl(), + : incomingPayment.getUrl(incomingPayment.paymentPointer), receiveAmount }) diff --git a/packages/backend/src/open_payments/payment_pointer/model.test.ts b/packages/backend/src/open_payments/payment_pointer/model.test.ts index c3a87565a4..5ae2ae0bd1 100644 --- a/packages/backend/src/open_payments/payment_pointer/model.test.ts +++ b/packages/backend/src/open_payments/payment_pointer/model.test.ts @@ -194,7 +194,7 @@ type RouteTestsOptions = Omit< > & { getPaymentPointer: () => Promise get: (ctx: ReadContext) => Promise - getBody: (model: M, list?: boolean) => Promise> + getBody: (model: M, list?: boolean) => Record list?: (ctx: ListContext) => Promise urlPath: string } @@ -230,7 +230,7 @@ export const getRouteTests = ({ expect(ctx.response).toSatisfyApiSpec() } expect(ctx.body).toEqual({ - result: expectedMatch ? [await getBody(expectedMatch, true)] : [], + result: expectedMatch ? [getBody(expectedMatch, true)] : [], pagination: { hasPreviousPage: false, hasNextPage: false, @@ -261,7 +261,7 @@ export const getRouteTests = ({ if (expectedMatch) { await expect(get(ctx)).resolves.toBeUndefined() expect(ctx.response).toSatisfyApiSpec() - expect(ctx.body).toEqual(await getBody(expectedMatch)) + expect(ctx.body).toEqual(getBody(expectedMatch)) } else { await expect(get(ctx)).rejects.toMatchObject({ status: 404, @@ -319,11 +319,9 @@ export const getRouteTests = ({ expect(ctx.response).toSatisfyApiSpec() expect(ctx.body).toEqual({ pagination, - result: await Promise.all( - models - .slice(startIndex, endIndex + 1) - .map(async (model) => await getBody(model, true)) - ) + result: models + .slice(startIndex, endIndex + 1) + .map((model) => getBody(model, true)) }) } ) diff --git a/packages/backend/src/open_payments/payment_pointer/routes.ts b/packages/backend/src/open_payments/payment_pointer/routes.ts index 92a0ba606d..2059a63d47 100644 --- a/packages/backend/src/open_payments/payment_pointer/routes.ts +++ b/packages/backend/src/open_payments/payment_pointer/routes.ts @@ -40,9 +40,7 @@ export async function getPaymentPointer( interface ListSubresourceOptions { ctx: ListContext getPaymentPointerPage: PaymentPointerSubresourceService['getPaymentPointerPage'] - toBody: - | ((model: M) => Record) - | ((model: M) => Promise>) + toBody: (model: M) => Record } export const listSubresource = async ({ @@ -68,7 +66,7 @@ export const listSubresource = async ({ ) const result = { pagination: pageInfo, - result: await Promise.all(page.map(async (item: M) => await toBody(item))) + result: page.map((item: M) => toBody(item)) } ctx.body = result } diff --git a/packages/backend/src/open_payments/quote/model.ts b/packages/backend/src/open_payments/quote/model.ts index 7684dacd09..1b70de7b10 100644 --- a/packages/backend/src/open_payments/quote/model.ts +++ b/packages/backend/src/open_payments/quote/model.ts @@ -47,14 +47,7 @@ export class Quote extends PaymentPointerSubresource { private sendAmountValue!: bigint - public async getPaymentPointer(): Promise { - return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) - } - - public async getUrl(fetchedPaymentPointer?: PaymentPointer): Promise { - const paymentPointer = - fetchedPaymentPointer || (await this.getPaymentPointer()) - + public getUrl(paymentPointer: PaymentPointer): string { return `${paymentPointer.url}${Quote.urlPath}/${this.id}` } @@ -164,11 +157,9 @@ export class Quote extends PaymentPointerSubresource { } } - public async toOpenPaymentsType(): Promise { - const paymentPointer = await this.getPaymentPointer() - + public toOpenPaymentsType(paymentPointer: PaymentPointer): OpenPaymentsQuote { return { - id: await this.getUrl(paymentPointer), + id: this.getUrl(paymentPointer), paymentPointer: paymentPointer.url, receiveAmount: serializeAmount(this.receiveAmount), sendAmount: serializeAmount(this.sendAmount), diff --git a/packages/backend/src/open_payments/quote/routes.test.ts b/packages/backend/src/open_payments/quote/routes.test.ts index 269581aafa..9c78b95f28 100644 --- a/packages/backend/src/open_payments/quote/routes.test.ts +++ b/packages/backend/src/open_payments/quote/routes.test.ts @@ -96,7 +96,7 @@ describe('Quote Routes', (): void => { client }), get: (ctx) => quoteRoutes.get(ctx), - getBody: async (quote) => { + getBody: (quote) => { return { id: `${paymentPointer.url}/quotes/${quote.id}`, paymentPointer: paymentPointer.url, diff --git a/packages/backend/src/open_payments/quote/routes.ts b/packages/backend/src/open_payments/quote/routes.ts index efa12167e8..d327497a99 100644 --- a/packages/backend/src/open_payments/quote/routes.ts +++ b/packages/backend/src/open_payments/quote/routes.ts @@ -7,6 +7,7 @@ import { isQuoteError, errorToCode, errorToMessage } from './errors' import { Quote } from './model' import { AmountJSON, parseAmount } from '../amount' import { Quote as OpenPaymentsQuote } from 'open-payments' +import { PaymentPointer } from '../payment_pointer/model' interface ServiceDependencies { config: IAppConfig @@ -40,7 +41,7 @@ async function getQuote( paymentPointerId: ctx.paymentPointer.id }) if (!quote) return ctx.throw(404) - ctx.body = await quoteToBody(quote) + ctx.body = quoteToBody(ctx.paymentPointer, quote) } interface CreateBodyBase { @@ -80,7 +81,7 @@ async function createQuote( } ctx.status = 201 - ctx.body = await quoteToBody(quoteOrErr) + ctx.body = quoteToBody(ctx.paymentPointer, quoteOrErr) } catch (err) { if (isQuoteError(err)) { return ctx.throw(errorToCode[err], errorToMessage[err]) @@ -90,6 +91,9 @@ async function createQuote( } } -async function quoteToBody(quote: Quote): Promise { - return quote.toOpenPaymentsType() +function quoteToBody( + paymentPointer: PaymentPointer, + quote: Quote +): OpenPaymentsQuote { + return quote.toOpenPaymentsType(paymentPointer) } diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index e3376b0a91..8de6155c3c 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -247,7 +247,7 @@ describe('QuoteService', (): void => { paymentPointerId, receiver: toConnection ? connectionService.getUrl(incomingPayment) - : await incomingPayment.getUrl() + : incomingPayment.getUrl(receivingPaymentPointer) } if (sendAmount) options.sendAmount = sendAmount if (receiveAmount) options.receiveAmount = receiveAmount @@ -575,7 +575,7 @@ describe('QuoteService', (): void => { }) const options: CreateQuoteOptions = { paymentPointerId, - receiver: await incomingPayment.getUrl(), + receiver: incomingPayment.getUrl(receivingPaymentPointer), receiveAmount } const expected: ExpectedQuote = { @@ -651,13 +651,12 @@ describe('QuoteService', (): void => { `( 'fails to create $description', async ({ sendAmount, receiveAmount }): Promise => { + const incomingPayment = await createIncomingPayment(deps, { + paymentPointerId: receivingPaymentPointer.id + }) const options: CreateQuoteOptions = { paymentPointerId, - receiver: await ( - await createIncomingPayment(deps, { - paymentPointerId: receivingPaymentPointer.id - }) - ).getUrl() + receiver: incomingPayment.getUrl(receivingPaymentPointer) } if (sendAmount) options.sendAmount = sendAmount if (receiveAmount) options.receiveAmount = receiveAmount @@ -672,14 +671,14 @@ describe('QuoteService', (): void => { jest .spyOn(ratesService, 'prices') .mockImplementation(() => Promise.reject(new Error('fail'))) + const incomingPayment = await createIncomingPayment(deps, { + paymentPointerId: receivingPaymentPointer.id + }) + await expect( quoteService.create({ paymentPointerId, - receiver: await ( - await createIncomingPayment(deps, { - paymentPointerId: receivingPaymentPointer.id - }) - ).getUrl(), + receiver: incomingPayment.getUrl(receivingPaymentPointer), sendAmount }) ).rejects.toThrow('missing prices') diff --git a/packages/backend/src/open_payments/receiver/model.test.ts b/packages/backend/src/open_payments/receiver/model.test.ts index e8a74a3a1e..bfa9af0ca0 100644 --- a/packages/backend/src/open_payments/receiver/model.test.ts +++ b/packages/backend/src/open_payments/receiver/model.test.ts @@ -44,7 +44,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) const receiver = Receiver.fromIncomingPayment( - await incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(paymentPointer, connection) ) expect(receiver).toEqual({ @@ -53,7 +53,7 @@ describe('Receiver Model', (): void => { ilpAddress: expect.any(String), sharedSecret: expect.any(Buffer), incomingPayment: { - id: await incomingPayment.getUrl(), + id: incomingPayment.getUrl(paymentPointer), paymentPointer: paymentPointer.url, updatedAt: incomingPayment.updatedAt, createdAt: incomingPayment.createdAt, @@ -76,7 +76,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) const openPaymentsIncomingPayment = - await incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(paymentPointer, connection) expect(() => Receiver.fromIncomingPayment(openPaymentsIncomingPayment) @@ -94,7 +94,7 @@ describe('Receiver Model', (): void => { assert(connection instanceof Connection) const openPaymentsIncomingPayment = - await incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(paymentPointer, connection) expect(() => Receiver.fromIncomingPayment(openPaymentsIncomingPayment) @@ -112,7 +112,7 @@ describe('Receiver Model', (): void => { ;(connection.ilpAddress as string) = 'not base 64 encoded' const openPaymentsIncomingPayment = - await incomingPayment.toOpenPaymentsType(connection) + await incomingPayment.toOpenPaymentsType(paymentPointer, connection) expect(() => Receiver.fromIncomingPayment(openPaymentsIncomingPayment) diff --git a/packages/backend/src/open_payments/receiver/service.test.ts b/packages/backend/src/open_payments/receiver/service.test.ts index d03cbb089f..0bca137283 100644 --- a/packages/backend/src/open_payments/receiver/service.test.ts +++ b/packages/backend/src/open_payments/receiver/service.test.ts @@ -194,15 +194,15 @@ describe('Receiver Service', (): void => { ) await expect( - receiverService.get(await incomingPayment.getUrl()) + receiverService.get(incomingPayment.getUrl(paymentPointer)) ).resolves.toEqual({ assetCode: incomingPayment.receivedAmount.assetCode, assetScale: incomingPayment.receivedAmount.assetScale, ilpAddress: expect.any(String), sharedSecret: expect.any(Buffer), incomingPayment: { - id: await incomingPayment.getUrl(), - paymentPointer: (await incomingPayment.getPaymentPointer()).url, + id: incomingPayment.getUrl(paymentPointer), + paymentPointer: paymentPointer.url, completed: incomingPayment.completed, receivedAmount: incomingPayment.receivedAmount, incomingAmount: incomingPayment.incomingAmount, diff --git a/packages/backend/src/open_payments/receiver/service.ts b/packages/backend/src/open_payments/receiver/service.ts index b413b10bd0..70b5aa3a76 100644 --- a/packages/backend/src/open_payments/receiver/service.ts +++ b/packages/backend/src/open_payments/receiver/service.ts @@ -131,7 +131,7 @@ async function createLocalIncomingPayment( throw new Error(errorMessage) } - return incomingPaymentOrError.toOpenPaymentsType(connection) + return incomingPaymentOrError.toOpenPaymentsType(paymentPointer, connection) } async function getReceiver( @@ -274,7 +274,7 @@ async function getLocalIncomingPayment({ return undefined } - return incomingPayment.toOpenPaymentsType(connection) + return incomingPayment.toOpenPaymentsType(paymentPointer, connection) } async function getIncomingPaymentGrant( From 28a34df8d39b26cfa952532888506126ad2f18b8 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 18:41:50 +0100 Subject: [PATCH 25/31] chore(backend): remove $formatJson where applicable --- .../open_payments/payment/incoming/model.ts | 28 ------------------ .../open_payments/payment/outgoing/model.ts | 29 ------------------- 2 files changed, 57 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 45d947de0b..0008573e62 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -214,34 +214,6 @@ export class IncomingPayment } } - $formatJson(json: Pojo): Pojo { - json = super.$formatJson(json) - const payment: Pojo = { - id: json.id, - receivedAmount: { - ...json.receivedAmount, - value: json.receivedAmount.value.toString() - }, - completed: json.completed, - createdAt: json.createdAt, - updatedAt: json.updatedAt, - expiresAt: json.expiresAt.toISOString() - } - if (json.incomingAmount) { - payment.incomingAmount = { - ...json.incomingAmount, - value: json.incomingAmount.value.toString() - } - } - if (json.description) { - payment.description = json.description - } - if (json.externalRef) { - payment.externalRef = json.externalRef - } - return payment - } - public toOpenPaymentsType( paymentPointer: PaymentPointer ): OpenPaymentsIncomingPayment diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index 736cca0950..bdcc89f026 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -156,35 +156,6 @@ export class OutgoingPayment return data } - $formatJson(json: Pojo): Pojo { - json = super.$formatJson(json) - return { - id: json.id, - state: json.state, - receiver: json.receiver, - sendAmount: { - ...json.sendAmount, - value: json.sendAmount.value.toString() - }, - sentAmount: { - ...json.sentAmount, - value: json.sentAmount.value.toString() - }, - receiveAmount: { - ...json.receiveAmount, - value: json.receiveAmount.value.toString() - }, - description: json.description, - externalRef: json.externalRef, - createdAt: json.createdAt, - updatedAt: json.updatedAt - } - } - - public async getPaymentPointer(): Promise { - return this.paymentPointer ?? (await this.$relatedQuery('paymentPointer')) - } - public toOpenPaymentsType( paymentPointer: PaymentPointer ): OpenPaymentsOutgoingPayment { From 5e954860ad3af666e632b3991f7a6a59e4d34a49 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 19:03:25 +0100 Subject: [PATCH 26/31] chore(backend): remove $formatJson where applicable --- packages/backend/src/open_payments/payment/incoming/model.ts | 2 +- packages/backend/src/open_payments/payment/outgoing/model.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index 0008573e62..c310120aba 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -1,4 +1,4 @@ -import { Model, ModelOptions, Pojo, QueryContext } from 'objection' +import { Model, ModelOptions, QueryContext } from 'objection' import { v4 as uuid } from 'uuid' import { Amount, AmountJSON, serializeAmount } from '../../amount' diff --git a/packages/backend/src/open_payments/payment/outgoing/model.ts b/packages/backend/src/open_payments/payment/outgoing/model.ts index bdcc89f026..ebdab291f5 100644 --- a/packages/backend/src/open_payments/payment/outgoing/model.ts +++ b/packages/backend/src/open_payments/payment/outgoing/model.ts @@ -1,4 +1,4 @@ -import { Model, ModelOptions, Pojo, QueryContext } from 'objection' +import { Model, ModelOptions, QueryContext } from 'objection' import { DbErrors } from 'objection-db-errors' import { LiquidityAccount } from '../../../accounting/service' From b6d25c6a0947aaf1d027f0e074244df75aab915a Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 19:10:27 +0100 Subject: [PATCH 27/31] chore(backend): add test for incomingPayment model --- .../payment/incoming/model.test.ts | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 packages/backend/src/open_payments/payment/incoming/model.test.ts diff --git a/packages/backend/src/open_payments/payment/incoming/model.test.ts b/packages/backend/src/open_payments/payment/incoming/model.test.ts new file mode 100644 index 0000000000..b57e7e31f1 --- /dev/null +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -0,0 +1,127 @@ +import { IocContract } from '@adonisjs/fold' +import { createTestApp, TestContainer } from '../../../tests/app' +import { Config, IAppConfig } from '../../../config/app' +import { initIocContainer } from '../../..' +import { AppServices } from '../../../app' +import { createIncomingPayment } from '../../../tests/incomingPayment' +import { createPaymentPointer } from '../../../tests/paymentPointer' +import { truncateTables } from '../../../tests/tableManager' +import { Connection } from '../../connection/model' +import { serializeAmount } from '../../amount' +import { IlpAddress } from 'ilp-packet' +import { IncomingPayment } from './model' + +describe('Incoming Payment Model', (): void => { + let deps: IocContract + let appContainer: TestContainer + let config: IAppConfig + + beforeAll(async (): Promise => { + deps = initIocContainer(Config) + appContainer = await createTestApp(deps) + config = await deps.use('config') + }) + + afterEach(async (): Promise => { + jest.useRealTimers() + await truncateTables(appContainer.knex) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + + describe('toOpenPaymentsType', () => { + test('returns incoming payment without connection provided', async () => { + const paymentPointer = await createPaymentPointer(deps) + const incomingPayment = await createIncomingPayment(deps, { + paymentPointerId: paymentPointer.id, + description: 'my payment' + }) + + expect(incomingPayment.toOpenPaymentsType(paymentPointer)).toEqual({ + id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, + paymentPointer: paymentPointer.url, + updatedAt: incomingPayment.updatedAt, + createdAt: incomingPayment.createdAt, + completed: incomingPayment.completed, + receivedAmount: serializeAmount(incomingPayment.receivedAmount), + incomingAmount: incomingPayment.incomingAmount + ? serializeAmount(incomingPayment.incomingAmount) + : undefined, + expiresAt: incomingPayment.expiresAt, + description: incomingPayment.description ?? undefined, + externalRef: incomingPayment.externalRef ?? undefined + }) + }) + + test('returns incoming payment with connection as string', async () => { + const paymentPointer = await createPaymentPointer(deps) + const incomingPayment = await createIncomingPayment(deps, { + paymentPointerId: paymentPointer.id, + description: 'my payment' + }) + + const connection = `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}` + + expect( + incomingPayment.toOpenPaymentsType(paymentPointer, connection) + ).toEqual({ + id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, + paymentPointer: paymentPointer.url, + updatedAt: incomingPayment.updatedAt, + createdAt: incomingPayment.createdAt, + completed: incomingPayment.completed, + receivedAmount: serializeAmount(incomingPayment.receivedAmount), + incomingAmount: incomingPayment.incomingAmount + ? serializeAmount(incomingPayment.incomingAmount) + : undefined, + expiresAt: incomingPayment.expiresAt, + description: incomingPayment.description ?? undefined, + externalRef: incomingPayment.externalRef ?? undefined, + ilpStreamConnection: connection + }) + }) + + test('returns incoming payment with connection as object', async () => { + const paymentPointer = await createPaymentPointer(deps) + const incomingPayment = await createIncomingPayment(deps, { + paymentPointerId: paymentPointer.id, + description: 'my payment' + }) + + const connection = Connection.fromPayment({ + payment: incomingPayment, + openPaymentsUrl: config.openPaymentsUrl, + credentials: { + ilpAddress: 'test.ilp' as IlpAddress, + sharedSecret: Buffer.from('') + } + }) + + expect( + incomingPayment.toOpenPaymentsType(paymentPointer, connection) + ).toEqual({ + id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, + paymentPointer: paymentPointer.url, + updatedAt: incomingPayment.updatedAt, + createdAt: incomingPayment.createdAt, + completed: incomingPayment.completed, + receivedAmount: serializeAmount(incomingPayment.receivedAmount), + incomingAmount: incomingPayment.incomingAmount + ? serializeAmount(incomingPayment.incomingAmount) + : undefined, + expiresAt: incomingPayment.expiresAt, + description: incomingPayment.description ?? undefined, + externalRef: incomingPayment.externalRef ?? undefined, + ilpStreamConnection: { + id: `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}`, + ilpAddress: 'test.ilp', + sharedSecret: expect.any(String), + assetCode: incomingPayment.asset.code, + assetScale: incomingPayment.asset.scale + } + }) + }) + }) +}) From 09a0c10b52d55966cfca9fa5efd9943fcc95e22e Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 19:53:56 +0100 Subject: [PATCH 28/31] chore(backend): fix incomingPayment model test --- .../open_payments/payment/incoming/model.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 b57e7e31f1..70f142650d 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -42,8 +42,6 @@ describe('Incoming Payment Model', (): void => { expect(incomingPayment.toOpenPaymentsType(paymentPointer)).toEqual({ id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, paymentPointer: paymentPointer.url, - updatedAt: incomingPayment.updatedAt, - createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount @@ -51,7 +49,9 @@ describe('Incoming Payment Model', (): void => { : undefined, expiresAt: incomingPayment.expiresAt, description: incomingPayment.description ?? undefined, - externalRef: incomingPayment.externalRef ?? undefined + externalRef: incomingPayment.externalRef ?? undefined, + updatedAt: incomingPayment.updatedAt.toISOString(), + createdAt: incomingPayment.createdAt.toISOString() }) }) @@ -69,8 +69,6 @@ describe('Incoming Payment Model', (): void => { ).toEqual({ id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, paymentPointer: paymentPointer.url, - updatedAt: incomingPayment.updatedAt, - createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount @@ -79,6 +77,8 @@ describe('Incoming Payment Model', (): void => { expiresAt: incomingPayment.expiresAt, description: incomingPayment.description ?? undefined, externalRef: incomingPayment.externalRef ?? undefined, + updatedAt: incomingPayment.updatedAt.toISOString(), + createdAt: incomingPayment.createdAt.toISOString(), ilpStreamConnection: connection }) }) @@ -104,16 +104,16 @@ describe('Incoming Payment Model', (): void => { ).toEqual({ id: `${paymentPointer.url}${IncomingPayment.urlPath}/${incomingPayment.id}`, paymentPointer: paymentPointer.url, - updatedAt: incomingPayment.updatedAt, - createdAt: incomingPayment.createdAt, completed: incomingPayment.completed, receivedAmount: serializeAmount(incomingPayment.receivedAmount), incomingAmount: incomingPayment.incomingAmount ? serializeAmount(incomingPayment.incomingAmount) : undefined, - expiresAt: incomingPayment.expiresAt, + expiresAt: incomingPayment.expiresAt.toISOString(), description: incomingPayment.description ?? undefined, externalRef: incomingPayment.externalRef ?? undefined, + updatedAt: incomingPayment.updatedAt.toISOString(), + createdAt: incomingPayment.createdAt.toISOString(), ilpStreamConnection: { id: `${config.openPaymentsUrl}/connections/${incomingPayment.connectionId}`, ilpAddress: 'test.ilp', From c079942267b8a938a6948da9159fe8580f0ca95d Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 22:08:33 +0100 Subject: [PATCH 29/31] chore(open-payments): use suggestion --- packages/open-payments/src/test/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index d4b0dde2b6..fa2116c9c8 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -116,7 +116,7 @@ export const mockIncomingPaymentWithConnection = ( overrides?: Partial ): IncomingPaymentWithConnection => ({ ...mockIncomingPayment(), - ilpStreamConnection: mockILPStreamConnection(), + ilpStreamConnection: mockILPStreamConnection(overrides?.ilpStreamConnection), ...overrides }) From c55e586e06cc57bf7d80d654e27538e558cac6d7 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 22:08:42 +0100 Subject: [PATCH 30/31] chore(backend): fix test --- .../backend/src/open_payments/payment/incoming/model.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 70f142650d..1c64715c7f 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.test.ts @@ -47,7 +47,7 @@ describe('Incoming Payment Model', (): void => { incomingAmount: incomingPayment.incomingAmount ? serializeAmount(incomingPayment.incomingAmount) : undefined, - expiresAt: incomingPayment.expiresAt, + expiresAt: incomingPayment.expiresAt.toISOString(), description: incomingPayment.description ?? undefined, externalRef: incomingPayment.externalRef ?? undefined, updatedAt: incomingPayment.updatedAt.toISOString(), @@ -74,7 +74,7 @@ describe('Incoming Payment Model', (): void => { incomingAmount: incomingPayment.incomingAmount ? serializeAmount(incomingPayment.incomingAmount) : undefined, - expiresAt: incomingPayment.expiresAt, + expiresAt: incomingPayment.expiresAt.toISOString(), description: incomingPayment.description ?? undefined, externalRef: incomingPayment.externalRef ?? undefined, updatedAt: incomingPayment.updatedAt.toISOString(), From fc311e2655dcd18ff5a4937cb95071a06f7f2d9d Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Thu, 2 Feb 2023 22:08:52 +0100 Subject: [PATCH 31/31] chore(backend): use suggestion --- packages/backend/src/open_payments/payment/outgoing/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/open_payments/payment/outgoing/service.ts b/packages/backend/src/open_payments/payment/outgoing/service.ts index f8c8c36edb..4a37f971da 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.ts @@ -293,7 +293,7 @@ async function validateGrant( payment: grantPayment }) ) { - if (grantPayment.state === OutgoingPaymentState.Failed) { + if (grantPayment.failed) { const totalSent = await deps.accountingService.getTotalSent( grantPayment.id )