Skip to content

Commit

Permalink
feat: Embed passport variables in GovPay metadata (#3015)
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr authored Apr 18, 2024
1 parent 8186afc commit 8a69029
Show file tree
Hide file tree
Showing 17 changed files with 66 additions and 110 deletions.
33 changes: 12 additions & 21 deletions api.planx.uk/modules/pay/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { gql } from "graphql-request";
import { $api } from "../../client";
import { ServerError } from "../../errors";
import { GovPayMetadata } from "./types";
import { formatGovPayMetadata } from "@opensystemslab/planx-core";
import {
GovPayMetadataValue,
Passport,
} from "@opensystemslab/planx-core/types";

/**
* Confirm that this local authority (aka team) has a pay token
Expand Down Expand Up @@ -41,6 +46,7 @@ interface GetPaymentRequestDetails {
slug: string;
};
};
passport: Passport;
};
} | null;
}
Expand All @@ -63,6 +69,7 @@ export async function fetchPaymentRequestDetails(
slug
}
}
passport: data(path: "$.passport")
}
}
}
Expand Down Expand Up @@ -92,6 +99,7 @@ export async function fetchPaymentRequestDetails(
if (paymentAmount) req.params.paymentAmount = paymentAmount;

res.locals.govPayMetadata = paymentRequest.govPayMetadata;
res.locals.passport = paymentRequest.session.passport;

next();
}
Expand All @@ -102,7 +110,7 @@ interface GovPayCreatePayment {
reference: string;
description: string;
return_url: string;
metadata: Record<string, string | boolean>;
metadata: Record<string, GovPayMetadataValue>;
}

export async function buildPaymentPayload(
Expand All @@ -129,28 +137,11 @@ 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 metadata = formatGovPayMetadata(
res.locals.govPayMetadata,
res.locals.passport,
);

const defaultGovPayMetadata = {
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,
};

const metadata = Object.keys(govPayMetadata).length
? govPayMetadata
: defaultGovPayMetadata;

const createPaymentBody: GovPayCreatePayment = {
amount: parseInt(req.params.paymentAmount),
reference: req.query.sessionId as string,
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"dependencies": {
"@airbrake/node": "^2.1.8",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#15bde6b",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#52f76e7",
"@types/isomorphic-fetch": "^0.0.36",
"adm-zip": "^0.5.10",
"aws-sdk": "^2.1467.0",
Expand Down
8 changes: 4 additions & 4 deletions api.planx.uk/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions api.planx.uk/tests/mocks/inviteToPayMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export const createPaymentRequestQueryMock = {
payeeName: payee.name,
payeeEmail: payee.email,
sessionPreviewData: sessionPreviewData,
govPayMetadata: [
{ key: "source", value: "PlanX" },
{ key: "isInviteToPay", value: true },
{ key: "flow", value: validSession.flow.slug },
],
},
};

Expand Down
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@
"prettier": "^3.2.4",
"typescript": "^5.4.3"
}
}
}
2 changes: 1 addition & 1 deletion e2e/tests/api-driven/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"dependencies": {
"@cucumber/cucumber": "^9.3.0",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#15bde6b",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#52f76e7",
"axios": "^1.6.8",
"dotenv": "^16.3.1",
"dotenv-expand": "^10.0.0",
Expand Down
8 changes: 4 additions & 4 deletions e2e/tests/api-driven/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/tests/ui-driven/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"postinstall": "./install-dependencies.sh"
},
"dependencies": {
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#15bde6b",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#52f76e7",
"axios": "^1.6.8",
"dotenv": "^16.3.1",
"eslint": "^8.56.0",
Expand Down
8 changes: 4 additions & 4 deletions e2e/tests/ui-driven/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion editor.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@mui/material": "^5.15.2",
"@mui/utils": "^5.15.2",
"@opensystemslab/map": "^0.8.1",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#15bde6b",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#52f76e7",
"@tiptap/core": "^2.0.3",
"@tiptap/extension-bold": "^2.0.3",
"@tiptap/extension-bubble-menu": "^2.1.13",
Expand Down
10 changes: 5 additions & 5 deletions editor.planx.uk/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions editor.planx.uk/src/@planx/components/Pay/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import DataObjectIcon from "@mui/icons-material/DataObject";
import Box from "@mui/material/Box";
import Link from "@mui/material/Link";
import Typography from "@mui/material/Typography";
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { GovPayMetadata, ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import {
GovPayMetadata,
Pay,
REQUIRED_GOVPAY_METADATA,
validationSchema,
Expand All @@ -30,7 +29,10 @@ import ErrorWrapper from "ui/shared/ErrorWrapper";
import Input from "ui/shared/Input";
import InputRow from "ui/shared/InputRow";

type FormikGovPayMetadata = Record<keyof GovPayMetadata, string>[] | string | undefined;
type FormikGovPayMetadata =
| Record<keyof GovPayMetadata, string>[]
| 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";
Expand Down Expand Up @@ -113,6 +115,7 @@ function GovPayMetadataEditor(props: ListManagerEditorProps<GovPayMetadata>) {
placeholder="key"
/>
<Input
format={currVal.toString().startsWith("@") ? "data" : undefined}
aria-labelledby="value-label"
disabled={isDisabled}
value={currVal}
Expand Down Expand Up @@ -284,10 +287,11 @@ const Component: React.FC<Props> = (props: Props) => {
</Link>{" "}
for more details.
</Typography>
<Typography variant="subtitle2" sx={{ mb: 2 }}>Any values beginning with @ will be dynamically read from data values set throughout the flow.</Typography>
<ErrorWrapper
error={
typeof errors.govPayMetadata === "string" &&
touched.govPayMetadata
touched.govPayMetadata
? errors.govPayMetadata
: undefined
}
Expand Down
8 changes: 4 additions & 4 deletions editor.planx.uk/src/@planx/components/Pay/Public/Pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function Component(props: Props) {
await axios
.post(
getGovUkPayUrlForTeam({ sessionId, flowId, teamSlug }),
createPayload(fee, sessionId, metadata),
createPayload(fee, sessionId, metadata, passport),
)
.then(async (res) => {
const payment = await resolvePaymentResponse(res.data);
Expand All @@ -273,9 +273,9 @@ function Component(props: Props) {
return (
<>
{state.status === "init" ||
state.status === "retry" ||
state.status === "unsupported_team" ||
state.status === "undefined_fee" ? (
state.status === "retry" ||
state.status === "unsupported_team" ||
state.status === "undefined_fee" ? (
<Confirm
{...props}
fee={fee}
Expand Down
23 changes: 2 additions & 21 deletions editor.planx.uk/src/@planx/components/Pay/model.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatMetadata, govPayMetadataSchema } from "./model";
import { govPayMetadataSchema } from "./model";

describe("GovPayMetadata Schema", () => {
const validate = async (payload: unknown) =>
Expand All @@ -14,7 +14,7 @@ describe("GovPayMetadata Schema", () => {
const errors = await validate([]);
expect(errors).toHaveLength(1);
expect(errors[0]).toMatch(
/Keys flow, source and isInviteToPay must be present/,
/Keys flow, source and isInviteToPay must be present/
);
});

Expand Down Expand Up @@ -108,22 +108,3 @@ 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",
});
});
});
Loading

0 comments on commit 8a69029

Please sign in to comment.