diff --git a/.changeset/moody-students-greet.md b/.changeset/moody-students-greet.md new file mode 100644 index 00000000..f2774d37 --- /dev/null +++ b/.changeset/moody-students-greet.md @@ -0,0 +1,5 @@ +--- +'@interledger/openapi': minor +--- + +Removed payment pointer/wallet address from resource urls diff --git a/README.md b/README.md index 5dc9b1e4..b0253a1c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Open Payments + Open Payments

--- diff --git a/openapi/resource-server.yaml b/openapi/resource-server.yaml index f053847d..c0ff4abc 100644 --- a/openapi/resource-server.yaml +++ b/openapi/resource-server.yaml @@ -32,16 +32,8 @@ info: contact: email: tech@interledger.org servers: - - url: '{walletAddress}' - description: 'Server for when Wallet Address has a pathname (ie https://openpayments.guide/alice)' - variables: - walletAddress: - default: https://openpayments.guide/alice - - url: '{walletAddress}/.well-known/pay' - description: 'Server for when Wallet Address has no pathname (ie https://openpayments.guide)' - variables: - walletAddress: - default: https://openpayments.guide + - url: '/' + description: 'Server for wallet address subresources or Connection resources (ie https://openpayments.guide/alice)' tags: - name: wallet-address description: wallet address operations @@ -119,7 +111,7 @@ paths: examples: New Incoming Payment for $25: value: - id: 'https://openpayments.guide/alice/incoming-payments/08394f02-7b7b-45e2-b645-51d04e7c330c' + id: 'https://openpayments.guide/incoming-payments/08394f02-7b7b-45e2-b645-51d04e7c330c' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '2500' @@ -216,7 +208,7 @@ paths: hasPreviousPage: false hasNextPage: true result: - - id: 'https://openpayments.guide/alice/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' + - id: 'https://openpayments.guide/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '250' @@ -226,14 +218,14 @@ paths: value: '250' assetCode: USD assetScale: 2 - expiresAt: '2022-04-12T23:20:50.52Z' - createdAt: '2022-03-12T23:20:50.52Z' - updatedAt: '2022-04-01T10:24:36.11Z' metadata: description: 'Hi Mo, this is for the cappuccino I bought for you the other day.' externalRef: Coffee w/ Mo on 10 March 22 + expiresAt: '2022-04-12T23:20:50.52Z' + createdAt: '2022-03-12T23:20:50.52Z' + updatedAt: '2022-04-01T10:24:36.11Z' completed: true - - id: 'https://openpayments.guide/alice/incoming-payments/32abc219-3dc3-44ec-a225-790cacfca8fa' + - id: 'https://openpayments.guide/incoming-payments/32abc219-3dc3-44ec-a225-790cacfca8fa' walletAddress: 'https://openpayments.guide/alice/' receivedAmount: value: '100' @@ -253,7 +245,7 @@ paths: hasPreviousPage: true hasNextPage: false result: - - id: 'https://openpayments.guide/alice/incoming-payments/32abc219-3dc3-44ec-a225-790cacfca8fa' + - id: 'https://openpayments.guide/incoming-payments/32abc219-3dc3-44ec-a225-790cacfca8fa' walletAddress: 'https://openpayments.guide/alice/' receivedAmount: value: '100' @@ -265,7 +257,7 @@ paths: updatedAt: '2022-04-01T10:24:36.11Z' metadata: description: 'I love your website, Alice! Thanks for the great content' - - id: 'https://openpayments.guide/alice/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' + - id: 'https://openpayments.guide/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '250' @@ -311,7 +303,7 @@ paths: examples: New Fixed Send Outgoing Payment for $25: value: - id: 'https://openpayments.guide/alice/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + id: 'https://openpayments.guide/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/bob/incoming-payments/48884225-b393-4872-90de-1b737e2491c2' @@ -341,7 +333,7 @@ paths: examples: Create an outgoing payment based on a quote: value: - quoteId: 'https://openpayments.guide/alice/quotes/ab03296b-0c8b-4776-b94e-7ee27d868d4d' + quoteId: 'https://openpayments.guide/quotes/ab03296b-0c8b-4776-b94e-7ee27d868d4d' metadata: externalRef: INV2022-02-0137 schema: @@ -397,7 +389,7 @@ paths: hasPreviousPage: false hasNextPage: true result: - - id: 'https://openpayments.guide/alice/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + - id: 'https://openpayments.guide/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/aplusvideo/incoming-payments/45d495ad-b763-4882-88d7-aa14d261686e' @@ -418,7 +410,7 @@ paths: metadata: description: APlusVideo subscription externalRef: 'customer: 847458475' - - id: 'https://openpayments.guide/alice/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' + - id: 'https://openpayments.guide/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/shoeshop/incoming-payments/2fe92c6f-ef0d-487c-8759-3784eae6bce9' @@ -447,7 +439,7 @@ paths: hasPreviousPage: true hasNextPage: false result: - - id: 'https://openpayments.guide/alice/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' + - id: 'https://openpayments.guide/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/shoeshop/incoming-payments/2fe92c6f-ef0d-487c-8759-3784eae6bce9' @@ -468,7 +460,7 @@ paths: metadata: description: Thank you for your purchase at ShoeShop! externalRef: INV2022-8943756 - - id: 'https://openpayments.guide/alice/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + - id: 'https://openpayments.guide/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/aplusvideo/incoming-payments/45d495ad-b763-4882-88d7-aa14d261686e' @@ -518,7 +510,7 @@ paths: examples: New Fixed Send Quote for $25: value: - id: 'https://openpayments.guide/alice/quotes/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + id: 'https://openpayments.guide/quotes/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' receiver: 'https://openpayments.guide/aplusvideo/incoming-payments/45d495ad-b763-4882-88d7-aa14d261686e' debitAmount: @@ -550,7 +542,7 @@ paths: assetScale: 2 Create fixed-receive-amount quote for $25: value: - receiver: 'https://openpayments.guide/alice/incoming-payments/37a0d0ee-26dc-4c66-89e0-01fbf93156f7' + receiver: 'https://openpayments.guide/incoming-payments/37a0d0ee-26dc-4c66-89e0-01fbf93156f7' receiveAmount: value: '2500' assetCode: USD @@ -614,7 +606,7 @@ paths: examples: Incoming Payment for $25 with $12.34 received so far: value: - id: 'https://openpayments.guide/alice/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' + id: 'https://openpayments.guide/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '2500' @@ -664,7 +656,7 @@ paths: examples: Completed Incoming Payment: value: - id: 'https://openpayments.guide/alice/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' + id: 'https://openpayments.guide/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '250' @@ -715,7 +707,7 @@ paths: id: 'https://openpayments.guide/bob/outgoing-payments/3859b39e-4666-4ce5-8745-72f1864c5371' walletAddress: 'https://openpayments.guide/bob/' failed: false - receiver: 'https://openpayments.guide/alice/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' + receiver: 'https://openpayments.guide/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' debitAmount: value: '2500' assetCode: USD @@ -763,7 +755,7 @@ paths: value: id: 'https://openpayments.guide/bob/quotes/3859b39e-4666-4ce5-8745-72f1864c5371' walletAddress: 'https://openpayments.guide/bob/' - receiver: 'https://openpayments.guide/alice/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' + receiver: 'https://openpayments.guide/incoming-payments/2f1b0150-db73-49e8-8713-628baa4a17ff' debitAmount: value: '2500' assetCode: USD @@ -849,7 +841,7 @@ components: description: 'An **incoming payment** resource represents a payment that will be, is currently being, or has been received by the account.' type: object examples: - - id: 'https://openpayments.guide/alice/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' + - id: 'https://openpayments.guide/incoming-payments/016da9d5-c9a4-4c80-a354-86b915a04ff8' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '250' @@ -866,7 +858,7 @@ components: metadata: description: 'Hi Mo, this is for the cappuccino I bought for you the other day.' externalRef: Coffee w/ Mo on 10 March 22 - - id: 'https://openpayments.guide/alice/incoming-payments/456da9d5-c9a4-4c80-a354-86b915a04ff8' + - id: 'https://openpayments.guide/incoming-payments/456da9d5-c9a4-4c80-a354-86b915a04ff8' walletAddress: 'https://openpayments.guide/alice/' incomingAmount: value: '2500' @@ -879,6 +871,7 @@ components: expiresAt: '2022-04-12T23:20:50.52Z' createdAt: '2022-03-12T23:20:50.52Z' updatedAt: '2022-03-12T23:20:50.52Z' + additionalProperties: false properties: id: type: string @@ -975,7 +968,7 @@ components: description: 'An **outgoing payment** resource represents a payment that will be, is currently being, or has previously been, sent from the wallet address.' type: object examples: - - id: 'https://openpayments.guide/alice/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + - id: 'https://openpayments.guide/outgoing-payments/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/aplusvideo/incoming-payments/45d495ad-b763-4882-88d7-aa14d261686e' @@ -996,7 +989,7 @@ components: metadata: description: APlusVideo subscription externalRef: 'customer: 847458475' - - id: 'https://openpayments.guide/alice/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' + - id: 'https://openpayments.guide/outgoing-payments/0cffa5a4-58fd-4cc8-8e01-7145c72bf07c' walletAddress: 'https://openpayments.guide/alice/' failed: false receiver: 'https://openpayments.guide/shoeshop/2fe92c6f-ef0d-487c-8759-3784eae6bce9' @@ -1071,7 +1064,7 @@ components: description: A **quote** resource represents the quoted amount details with which an Outgoing Payment may be created. type: object examples: - - id: 'https://openpayments.guide/alice/quotes/ab03296b-0c8b-4776-b94e-7ee27d868d4d' + - id: 'https://openpayments.guide/quotes/ab03296b-0c8b-4776-b94e-7ee27d868d4d' walletAddress: 'https://openpayments.guide/alice/' receiver: 'https://openpayments.guide/shoeshop/incoming-payments/2fe92c6f-ef0d-487c-8759-3784eae6bce9' receiveAmount: @@ -1088,7 +1081,7 @@ components: assetScale: 2 createdAt: '2022-03-12T23:20:50.52Z' expiresAt: '2022-04-12T23:20:50.52Z' - - id: 'https://openpayments.guide/alice/quotes/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' + - id: 'https://openpayments.guide/quotes/8c68d3cc-0a0f-4216-98b4-4fa44a6c88cf' walletAddress: 'https://openpayments.guide/alice/' receiver: 'https://openpayments.guide/aplusvideo/incoming-payments/45d495ad-b763-4882-88d7-aa14d261686e' debitAmount: @@ -1267,6 +1260,8 @@ components: schema: type: integer minimum: 1 + maximum: 100 + default: 10 name: first in: query description: The number of items to return after the cursor. @@ -1274,6 +1269,8 @@ components: schema: type: integer minimum: 1 + maximum: 100 + default: 10 name: last in: query description: The number of items to return before the cursor. diff --git a/openapi/schemas.yaml b/openapi/schemas.yaml index 8a14d434..a31d1ac5 100644 --- a/openapi/schemas.yaml +++ b/openapi/schemas.yaml @@ -44,5 +44,5 @@ components: format: uri pattern: '^https://(.+)/(incoming-payments|connections)/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' examples: - - 'https://openpayments.guide/alice/incoming-payments/08394f02-7b7b-45e2-b645-51d04e7c330c' + - 'https://openpayments.guide/incoming-payments/08394f02-7b7b-45e2-b645-51d04e7c330c' - 'https://openpayments.guide/connections/016da9d5-c9a4-4c80-a354-86b915a04ff8' diff --git a/packages/open-payments/src/client/incoming-payment.test.ts b/packages/open-payments/src/client/incoming-payment.test.ts index 2b374a26..f84d93ca 100644 --- a/packages/open-payments/src/client/incoming-payment.test.ts +++ b/packages/open-payments/src/client/incoming-payment.test.ts @@ -11,11 +11,8 @@ import { import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi' import { defaultAxiosInstance, - mockILPStreamConnection, mockIncomingPayment, mockIncomingPaymentPaginationResult, - mockIncomingPaymentWithConnection, - mockIncomingPaymentWithConnectionUrl, mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -44,13 +41,14 @@ describe('incoming-payment', (): void => { const axiosInstance = defaultAxiosInstance const logger = silentLogger - const walletAddress = 'http://localhost:1000/.well-known/pay' + const walletAddress = 'http://localhost:1000/alice/.well-known/pay' + const serverAddress = 'http://localhost:1000' const accessToken = 'accessToken' const openApiValidators = mockOpenApiResponseValidators() describe('getIncomingPayment', (): void => { test('returns incoming payment if passes validation', async (): Promise => { - const incomingPayment = mockIncomingPaymentWithConnection() + const incomingPayment = mockIncomingPayment() nock(walletAddress) .get('/incoming-payments/1') @@ -68,7 +66,7 @@ describe('incoming-payment', (): void => { }) test('throws if incoming payment does not pass validation', async (): Promise => { - const incomingPayment = mockIncomingPaymentWithConnection({ + const incomingPayment = mockIncomingPayment({ incomingAmount: { assetCode: 'USD', assetScale: 2, @@ -101,7 +99,7 @@ describe('incoming-payment', (): void => { }) test('throws if incoming payment does not pass open api validation', async (): Promise => { - const incomingPayment = mockIncomingPaymentWithConnection() + const incomingPayment = mockIncomingPayment() nock(walletAddress) .get('/incoming-payments/1') @@ -131,7 +129,7 @@ describe('incoming-payment', (): void => { `( 'returns the incoming payment on success', async ({ incomingAmount, expiresAt, metadata }): Promise => { - const incomingPayment = mockIncomingPaymentWithConnection({ + const incomingPayment = mockIncomingPayment({ incomingAmount, expiresAt, metadata @@ -164,7 +162,7 @@ describe('incoming-payment', (): void => { value: '10' } - const incomingPayment = mockIncomingPaymentWithConnection({ + const incomingPayment = mockIncomingPayment({ incomingAmount: amount, receivedAmount: amount, completed: false @@ -186,7 +184,7 @@ describe('incoming-payment', (): void => { }) test('throws if the created incoming payment does not pass open api validation', async (): Promise => { - const incomingPayment = mockIncomingPaymentWithConnection() + const incomingPayment = mockIncomingPayment() const scope = nock(walletAddress) .post('/incoming-payments') @@ -210,14 +208,14 @@ describe('incoming-payment', (): void => { completed: true }) - const scope = nock(walletAddress) + const scope = nock(serverAddress) .post(`/incoming-payments/${incomingPayment.id}/complete`) .reply(200, incomingPayment) const result = await completeIncomingPayment( { axiosInstance, logger }, { - url: `${walletAddress}/incoming-payments/${incomingPayment.id}`, + url: `${serverAddress}/incoming-payments/${incomingPayment.id}`, accessToken }, openApiValidators.successfulValidator @@ -287,7 +285,7 @@ describe('incoming-payment', (): void => { async ({ first, cursor }): Promise => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: Array(first).fill(mockIncomingPaymentWithConnectionUrl()) + result: Array(first).fill(mockIncomingPayment()) }) const scope = nock(walletAddress) @@ -330,7 +328,7 @@ describe('incoming-payment', (): void => { async ({ last, cursor }): Promise => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: Array(last).fill(mockIncomingPaymentWithConnectionUrl()) + result: Array(last).fill(mockIncomingPayment()) }) const scope = nock(walletAddress) @@ -364,7 +362,7 @@ describe('incoming-payment', (): void => { }) test('throws if an incoming payment does not pass validation', async (): Promise => { - const incomingPayment = mockIncomingPaymentWithConnectionUrl({ + const incomingPayment = mockIncomingPayment({ incomingAmount: { assetCode: 'USD', assetScale: 2, @@ -520,55 +518,6 @@ describe('incoming-payment', (): void => { 'Incoming amount matches received amount but payment is not completed' ) }) - - test('throws if receiving amount asset code is different that ilp connection asset code', async (): Promise => { - const ilpStreamConnection = mockILPStreamConnection({ - assetCode: 'CAD' - }) - - const incomingPayment = mockIncomingPaymentWithConnection({ - incomingAmount: { - assetCode: 'USD', - assetScale: 2, - value: '5' - }, - receivedAmount: { - assetCode: 'USD', - assetScale: 2, - value: '0' - }, - ilpStreamConnection - }) - - expect(() => validateIncomingPayment(incomingPayment)).toThrow( - 'Stream connection asset information does not match incoming payment asset information' - ) - }) - - test('throws if receiving amount asset scale is different that ilp connection asset scale', async (): Promise => { - const ilpStreamConnection = mockILPStreamConnection({ - assetCode: 'USD', - assetScale: 1 - }) - - const incomingPayment = mockIncomingPaymentWithConnection({ - incomingAmount: { - assetCode: 'USD', - assetScale: 2, - value: '5' - }, - receivedAmount: { - assetCode: 'USD', - assetScale: 2, - value: '0' - }, - ilpStreamConnection - }) - - expect(() => validateIncomingPayment(incomingPayment)).toThrow( - 'Stream connection asset information does not match incoming payment asset information' - ) - }) }) describe('validateCreatedIncomingPayment', (): void => { @@ -653,7 +602,7 @@ describe('incoming-payment', (): void => { const getSpy = jest .spyOn(requestors, 'get') - .mockResolvedValueOnce(mockIncomingPaymentWithConnection()) + .mockResolvedValueOnce(mockIncomingPayment()) await createIncomingPaymentRoutes({ openApi, @@ -679,7 +628,7 @@ describe('incoming-payment', (): void => { const incomingPaymentPaginationResult = mockIncomingPaymentPaginationResult({ - result: [mockIncomingPaymentWithConnectionUrl()] + result: [mockIncomingPayment()] }) const url = `${walletAddress}${getRSPath('/incoming-payments')}` @@ -727,9 +676,7 @@ describe('incoming-payment', (): void => { const postSpy = jest .spyOn(requestors, 'post') - .mockResolvedValueOnce( - mockIncomingPaymentWithConnection(incomingPaymentCreateArgs) - ) + .mockResolvedValueOnce(mockIncomingPayment(incomingPaymentCreateArgs)) await createIncomingPaymentRoutes({ openApi, diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index 44fb41ce..19c2f274 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -10,23 +10,16 @@ import { getRSPath, CreateIncomingPaymentArgs, PaginationArgs, - IncomingPaymentPaginationResult, - IncomingPaymentWithConnectionUrl, - IncomingPaymentWithConnection + IncomingPaymentPaginationResult } 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, @@ -40,7 +33,7 @@ export const createIncomingPaymentRoutes = ( const { axiosInstance, openApi, logger } = deps const getIncomingPaymentOpenApiValidator = - openApi.createResponseValidator({ + openApi.createResponseValidator({ path: getRSPath('/incoming-payments/{id}'), method: HttpMethod.GET }) @@ -99,7 +92,7 @@ export const createIncomingPaymentRoutes = ( export const getIncomingPayment = async ( deps: BaseDeps, args: ResourceRequestArgs, - validateOpenApiResponse: ResponseValidator + validateOpenApiResponse: ResponseValidator ) => { const { axiosInstance, logger } = deps const { url } = args @@ -222,9 +215,9 @@ export const listIncomingPayment = async ( return incomingPayments } -export const validateIncomingPayment = ( - payment: T -): T => { +export const validateIncomingPayment = ( + payment: IncomingPayment +): IncomingPayment => { if (payment.incomingAmount) { const { incomingAmount, receivedAmount } = payment if ( @@ -245,32 +238,19 @@ export const validateIncomingPayment = ( } } - if ( - 'ilpStreamConnection' in payment && - typeof payment.ilpStreamConnection === 'object' && - (payment.ilpStreamConnection.assetCode !== - payment.receivedAmount.assetCode || - payment.ilpStreamConnection.assetScale !== - payment.receivedAmount.assetScale) - ) { - throw new Error( - 'Stream connection asset information does not match incoming payment asset information' - ) - } - return payment } export const validateCreatedIncomingPayment = ( - payment: IncomingPaymentWithConnection -): IncomingPaymentWithConnection => { + payment: IncomingPayment +): IncomingPayment => { const { receivedAmount, completed } = payment if (BigInt(receivedAmount.value) !== BigInt(0)) { throw new Error('Received amount is a non-zero value.') } - if (completed === true) { + if (completed) { throw new Error('Can not create a completed incoming payment.') } @@ -282,7 +262,7 @@ export const validateCompletedIncomingPayment = ( ): IncomingPayment => { const { completed } = payment - if (completed === false) { + if (!completed) { throw new Error('Incoming payment could not be completed.') } diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index c3309515..bbd7265c 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -2,9 +2,6 @@ export { GrantRequest, GrantContinuationRequest, IncomingPayment, - IncomingPaymentWithConnection, - IncomingPaymentWithConnectionUrl, - ILPStreamConnection, Quote, OutgoingPayment, PendingGrant, @@ -28,11 +25,8 @@ export { } from './client' export { - mockILPStreamConnection, mockWalletAddress, mockIncomingPayment, - mockIncomingPaymentWithConnection, - mockIncomingPaymentWithConnectionUrl, mockOutgoingPayment, mockIncomingPaymentPaginationResult, mockOutgoingPaymentPaginationResult, diff --git a/packages/open-payments/src/openapi/generated/resource-server-types.ts b/packages/open-payments/src/openapi/generated/resource-server-types.ts index 223be829..2e302cdc 100644 --- a/packages/open-payments/src/openapi/generated/resource-server-types.ts +++ b/packages/open-payments/src/openapi/generated/resource-server-types.ts @@ -127,23 +127,6 @@ export interface components { "json-web-key-set": { keys: components["schemas"]["json-web-key"][]; }; - /** - * ILP Stream Connection - * @description An **ILP STREAM Connection** is an endpoint that returns unique STREAM connection credentials to establish a STREAM connection to the underlying account. - */ - "ilp-stream-connection": { - /** - * Format: uri - * @description The URL identifying the endpoint. - */ - id: string; - /** @description The ILP address to use when establishing a STREAM connection. */ - ilpAddress: string; - /** @description The base64 url-encoded shared secret to use when establishing a STREAM connection. */ - sharedSecret: string; - assetCode: external["schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["schemas.yaml"]["components"]["schemas"]["assetScale"]; - }; /** * Incoming Payment * @description An **incoming payment** resource represents a payment that will be, is currently being, or has been received by the account. @@ -183,24 +166,6 @@ export interface components { */ updatedAt: string; }; - /** - * Incoming Payment with Connection - * @description An **incoming payment** resource with the Interledger STREAM Connection to use to pay into the wallet address under this incoming payment. - */ - "incoming-payment-with-connection": components["schemas"]["incoming-payment"] & { - ilpStreamConnection?: components["schemas"]["ilp-stream-connection"]; - }; - /** - * Incoming Payment with Connection - * @description An **incoming payment** resource with the url for the Interledger STREAM Connection resource to use to pay into the wallet address under this incoming payment. - */ - "incoming-payment-with-connection-url": components["schemas"]["incoming-payment"] & { - /** - * Format: uri - * @description Endpoint that returns unique STREAM connection credentials to establish a STREAM connection to the underlying account. - */ - ilpStreamConnection?: string; - }; /** * Public Incoming Payment * @description An **incoming payment** resource with public details. @@ -388,7 +353,7 @@ export interface operations { content: { "application/json": { pagination?: components["schemas"]["page-info"]; - result?: components["schemas"]["incoming-payment-with-connection-url"][]; + result?: components["schemas"]["incoming-payment"][]; }; }; }; @@ -422,7 +387,7 @@ export interface operations { /** Incoming Payment Created */ 201: { content: { - "application/json": components["schemas"]["incoming-payment-with-connection"]; + "application/json": components["schemas"]["incoming-payment"]; }; }; 401: components["responses"]["401"]; @@ -589,7 +554,7 @@ export interface operations { 200: { content: { "application/json": Partial< - components["schemas"]["incoming-payment-with-connection"] + components["schemas"]["incoming-payment"] > & Partial; }; diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index e7729597..ed0a6d62 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -2,7 +2,6 @@ import { generateKeyPairSync } from 'crypto' import createLogger from 'pino' import { createAxiosInstance } from '../client/requests' import { - ILPStreamConnection, IncomingPayment, GrantRequest, GrantContinuationRequest, @@ -13,12 +12,9 @@ import { AccessToken, Quote, IncomingPaymentPaginationResult, - IncomingPaymentWithConnection, - IncomingPaymentWithConnectionUrl, PendingGrant, Grant } from '../types' -import base64url from 'base64url' import { v4 as uuid } from 'uuid' import { ResponseValidator } from '@interledger/openapi' @@ -80,17 +76,6 @@ export const mockWalletAddress = ( ...overrides }) -export const mockILPStreamConnection = ( - overrides?: Partial -): ILPStreamConnection => ({ - id: `https://example.com/.well-known/pay/connections/${uuid()}`, - sharedSecret: base64url('sharedSecret'), - ilpAddress: 'test.ilpAddress', - assetCode: 'USD', - assetScale: 2, - ...overrides -}) - export const mockIncomingPayment = ( overrides?: Partial ): IncomingPayment => ({ @@ -112,22 +97,6 @@ export const mockIncomingPayment = ( ...overrides }) -export const mockIncomingPaymentWithConnection = ( - overrides?: Partial -): IncomingPaymentWithConnection => ({ - ...mockIncomingPayment(), - ilpStreamConnection: mockILPStreamConnection(overrides?.ilpStreamConnection), - ...overrides -}) - -export const mockIncomingPaymentWithConnectionUrl = ( - overrides?: Partial -): IncomingPaymentWithConnectionUrl => ({ - ...mockIncomingPayment(), - ilpStreamConnection: mockILPStreamConnection().id, - ...overrides -}) - export const mockIncomingPaymentPaginationResult = ( overrides?: Partial ): IncomingPaymentPaginationResult => { diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 5809acc4..18c9d39b 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -13,16 +13,9 @@ export const getRSPath =

(path: P): string => path as string 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 ILPStreamConnection = - RSComponents['schemas']['ilp-stream-connection'] +export type IncomingPaymentPaginationResult = PaginationResult export type OutgoingPayment = RSComponents['schemas']['outgoing-payment'] export type CreateOutgoingPaymentArgs = RSOperations['create-outgoing-payment']['requestBody']['content']['application/json'] diff --git a/packages/openapi/src/middleware.test.ts b/packages/openapi/src/middleware.test.ts index 2827385a..f7482884 100644 --- a/packages/openapi/src/middleware.test.ts +++ b/packages/openapi/src/middleware.test.ts @@ -4,6 +4,7 @@ import { v4 as uuid } from 'uuid' import { createOpenAPI, OpenAPI, HttpMethod } from './' import { createValidatorMiddleware } from './middleware' +import * as path from 'path' declare module 'koa' { interface Request { @@ -34,9 +35,8 @@ export function createContext( return ctx as T } -const PATH = '/{accountId}/incoming-payments' -const SPEC = - 'https://github.com/interledger/open-payments/raw/3930448672cfc678ec2bc02938566a316d83871c/open-api-spec.yaml' +const PATH = '/incoming-payments' +const SPEC = path.resolve(__dirname, '../../../openapi/resource-server.yaml') describe('OpenAPI Validator', (): void => { let openApi: OpenAPI @@ -66,29 +66,6 @@ describe('OpenAPI Validator', (): void => { next = jest.fn() }) - test.each` - accountId | message | description - ${undefined} | ${"must have required property 'accountId'"} | ${'missing'} - ${2} | ${'accountId must be string'} | ${'invalid'} - `( - 'returns 400 on $description path parameter', - async ({ accountId, message }): Promise => { - const ctx = createContext( - { - headers: { Accept: 'application/json' } - }, - { - accountId - } - ) - await expect(validateListMiddleware(ctx, next)).rejects.toMatchObject({ - status: 400, - message - }) - expect(next).not.toHaveBeenCalled() - } - ) - test('coerces query parameter type', async (): Promise => { const first = 5 const next = jest.fn().mockImplementation(() => { @@ -100,10 +77,9 @@ describe('OpenAPI Validator', (): void => { headers: { Accept: 'application/json' }, url: `${PATH}?first=${first}` }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) await expect(validateListMiddleware(ctx, next)).resolves.toBeUndefined() expect(next).toHaveBeenCalled() }) @@ -114,10 +90,9 @@ describe('OpenAPI Validator', (): void => { headers: { Accept: 'application/json' }, url: `${PATH}?first=NaN` }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) await expect(validateListMiddleware(ctx, next)).rejects.toMatchObject({ status: 400, message: 'first must be integer' @@ -136,10 +111,9 @@ describe('OpenAPI Validator', (): void => { { headers }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) await expect(validatePostMiddleware(ctx, next)).rejects.toMatchObject({ status, message @@ -167,10 +141,9 @@ describe('OpenAPI Validator', (): void => { 'Content-Type': 'application/json' } }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) ctx.request.body = body await expect(validatePostMiddleware(ctx, next)).rejects.toMatchObject({ status: 400, @@ -183,14 +156,11 @@ describe('OpenAPI Validator', (): void => { test('sets default query params and calls next on valid request', async (): Promise => { const ctx = createContext( { - headers: { - Accept: 'application/json' - } + headers: { Accept: 'application/json' } }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) const next = jest.fn().mockImplementation(() => { expect(ctx.request.query).toEqual({ first: 10, @@ -204,7 +174,7 @@ describe('OpenAPI Validator', (): void => { const body = { id: `https://${accountId}/incoming-payments/${uuid()}`, - accountId: `https://${accountId}`, + walletAddress: 'https://openpayments.guide/alice', receivedAmount: { value: '0', assetCode: 'USD', @@ -216,8 +186,8 @@ describe('OpenAPI Validator', (): void => { test.each` status | body | message | description ${202} | ${{}} | ${'An unknown status code was used and no default was provided.'} | ${'status code'} - ${201} | ${{ ...body, invalid: 'field' }} | ${'response must NOT have additional properties: invalid'} | ${'body'} - ${201} | ${{ ...body, receivedAmount: { ...body.receivedAmount, value: '-1' } }} | ${'response.receivedAmount.value must match format "uint64"'} | ${'body'} + ${201} | ${{ ...body, invalid: 'field' }} | ${'response must NOT have additional properties: invalid'} | ${'body with additional property'} + ${201} | ${{ ...body, receivedAmount: { ...body.receivedAmount, value: '-1' } }} | ${'response.receivedAmount.value must match format "uint64"'} | ${'body with invalid type'} `( 'returns 500 on invalid response $description', async ({ status, body, message }): Promise => { @@ -228,10 +198,9 @@ describe('OpenAPI Validator', (): void => { 'Content-Type': 'application/json' } }, - { - accountId - } + {} ) + addTestSignatureHeaders(ctx) ctx.request.body = {} const next = jest.fn().mockImplementation(() => { ctx.status = status @@ -246,3 +215,8 @@ describe('OpenAPI Validator', (): void => { ) }) }) + +function addTestSignatureHeaders(ctx: Koa.Context) { + ctx.request.headers['Signature-Input'] = 'test signature input' + ctx.request.headers['Signature'] = 'test signature' +}