diff --git a/api.planx.uk/modules/pay/middleware.ts b/api.planx.uk/modules/pay/middleware.ts index e11a537714..4eed0d30be 100644 --- a/api.planx.uk/modules/pay/middleware.ts +++ b/api.planx.uk/modules/pay/middleware.ts @@ -2,6 +2,7 @@ import { NextFunction, Request, RequestHandler, Response } from "express"; import { gql } from "graphql-request"; import { $api } from "../../client"; import { ServerError } from "../../errors"; +import { GovPayMetadata } from "./types"; /** * Confirm that this local authority (aka team) has a pay token @@ -32,6 +33,7 @@ interface GetPaymentRequestDetails { paymentRequest: { sessionId: string; paymentAmount: number; + govPayMetadata: GovPayMetadata[]; session: { flowId: string; flow: { @@ -45,7 +47,7 @@ interface GetPaymentRequestDetails { export async function fetchPaymentRequestDetails( req: Request, - _res: Response, + res: Response, next: NextFunction, ) { const query = gql` @@ -53,6 +55,7 @@ export async function fetchPaymentRequestDetails( paymentRequest: payment_requests_by_pk(id: $paymentRequestId) { sessionId: session_id paymentAmount: payment_amount + govPayMetadata: govpay_metadata session { flowId: flow_id flow { @@ -88,6 +91,8 @@ export async function fetchPaymentRequestDetails( const paymentAmount = paymentRequest.paymentAmount.toString(); if (paymentAmount) req.params.paymentAmount = paymentAmount; + res.locals.govPayMetadata = paymentRequest.govPayMetadata; + next(); } @@ -97,16 +102,12 @@ interface GovPayCreatePayment { reference: string; description: string; return_url: string; - metadata?: { - source: "PlanX"; - flow: string; - inviteToPay: boolean; - }; + metadata: Record; } export async function buildPaymentPayload( req: Request, - _res: Response, + res: Response, next: NextFunction, ) { if (!req.query.returnURL) { @@ -127,21 +128,20 @@ export async function buildPaymentPayload( ); } + // Convert metadata to format required by GovPay + const govPayMetadata = Object.fromEntries( + res.locals.govPayMetadata.map(({ key, value }: GovPayMetadata) => [ + key, + value, + ]), + ); + const createPaymentBody: GovPayCreatePayment = { amount: parseInt(req.params.paymentAmount), reference: req.query.sessionId as string, description: "New application (nominated payee)", return_url: req.query.returnURL as string, - metadata: { - source: "PlanX", - // Payment requests have /pay path suffix, so get flow-slug from second-to-last position - flow: - (req.query.returnURL as string) - .split("?")?.[0] - ?.split("/") - ?.slice(-2, -1)?.[0] || "Could not parse service name", - inviteToPay: true, - }, + metadata: govPayMetadata, }; req.body = createPaymentBody; diff --git a/api.planx.uk/modules/pay/types.ts b/api.planx.uk/modules/pay/types.ts index 4c16ea8c8b..9ce0d7a328 100644 --- a/api.planx.uk/modules/pay/types.ts +++ b/api.planx.uk/modules/pay/types.ts @@ -49,3 +49,8 @@ export type PaymentRequestProxyController = ValidatedRequestHandler< typeof paymentRequestProxySchema, Buffer >; + +export interface GovPayMetadata { + key: string; + value: string | boolean; +} diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index 3ab0f288e7..a1e8a7800b 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#eda1e67", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#10f239a", "@types/isomorphic-fetch": "^0.0.36", "adm-zip": "^0.5.10", "aws-sdk": "^2.1467.0", diff --git a/api.planx.uk/pnpm-lock.yaml b/api.planx.uk/pnpm-lock.yaml index fc487f83d0..949e6077c7 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#eda1e67 - version: github.com/theopensystemslab/planx-core/eda1e67 + specifier: git+https://github.com/theopensystemslab/planx-core#10f239a + version: github.com/theopensystemslab/planx-core/10f239a '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -4213,8 +4213,8 @@ packages: punycode: 1.4.1 dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -8419,8 +8419,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/eda1e67: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/eda1e67} + github.com/theopensystemslab/planx-core/10f239a: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/10f239a} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -8436,7 +8436,7 @@ packages: copyfiles: 2.4.1 docx: 8.5.0 eslint: 8.57.0 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.2 diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index db22e08b4c..49f7f99bea 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#eda1e67", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#10f239a", "axios": "^1.6.8", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index 5405a8121b..4331afd8ec 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#eda1e67 - version: github.com/theopensystemslab/planx-core/eda1e67 + specifier: git+https://github.com/theopensystemslab/planx-core#10f239a + version: github.com/theopensystemslab/planx-core/10f239a axios: specifier: ^1.6.8 version: 1.6.8 @@ -1475,8 +1475,8 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -2943,8 +2943,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/eda1e67: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/eda1e67} + github.com/theopensystemslab/planx-core/10f239a: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/10f239a} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2960,7 +2960,7 @@ packages: copyfiles: 2.4.1 docx: 8.5.0 eslint: 8.57.0 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.2 diff --git a/e2e/tests/api-driven/src/invite-to-pay/mocks/flow.json b/e2e/tests/api-driven/src/invite-to-pay/mocks/flow.json index 9d81584fc9..d7850daf47 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/mocks/flow.json +++ b/e2e/tests/api-driven/src/invite-to-pay/mocks/flow.json @@ -63279,7 +63279,21 @@ "yourDetailsTitle": "Your details", "instructionsTitle": "How to pay", "secondaryPageTitle": "Invite someone else to pay for this application", - "instructionsDescription": "

You can pay for your application by using GOV.UK Pay.

Your application will be sent after you have paid the fee. Wait until you see an application sent message before closing your browser.

" + "instructionsDescription": "

You can pay for your application by using GOV.UK Pay.

Your application will be sent after you have paid the fee. Wait until you see an application sent message before closing your browser.

", + "govPayMetadata": [ + { + "key": "flow", + "value": "flow-name" + }, + { + "key": "source", + "value": "PlanX" + }, + { + "key": "isInviteToPay", + "value": true + } + ] }, "type": 400 }, diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index 8372e306d7..d223035a97 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#eda1e67", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#10f239a", "axios": "^1.6.8", "dotenv": "^16.3.1", "eslint": "^8.56.0", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 1a703c4ed7..4c045392bb 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#eda1e67 - version: github.com/theopensystemslab/planx-core/eda1e67 + specifier: git+https://github.com/theopensystemslab/planx-core#10f239a + version: github.com/theopensystemslab/planx-core/10f239a axios: specifier: ^1.6.8 version: 1.6.8 @@ -1349,8 +1349,8 @@ packages: punycode: 1.4.1 dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -2609,8 +2609,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/eda1e67: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/eda1e67} + github.com/theopensystemslab/planx-core/10f239a: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/10f239a} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2626,7 +2626,7 @@ packages: copyfiles: 2.4.1 docx: 8.5.0 eslint: 8.57.0 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.2 diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index 93a2676960..903c9d9da7 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -13,7 +13,7 @@ "@mui/styles": "^5.15.2", "@mui/utils": "^5.15.2", "@opensystemslab/map": "^0.8.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#eda1e67", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#10f239a", "@tiptap/core": "^2.0.3", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.13", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index ec1de02653..5e5e396911 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -41,8 +41,8 @@ dependencies: specifier: ^0.8.0 version: 0.8.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#eda1e67 - version: github.com/theopensystemslab/planx-core/eda1e67(@types/react@18.2.45) + specifier: git+https://github.com/theopensystemslab/planx-core#10f239a + version: github.com/theopensystemslab/planx-core/10f239a(@types/react@18.2.45) '@tiptap/core': specifier: ^2.0.3 version: 2.0.3(@tiptap/pm@2.0.3) @@ -341,7 +341,7 @@ devDependencies: version: 14.2.1(react-dom@18.2.0)(react@18.2.0) '@testing-library/user-event': specifier: ^14.4.3 - version: 14.4.3(@testing-library/dom@8.20.1) + version: 14.4.3(@testing-library/dom@9.3.4) '@types/dompurify': specifier: ^3.0.5 version: 3.0.5 @@ -7097,20 +7097,6 @@ packages: /@swc/types@0.1.5: resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} - /@testing-library/dom@8.20.1: - resolution: {integrity: sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==} - engines: {node: '>=12'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/runtime': 7.24.0 - '@types/aria-query': 5.0.4 - aria-query: 5.1.3 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - dev: true - /@testing-library/dom@9.3.4: resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} engines: {node: '>=14'} @@ -7154,15 +7140,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@testing-library/user-event@14.4.3(@testing-library/dom@8.20.1): - resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} - engines: {node: '>=12', npm: '>=6'} - peerDependencies: - '@testing-library/dom': '>=7.21.4' - dependencies: - '@testing-library/dom': 8.20.1 - dev: true - /@testing-library/user-event@14.4.3(@testing-library/dom@9.3.4): resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} engines: {node: '>=12', npm: '>=6'} @@ -12020,8 +11997,8 @@ packages: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -21115,9 +21092,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/eda1e67(@types/react@18.2.45): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/eda1e67} - id: github.com/theopensystemslab/planx-core/eda1e67 + github.com/theopensystemslab/planx-core/10f239a(@types/react@18.2.45): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/10f239a} + id: github.com/theopensystemslab/planx-core/10f239a name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -21133,7 +21110,7 @@ packages: copyfiles: 2.4.1 docx: 8.5.0 eslint: 8.57.0 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.2 diff --git a/editor.planx.uk/src/@planx/components/Pay/Editor.tsx b/editor.planx.uk/src/@planx/components/Pay/Editor.tsx index b5f832b661..afb17f929a 100644 --- a/editor.planx.uk/src/@planx/components/Pay/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Pay/Editor.tsx @@ -31,6 +31,8 @@ import ErrorWrapper from "ui/shared/ErrorWrapper"; import Input from "ui/shared/Input"; import InputRow from "ui/shared/InputRow"; +type FormikGovPayMetadata = Record[] | string | undefined; + const GOVPAY_DOCS_URL = "https://docs.payments.service.gov.uk/reporting/#add-more-information-to-a-payment-39-custom-metadata-39-or-39-reporting-columns-39"; @@ -40,7 +42,7 @@ const GOVPAY_DOCS_URL = * Docs: https://formik.org/docs/api/fieldarray#fieldarray-validation-gotchas */ const parseError = ( - errors: string | undefined | GovPayMetadata[], + errors: FormikGovPayMetadata, index: number, ): string | undefined => { // No errors @@ -61,7 +63,7 @@ const parseError = ( * Please see parseError() for additional context */ const parseTouched = ( - touched: string | undefined | GovPayMetadata[], + touched: string | undefined | FormikGovPayMetadata, index: number, ): string | undefined => { // No errors @@ -90,11 +92,11 @@ function GovPayMetadataEditor(props: ListManagerEditorProps) { const isDisabled = isFieldDisabled(currKey, props.index); const { errors, touched } = useFormikContext(); const error = parseError( - errors.govPayMetadata as string | undefined | GovPayMetadata[], + errors.govPayMetadata as FormikGovPayMetadata, props.index, ); const isTouched = parseTouched( - touched.govPayMetadata as string | undefined | GovPayMetadata[], + touched.govPayMetadata as FormikGovPayMetadata, props.index, ); diff --git a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.fixture.tsx b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.fixture.tsx index d99a8a7fe6..d27d0ad125 100644 --- a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.fixture.tsx +++ b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.fixture.tsx @@ -10,6 +10,7 @@ export default function Fixture() { description="" fn="fee" color="#efefef" + govPayMetadata={[]} /> ); } diff --git a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.test.tsx b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.test.tsx index 89d95d0ab6..4eca867eac 100644 --- a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.test.tsx +++ b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.test.tsx @@ -90,6 +90,7 @@ describe("Pay component when fee is undefined or £0", () => { title="Pay for your application" fn="application.fee.typo" handleSubmit={handleSubmit} + govPayMetadata={[]} />, ); @@ -114,6 +115,7 @@ describe("Pay component when fee is undefined or £0", () => { title="Pay for your application" fn="application.fee.payable" handleSubmit={handleSubmit} + govPayMetadata={[]} />, ); diff --git a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.tsx b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.tsx index 0581730d6e..db92943145 100644 --- a/editor.planx.uk/src/@planx/components/Pay/Public/Pay.tsx +++ b/editor.planx.uk/src/@planx/components/Pay/Public/Pay.tsx @@ -236,7 +236,7 @@ function Component(props: Props) { await axios .post( getGovUkPayUrlForTeam({ sessionId, flowId, teamSlug }), - createPayload(fee, sessionId), + createPayload(fee, sessionId, props.govPayMetadata), ) .then(async (res) => { const payment = await resolvePaymentResponse(res.data); diff --git a/editor.planx.uk/src/@planx/components/Pay/model.test.ts b/editor.planx.uk/src/@planx/components/Pay/model.test.ts index 9a2e0370c5..00d8ef1964 100644 --- a/editor.planx.uk/src/@planx/components/Pay/model.test.ts +++ b/editor.planx.uk/src/@planx/components/Pay/model.test.ts @@ -1,4 +1,4 @@ -import { govPayMetadataSchema } from "./model"; +import { formatMetadata, govPayMetadataSchema } from "./model"; describe("GovPayMetadata Schema", () => { const validate = async (payload: unknown) => @@ -108,3 +108,22 @@ describe("GovPayMetadata Schema", () => { expect(errors[0]).toMatch(/A maximum of 10 fields can be set as metadata/); }); }); + +describe("formatMetadata() helper", () => { + it("handles empty metadata", () => { + const result = formatMetadata([]); + expect(result).toMatchObject({}); + }); + + it("converts metadata from the format generated in the form, to the format required by GovPay", () => { + const result = formatMetadata([ + { key: "firstKey", value: "firstValue" }, + { key: "secondKey", value: "secondValue" }, + ]); + + expect(result).toMatchObject({ + firstKey: "firstValue", + secondKey: "secondValue", + }); + }); +}); diff --git a/editor.planx.uk/src/@planx/components/Pay/model.ts b/editor.planx.uk/src/@planx/components/Pay/model.ts index 9229073e2c..902be86ce4 100644 --- a/editor.planx.uk/src/@planx/components/Pay/model.ts +++ b/editor.planx.uk/src/@planx/components/Pay/model.ts @@ -6,7 +6,7 @@ import type { MoreInformation } from "../shared"; export interface GovPayMetadata { key: string; - value: string; + value: string | boolean; } export interface Pay extends MoreInformation { @@ -25,7 +25,7 @@ export interface Pay extends MoreInformation { yourDetailsTitle?: string; yourDetailsDescription?: string; yourDetailsLabel?: string; - govPayMetadata?: GovPayMetadata[]; + govPayMetadata: GovPayMetadata[]; } // https://docs.payments.service.gov.uk/making_payments/#creating-a-payment @@ -46,11 +46,7 @@ export interface GovUKCreatePaymentPayload { }; }; language?: string; - metadata?: { - source: "PlanX"; - flow: string; - inviteToPay: boolean; - }; + metadata?: Record; } export const toPence = (decimal: number) => Math.trunc(decimal * 100); @@ -68,18 +64,20 @@ export const formattedPriceWithCurrencySymbol = ( export const createPayload = ( fee: number, reference: string, + metadata: GovPayMetadata[], ): GovUKCreatePaymentPayload => ({ amount: toPence(fee), reference, description: "New application", return_url: getReturnURL(reference), - metadata: { - source: "PlanX", - flow: useStore.getState().flowSlug, - inviteToPay: false, - }, + metadata: formatMetadata(metadata), }); +export const formatMetadata = ( + metadata: GovPayMetadata[], +): GovUKCreatePaymentPayload["metadata"] => + Object.fromEntries(metadata.map(({ key, value }) => [key, value])); + /** * For Save & Return, include sessionId and email as query params so the session can be picked up */ diff --git a/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/down.sql b/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/down.sql new file mode 100644 index 0000000000..814973bc5f --- /dev/null +++ b/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/down.sql @@ -0,0 +1 @@ +ALTER TABLE "public"."payment_requests" DROP COLUMN "govpay_metadata"; diff --git a/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/up.sql b/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/up.sql new file mode 100644 index 0000000000..3ad3493ef0 --- /dev/null +++ b/hasura.planx.uk/migrations/1710872311654_alter_table_public_payment_requests_add_column_govpay_metadata/up.sql @@ -0,0 +1,2 @@ +alter table "public"."payment_requests" add column "govpay_metadata" jsonb + not null default '[]';