@defra/forms-engine-plugin 4.0.61 → 4.0.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.server/server/plugins/engine/components/PaymentField.d.ts +3 -17
  2. package/.server/server/plugins/engine/components/PaymentField.js +12 -3
  3. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  4. package/.server/server/plugins/engine/helpers.d.ts +1 -0
  5. package/.server/server/plugins/engine/plugin.js +3 -1
  6. package/.server/server/plugins/engine/plugin.js.map +1 -1
  7. package/.server/server/plugins/engine/routes/payment-helper.d.ts +3 -1
  8. package/.server/server/plugins/engine/routes/payment-helper.js +5 -5
  9. package/.server/server/plugins/engine/routes/payment-helper.js.map +1 -1
  10. package/.server/server/plugins/engine/routes/payment-helper.test.js +4 -2
  11. package/.server/server/plugins/engine/routes/payment-helper.test.js.map +1 -1
  12. package/.server/server/plugins/engine/routes/payment.js +7 -1
  13. package/.server/server/plugins/engine/routes/payment.js.map +1 -1
  14. package/.server/server/plugins/engine/services/formsService.d.ts +7 -0
  15. package/.server/server/plugins/engine/services/formsService.js +11 -0
  16. package/.server/server/plugins/engine/services/formsService.js.map +1 -1
  17. package/.server/server/plugins/engine/services/formsService.test.js +4 -1
  18. package/.server/server/plugins/engine/services/formsService.test.js.map +1 -1
  19. package/.server/server/plugins/engine/types.d.ts +5 -2
  20. package/.server/server/plugins/engine/types.js.map +1 -1
  21. package/.server/server/plugins/payment/helper.d.ts +4 -11
  22. package/.server/server/plugins/payment/helper.js +11 -19
  23. package/.server/server/plugins/payment/helper.js.map +1 -1
  24. package/.server/server/plugins/payment/helper.test.js +1 -22
  25. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  26. package/.server/server/plugins/payment/service.d.ts +3 -3
  27. package/.server/server/plugins/payment/service.js +25 -15
  28. package/.server/server/plugins/payment/service.js.map +1 -1
  29. package/.server/server/plugins/payment/service.test.js +8 -6
  30. package/.server/server/plugins/payment/service.test.js.map +1 -1
  31. package/.server/server/types.d.ts +1 -0
  32. package/.server/server/types.js.map +1 -1
  33. package/.server/server/utils/file-form-service.js +10 -0
  34. package/.server/server/utils/file-form-service.js.map +1 -1
  35. package/.server/server/utils/file-form-service.test.js +5 -0
  36. package/.server/server/utils/file-form-service.test.js.map +1 -1
  37. package/.server/typings/hapi/index.d.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/server/plugins/engine/beta/form-context.test.ts +2 -1
  40. package/src/server/plugins/engine/components/PaymentField.test.ts +139 -5
  41. package/src/server/plugins/engine/components/PaymentField.ts +29 -21
  42. package/src/server/plugins/engine/plugin.ts +3 -1
  43. package/src/server/plugins/engine/routes/payment-helper.js +9 -5
  44. package/src/server/plugins/engine/routes/payment-helper.test.js +4 -1
  45. package/src/server/plugins/engine/routes/payment.js +8 -1
  46. package/src/server/plugins/engine/services/formsService.js +11 -0
  47. package/src/server/plugins/engine/services/formsService.test.js +6 -1
  48. package/src/server/plugins/engine/types.ts +6 -1
  49. package/src/server/plugins/payment/helper.js +15 -23
  50. package/src/server/plugins/payment/helper.test.js +1 -32
  51. package/src/server/plugins/payment/service.js +42 -28
  52. package/src/server/plugins/payment/service.test.js +22 -24
  53. package/src/server/types.ts +1 -0
  54. package/src/server/utils/file-form-service.js +11 -0
  55. package/src/server/utils/file-form-service.test.js +13 -0
  56. package/src/typings/hapi/index.d.ts +1 -0
@@ -2,8 +2,8 @@ import { type FormMetadata, type PaymentFieldComponent } from '@defra/forms-mode
2
2
  import { type ObjectSchema } from 'joi';
3
3
  import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js';
4
4
  import { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js';
5
- import { type AnyFormRequest, type FormContext, type FormRequestPayload, type FormResponseToolkit } from '~/src/server/plugins/engine/types/index.js';
6
- import { type ErrorMessageTemplateList, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
5
+ import { type FormContext, type FormRequestPayload, type FormResponseToolkit } from '~/src/server/plugins/engine/types/index.js';
6
+ import { type ErrorMessageTemplateList, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState, type PaymentExternalArgs } from '~/src/server/plugins/engine/types.js';
7
7
  export declare class PaymentField extends FormComponent {
8
8
  options: PaymentFieldComponent['options'];
9
9
  formSchema: ObjectSchema;
@@ -92,7 +92,7 @@ export declare class PaymentField extends FormComponent {
92
92
  /**
93
93
  * Dispatcher for external redirect to GOV.UK Pay
94
94
  */
95
- static dispatcher(request: FormRequestPayload, h: FormResponseToolkit, args: PaymentDispatcherArgs): Promise<unknown>;
95
+ static dispatcher(request: FormRequestPayload, h: FormResponseToolkit, args: PaymentExternalArgs): Promise<unknown>;
96
96
  /**
97
97
  * Called on form submission to capture the payment
98
98
  * @see https://docs.payments.service.gov.uk/delayed_capture/#delay-taking-a-payment
@@ -104,20 +104,6 @@ export declare class PaymentField extends FormComponent {
104
104
  */
105
105
  private markPaymentCaptured;
106
106
  }
107
- export interface PaymentDispatcherArgs {
108
- controller: {
109
- model: {
110
- formId: string;
111
- basePath: string;
112
- name: string;
113
- };
114
- getState: (request: AnyFormRequest) => Promise<FormSubmissionState>;
115
- };
116
- component: PaymentField;
117
- sourceUrl: string;
118
- isLive: boolean;
119
- isPreview: boolean;
120
- }
121
107
  /**
122
108
  * Session data stored when dispatching to GOV.UK Pay
123
109
  */
@@ -1,8 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { StatusCodes } from 'http-status-codes';
3
3
  import joi from 'joi';
4
+ import { COMPONENT_STATE_ERROR } from "../../../constants.js";
4
5
  import { FormComponent } from "./FormComponent.js";
5
- import { getPluginOptions } from "../helpers.js";
6
+ import { createError, getPluginOptions } from "../helpers.js";
6
7
  import { PaymentErrorTypes, PaymentPreAuthError, PaymentSubmissionError } from "../pageControllers/errors.js";
7
8
  import { createPaymentService, formatCurrency } from "../../payment/helper.js";
8
9
  export class PaymentField extends FormComponent {
@@ -133,7 +134,8 @@ export class PaymentField extends FormComponent {
133
134
  }
134
135
  const isLivePayment = args.isLive && !args.isPreview;
135
136
  const formId = args.controller.model.formId;
136
- const paymentService = createPaymentService(isLivePayment, formId);
137
+ const formsService = model.services.formsService;
138
+ const paymentService = await createPaymentService(isLivePayment, formId, formsService);
137
139
  const uuid = randomUUID();
138
140
  const reference = state.$$__referenceNumber;
139
141
  const amount = options.amount;
@@ -146,6 +148,12 @@ export class PaymentField extends FormComponent {
146
148
  formId,
147
149
  slug
148
150
  });
151
+ if (!payment) {
152
+ const message = isLivePayment ? 'There is a problem and we cannot take a payment. Contact us (details in the footer of this form) or save your progress and return to the form later.' : 'Add a valid test API key before you can preview the payment journey.';
153
+ const govukError = createError(componentName, message);
154
+ request.yar.flash(COMPONENT_STATE_ERROR, govukError, true);
155
+ return h.redirect(request.url.href).code(StatusCodes.SEE_OTHER);
156
+ }
149
157
  const sessionData = {
150
158
  uuid,
151
159
  formId,
@@ -179,7 +187,8 @@ export class PaymentField extends FormComponent {
179
187
  isLivePayment,
180
188
  formId
181
189
  } = paymentState;
182
- const paymentService = createPaymentService(isLivePayment, formId);
190
+ const formsService = this.model.services.formsService;
191
+ const paymentService = await createPaymentService(isLivePayment, formId, formsService);
183
192
 
184
193
  /**
185
194
  * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle
@@ -1 +1 @@
1
- {"version":3,"file":"PaymentField.js","names":["randomUUID","StatusCodes","joi","FormComponent","getPluginOptions","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","createPaymentService","formatCurrency","PaymentField","isAppendageStateSingleObject","constructor","def","props","options","paymentStateSchema","object","paymentId","string","required","reference","amount","number","description","uuid","formId","isLivePayment","boolean","preAuth","status","valid","createdAt","isoDate","unknown","label","formSchema","stateSchema","getPaymentStateFromState","state","value","name","isPaymentState","undefined","getDisplayStringFromState","getViewModel","payload","errors","viewModel","paymentState","Array","isArray","isState","getFormValue","getContextValueFromState","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","dispatcher","request","h","args","componentName","component","model","controller","getState","baseUrl","server","summaryUrl","basePath","existingPaymentState","redirect","code","SEE_OTHER","isLive","isPreview","paymentService","$$__referenceNumber","slug","payCallbackUrl","paymentPageUrl","sourceUrl","amountInPence","Math","round","payment","createPayment","sessionData","returnUrl","failureUrl","yar","set","paymentUrl","onSubmit","_metadata","context","PaymentIncomplete","capture","getPaymentStatus","checkPaymentAmount","markPaymentCaptured","PaymentExpired","captured","capturePayment","updatedState","Date","toISOString","page","currentState","mergeState"],"sources":["../../../../../src/server/plugins/engine/components/PaymentField.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nimport {\n type FormMetadata,\n type PaymentFieldComponent\n} from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport joi, { type ObjectSchema } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport { getPluginOptions } from '~/src/server/plugins/engine/helpers.js'\nimport {\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type AnyFormRequest,\n type FormContext,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n createPaymentService,\n formatCurrency\n} from '~/src/server/plugins/payment/helper.js'\n\nexport class PaymentField extends FormComponent {\n declare options: PaymentFieldComponent['options']\n declare formSchema: ObjectSchema\n declare stateSchema: ObjectSchema\n isAppendageStateSingleObject = true\n\n constructor(\n def: PaymentFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n this.options = def.options\n\n const paymentStateSchema = joi\n .object({\n paymentId: joi.string().required(),\n reference: joi.string().required(),\n amount: joi.number().required(),\n description: joi.string().required(),\n uuid: joi.string().uuid().required(),\n formId: joi.string().required(),\n isLivePayment: joi.boolean().required(),\n preAuth: joi\n .object({\n status: joi\n .string()\n .valid('success', 'failed', 'started')\n .required(),\n createdAt: joi.string().isoDate().required()\n })\n .required()\n })\n .unknown(true)\n .label(this.label)\n\n this.formSchema = paymentStateSchema\n // 'required()' forces the payment page to be invalid until we have valid payment state\n // i.e. the user will automatically be directed back to the payment page\n // if they attempt to access future pages when no payment entered yet\n this.stateSchema = paymentStateSchema.required()\n }\n\n /**\n * Gets the PaymentState from form submission state\n */\n getPaymentStateFromState(\n state: FormSubmissionState\n ): PaymentState | undefined {\n const value = state[this.name]\n return this.isPaymentState(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getPaymentStateFromState(state)\n\n if (!value) {\n return ''\n }\n\n return `${formatCurrency(value.amount)} - ${value.description}`\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n\n // Payload is pre-populated from state if a payment has already been made\n const paymentState = this.isPaymentState(payload[this.name] as unknown)\n ? (payload[this.name] as unknown as PaymentState)\n : undefined\n\n // When user initially visits the payment page, there is no payment state yet so the amount is read form the form definition.\n const amount = paymentState?.amount ?? this.options.amount\n\n return {\n ...viewModel,\n amount: formatCurrency(amount),\n description: this.options.description,\n paymentState\n }\n }\n\n /**\n * Type guard to check if value is PaymentState\n */\n isPaymentState(value: unknown): value is PaymentState {\n return PaymentField.isPaymentState(value)\n }\n\n /**\n * Static type guard to check if value is PaymentState\n */\n static isPaymentState(value: unknown): value is PaymentState {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n const state = value as PaymentState\n return (\n typeof state.paymentId === 'string' &&\n typeof state.amount === 'number' &&\n typeof state.description === 'string'\n )\n }\n\n /**\n * Override base isState to validate PaymentState\n */\n isState(value?: FormStateValue | FormState): value is FormState {\n return this.isPaymentState(value)\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isPaymentState(value)\n ? (value as unknown as NonNullable<FormStateValue>)\n : undefined\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n return this.isPaymentState(state)\n ? `Reference: ${state.reference}\\nAmount: ${formatCurrency(state.amount)}`\n : ''\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return PaymentField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'paymentRequired',\n template: 'Complete the payment to continue'\n }\n ],\n advancedSettingsErrors: []\n }\n }\n\n /**\n * Dispatcher for external redirect to GOV.UK Pay\n */\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PaymentDispatcherArgs\n ): Promise<unknown> {\n const { options, name: componentName } = args.component\n const { model } = args.controller\n\n const state = await args.controller.getState(request)\n const { baseUrl } = getPluginOptions(request.server)\n const summaryUrl = `${baseUrl}/${model.basePath}/summary`\n\n const existingPaymentState = state[componentName]\n if (\n PaymentField.isPaymentState(existingPaymentState) &&\n existingPaymentState.preAuth?.status === 'success'\n ) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const isLivePayment = args.isLive && !args.isPreview\n const formId = args.controller.model.formId\n const paymentService = createPaymentService(isLivePayment, formId)\n\n const uuid = randomUUID()\n\n const reference = state.$$__referenceNumber as string\n const amount = options.amount\n\n const description = options.description\n\n const slug = `/${model.basePath}`\n\n const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`\n const paymentPageUrl = args.sourceUrl\n\n const amountInPence = Math.round(amount * 100)\n const payment = await paymentService.createPayment(\n amountInPence,\n description,\n payCallbackUrl,\n reference,\n isLivePayment,\n { formId, slug }\n )\n\n const sessionData: PaymentSessionData = {\n uuid,\n formId,\n reference,\n amount,\n description,\n paymentId: payment.paymentId,\n componentName,\n returnUrl: summaryUrl,\n failureUrl: paymentPageUrl,\n isLivePayment\n }\n\n request.yar.set(`payment-${uuid}`, sessionData)\n\n return h.redirect(payment.paymentUrl).code(StatusCodes.SEE_OTHER)\n }\n\n /**\n * Called on form submission to capture the payment\n * @see https://docs.payments.service.gov.uk/delayed_capture/#delay-taking-a-payment\n */\n async onSubmit(\n request: FormRequestPayload,\n _metadata: FormMetadata,\n context: FormContext\n ): Promise<void> {\n const paymentState = this.getPaymentStateFromState(context.state)\n\n if (!paymentState) {\n throw new PaymentPreAuthError(\n this,\n 'Complete the payment to continue',\n true,\n PaymentErrorTypes.PaymentIncomplete\n )\n }\n\n if (paymentState.capture?.status === 'success') {\n return\n }\n\n const { paymentId, isLivePayment, formId } = paymentState\n const paymentService = createPaymentService(isLivePayment, formId)\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const status = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n PaymentSubmissionError.checkPaymentAmount(\n status.amount,\n this.options.amount,\n this\n )\n\n if (status.state.status === 'success') {\n await this.markPaymentCaptured(request, paymentState)\n return\n }\n\n if (status.state.status !== 'capturable') {\n throw new PaymentPreAuthError(\n this,\n 'Your payment authorisation has expired. Please add your payment details again.',\n true,\n PaymentErrorTypes.PaymentExpired\n )\n }\n\n const captured = await paymentService.capturePayment(\n paymentId,\n status.amount\n )\n\n if (!captured) {\n throw new PaymentPreAuthError(\n this,\n 'There was a problem and your form was not submitted. Try submitting the form again.',\n false\n )\n }\n\n await this.markPaymentCaptured(request, paymentState)\n }\n\n /**\n * Updates payment state to mark capture as successful\n * This ensures we don't try to re-capture on submission retry\n */\n private async markPaymentCaptured(\n request: FormRequestPayload,\n paymentState: PaymentState\n ): Promise<void> {\n const updatedState: PaymentState = {\n ...paymentState,\n capture: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n if (this.page) {\n const currentState = await this.page.getState(request)\n await this.page.mergeState(request, currentState, {\n [this.name]: updatedState\n })\n }\n }\n}\n\nexport interface PaymentDispatcherArgs {\n controller: {\n model: {\n formId: string\n basePath: string\n name: string\n }\n getState: (request: AnyFormRequest) => Promise<FormSubmissionState>\n }\n component: PaymentField\n sourceUrl: string\n isLive: boolean\n isPreview: boolean\n}\n\n/**\n * Session data stored when dispatching to GOV.UK Pay\n */\nexport interface PaymentSessionData {\n uuid: string\n formId: string\n reference: string\n amount: number\n description: string\n paymentId: string\n componentName: string\n returnUrl: string\n failureUrl: string\n isLivePayment: boolean\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"PaymentField.js","names":["randomUUID","StatusCodes","joi","COMPONENT_STATE_ERROR","FormComponent","createError","getPluginOptions","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","createPaymentService","formatCurrency","PaymentField","isAppendageStateSingleObject","constructor","def","props","options","paymentStateSchema","object","paymentId","string","required","reference","amount","number","description","uuid","formId","isLivePayment","boolean","preAuth","status","valid","createdAt","isoDate","unknown","label","formSchema","stateSchema","getPaymentStateFromState","state","value","name","isPaymentState","undefined","getDisplayStringFromState","getViewModel","payload","errors","viewModel","paymentState","Array","isArray","isState","getFormValue","getContextValueFromState","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","dispatcher","request","h","args","componentName","component","model","controller","getState","baseUrl","server","summaryUrl","basePath","existingPaymentState","redirect","code","SEE_OTHER","isLive","isPreview","formsService","services","paymentService","$$__referenceNumber","slug","payCallbackUrl","paymentPageUrl","sourceUrl","amountInPence","Math","round","payment","createPayment","message","govukError","yar","flash","url","href","sessionData","returnUrl","failureUrl","set","paymentUrl","onSubmit","_metadata","context","PaymentIncomplete","capture","getPaymentStatus","checkPaymentAmount","markPaymentCaptured","PaymentExpired","captured","capturePayment","updatedState","Date","toISOString","page","currentState","mergeState"],"sources":["../../../../../src/server/plugins/engine/components/PaymentField.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nimport {\n type FormMetadata,\n type PaymentFieldComponent\n} from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport joi, { type ObjectSchema } from 'joi'\n\nimport { COMPONENT_STATE_ERROR } from '~/src/server/constants.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport {\n createError,\n getPluginOptions\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type FormContext,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type PaymentExternalArgs\n} from '~/src/server/plugins/engine/types.js'\nimport {\n createPaymentService,\n formatCurrency\n} from '~/src/server/plugins/payment/helper.js'\n\nexport class PaymentField extends FormComponent {\n declare options: PaymentFieldComponent['options']\n declare formSchema: ObjectSchema\n declare stateSchema: ObjectSchema\n isAppendageStateSingleObject = true\n\n constructor(\n def: PaymentFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n this.options = def.options\n\n const paymentStateSchema = joi\n .object({\n paymentId: joi.string().required(),\n reference: joi.string().required(),\n amount: joi.number().required(),\n description: joi.string().required(),\n uuid: joi.string().uuid().required(),\n formId: joi.string().required(),\n isLivePayment: joi.boolean().required(),\n preAuth: joi\n .object({\n status: joi\n .string()\n .valid('success', 'failed', 'started')\n .required(),\n createdAt: joi.string().isoDate().required()\n })\n .required()\n })\n .unknown(true)\n .label(this.label)\n\n this.formSchema = paymentStateSchema\n // 'required()' forces the payment page to be invalid until we have valid payment state\n // i.e. the user will automatically be directed back to the payment page\n // if they attempt to access future pages when no payment entered yet\n this.stateSchema = paymentStateSchema.required()\n }\n\n /**\n * Gets the PaymentState from form submission state\n */\n getPaymentStateFromState(\n state: FormSubmissionState\n ): PaymentState | undefined {\n const value = state[this.name]\n return this.isPaymentState(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getPaymentStateFromState(state)\n\n if (!value) {\n return ''\n }\n\n return `${formatCurrency(value.amount)} - ${value.description}`\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n\n // Payload is pre-populated from state if a payment has already been made\n const paymentState = this.isPaymentState(payload[this.name] as unknown)\n ? (payload[this.name] as unknown as PaymentState)\n : undefined\n\n // When user initially visits the payment page, there is no payment state yet so the amount is read form the form definition.\n const amount = paymentState?.amount ?? this.options.amount\n\n return {\n ...viewModel,\n amount: formatCurrency(amount),\n description: this.options.description,\n paymentState\n }\n }\n\n /**\n * Type guard to check if value is PaymentState\n */\n isPaymentState(value: unknown): value is PaymentState {\n return PaymentField.isPaymentState(value)\n }\n\n /**\n * Static type guard to check if value is PaymentState\n */\n static isPaymentState(value: unknown): value is PaymentState {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n const state = value as PaymentState\n return (\n typeof state.paymentId === 'string' &&\n typeof state.amount === 'number' &&\n typeof state.description === 'string'\n )\n }\n\n /**\n * Override base isState to validate PaymentState\n */\n isState(value?: FormStateValue | FormState): value is FormState {\n return this.isPaymentState(value)\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isPaymentState(value)\n ? (value as unknown as NonNullable<FormStateValue>)\n : undefined\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n return this.isPaymentState(state)\n ? `Reference: ${state.reference}\\nAmount: ${formatCurrency(state.amount)}`\n : ''\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return PaymentField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'paymentRequired',\n template: 'Complete the payment to continue'\n }\n ],\n advancedSettingsErrors: []\n }\n }\n\n /**\n * Dispatcher for external redirect to GOV.UK Pay\n */\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PaymentExternalArgs\n ): Promise<unknown> {\n const { options, name: componentName } = args.component\n const { model } = args.controller\n\n const state = await args.controller.getState(request)\n const { baseUrl } = getPluginOptions(request.server)\n const summaryUrl = `${baseUrl}/${model.basePath}/summary`\n\n const existingPaymentState = state[componentName]\n if (\n PaymentField.isPaymentState(existingPaymentState) &&\n existingPaymentState.preAuth?.status === 'success'\n ) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const isLivePayment = args.isLive && !args.isPreview\n const formId = args.controller.model.formId\n const formsService = model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n const uuid = randomUUID()\n\n const reference = state.$$__referenceNumber as string\n const amount = options.amount\n\n const description = options.description\n\n const slug = `/${model.basePath}`\n\n const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`\n const paymentPageUrl = args.sourceUrl\n\n const amountInPence = Math.round(amount * 100)\n const payment = await paymentService.createPayment(\n amountInPence,\n description,\n payCallbackUrl,\n reference,\n isLivePayment,\n { formId, slug }\n )\n\n if (!payment) {\n const message = isLivePayment\n ? 'There is a problem and we cannot take a payment. Contact us (details in the footer of this form) or save your progress and return to the form later.'\n : 'Add a valid test API key before you can preview the payment journey.'\n const govukError = createError(componentName, message)\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n return h.redirect(request.url.href).code(StatusCodes.SEE_OTHER)\n }\n\n const sessionData: PaymentSessionData = {\n uuid,\n formId,\n reference,\n amount,\n description,\n paymentId: payment.paymentId,\n componentName,\n returnUrl: summaryUrl,\n failureUrl: paymentPageUrl,\n isLivePayment\n }\n\n request.yar.set(`payment-${uuid}`, sessionData)\n\n return h.redirect(payment.paymentUrl).code(StatusCodes.SEE_OTHER)\n }\n\n /**\n * Called on form submission to capture the payment\n * @see https://docs.payments.service.gov.uk/delayed_capture/#delay-taking-a-payment\n */\n async onSubmit(\n request: FormRequestPayload,\n _metadata: FormMetadata,\n context: FormContext\n ): Promise<void> {\n const paymentState = this.getPaymentStateFromState(context.state)\n\n if (!paymentState) {\n throw new PaymentPreAuthError(\n this,\n 'Complete the payment to continue',\n true,\n PaymentErrorTypes.PaymentIncomplete\n )\n }\n\n if (paymentState.capture?.status === 'success') {\n return\n }\n\n const { paymentId, isLivePayment, formId } = paymentState\n const formsService = this.model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const status = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n PaymentSubmissionError.checkPaymentAmount(\n status.amount,\n this.options.amount,\n this\n )\n\n if (status.state.status === 'success') {\n await this.markPaymentCaptured(request, paymentState)\n return\n }\n\n if (status.state.status !== 'capturable') {\n throw new PaymentPreAuthError(\n this,\n 'Your payment authorisation has expired. Please add your payment details again.',\n true,\n PaymentErrorTypes.PaymentExpired\n )\n }\n\n const captured = await paymentService.capturePayment(\n paymentId,\n status.amount\n )\n\n if (!captured) {\n throw new PaymentPreAuthError(\n this,\n 'There was a problem and your form was not submitted. Try submitting the form again.',\n false\n )\n }\n\n await this.markPaymentCaptured(request, paymentState)\n }\n\n /**\n * Updates payment state to mark capture as successful\n * This ensures we don't try to re-capture on submission retry\n */\n private async markPaymentCaptured(\n request: FormRequestPayload,\n paymentState: PaymentState\n ): Promise<void> {\n const updatedState: PaymentState = {\n ...paymentState,\n capture: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n if (this.page) {\n const currentState = await this.page.getState(request)\n await this.page.mergeState(request, currentState, {\n [this.name]: updatedState\n })\n }\n }\n}\n\n/**\n * Session data stored when dispatching to GOV.UK Pay\n */\nexport interface PaymentSessionData {\n uuid: string\n formId: string\n reference: string\n amount: number\n description: string\n paymentId: string\n componentName: string\n returnUrl: string\n failureUrl: string\n isLivePayment: boolean\n}\n"],"mappings":"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","ignoreList":[]}
@@ -72,6 +72,7 @@ export declare function getPluginOptions(server: Server): {
72
72
  viewContext?: (request: AnyFormRequest | null) => Record<string, unknown> | Promise<Record<string, unknown>>;
73
73
  saveAndExit?: import("~/src/server/plugins/engine/types.js").PluginOptions["saveAndExit"];
74
74
  baseUrl: string;
75
+ services: import("~/src/server/plugins/engine/types.js").PluginOptions["services"];
75
76
  };
76
77
  /**
77
78
  * Handles logging and issuing a permanent redirect for legacy routes.
@@ -25,7 +25,8 @@ export const plugin = {
25
25
  onRequest,
26
26
  ordnanceSurveyApiKey,
27
27
  baseUrl,
28
- ordnanceSurveyApiSecret
28
+ ordnanceSurveyApiSecret,
29
+ services
29
30
  } = options;
30
31
  const cacheService = typeof cache === 'string' ? new CacheService({
31
32
  server,
@@ -58,6 +59,7 @@ export const plugin = {
58
59
  server.expose('cacheService', cacheService);
59
60
  server.expose('saveAndExit', saveAndExit);
60
61
  server.expose('baseUrl', baseUrl);
62
+ server.expose('services', services);
61
63
  server.app.model = model;
62
64
 
63
65
  // In-memory cache of FormModel items, exposed
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["validatePluginOptions","getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getPaymentRoutes","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerVision","mapPlugin","postcodeLookupPlugin","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cache","saveAndExit","nunjucks","nunjucksOptions","viewContext","preparePageEventRequestOptions","onRequest","ordnanceSurveyApiKey","baseUrl","ordnanceSurveyApiSecret","cacheService","cacheName","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getPaymentRoutes } from '~/src/server/plugins/engine/routes/payment.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport { mapPlugin } from '~/src/server/plugins/map/index.js'\nimport { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n options = validatePluginOptions(options)\n\n const {\n model,\n cache,\n saveAndExit,\n nunjucks: nunjucksOptions,\n viewContext,\n preparePageEventRequestOptions,\n onRequest,\n ordnanceSurveyApiKey,\n baseUrl,\n ordnanceSurveyApiSecret\n } = options\n\n const cacheService =\n typeof cache === 'string'\n ? new CacheService({ server, cacheName: cache })\n : cache\n\n await registerVision(server, options)\n\n // Register the postcode lookup plugin only if we have an OS api key\n if (ordnanceSurveyApiKey) {\n await server.register({\n plugin: postcodeLookupPlugin,\n options: {\n ordnanceSurveyApiKey\n }\n })\n }\n\n // Register the maps plugin only if we have an OS api key & secret\n if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {\n await server.register({\n plugin: mapPlugin,\n options: {\n ordnanceSurveyApiKey,\n ordnanceSurveyApiSecret\n }\n })\n }\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n server.expose('saveAndExit', saveAndExit)\n server.expose('baseUrl', baseUrl)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getPaymentRoutes(),\n ...getFileUploadStatusRoutes(),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions, onRequest),\n ...getRepeaterItemDeleteRoutes(\n getRouteOptions,\n postRouteOptions,\n onRequest\n ),\n\n ...getQuestionRoutes(\n getRouteOptions,\n postRouteOptions,\n preparePageEventRequestOptions,\n onRequest\n )\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"AASA,SAASA,qBAAqB;AAC9B,SAASC,SAAS,IAAIC,yBAAyB;AAC/C,SAASC,sBAAsB;AAC/B,SAASF,SAAS,IAAIG,gBAAgB;AACtC,SAASH,SAAS,IAAII,iBAAiB;AACvC,SAASJ,SAAS,IAAIK,2BAA2B;AACjD,SAASL,SAAS,IAAIM,wBAAwB;AAE9C,SAASC,cAAc;AACvB,SAASC,SAAS;AAClB,SAASC,oBAAoB;AAK7B,SAASC,YAAY;AAErB,OAAO,MAAMC,MAAM,GAAG;EACpBC,IAAI,EAAE,4BAA4B;EAClCC,YAAY,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,CAAC;EACvDC,QAAQ,EAAE,IAAI;EACd,MAAMC,QAAQA,CAACC,MAAc,EAAEC,OAAsB,EAAE;IACrDA,OAAO,GAAGlB,qBAAqB,CAACkB,OAAO,CAAC;IAExC,MAAM;MACJC,KAAK;MACLC,KAAK;MACLC,WAAW;MACXC,QAAQ,EAAEC,eAAe;MACzBC,WAAW;MACXC,8BAA8B;MAC9BC,SAAS;MACTC,oBAAoB;MACpBC,OAAO;MACPC;IACF,CAAC,GAAGX,OAAO;IAEX,MAAMY,YAAY,GAChB,OAAOV,KAAK,KAAK,QAAQ,GACrB,IAAIT,YAAY,CAAC;MAAEM,MAAM;MAAEc,SAAS,EAAEX;IAAM,CAAC,CAAC,GAC9CA,KAAK;IAEX,MAAMZ,cAAc,CAACS,MAAM,EAAEC,OAAO,CAAC;;IAErC;IACA,IAAIS,oBAAoB,EAAE;MACxB,MAAMV,MAAM,CAACD,QAAQ,CAAC;QACpBJ,MAAM,EAAEF,oBAAoB;QAC5BQ,OAAO,EAAE;UACPS;QACF;MACF,CAAC,CAAC;IACJ;;IAEA;IACA,IAAIA,oBAAoB,IAAIE,uBAAuB,EAAE;MACnD,MAAMZ,MAAM,CAACD,QAAQ,CAAC;QACpBJ,MAAM,EAAEH,SAAS;QACjBS,OAAO,EAAE;UACPS,oBAAoB;UACpBE;QACF;MACF,CAAC,CAAC;IACJ;IAEAZ,MAAM,CAACe,MAAM,CAAC,gBAAgB,EAAET,eAAe,CAACU,cAAc,CAAC;IAC/DhB,MAAM,CAACe,MAAM,CAAC,aAAa,EAAER,WAAW,CAAC;IACzCP,MAAM,CAACe,MAAM,CAAC,cAAc,EAAEF,YAAY,CAAC;IAC3Cb,MAAM,CAACe,MAAM,CAAC,aAAa,EAAEX,WAAW,CAAC;IACzCJ,MAAM,CAACe,MAAM,CAAC,SAAS,EAAEJ,OAAO,CAAC;IAEjCX,MAAM,CAACiB,GAAG,CAACf,KAAK,GAAGA,KAAK;;IAExB;IACA;IACA,MAAMgB,SAAS,GAAG,IAAIC,GAAG,CAAgD,CAAC;IAC1EnB,MAAM,CAACiB,GAAG,CAACG,MAAM,GAAGF,SAAS;IAE7B,MAAMG,kBAAkB,GAAGnC,sBAAsB,CAACc,MAAM,EAAEC,OAAO,CAAC;IAElE,MAAMqB,eAA8C,GAAG;MACrDC,GAAG,EAAE,CACH;QACEC,MAAM,EACJH;MACJ,CAAC;IAEL,CAAC;IAED,MAAMI,gBAAsD,GAAG;MAC7DC,OAAO,EAAE;QACPC,KAAK,EAAE;MACT,CAAC;MACDJ,GAAG,EAAE,CACH;QACEC,MAAM,EACJH;MACJ,CAAC;IAEL,CAAC;IAED,MAAMO,MAAM,GAAG,CACb,GAAGzC,gBAAgB,CAAC,CAAC,EACrB,GAAGF,yBAAyB,CAAC,CAAC,EAC9B,GAAGK,wBAAwB,CAACgC,eAAe,EAAEG,gBAAgB,EAAEhB,SAAS,CAAC,EACzE,GAAGpB,2BAA2B,CAC5BiC,eAAe,EACfG,gBAAgB,EAChBhB,SACF,CAAC,EAED,GAAGrB,iBAAiB,CAClBkC,eAAe,EACfG,gBAAgB,EAChBjB,8BAA8B,EAC9BC,SACF,CAAC,CACF;IAEDT,MAAM,CAAC6B,KAAK,CAACD,MAAkC,CAAC,EAAC;EACnD;AACF,CAAiC","ignoreList":[]}
1
+ {"version":3,"file":"plugin.js","names":["validatePluginOptions","getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getPaymentRoutes","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerVision","mapPlugin","postcodeLookupPlugin","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cache","saveAndExit","nunjucks","nunjucksOptions","viewContext","preparePageEventRequestOptions","onRequest","ordnanceSurveyApiKey","baseUrl","ordnanceSurveyApiSecret","services","cacheService","cacheName","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getPaymentRoutes } from '~/src/server/plugins/engine/routes/payment.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport { mapPlugin } from '~/src/server/plugins/map/index.js'\nimport { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n options = validatePluginOptions(options)\n\n const {\n model,\n cache,\n saveAndExit,\n nunjucks: nunjucksOptions,\n viewContext,\n preparePageEventRequestOptions,\n onRequest,\n ordnanceSurveyApiKey,\n baseUrl,\n ordnanceSurveyApiSecret,\n services\n } = options\n\n const cacheService =\n typeof cache === 'string'\n ? new CacheService({ server, cacheName: cache })\n : cache\n\n await registerVision(server, options)\n\n // Register the postcode lookup plugin only if we have an OS api key\n if (ordnanceSurveyApiKey) {\n await server.register({\n plugin: postcodeLookupPlugin,\n options: {\n ordnanceSurveyApiKey\n }\n })\n }\n\n // Register the maps plugin only if we have an OS api key & secret\n if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {\n await server.register({\n plugin: mapPlugin,\n options: {\n ordnanceSurveyApiKey,\n ordnanceSurveyApiSecret\n }\n })\n }\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n server.expose('saveAndExit', saveAndExit)\n server.expose('baseUrl', baseUrl)\n server.expose('services', services)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getPaymentRoutes(),\n ...getFileUploadStatusRoutes(),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions, onRequest),\n ...getRepeaterItemDeleteRoutes(\n getRouteOptions,\n postRouteOptions,\n onRequest\n ),\n\n ...getQuestionRoutes(\n getRouteOptions,\n postRouteOptions,\n preparePageEventRequestOptions,\n onRequest\n )\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"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","ignoreList":[]}
@@ -2,9 +2,10 @@
2
2
  * Validates session data and retrieves payment status
3
3
  * @param {Request} request - the request
4
4
  * @param {string} uuid - the payment UUID
5
+ * @param {FormsService} formsService - the forms service
5
6
  * @returns {Promise<{ session: PaymentSessionData, sessionKey: string, paymentStatus: GetPaymentResponse }>}
6
7
  */
7
- export function getPaymentContext(request: Request, uuid: string): Promise<{
8
+ export function getPaymentContext(request: Request, uuid: string, formsService: FormsService): Promise<{
8
9
  session: PaymentSessionData;
9
10
  sessionKey: string;
10
11
  paymentStatus: GetPaymentResponse;
@@ -32,5 +33,6 @@ export function buildPaymentInfo(action: string, outcome: string, reason: string
32
33
  */
33
34
  export function convertPenceToPounds(amount: number): string;
34
35
  import type { Request } from '@hapi/hapi';
36
+ import type { FormsService } from '~/src/server/types.js';
35
37
  import type { PaymentSessionData } from '~/src/server/plugins/payment/types.js';
36
38
  import type { GetPaymentResponse } from '~/src/server/plugins/payment/types.js';
@@ -1,15 +1,15 @@
1
1
  import Boom from '@hapi/boom';
2
2
  import { PAYMENT_SESSION_PREFIX } from "./payment.js";
3
- import { getPaymentApiKey } from "../../payment/helper.js";
4
- import { PaymentService } from "../../payment/service.js";
3
+ import { createPaymentService } from "../../payment/helper.js";
5
4
 
6
5
  /**
7
6
  * Validates session data and retrieves payment status
8
7
  * @param {Request} request - the request
9
8
  * @param {string} uuid - the payment UUID
9
+ * @param {FormsService} formsService - the forms service
10
10
  * @returns {Promise<{ session: PaymentSessionData, sessionKey: string, paymentStatus: GetPaymentResponse }>}
11
11
  */
12
- export async function getPaymentContext(request, uuid) {
12
+ export async function getPaymentContext(request, uuid, formsService) {
13
13
  const sessionKey = `${PAYMENT_SESSION_PREFIX}${uuid}`;
14
14
  const session = /** @type {PaymentSessionData | null} */
15
15
  request.yar.get(sessionKey);
@@ -24,8 +24,7 @@ export async function getPaymentContext(request, uuid) {
24
24
  if (!paymentId) {
25
25
  throw Boom.badRequest('No paymentId in session');
26
26
  }
27
- const apiKey = getPaymentApiKey(isLivePayment, formId);
28
- const paymentService = new PaymentService(apiKey);
27
+ const paymentService = await createPaymentService(isLivePayment, formId, formsService);
29
28
  const paymentStatus = await paymentService.getPaymentStatus(paymentId, isLivePayment);
30
29
  return {
31
30
  session,
@@ -65,5 +64,6 @@ export function convertPenceToPounds(amount) {
65
64
  /**
66
65
  * @import { Request } from '@hapi/hapi'
67
66
  * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'
67
+ * @import { FormsService } from '~/src/server/types.js'
68
68
  */
69
69
  //# sourceMappingURL=payment-helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payment-helper.js","names":["Boom","PAYMENT_SESSION_PREFIX","getPaymentApiKey","PaymentService","getPaymentContext","request","uuid","sessionKey","session","yar","get","badRequest","paymentId","isLivePayment","formId","apiKey","paymentService","paymentStatus","getPaymentStatus","buildPaymentInfo","action","outcome","reason","event","category","type","reference","convertPenceToPounds","amount"],"sources":["../../../../../src/server/plugins/engine/routes/payment-helper.js"],"sourcesContent":["import Boom from '@hapi/boom'\n\nimport { PAYMENT_SESSION_PREFIX } from '~/src/server/plugins/engine/routes/payment.js'\nimport { getPaymentApiKey } from '~/src/server/plugins/payment/helper.js'\nimport { PaymentService } from '~/src/server/plugins/payment/service.js'\n\n/**\n * Validates session data and retrieves payment status\n * @param {Request} request - the request\n * @param {string} uuid - the payment UUID\n * @returns {Promise<{ session: PaymentSessionData, sessionKey: string, paymentStatus: GetPaymentResponse }>}\n */\nexport async function getPaymentContext(request, uuid) {\n const sessionKey = `${PAYMENT_SESSION_PREFIX}${uuid}`\n const session = /** @type {PaymentSessionData | null} */ (\n request.yar.get(sessionKey)\n )\n\n if (!session) {\n throw Boom.badRequest(`No payment session found for uuid=${uuid}`)\n }\n\n const { paymentId, isLivePayment, formId } = session\n\n if (!paymentId) {\n throw Boom.badRequest('No paymentId in session')\n }\n\n const apiKey = getPaymentApiKey(isLivePayment, formId)\n const paymentService = new PaymentService(apiKey)\n const paymentStatus = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n return { session, sessionKey, paymentStatus }\n}\n\n/**\n * Builds an object for logging payment information\n * @param {string} action\n * @param {string} outcome\n * @param {string} reason\n * @param {boolean} isLivePayment\n * @param {string} paymentId\n */\nexport function buildPaymentInfo(\n action,\n outcome,\n reason,\n isLivePayment,\n paymentId\n) {\n return {\n event: {\n category: 'payment',\n action,\n outcome,\n reason,\n type: isLivePayment ? 'live' : 'test',\n reference: paymentId\n }\n }\n}\n\n/**\n * @param {number} amount\n */\nexport function convertPenceToPounds(amount) {\n return `${amount / 100}`\n}\n\n/**\n * @import { Request } from '@hapi/hapi'\n * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'\n */\n"],"mappings":"AAAA,OAAOA,IAAI,MAAM,YAAY;AAE7B,SAASC,sBAAsB;AAC/B,SAASC,gBAAgB;AACzB,SAASC,cAAc;;AAEvB;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,iBAAiBA,CAACC,OAAO,EAAEC,IAAI,EAAE;EACrD,MAAMC,UAAU,GAAG,GAAGN,sBAAsB,GAAGK,IAAI,EAAE;EACrD,MAAME,OAAO,GAAG;EACdH,OAAO,CAACI,GAAG,CAACC,GAAG,CAACH,UAAU,CAC3B;EAED,IAAI,CAACC,OAAO,EAAE;IACZ,MAAMR,IAAI,CAACW,UAAU,CAAC,qCAAqCL,IAAI,EAAE,CAAC;EACpE;EAEA,MAAM;IAAEM,SAAS;IAAEC,aAAa;IAAEC;EAAO,CAAC,GAAGN,OAAO;EAEpD,IAAI,CAACI,SAAS,EAAE;IACd,MAAMZ,IAAI,CAACW,UAAU,CAAC,yBAAyB,CAAC;EAClD;EAEA,MAAMI,MAAM,GAAGb,gBAAgB,CAACW,aAAa,EAAEC,MAAM,CAAC;EACtD,MAAME,cAAc,GAAG,IAAIb,cAAc,CAACY,MAAM,CAAC;EACjD,MAAME,aAAa,GAAG,MAAMD,cAAc,CAACE,gBAAgB,CACzDN,SAAS,EACTC,aACF,CAAC;EAED,OAAO;IAAEL,OAAO;IAAED,UAAU;IAAEU;EAAc,CAAC;AAC/C;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,gBAAgBA,CAC9BC,MAAM,EACNC,OAAO,EACPC,MAAM,EACNT,aAAa,EACbD,SAAS,EACT;EACA,OAAO;IACLW,KAAK,EAAE;MACLC,QAAQ,EAAE,SAAS;MACnBJ,MAAM;MACNC,OAAO;MACPC,MAAM;MACNG,IAAI,EAAEZ,aAAa,GAAG,MAAM,GAAG,MAAM;MACrCa,SAAS,EAAEd;IACb;EACF,CAAC;AACH;;AAEA;AACA;AACA;AACA,OAAO,SAASe,oBAAoBA,CAACC,MAAM,EAAE;EAC3C,OAAO,GAAGA,MAAM,GAAG,GAAG,EAAE;AAC1B;;AAEA;AACA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"payment-helper.js","names":["Boom","PAYMENT_SESSION_PREFIX","createPaymentService","getPaymentContext","request","uuid","formsService","sessionKey","session","yar","get","badRequest","paymentId","isLivePayment","formId","paymentService","paymentStatus","getPaymentStatus","buildPaymentInfo","action","outcome","reason","event","category","type","reference","convertPenceToPounds","amount"],"sources":["../../../../../src/server/plugins/engine/routes/payment-helper.js"],"sourcesContent":["import Boom from '@hapi/boom'\n\nimport { PAYMENT_SESSION_PREFIX } from '~/src/server/plugins/engine/routes/payment.js'\nimport { createPaymentService } from '~/src/server/plugins/payment/helper.js'\n\n/**\n * Validates session data and retrieves payment status\n * @param {Request} request - the request\n * @param {string} uuid - the payment UUID\n * @param {FormsService} formsService - the forms service\n * @returns {Promise<{ session: PaymentSessionData, sessionKey: string, paymentStatus: GetPaymentResponse }>}\n */\nexport async function getPaymentContext(request, uuid, formsService) {\n const sessionKey = `${PAYMENT_SESSION_PREFIX}${uuid}`\n const session = /** @type {PaymentSessionData | null} */ (\n request.yar.get(sessionKey)\n )\n\n if (!session) {\n throw Boom.badRequest(`No payment session found for uuid=${uuid}`)\n }\n\n const { paymentId, isLivePayment, formId } = session\n\n if (!paymentId) {\n throw Boom.badRequest('No paymentId in session')\n }\n\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n const paymentStatus = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n return { session, sessionKey, paymentStatus }\n}\n\n/**\n * Builds an object for logging payment information\n * @param {string} action\n * @param {string} outcome\n * @param {string} reason\n * @param {boolean} isLivePayment\n * @param {string} paymentId\n */\nexport function buildPaymentInfo(\n action,\n outcome,\n reason,\n isLivePayment,\n paymentId\n) {\n return {\n event: {\n category: 'payment',\n action,\n outcome,\n reason,\n type: isLivePayment ? 'live' : 'test',\n reference: paymentId\n }\n }\n}\n\n/**\n * @param {number} amount\n */\nexport function convertPenceToPounds(amount) {\n return `${amount / 100}`\n}\n\n/**\n * @import { Request } from '@hapi/hapi'\n * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'\n * @import { FormsService } from '~/src/server/types.js'\n */\n"],"mappings":"AAAA,OAAOA,IAAI,MAAM,YAAY;AAE7B,SAASC,sBAAsB;AAC/B,SAASC,oBAAoB;;AAE7B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,iBAAiBA,CAACC,OAAO,EAAEC,IAAI,EAAEC,YAAY,EAAE;EACnE,MAAMC,UAAU,GAAG,GAAGN,sBAAsB,GAAGI,IAAI,EAAE;EACrD,MAAMG,OAAO,GAAG;EACdJ,OAAO,CAACK,GAAG,CAACC,GAAG,CAACH,UAAU,CAC3B;EAED,IAAI,CAACC,OAAO,EAAE;IACZ,MAAMR,IAAI,CAACW,UAAU,CAAC,qCAAqCN,IAAI,EAAE,CAAC;EACpE;EAEA,MAAM;IAAEO,SAAS;IAAEC,aAAa;IAAEC;EAAO,CAAC,GAAGN,OAAO;EAEpD,IAAI,CAACI,SAAS,EAAE;IACd,MAAMZ,IAAI,CAACW,UAAU,CAAC,yBAAyB,CAAC;EAClD;EAEA,MAAMI,cAAc,GAAG,MAAMb,oBAAoB,CAC/CW,aAAa,EACbC,MAAM,EACNR,YACF,CAAC;EACD,MAAMU,aAAa,GAAG,MAAMD,cAAc,CAACE,gBAAgB,CACzDL,SAAS,EACTC,aACF,CAAC;EAED,OAAO;IAAEL,OAAO;IAAED,UAAU;IAAES;EAAc,CAAC;AAC/C;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,gBAAgBA,CAC9BC,MAAM,EACNC,OAAO,EACPC,MAAM,EACNR,aAAa,EACbD,SAAS,EACT;EACA,OAAO;IACLU,KAAK,EAAE;MACLC,QAAQ,EAAE,SAAS;MACnBJ,MAAM;MACNC,OAAO;MACPC,MAAM;MACNG,IAAI,EAAEX,aAAa,GAAG,MAAM,GAAG,MAAM;MACrCY,SAAS,EAAEb;IACb;EACF,CAAC;AACH;;AAEA;AACA;AACA;AACA,OAAO,SAASc,oBAAoBA,CAACC,MAAM,EAAE;EAC3C,OAAO,GAAGA,MAAM,GAAG,GAAG,EAAE;AAC1B;;AAEA;AACA;AACA;AACA;AACA","ignoreList":[]}
@@ -50,9 +50,11 @@ describe('payment helper', () => {
50
50
  payload: getPaymentStatusApiResult,
51
51
  error: undefined
52
52
  });
53
-
53
+ const mockFormsService = {
54
+ getFormSecret: () => 'secret-value'
55
+ };
54
56
  // @ts-expect-error - partial request mock
55
- const res = await getPaymentContext(mockRequest, uuid);
57
+ const res = await getPaymentContext(mockRequest, uuid, mockFormsService);
56
58
  expect(res).toEqual({
57
59
  paymentStatus: {
58
60
  paymentId: 'payment-id-12345',
@@ -1 +1 @@
1
- {"version":3,"file":"payment-helper.test.js","names":["buildPaymentInfo","getPaymentContext","get","jest","mock","describe","uuid","it","mockRequest","yar","fn","mockReturnValueOnce","undefined","expect","rejects","toThrow","paymentId","isLivePayment","formId","getPaymentStatusApiResult","payment_id","_links","next_url","href","state","status","mocked","mockResolvedValueOnce","res","statusCode","headers","payload","error","toEqual","paymentStatus","session","sessionKey","event","category","action","outcome","reason","type","reference"],"sources":["../../../../../src/server/plugins/engine/routes/payment-helper.test.js"],"sourcesContent":["import {\n buildPaymentInfo,\n getPaymentContext\n} from '~/src/server/plugins/engine/routes/payment-helper.js'\nimport { get } from '~/src/server/services/httpService.js'\n\njest.mock('~/src/server/services/httpService.ts')\n\ndescribe('payment helper', () => {\n const uuid = '5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n it('should throw if no session', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce(undefined)\n }\n }\n // @ts-expect-error - partial request mock\n await expect(() => getPaymentContext(mockRequest, uuid)).rejects.toThrow(\n 'No payment session found for uuid=5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n )\n })\n\n it('should throw if no payment id', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce({})\n }\n }\n // @ts-expect-error - partial request mock\n await expect(() => getPaymentContext(mockRequest, uuid)).rejects.toThrow(\n 'No paymentId in session'\n )\n })\n\n it('should get context successfully', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce({\n paymentId: 'payment-id',\n isLivePayment: false,\n formId: 'formid'\n })\n }\n }\n\n const getPaymentStatusApiResult = {\n payment_id: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n }\n\n jest.mocked(get).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: getPaymentStatusApiResult,\n error: undefined\n })\n\n // @ts-expect-error - partial request mock\n const res = await getPaymentContext(mockRequest, uuid)\n expect(res).toEqual({\n paymentStatus: {\n paymentId: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n },\n session: {\n formId: 'formid',\n isLivePayment: false,\n paymentId: 'payment-id'\n },\n sessionKey: 'payment-5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n })\n })\n\n it('should create logging info for a test payment', () => {\n const res = buildPaymentInfo(\n 'action1',\n 'outcome1',\n 'reason1',\n false,\n 'pay-123'\n )\n expect(res).toEqual({\n event: {\n category: 'payment',\n action: 'action1',\n outcome: 'outcome1',\n reason: 'reason1',\n type: 'test',\n reference: 'pay-123'\n }\n })\n })\n\n it('should create logging info for a live payment', () => {\n const res = buildPaymentInfo(\n 'action2',\n 'outcome2',\n 'reason2',\n true,\n 'pay-123'\n )\n expect(res).toEqual({\n event: {\n category: 'payment',\n action: 'action2',\n outcome: 'outcome2',\n reason: 'reason2',\n type: 'live',\n reference: 'pay-123'\n }\n })\n })\n})\n\n/**\n * @import { IncomingMessage } from 'node:http'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"payment-helper.test.js","names":["buildPaymentInfo","getPaymentContext","get","jest","mock","describe","uuid","it","mockRequest","yar","fn","mockReturnValueOnce","undefined","expect","rejects","toThrow","paymentId","isLivePayment","formId","getPaymentStatusApiResult","payment_id","_links","next_url","href","state","status","mocked","mockResolvedValueOnce","res","statusCode","headers","payload","error","mockFormsService","getFormSecret","toEqual","paymentStatus","session","sessionKey","event","category","action","outcome","reason","type","reference"],"sources":["../../../../../src/server/plugins/engine/routes/payment-helper.test.js"],"sourcesContent":["import {\n buildPaymentInfo,\n getPaymentContext\n} from '~/src/server/plugins/engine/routes/payment-helper.js'\nimport { get } from '~/src/server/services/httpService.js'\n\njest.mock('~/src/server/services/httpService.ts')\n\ndescribe('payment helper', () => {\n const uuid = '5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n it('should throw if no session', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce(undefined)\n }\n }\n // @ts-expect-error - partial request mock\n await expect(() => getPaymentContext(mockRequest, uuid)).rejects.toThrow(\n 'No payment session found for uuid=5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n )\n })\n\n it('should throw if no payment id', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce({})\n }\n }\n // @ts-expect-error - partial request mock\n await expect(() => getPaymentContext(mockRequest, uuid)).rejects.toThrow(\n 'No paymentId in session'\n )\n })\n\n it('should get context successfully', async () => {\n const mockRequest = {\n yar: {\n get: jest.fn().mockReturnValueOnce({\n paymentId: 'payment-id',\n isLivePayment: false,\n formId: 'formid'\n })\n }\n }\n\n const getPaymentStatusApiResult = {\n payment_id: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n }\n\n jest.mocked(get).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: getPaymentStatusApiResult,\n error: undefined\n })\n\n const mockFormsService = {\n getFormSecret: () => 'secret-value'\n }\n // @ts-expect-error - partial request mock\n const res = await getPaymentContext(mockRequest, uuid, mockFormsService)\n expect(res).toEqual({\n paymentStatus: {\n paymentId: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n },\n session: {\n formId: 'formid',\n isLivePayment: false,\n paymentId: 'payment-id'\n },\n sessionKey: 'payment-5a54c2fe-da49-4202-8cd3-2121eaca03c3'\n })\n })\n\n it('should create logging info for a test payment', () => {\n const res = buildPaymentInfo(\n 'action1',\n 'outcome1',\n 'reason1',\n false,\n 'pay-123'\n )\n expect(res).toEqual({\n event: {\n category: 'payment',\n action: 'action1',\n outcome: 'outcome1',\n reason: 'reason1',\n type: 'test',\n reference: 'pay-123'\n }\n })\n })\n\n it('should create logging info for a live payment', () => {\n const res = buildPaymentInfo(\n 'action2',\n 'outcome2',\n 'reason2',\n true,\n 'pay-123'\n )\n expect(res).toEqual({\n event: {\n category: 'payment',\n action: 'action2',\n outcome: 'outcome2',\n reason: 'reason2',\n type: 'live',\n reference: 'pay-123'\n }\n })\n })\n})\n\n/**\n * @import { IncomingMessage } from 'node:http'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -3,6 +3,7 @@ import { StatusCodes } from 'http-status-codes';
3
3
  import Joi from 'joi';
4
4
  import { createLogger } from "../../../common/helpers/logging/logger.js";
5
5
  import { EXTERNAL_STATE_APPENDAGE } from "../../../constants.js";
6
+ import { getPluginOptions } from "../helpers.js";
6
7
  import { buildPaymentInfo, convertPenceToPounds, getPaymentContext } from "./payment-helper.js";
7
8
  export const PAYMENT_RETURN_PATH = '/payment-callback';
8
9
  export const PAYMENT_SESSION_PREFIX = 'payment-';
@@ -104,11 +105,14 @@ function getReturnRoute() {
104
105
  const {
105
106
  uuid
106
107
  } = /** @type {{ uuid: string }} */request.query;
108
+ const {
109
+ services
110
+ } = getPluginOptions(request.server);
107
111
  const {
108
112
  session,
109
113
  sessionKey,
110
114
  paymentStatus
111
- } = await getPaymentContext(request, uuid);
115
+ } = await getPaymentContext(request, uuid, /** @type {FormsService} */services?.formsService);
112
116
 
113
117
  /**
114
118
  * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle
@@ -158,5 +162,7 @@ function getReturnRoute() {
158
162
  * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'
159
163
  * @import { PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'
160
164
  * @import { ExternalStateAppendage, FormState } from '~/src/server/plugins/engine/types.js'
165
+ * @import { PluginOptions } from '~/src/server/plugins/engine/types.js'
166
+ * @import { FormsService } from '~/src/server/types.js'
161
167
  */
162
168
  //# sourceMappingURL=payment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payment.js","names":["Boom","StatusCodes","Joi","createLogger","EXTERNAL_STATE_APPENDAGE","buildPaymentInfo","convertPenceToPounds","getPaymentContext","PAYMENT_RETURN_PATH","PAYMENT_SESSION_PREFIX","logger","flashComponentState","request","session","paymentStatus","paymentState","paymentId","reference","amount","description","uuid","formId","isLivePayment","payerEmail","email","preAuth","status","createdAt","Date","toISOString","appendage","component","componentName","data","yar","flash","getRoutes","getReturnRoute","logPaymentSuccess","info","state","logPaymentFailure","handlePaymentSuccess","h","sessionKey","clear","redirect","returnUrl","code","SEE_OTHER","handlePaymentFailure","failureUrl","method","path","handler","query","nextUrl","_links","next_url","href","badRequest","unknownStatus","internal","options","validate","object","keys","string","required"],"sources":["../../../../../src/server/plugins/engine/routes/payment.js"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { EXTERNAL_STATE_APPENDAGE } from '~/src/server/constants.js'\nimport {\n buildPaymentInfo,\n convertPenceToPounds,\n getPaymentContext\n} from '~/src/server/plugins/engine/routes/payment-helper.js'\n\nexport const PAYMENT_RETURN_PATH = '/payment-callback'\nexport const PAYMENT_SESSION_PREFIX = 'payment-'\n\nconst logger = createLogger()\n\n/**\n * Flash form component state after successful payment\n * @param {Request} request - the request\n * @param {PaymentSessionData} session - the session data containing payment state\n * @param {GetPaymentResponse} paymentStatus - the payment status response from GOV.UK Pay\n */\nfunction flashComponentState(request, session, paymentStatus) {\n /** @type {PaymentState} */\n const paymentState = {\n paymentId: paymentStatus.paymentId,\n reference: session.reference,\n amount: session.amount,\n description: session.description,\n uuid: session.uuid,\n formId: session.formId,\n isLivePayment: session.isLivePayment,\n payerEmail: paymentStatus.email,\n preAuth: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n /** @type {ExternalStateAppendage} */\n const appendage = {\n component: session.componentName,\n data: /** @type {FormState} */ (/** @type {unknown} */ (paymentState))\n }\n\n request.yar.flash(EXTERNAL_STATE_APPENDAGE, appendage, true)\n}\n\n/**\n * Gets the payment routes for handling GOV.UK Pay callbacks\n * @returns {ServerRoute[]}\n */\nexport function getRoutes() {\n return [getReturnRoute()]\n}\n\n/**\n * Logs successful payment\n * @param {PaymentSessionData} session - the session data\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction logPaymentSuccess(session, paymentStatus) {\n logger.info(\n buildPaymentInfo(\n 'pre-auth',\n 'success',\n `${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,\n session.isLivePayment,\n paymentStatus.paymentId\n ),\n `[payment] Successful pre-auth for paymentId=${paymentStatus.paymentId}`\n )\n}\n\n/**\n * Logs failed/cancelled payment\n * @param {PaymentSessionData} session - the session data\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction logPaymentFailure(session, paymentStatus) {\n logger.info(\n buildPaymentInfo(\n 'pre-auth',\n 'failed/cancelled',\n `${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,\n session.isLivePayment,\n paymentStatus.paymentId\n ),\n `[payment] Failed/cancelled pre-auth for paymentId=${paymentStatus.paymentId}`\n )\n}\n\n/**\n * Handles successful payment states (capturable/success)\n * @param {Request} request - the request\n * @param {ResponseToolkit} h - the response toolkit\n * @param {PaymentSessionData} session - the session data\n * @param {string} sessionKey - the session key\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction handlePaymentSuccess(request, h, session, sessionKey, paymentStatus) {\n flashComponentState(request, session, paymentStatus)\n request.yar.clear(sessionKey)\n return h.redirect(session.returnUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Handles failed/cancelled/error payment states\n * @param {Request} request - the request\n * @param {ResponseToolkit} h - the response toolkit\n * @param {PaymentSessionData} session - the session data\n * @param {string} sessionKey - the session key\n */\nfunction handlePaymentFailure(request, h, session, sessionKey) {\n request.yar.clear(sessionKey)\n return h.redirect(session.failureUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Route handler for payment return URL\n * This is called when GOV.UK Pay redirects the user back after payment\n * @returns {ServerRoute}\n */\nfunction getReturnRoute() {\n return {\n method: 'GET',\n path: PAYMENT_RETURN_PATH,\n async handler(request, h) {\n const { uuid } = /** @type {{ uuid: string }} */ (request.query)\n const { session, sessionKey, paymentStatus } = await getPaymentContext(\n request,\n uuid\n )\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const { status } = paymentStatus.state\n\n switch (status) {\n case 'capturable':\n case 'success':\n logPaymentSuccess(session, paymentStatus)\n return handlePaymentSuccess(\n request,\n h,\n session,\n sessionKey,\n paymentStatus\n )\n\n case 'cancelled':\n case 'failed':\n case 'error':\n logPaymentFailure(session, paymentStatus)\n return handlePaymentFailure(request, h, session, sessionKey)\n\n case 'created':\n case 'started':\n case 'submitted': {\n const nextUrl = paymentStatus._links.next_url?.href\n\n if (!nextUrl) {\n throw Boom.badRequest(\n `Payment in state '${status}' but no next_url available`\n )\n }\n\n return h.redirect(nextUrl).code(StatusCodes.SEE_OTHER)\n }\n\n default: {\n const unknownStatus = /** @type {string} */ (status)\n throw Boom.internal(`Unknown payment status: ${unknownStatus}`)\n }\n }\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n uuid: Joi.string().uuid().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * @import { Request, ResponseToolkit, ServerRoute } from '@hapi/hapi'\n * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'\n * @import { PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\n * @import { ExternalStateAppendage, FormState } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"payment.js","names":["Boom","StatusCodes","Joi","createLogger","EXTERNAL_STATE_APPENDAGE","getPluginOptions","buildPaymentInfo","convertPenceToPounds","getPaymentContext","PAYMENT_RETURN_PATH","PAYMENT_SESSION_PREFIX","logger","flashComponentState","request","session","paymentStatus","paymentState","paymentId","reference","amount","description","uuid","formId","isLivePayment","payerEmail","email","preAuth","status","createdAt","Date","toISOString","appendage","component","componentName","data","yar","flash","getRoutes","getReturnRoute","logPaymentSuccess","info","state","logPaymentFailure","handlePaymentSuccess","h","sessionKey","clear","redirect","returnUrl","code","SEE_OTHER","handlePaymentFailure","failureUrl","method","path","handler","query","services","server","formsService","nextUrl","_links","next_url","href","badRequest","unknownStatus","internal","options","validate","object","keys","string","required"],"sources":["../../../../../src/server/plugins/engine/routes/payment.js"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { EXTERNAL_STATE_APPENDAGE } from '~/src/server/constants.js'\nimport { getPluginOptions } from '~/src/server/plugins/engine/helpers.js'\nimport {\n buildPaymentInfo,\n convertPenceToPounds,\n getPaymentContext\n} from '~/src/server/plugins/engine/routes/payment-helper.js'\n\nexport const PAYMENT_RETURN_PATH = '/payment-callback'\nexport const PAYMENT_SESSION_PREFIX = 'payment-'\n\nconst logger = createLogger()\n\n/**\n * Flash form component state after successful payment\n * @param {Request} request - the request\n * @param {PaymentSessionData} session - the session data containing payment state\n * @param {GetPaymentResponse} paymentStatus - the payment status response from GOV.UK Pay\n */\nfunction flashComponentState(request, session, paymentStatus) {\n /** @type {PaymentState} */\n const paymentState = {\n paymentId: paymentStatus.paymentId,\n reference: session.reference,\n amount: session.amount,\n description: session.description,\n uuid: session.uuid,\n formId: session.formId,\n isLivePayment: session.isLivePayment,\n payerEmail: paymentStatus.email,\n preAuth: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n /** @type {ExternalStateAppendage} */\n const appendage = {\n component: session.componentName,\n data: /** @type {FormState} */ (/** @type {unknown} */ (paymentState))\n }\n\n request.yar.flash(EXTERNAL_STATE_APPENDAGE, appendage, true)\n}\n\n/**\n * Gets the payment routes for handling GOV.UK Pay callbacks\n * @returns {ServerRoute[]}\n */\nexport function getRoutes() {\n return [getReturnRoute()]\n}\n\n/**\n * Logs successful payment\n * @param {PaymentSessionData} session - the session data\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction logPaymentSuccess(session, paymentStatus) {\n logger.info(\n buildPaymentInfo(\n 'pre-auth',\n 'success',\n `${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,\n session.isLivePayment,\n paymentStatus.paymentId\n ),\n `[payment] Successful pre-auth for paymentId=${paymentStatus.paymentId}`\n )\n}\n\n/**\n * Logs failed/cancelled payment\n * @param {PaymentSessionData} session - the session data\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction logPaymentFailure(session, paymentStatus) {\n logger.info(\n buildPaymentInfo(\n 'pre-auth',\n 'failed/cancelled',\n `${paymentStatus.state.status} amount=${convertPenceToPounds(paymentStatus.amount)}`,\n session.isLivePayment,\n paymentStatus.paymentId\n ),\n `[payment] Failed/cancelled pre-auth for paymentId=${paymentStatus.paymentId}`\n )\n}\n\n/**\n * Handles successful payment states (capturable/success)\n * @param {Request} request - the request\n * @param {ResponseToolkit} h - the response toolkit\n * @param {PaymentSessionData} session - the session data\n * @param {string} sessionKey - the session key\n * @param {GetPaymentResponse} paymentStatus - the payment status from GOV.UK Pay\n */\nfunction handlePaymentSuccess(request, h, session, sessionKey, paymentStatus) {\n flashComponentState(request, session, paymentStatus)\n request.yar.clear(sessionKey)\n return h.redirect(session.returnUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Handles failed/cancelled/error payment states\n * @param {Request} request - the request\n * @param {ResponseToolkit} h - the response toolkit\n * @param {PaymentSessionData} session - the session data\n * @param {string} sessionKey - the session key\n */\nfunction handlePaymentFailure(request, h, session, sessionKey) {\n request.yar.clear(sessionKey)\n return h.redirect(session.failureUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Route handler for payment return URL\n * This is called when GOV.UK Pay redirects the user back after payment\n * @returns {ServerRoute}\n */\nfunction getReturnRoute() {\n return {\n method: 'GET',\n path: PAYMENT_RETURN_PATH,\n async handler(request, h) {\n const { uuid } = /** @type {{ uuid: string }} */ (request.query)\n\n const { services } = getPluginOptions(request.server)\n\n const { session, sessionKey, paymentStatus } = await getPaymentContext(\n request,\n uuid,\n /** @type {FormsService} */ (services?.formsService)\n )\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const { status } = paymentStatus.state\n\n switch (status) {\n case 'capturable':\n case 'success':\n logPaymentSuccess(session, paymentStatus)\n return handlePaymentSuccess(\n request,\n h,\n session,\n sessionKey,\n paymentStatus\n )\n\n case 'cancelled':\n case 'failed':\n case 'error':\n logPaymentFailure(session, paymentStatus)\n return handlePaymentFailure(request, h, session, sessionKey)\n\n case 'created':\n case 'started':\n case 'submitted': {\n const nextUrl = paymentStatus._links.next_url?.href\n\n if (!nextUrl) {\n throw Boom.badRequest(\n `Payment in state '${status}' but no next_url available`\n )\n }\n\n return h.redirect(nextUrl).code(StatusCodes.SEE_OTHER)\n }\n\n default: {\n const unknownStatus = /** @type {string} */ (status)\n throw Boom.internal(`Unknown payment status: ${unknownStatus}`)\n }\n }\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n uuid: Joi.string().uuid().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * @import { Request, ResponseToolkit, ServerRoute } from '@hapi/hapi'\n * @import { GetPaymentResponse, PaymentSessionData } from '~/src/server/plugins/payment/types.js'\n * @import { PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\n * @import { ExternalStateAppendage, FormState } from '~/src/server/plugins/engine/types.js'\n * @import { PluginOptions } from '~/src/server/plugins/engine/types.js'\n * @import { FormsService } from '~/src/server/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -17,6 +17,13 @@ export function getFormMetadataById(_id: string): Promise<FormMetadata>;
17
17
  * @returns {Promise<FormDefinition | undefined>}
18
18
  */
19
19
  export function getFormDefinition(_id: string, _state: FormStatus): Promise<FormDefinition | undefined>;
20
+ /**
21
+ * Dummy function to get a form secret.
22
+ * @param {string} _id - the id of the form
23
+ * @param {string} _secretName - the name of the secret
24
+ * @returns {Promise<string>}
25
+ */
26
+ export function getFormSecret(_id: string, _secretName: string): Promise<string>;
20
27
  import type { FormMetadata } from '@defra/forms-model';
21
28
  import type { FormStatus } from '@defra/forms-model';
22
29
  import type { FormDefinition } from '@defra/forms-model';
@@ -31,6 +31,17 @@ export function getFormDefinition(_id, _state) {
31
31
  throw error;
32
32
  }
33
33
 
34
+ // eslint-disable-next-line jsdoc/require-returns-check
35
+ /**
36
+ * Dummy function to get a form secret.
37
+ * @param {string} _id - the id of the form
38
+ * @param {string} _secretName - the name of the secret
39
+ * @returns {Promise<string>}
40
+ */
41
+ export function getFormSecret(_id, _secretName) {
42
+ throw error;
43
+ }
44
+
34
45
  /**
35
46
  * @import { FormStatus, FormDefinition, FormMetadata } from '@defra/forms-model'
36
47
  */
@@ -1 +1 @@
1
- {"version":3,"file":"formsService.js","names":["error","Error","getFormMetadata","_slug","getFormMetadataById","_id","getFormDefinition","_state"],"sources":["../../../../../src/server/plugins/engine/services/formsService.js"],"sourcesContent":["const error = Error(\n 'Not implemented. Consider setting up a form loader or providing a service implementation.'\n)\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _slug - the slug of the form\n * @returns {Promise<FormMetadata>}\n */\nexport function getFormMetadata(_slug) {\n throw error\n}\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _id - the id of the form\n * @returns {Promise<FormMetadata>}\n */\nexport function getFormMetadataById(_id) {\n throw error\n}\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _id - the id of the form\n * @param {FormStatus} _state - the state of the form\n * @returns {Promise<FormDefinition | undefined>}\n */\nexport function getFormDefinition(_id, _state) {\n throw error\n}\n\n/**\n * @import { FormStatus, FormDefinition, FormMetadata } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,MAAMA,KAAK,GAAGC,KAAK,CACjB,2FACF,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAACC,KAAK,EAAE;EACrC,MAAMH,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,mBAAmBA,CAACC,GAAG,EAAE;EACvC,MAAML,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASM,iBAAiBA,CAACD,GAAG,EAAEE,MAAM,EAAE;EAC7C,MAAMP,KAAK;AACb;;AAEA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"formsService.js","names":["error","Error","getFormMetadata","_slug","getFormMetadataById","_id","getFormDefinition","_state","getFormSecret","_secretName"],"sources":["../../../../../src/server/plugins/engine/services/formsService.js"],"sourcesContent":["const error = Error(\n 'Not implemented. Consider setting up a form loader or providing a service implementation.'\n)\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _slug - the slug of the form\n * @returns {Promise<FormMetadata>}\n */\nexport function getFormMetadata(_slug) {\n throw error\n}\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _id - the id of the form\n * @returns {Promise<FormMetadata>}\n */\nexport function getFormMetadataById(_id) {\n throw error\n}\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get form metadata.\n * @param {string} _id - the id of the form\n * @param {FormStatus} _state - the state of the form\n * @returns {Promise<FormDefinition | undefined>}\n */\nexport function getFormDefinition(_id, _state) {\n throw error\n}\n\n// eslint-disable-next-line jsdoc/require-returns-check\n/**\n * Dummy function to get a form secret.\n * @param {string} _id - the id of the form\n * @param {string} _secretName - the name of the secret\n * @returns {Promise<string>}\n */\nexport function getFormSecret(_id, _secretName) {\n throw error\n}\n\n/**\n * @import { FormStatus, FormDefinition, FormMetadata } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,MAAMA,KAAK,GAAGC,KAAK,CACjB,2FACF,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAACC,KAAK,EAAE;EACrC,MAAMH,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,mBAAmBA,CAACC,GAAG,EAAE;EACvC,MAAML,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASM,iBAAiBA,CAACD,GAAG,EAAEE,MAAM,EAAE;EAC7C,MAAMP,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASQ,aAAaA,CAACH,GAAG,EAAEI,WAAW,EAAE;EAC9C,MAAMT,KAAK;AACb;;AAEA;AACA;AACA","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  import { FormStatus } from '@defra/forms-model';
2
- import { getFormDefinition, getFormMetadata, getFormMetadataById } from "./formsService.js";
2
+ import { getFormDefinition, getFormMetadata, getFormMetadataById, getFormSecret } from "./formsService.js";
3
3
  describe('formsService', () => {
4
4
  it('getFormMetadata should throw error', () => {
5
5
  expect(() => getFormMetadata('slug')).toThrow();
@@ -10,5 +10,8 @@ describe('formsService', () => {
10
10
  it('getFormDefinition should throw error', () => {
11
11
  expect(() => getFormDefinition('id', FormStatus.Draft)).toThrow();
12
12
  });
13
+ it('getFormSecret should throw error', () => {
14
+ expect(() => getFormSecret('id', 'my-secret-name')).toThrow();
15
+ });
13
16
  });
14
17
  //# sourceMappingURL=formsService.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formsService.test.js","names":["FormStatus","getFormDefinition","getFormMetadata","getFormMetadataById","describe","it","expect","toThrow","Draft"],"sources":["../../../../../src/server/plugins/engine/services/formsService.test.js"],"sourcesContent":["import { FormStatus } from '@defra/forms-model'\n\nimport {\n getFormDefinition,\n getFormMetadata,\n getFormMetadataById\n} from '~/src/server/plugins/engine/services/formsService.js'\n\ndescribe('formsService', () => {\n it('getFormMetadata should throw error', () => {\n expect(() => getFormMetadata('slug')).toThrow()\n })\n\n it('getFormMetadataById should throw error', () => {\n expect(() => getFormMetadataById('id')).toThrow()\n })\n\n it('getFormDefinition should throw error', () => {\n expect(() => getFormDefinition('id', FormStatus.Draft)).toThrow()\n })\n})\n"],"mappings":"AAAA,SAASA,UAAU,QAAQ,oBAAoB;AAE/C,SACEC,iBAAiB,EACjBC,eAAe,EACfC,mBAAmB;AAGrBC,QAAQ,CAAC,cAAc,EAAE,MAAM;EAC7BC,EAAE,CAAC,oCAAoC,EAAE,MAAM;IAC7CC,MAAM,CAAC,MAAMJ,eAAe,CAAC,MAAM,CAAC,CAAC,CAACK,OAAO,CAAC,CAAC;EACjD,CAAC,CAAC;EAEFF,EAAE,CAAC,wCAAwC,EAAE,MAAM;IACjDC,MAAM,CAAC,MAAMH,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAACI,OAAO,CAAC,CAAC;EACnD,CAAC,CAAC;EAEFF,EAAE,CAAC,sCAAsC,EAAE,MAAM;IAC/CC,MAAM,CAAC,MAAML,iBAAiB,CAAC,IAAI,EAAED,UAAU,CAACQ,KAAK,CAAC,CAAC,CAACD,OAAO,CAAC,CAAC;EACnE,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"formsService.test.js","names":["FormStatus","getFormDefinition","getFormMetadata","getFormMetadataById","getFormSecret","describe","it","expect","toThrow","Draft"],"sources":["../../../../../src/server/plugins/engine/services/formsService.test.js"],"sourcesContent":["import { FormStatus } from '@defra/forms-model'\n\nimport {\n getFormDefinition,\n getFormMetadata,\n getFormMetadataById,\n getFormSecret\n} from '~/src/server/plugins/engine/services/formsService.js'\n\ndescribe('formsService', () => {\n it('getFormMetadata should throw error', () => {\n expect(() => getFormMetadata('slug')).toThrow()\n })\n\n it('getFormMetadataById should throw error', () => {\n expect(() => getFormMetadataById('id')).toThrow()\n })\n\n it('getFormDefinition should throw error', () => {\n expect(() => getFormDefinition('id', FormStatus.Draft)).toThrow()\n })\n\n it('getFormSecret should throw error', () => {\n expect(() => getFormSecret('id', 'my-secret-name')).toThrow()\n })\n})\n"],"mappings":"AAAA,SAASA,UAAU,QAAQ,oBAAoB;AAE/C,SACEC,iBAAiB,EACjBC,eAAe,EACfC,mBAAmB,EACnBC,aAAa;AAGfC,QAAQ,CAAC,cAAc,EAAE,MAAM;EAC7BC,EAAE,CAAC,oCAAoC,EAAE,MAAM;IAC7CC,MAAM,CAAC,MAAML,eAAe,CAAC,MAAM,CAAC,CAAC,CAACM,OAAO,CAAC,CAAC;EACjD,CAAC,CAAC;EAEFF,EAAE,CAAC,wCAAwC,EAAE,MAAM;IACjDC,MAAM,CAAC,MAAMJ,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAACK,OAAO,CAAC,CAAC;EACnD,CAAC,CAAC;EAEFF,EAAE,CAAC,sCAAsC,EAAE,MAAM;IAC/CC,MAAM,CAAC,MAAMN,iBAAiB,CAAC,IAAI,EAAED,UAAU,CAACS,KAAK,CAAC,CAAC,CAACD,OAAO,CAAC,CAAC;EACnE,CAAC,CAAC;EAEFF,EAAE,CAAC,kCAAkC,EAAE,MAAM;IAC3CC,MAAM,CAAC,MAAMH,aAAa,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAACI,OAAO,CAAC,CAAC;EAC/D,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -1,4 +1,4 @@
1
- import { type ComponentDef, type Event, type FormVersionMetadata, type Item, type List, type Page, type UkAddressFieldComponent } from '@defra/forms-model';
1
+ import { type ComponentDef, type Event, type FormVersionMetadata, type Item, type List, type Page, type PaymentFieldComponent, type UkAddressFieldComponent } from '@defra/forms-model';
2
2
  import { type PluginProperties, type Request, type ResponseObject } from '@hapi/hapi';
3
3
  import { type JoiExpression, type ValidationErrorItem } from 'joi';
4
4
  import { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js';
@@ -275,7 +275,7 @@ export interface ExternalArgs {
275
275
  component: ComponentDef;
276
276
  controller: QuestionPageController;
277
277
  sourceUrl: string;
278
- actionArgs: Record<string, string>;
278
+ actionArgs?: Record<string, string>;
279
279
  isLive: boolean;
280
280
  isPreview: boolean;
281
281
  }
@@ -285,6 +285,9 @@ export interface PostcodeLookupExternalArgs extends ExternalArgs {
285
285
  step: string;
286
286
  };
287
287
  }
288
+ export interface PaymentExternalArgs extends ExternalArgs {
289
+ component: PaymentFieldComponent;
290
+ }
288
291
  export interface ExternalStateAppendage {
289
292
  component: string;
290
293
  data: FormStateValue | FormState;