diff --git a/src/config.ts b/src/config.ts index 30d3fc4a..462fd321 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -export const payIdServerVersions: readonly string[] = ['1.0'] +export const payIdServerVersions: readonly string[] = ['1.0', '1.1'] export const adminApiVersions: readonly string[] = ['2020-05-28'] /** diff --git a/src/middlewares/payIds.ts b/src/middlewares/payIds.ts index 921f9ddf..f126d894 100644 --- a/src/middlewares/payIds.ts +++ b/src/middlewares/payIds.ts @@ -77,6 +77,7 @@ export default async function getPaymentInfo( // * NOTE: To append a memo, MUST set a memo in createMemo() const formattedPaymentInfo = formatPaymentInfo( preferredAddresses, + res.get('PayID-Version'), payId, createMemo, ) diff --git a/src/services/basePayId.ts b/src/services/basePayId.ts index 6211759b..4bb447d6 100644 --- a/src/services/basePayId.ts +++ b/src/services/basePayId.ts @@ -9,14 +9,15 @@ import { ParsedAcceptHeader } from './headers' * a Base PayID flow. * * @param addresses - Array of address information associated with a PayID. + * @param version - The PayID protocol response version. * @param payId - Optionally include a PayId. * @param memoFn - A function, taking an optional PaymentInformation object, - * that returns a string to be used as the memo. - * + * that returns a string to be used as the memo. * @returns The formatted PaymentInformation object. */ export function formatPaymentInfo( addresses: readonly AddressInformation[], + version: string, payId?: string, memoFn?: (paymentInformation: PaymentInformation) => string, ): PaymentInformation { @@ -25,7 +26,7 @@ export function formatPaymentInfo( return { paymentNetwork: address.paymentNetwork, ...(address.environment && { environment: address.environment }), - addressDetailsType: getAddressDetailsType(address), + addressDetailsType: getAddressDetailsType(address, version), addressDetails: address.details, } }), @@ -102,12 +103,17 @@ export function getPreferredAddressHeaderPair( * Gets the associated AddressDetailsType for an address. * * @param address - The address information associated with a PayID. + * @param version - The PayID protocol version. * @returns The AddressDetailsType for the address. */ export function getAddressDetailsType( address: AddressInformation, + version: string, ): AddressDetailsType { if (address.paymentNetwork === 'ACH') { + if (version === '1.0') { + return AddressDetailsType.AchAddress + } return AddressDetailsType.FiatAddress } return AddressDetailsType.CryptoAddress diff --git a/src/types/protocol.ts b/src/types/protocol.ts index a2aca0fe..b2e2099f 100644 --- a/src/types/protocol.ts +++ b/src/types/protocol.ts @@ -1,9 +1,11 @@ +/* eslint-disable no-inline-comments -- It is useful to have inline comments for interfaces. */ /** * Type of payment address in PaymentInformation. */ export enum AddressDetailsType { CryptoAddress = 'CryptoAddressDetails', - FiatAddress = 'FiatAddressDetails', + FiatAddress = 'FiatAddressDetails', // Replaces AchAddressDetails + AchAddress = 'AchAddressDetails', // Maintain compatibility for 1.0 } /** diff --git a/test/integration/e2e/public-api/basePayId.test.ts b/test/integration/e2e/public-api/basePayId.test.ts index dbbce07d..1f4c4958 100644 --- a/test/integration/e2e/public-api/basePayId.test.ts +++ b/test/integration/e2e/public-api/basePayId.test.ts @@ -134,7 +134,7 @@ describe('E2E - publicAPIRouter - Base PayID', function (): void { // WHEN we make a GET request to the public endpoint to retrieve payment info with an Accept header specifying ACH request(app.publicApiExpress) .get(payId) - .set('PayID-Version', '1.0') + .set('PayID-Version', '1.1') .set('Accept', acceptHeader) // THEN we get back our Accept header as the Content-Type .expect((res) => { diff --git a/test/unit/formatPaymentInfo.test.ts b/test/unit/formatPaymentInfo.test.ts index 653a4843..d3eded76 100644 --- a/test/unit/formatPaymentInfo.test.ts +++ b/test/unit/formatPaymentInfo.test.ts @@ -3,6 +3,8 @@ import { assert } from 'chai' import { formatPaymentInfo } from '../../src/services/basePayId' import { AddressDetailsType } from '../../src/types/protocol' +const version1dot1 = '1.1' + describe('Base PayID - formatPaymentInfo()', function (): void { it('Returns CryptoAddressDetails & FiatAddressDetails for addressDetailsTypes when formatting array with multiple AddressInformation', function () { // GIVEN an array of AddressInformation with an ACH entry @@ -48,7 +50,7 @@ describe('Base PayID - formatPaymentInfo()', function (): void { } // WHEN we format it - const paymentInfo = formatPaymentInfo(addressInfo, payId) + const paymentInfo = formatPaymentInfo(addressInfo, version1dot1, payId) // THEN we get back a PaymentInformation object with the appropriate address details assert.deepStrictEqual(paymentInfo, expectedPaymentInfo) @@ -80,7 +82,7 @@ describe('Base PayID - formatPaymentInfo()', function (): void { } // WHEN we format it and don't pass in a PayID - const paymentInfo = formatPaymentInfo(addressInfo) + const paymentInfo = formatPaymentInfo(addressInfo, version1dot1) // THEN we get back a PaymentInformation object without a PayID assert.deepStrictEqual(paymentInfo, expectedPaymentInfo) @@ -114,7 +116,7 @@ describe('Base PayID - formatPaymentInfo()', function (): void { } // WHEN we format it - const paymentInfo = formatPaymentInfo(addressInfo) + const paymentInfo = formatPaymentInfo(addressInfo, version1dot1) // THEN we get back a PaymentInformation object with no environment assert.deepStrictEqual(paymentInfo, expectedPaymentInfo) @@ -153,7 +155,12 @@ describe('Base PayID - formatPaymentInfo()', function (): void { const memoFn = (): string => 'memo' // WHEN we format the address information - const paymentInfo = formatPaymentInfo(addressInfo, payId, memoFn) + const paymentInfo = formatPaymentInfo( + addressInfo, + version1dot1, + payId, + memoFn, + ) // THEN we get back a PaymentInformation object with a memo assert.deepStrictEqual(paymentInfo, expectedPaymentInfo) diff --git a/test/unit/getAddressDetailsType.test.ts b/test/unit/getAddressDetailsType.test.ts index ea7f6f0f..f9d135ec 100644 --- a/test/unit/getAddressDetailsType.test.ts +++ b/test/unit/getAddressDetailsType.test.ts @@ -3,6 +3,9 @@ import { assert } from 'chai' import { getAddressDetailsType } from '../../src/services/basePayId' import { AddressDetailsType } from '../../src/types/protocol' +const version1dot0 = '1.0' +const version1dot1 = '1.1' + describe('Base PayID - getAddressDetailsType()', function (): void { it('Returns FiatAddressDetails for addressDetailsType when formatting ACH AddressInformation', function () { // GIVEN an array of AddressInformation with a single ACH (empty environment) entry @@ -16,12 +19,30 @@ describe('Base PayID - getAddressDetailsType()', function (): void { } // WHEN we get the address details type - const addressDetailsType = getAddressDetailsType(addressInfo) + const addressDetailsType = getAddressDetailsType(addressInfo, version1dot1) // THEN we get back an AddressDetailsType of FiatAddress assert.deepStrictEqual(addressDetailsType, AddressDetailsType.FiatAddress) }) + it('If using version 1.0, returns AchAddressDetails for addressDetailsType when formatting ACH AddressInformation', function () { + // GIVEN an array of AddressInformation with a single ACH (empty environment) entry + const addressInfo = { + paymentNetwork: 'ACH', + environment: null, + details: { + accountNumber: '000123456789', + routingNumber: '123456789', + }, + } + + // WHEN we get the address details type + const addressDetailsType = getAddressDetailsType(addressInfo, version1dot0) + + // THEN we get back an AddressDetailsType of FiatAddress + assert.deepStrictEqual(addressDetailsType, AddressDetailsType.AchAddress) + }) + it('Returns CryptoAddressDetails for addressDetailsType when formatting XRP AddressInformation', function () { // GIVEN an array of AddressInformation with a single XRP entry const addressInfo = { @@ -33,7 +54,7 @@ describe('Base PayID - getAddressDetailsType()', function (): void { } // WHEN we get the address details type - const addressDetailsType = getAddressDetailsType(addressInfo) + const addressDetailsType = getAddressDetailsType(addressInfo, version1dot1) // THEN we get back an AddressDetailsType of CryptoAddress assert.deepStrictEqual(addressDetailsType, AddressDetailsType.CryptoAddress)