@defra/forms-engine-plugin 4.0.46 → 4.0.48
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.
- package/.server/server/plugins/engine/models/SummaryViewModel.js +3 -3
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/models/__snapshots__/SummaryViewModel.test.ts.snap +5 -5
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +6 -6
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
- package/.server/server/plugins/engine/views/repeat-list-summary.html +1 -1
- package/.server/server/plugins/payment/helper.js +1 -1
- package/.server/server/plugins/payment/helper.js.map +1 -1
- package/.server/server/plugins/payment/service.js +5 -2
- package/.server/server/plugins/payment/service.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +11 -11
- package/src/server/plugins/engine/models/SummaryViewModel.ts +3 -3
- package/src/server/plugins/engine/models/__snapshots__/SummaryViewModel.test.ts.snap +5 -5
- package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +3 -3
- package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +7 -5
- package/src/server/plugins/engine/views/repeat-list-summary.html +1 -1
- package/src/server/plugins/payment/helper.js +1 -1
- package/src/server/plugins/payment/service.js +13 -2
|
@@ -158,12 +158,12 @@ function ItemRepeat(page, state, options) {
|
|
|
158
158
|
title
|
|
159
159
|
} = repeat.options;
|
|
160
160
|
const values = page.getListFromState(state);
|
|
161
|
-
const unit = values.length === 1 ?
|
|
161
|
+
const unit = values.length === 1 ? 'answer' : 'answers';
|
|
162
162
|
return {
|
|
163
163
|
name,
|
|
164
164
|
label: title,
|
|
165
|
-
title
|
|
166
|
-
value: values.length ? `You added ${values.length} ${unit}` : '',
|
|
165
|
+
title,
|
|
166
|
+
value: values.length ? `You have added ${values.length} ${unit}` : '',
|
|
167
167
|
href: getPageHref(page, options.path, {
|
|
168
168
|
returnUrl: getPageHref(page, page.getSummaryPath())
|
|
169
169
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SummaryViewModel.js","names":["SchemaVersion","PaymentField","getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","allowSaveAndExit","paymentState","paymentDetails","constructor","request","model","basePath","def","sections","isForceAccess","title","schema","V2","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","required","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { SchemaVersion, type Section } from '@defra/forms-model'\n\nimport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n allowSaveAndExit = false\n paymentState?: PaymentState\n paymentDetails?: CheckAnswers\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n if (def.schema === SchemaVersion.V2 && !page.title) {\n this.pageTitle = 'Check your answers before sending your form'\n }\n\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not provided'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[...sections, undefined].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n // PaymentField is rendered in its own section, skip it here\n if (field instanceof PaymentField) {\n continue\n }\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? title : `${title}s`\n\n return {\n name,\n label: title,\n title: values.length ? `${unit} added` : unit,\n value: values.length ? `You added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nexport function ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title:\n field.options.required === false\n ? `${field.label} (optional)`\n : field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"SummaryViewModel.js","names":["SchemaVersion","PaymentField","getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","allowSaveAndExit","paymentState","paymentDetails","constructor","request","model","basePath","def","sections","isForceAccess","title","schema","V2","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","required","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { SchemaVersion, type Section } from '@defra/forms-model'\n\nimport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n allowSaveAndExit = false\n paymentState?: PaymentState\n paymentDetails?: CheckAnswers\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n if (def.schema === SchemaVersion.V2 && !page.title) {\n this.pageTitle = 'Check your answers before sending your form'\n }\n\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not provided'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[...sections, undefined].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n // PaymentField is rendered in its own section, skip it here\n if (field instanceof PaymentField) {\n continue\n }\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? 'answer' : 'answers'\n\n return {\n name,\n label: title,\n title,\n value: values.length ? `You have added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nexport function ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title:\n field.options.required === false\n ? `${field.label} (optional)`\n : field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,7 +5,7 @@ exports[`SummaryViewModel Check answers (0 items) should use correct summary lab
|
|
|
5
5
|
{
|
|
6
6
|
"label": "Pizza",
|
|
7
7
|
"name": "pizza",
|
|
8
|
-
"title": "
|
|
8
|
+
"title": "Pizza",
|
|
9
9
|
"value": "",
|
|
10
10
|
},
|
|
11
11
|
{
|
|
@@ -22,8 +22,8 @@ exports[`SummaryViewModel Check answers (1 item) should use correct summary labe
|
|
|
22
22
|
{
|
|
23
23
|
"label": "Pizza",
|
|
24
24
|
"name": "pizza",
|
|
25
|
-
"title": "Pizza
|
|
26
|
-
"value": "You added 1
|
|
25
|
+
"title": "Pizza",
|
|
26
|
+
"value": "You have added 1 answer",
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"label": "How would you like to receive your pizza?",
|
|
@@ -39,8 +39,8 @@ exports[`SummaryViewModel Check answers (2 items) should use correct summary lab
|
|
|
39
39
|
{
|
|
40
40
|
"label": "Pizza",
|
|
41
41
|
"name": "pizza",
|
|
42
|
-
"title": "
|
|
43
|
-
"value": "You added 2
|
|
42
|
+
"title": "Pizza",
|
|
43
|
+
"value": "You have added 2 answers",
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"label": "How would you like to receive your pizza?",
|
|
@@ -161,8 +161,7 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
161
161
|
query
|
|
162
162
|
} = request;
|
|
163
163
|
const {
|
|
164
|
-
schema
|
|
165
|
-
options
|
|
164
|
+
schema
|
|
166
165
|
} = repeat;
|
|
167
166
|
const {
|
|
168
167
|
state
|
|
@@ -183,7 +182,7 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
183
182
|
// Show error if repeat limits apply
|
|
184
183
|
if (hasErrorMin || hasErrorMax) {
|
|
185
184
|
const count = hasErrorMax ? schema.max : schema.min;
|
|
186
|
-
const itemTitle =
|
|
185
|
+
const itemTitle = `answer${count === 1 ? '' : 's'}`;
|
|
187
186
|
context.errors = [{
|
|
188
187
|
path: [],
|
|
189
188
|
href: '',
|
|
@@ -229,10 +228,10 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
229
228
|
...viewModel,
|
|
230
229
|
context,
|
|
231
230
|
backLink: this.getBackLink(request, context),
|
|
232
|
-
pageTitle:
|
|
231
|
+
pageTitle: 'Are you sure you want to remove this answer?',
|
|
233
232
|
itemTitle: `${title} ${list.indexOf(item) + 1}`,
|
|
234
233
|
buttonConfirm: {
|
|
235
|
-
text:
|
|
234
|
+
text: 'Remove'
|
|
236
235
|
},
|
|
237
236
|
buttonCancel: {
|
|
238
237
|
text: 'Cancel'
|
|
@@ -346,11 +345,12 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
346
345
|
});
|
|
347
346
|
});
|
|
348
347
|
}
|
|
348
|
+
const unit = count === 1 ? 'answer' : 'answers';
|
|
349
349
|
return {
|
|
350
350
|
...this.viewModel,
|
|
351
351
|
backLink: this.getBackLink(request, context),
|
|
352
352
|
repeatTitle: title,
|
|
353
|
-
pageTitle: `You have added ${count} ${
|
|
353
|
+
pageTitle: `You have added ${count} ${unit}`,
|
|
354
354
|
showTitle: true,
|
|
355
355
|
context,
|
|
356
356
|
errors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","itemTitle","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema, options } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n const itemTitle = `${options.title}${count === 1 ? '' : 's'}`\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? `You can only add up to ${count} ${itemTitle}`\n : `You must add at least ${count} ${itemTitle}`\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context),\n pageTitle: `Are you sure you want to remove this ${title}?`,\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: `Remove ${title}` },\n buttonCancel: { text: 'Cancel' }\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const repeatCaption = `${this.repeat.options.title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { title } = repeat.options\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || 'Not provided'\n },\n actions: {\n items\n }\n })\n })\n }\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context),\n repeatTitle: title,\n pageTitle: `You have added ${count} ${title}${count === 1 ? '' : 's'}`,\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","itemTitle","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","unit","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n const itemTitle = `answer${count === 1 ? '' : 's'}`\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? `You can only add up to ${count} ${itemTitle}`\n : `You must add at least ${count} ${itemTitle}`\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context),\n pageTitle: 'Are you sure you want to remove this answer?',\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: 'Remove' },\n buttonCancel: { text: 'Cancel' }\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const repeatCaption = `${this.repeat.options.title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { title } = repeat.options\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || 'Not provided'\n },\n actions: {\n items\n }\n })\n })\n }\n\n const unit = count === 1 ? 'answer' : 'answers'\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context),\n repeatTitle: title,\n pageTitle: `You have added ${count} ${unit}`,\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -13,7 +13,7 @@ export const DEFAULT_PAYMENT_HELP_URL = 'https://www.gov.uk/government/organisat
|
|
|
13
13
|
export function getPaymentApiKey(isLivePayment, formId) {
|
|
14
14
|
const apiKeyValue = isLivePayment ? process.env[`PAYMENT_PROVIDER_API_KEY_LIVE_${formId}`] : process.env[`PAYMENT_PROVIDER_API_KEY_TEST_${formId}`];
|
|
15
15
|
if (!apiKeyValue) {
|
|
16
|
-
throw new Error(`Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`);
|
|
16
|
+
throw new Error(`[payment] Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`);
|
|
17
17
|
}
|
|
18
18
|
return apiKeyValue;
|
|
19
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","names":["format","PaymentService","DEFAULT_PAYMENT_HELP_URL","getPaymentApiKey","isLivePayment","formId","apiKeyValue","process","env","Error","createPaymentService","apiKey","formatPaymentDate","isoString","Date","formatPaymentAmount","amount","toFixed"],"sources":["../../../../src/server/plugins/payment/helper.js"],"sourcesContent":["import { format } from 'date-fns'\n\nimport { PaymentService } from '~/src/server/plugins/payment/service.js'\n\nexport const DEFAULT_PAYMENT_HELP_URL =\n 'https://www.gov.uk/government/organisations/department-for-environment-food-rural-affairs'\n\n/**\n * Determine which payment API key value to use.\n * If a draft preview form or a live preview form, read the TEST API key value specific to that form.\n * If a live (non-preview) form, read the LIVE API key value specific to that form.\n * @param {boolean} isLivePayment - true if this is a live payment (as opposed to a test one)\n * @param {string} formId - id of the form\n * @returns {string}\n */\nexport function getPaymentApiKey(isLivePayment, formId) {\n const apiKeyValue = isLivePayment\n ? process.env[`PAYMENT_PROVIDER_API_KEY_LIVE_${formId}`]\n : process.env[`PAYMENT_PROVIDER_API_KEY_TEST_${formId}`]\n\n if (!apiKeyValue) {\n throw new Error(\n `Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`\n )\n }\n return apiKeyValue\n}\n\n/**\n * Creates a PaymentService instance with the appropriate API key\n * @param {boolean} isLivePayment - true if this is a live payment\n * @param {string} formId - id of the form\n * @returns {PaymentService}\n */\nexport function createPaymentService(isLivePayment, formId) {\n const apiKey = getPaymentApiKey(isLivePayment, formId)\n return new PaymentService(apiKey)\n}\n\n/**\n * Formats a payment date for display\n * @param {string} isoString - ISO date string\n * @returns {string} Formatted date string (e.g., \"26 January 2026 5:01pm\")\n */\nexport function formatPaymentDate(isoString) {\n return format(new Date(isoString), 'd MMMM yyyy h:mmaaa')\n}\n\n/**\n * Formats a payment amount with two decimal places\n * @param {number} amount - amount in pounds\n * @returns {string} Formatted amount (e.g., \"£10.00\")\n */\nexport function formatPaymentAmount(amount) {\n return `£${amount.toFixed(2)}`\n}\n"],"mappings":"AAAA,SAASA,MAAM,QAAQ,UAAU;AAEjC,SAASC,cAAc;AAEvB,OAAO,MAAMC,wBAAwB,GACnC,2FAA2F;;AAE7F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAACC,aAAa,EAAEC,MAAM,EAAE;EACtD,MAAMC,WAAW,GAAGF,aAAa,GAC7BG,OAAO,CAACC,GAAG,CAAC,iCAAiCH,MAAM,EAAE,CAAC,GACtDE,OAAO,CAACC,GAAG,CAAC,iCAAiCH,MAAM,EAAE,CAAC;EAE1D,IAAI,CAACC,WAAW,EAAE;IAChB,MAAM,IAAIG,KAAK,CACb
|
|
1
|
+
{"version":3,"file":"helper.js","names":["format","PaymentService","DEFAULT_PAYMENT_HELP_URL","getPaymentApiKey","isLivePayment","formId","apiKeyValue","process","env","Error","createPaymentService","apiKey","formatPaymentDate","isoString","Date","formatPaymentAmount","amount","toFixed"],"sources":["../../../../src/server/plugins/payment/helper.js"],"sourcesContent":["import { format } from 'date-fns'\n\nimport { PaymentService } from '~/src/server/plugins/payment/service.js'\n\nexport const DEFAULT_PAYMENT_HELP_URL =\n 'https://www.gov.uk/government/organisations/department-for-environment-food-rural-affairs'\n\n/**\n * Determine which payment API key value to use.\n * If a draft preview form or a live preview form, read the TEST API key value specific to that form.\n * If a live (non-preview) form, read the LIVE API key value specific to that form.\n * @param {boolean} isLivePayment - true if this is a live payment (as opposed to a test one)\n * @param {string} formId - id of the form\n * @returns {string}\n */\nexport function getPaymentApiKey(isLivePayment, formId) {\n const apiKeyValue = isLivePayment\n ? process.env[`PAYMENT_PROVIDER_API_KEY_LIVE_${formId}`]\n : process.env[`PAYMENT_PROVIDER_API_KEY_TEST_${formId}`]\n\n if (!apiKeyValue) {\n throw new Error(\n `[payment] Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`\n )\n }\n return apiKeyValue\n}\n\n/**\n * Creates a PaymentService instance with the appropriate API key\n * @param {boolean} isLivePayment - true if this is a live payment\n * @param {string} formId - id of the form\n * @returns {PaymentService}\n */\nexport function createPaymentService(isLivePayment, formId) {\n const apiKey = getPaymentApiKey(isLivePayment, formId)\n return new PaymentService(apiKey)\n}\n\n/**\n * Formats a payment date for display\n * @param {string} isoString - ISO date string\n * @returns {string} Formatted date string (e.g., \"26 January 2026 5:01pm\")\n */\nexport function formatPaymentDate(isoString) {\n return format(new Date(isoString), 'd MMMM yyyy h:mmaaa')\n}\n\n/**\n * Formats a payment amount with two decimal places\n * @param {number} amount - amount in pounds\n * @returns {string} Formatted amount (e.g., \"£10.00\")\n */\nexport function formatPaymentAmount(amount) {\n return `£${amount.toFixed(2)}`\n}\n"],"mappings":"AAAA,SAASA,MAAM,QAAQ,UAAU;AAEjC,SAASC,cAAc;AAEvB,OAAO,MAAMC,wBAAwB,GACnC,2FAA2F;;AAE7F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAACC,aAAa,EAAEC,MAAM,EAAE;EACtD,MAAMC,WAAW,GAAGF,aAAa,GAC7BG,OAAO,CAACC,GAAG,CAAC,iCAAiCH,MAAM,EAAE,CAAC,GACtDE,OAAO,CAACC,GAAG,CAAC,iCAAiCH,MAAM,EAAE,CAAC;EAE1D,IAAI,CAACC,WAAW,EAAE;IAChB,MAAM,IAAIG,KAAK,CACb,yCAAyCL,aAAa,GAAG,MAAM,GAAG,MAAM,YAAYC,MAAM,EAC5F,CAAC;EACH;EACA,OAAOC,WAAW;AACpB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,oBAAoBA,CAACN,aAAa,EAAEC,MAAM,EAAE;EAC1D,MAAMM,MAAM,GAAGR,gBAAgB,CAACC,aAAa,EAAEC,MAAM,CAAC;EACtD,OAAO,IAAIJ,cAAc,CAACU,MAAM,CAAC;AACnC;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAACC,SAAS,EAAE;EAC3C,OAAOb,MAAM,CAAC,IAAIc,IAAI,CAACD,SAAS,CAAC,EAAE,qBAAqB,CAAC;AAC3D;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,mBAAmBA,CAACC,MAAM,EAAE;EAC1C,OAAO,IAAIA,MAAM,CAACC,OAAO,CAAC,CAAC,CAAC,EAAE;AAChC","ignoreList":[]}
|
|
@@ -42,6 +42,7 @@ export class PaymentService {
|
|
|
42
42
|
return_url: returnUrl,
|
|
43
43
|
delayed_capture: true
|
|
44
44
|
});
|
|
45
|
+
logger.info(`[payment] Created payment and user taken to enter pre-auth details for paymentId=${response.payment_id}`);
|
|
45
46
|
return {
|
|
46
47
|
paymentId: response.payment_id,
|
|
47
48
|
paymentUrl: response._links.next_url.href
|
|
@@ -63,8 +64,10 @@ export class PaymentService {
|
|
|
63
64
|
const errorMessage = response.error instanceof Error ? response.error.message : JSON.stringify(response.error);
|
|
64
65
|
throw new Error(`Failed to get payment status: ${errorMessage}`);
|
|
65
66
|
}
|
|
67
|
+
const state = response.payload.state;
|
|
68
|
+
logger.info(`[payment] Got payment status for paymentId=${paymentId}: ${state.status} message:${state.message ?? 'N/A'} code:${state.code ?? 'N/A'}`);
|
|
66
69
|
return {
|
|
67
|
-
state
|
|
70
|
+
state,
|
|
68
71
|
_links: response.payload._links,
|
|
69
72
|
email: response.payload.email,
|
|
70
73
|
paymentId: response.payload.payment_id,
|
|
@@ -89,7 +92,7 @@ export class PaymentService {
|
|
|
89
92
|
});
|
|
90
93
|
const statusCode = response.res.statusCode;
|
|
91
94
|
if (statusCode === StatusCodes.OK || statusCode === StatusCodes.NO_CONTENT) {
|
|
92
|
-
logger.info(`[payment] Successfully captured payment
|
|
95
|
+
logger.info(`[payment] Successfully captured payment for paymentId=${paymentId}`);
|
|
93
96
|
return true;
|
|
94
97
|
}
|
|
95
98
|
logger.error(`[payment] Capture failed for paymentId=${paymentId}: HTTP ${statusCode}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":["StatusCodes","createLogger","get","post","postJson","PAYMENT_BASE_URL","PAYMENT_ENDPOINT","logger","getAuthHeaders","apiKey","Authorization","PaymentService","constructor","createPayment","amount","description","returnUrl","reference","metadata","response","postToPayProvider","return_url","delayed_capture","
|
|
1
|
+
{"version":3,"file":"service.js","names":["StatusCodes","createLogger","get","post","postJson","PAYMENT_BASE_URL","PAYMENT_ENDPOINT","logger","getAuthHeaders","apiKey","Authorization","PaymentService","constructor","createPayment","amount","description","returnUrl","reference","metadata","response","postToPayProvider","return_url","delayed_capture","info","payment_id","paymentId","paymentUrl","_links","next_url","href","getPaymentStatus","getByType","headers","json","error","errorMessage","Error","message","JSON","stringify","state","payload","status","code","email","err","capturePayment","statusCode","res","OK","NO_CONTENT","postJsonByType"],"sources":["../../../../src/server/plugins/payment/service.js"],"sourcesContent":["import { StatusCodes } from 'http-status-codes'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { get, post, postJson } from '~/src/server/services/httpService.js'\n\nconst PAYMENT_BASE_URL = 'https://publicapi.payments.service.gov.uk'\nconst PAYMENT_ENDPOINT = '/v1/payments'\n\nconst logger = createLogger()\n\n/**\n * @param {string} apiKey\n * @returns {{ Authorization: string }}\n */\nfunction getAuthHeaders(apiKey) {\n return {\n Authorization: `Bearer ${apiKey}`\n }\n}\n\nexport class PaymentService {\n /** @type {string} */\n #apiKey\n\n /**\n * @param {string} apiKey - API key to use (global config for test value, per-form config for live value)\n */\n constructor(apiKey) {\n this.#apiKey = apiKey\n }\n\n /**\n * Creates a payment with delayed capture (pre-authorisation)\n * @param {number} amount - in pence\n * @param {string} description\n * @param {string} returnUrl\n * @param {string} reference\n * @param {{ formId: string, slug: string }} metadata\n */\n async createPayment(amount, description, returnUrl, reference, metadata) {\n const response = await this.postToPayProvider({\n amount,\n description,\n reference,\n metadata,\n return_url: returnUrl,\n delayed_capture: true\n })\n\n logger.info(\n `[payment] Created payment and user taken to enter pre-auth details for paymentId=${response.payment_id}`\n )\n\n return {\n paymentId: response.payment_id,\n paymentUrl: response._links.next_url.href\n }\n }\n\n /**\n * @param {string} paymentId\n * @returns {Promise<GetPaymentResponse>}\n */\n async getPaymentStatus(paymentId) {\n const getByType = /** @type {typeof get<GetPaymentApiResponse>} */ (get)\n\n try {\n const response = await getByType(\n `${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}/${paymentId}`,\n {\n headers: getAuthHeaders(this.#apiKey),\n json: true\n }\n )\n\n if (response.error) {\n const errorMessage =\n response.error instanceof Error\n ? response.error.message\n : JSON.stringify(response.error)\n throw new Error(`Failed to get payment status: ${errorMessage}`)\n }\n\n const state = response.payload.state\n logger.info(\n `[payment] Got payment status for paymentId=${paymentId}: ${state.status} message:${state.message ?? 'N/A'} code:${state.code ?? 'N/A'}`\n )\n\n return {\n state,\n _links: response.payload._links,\n email: response.payload.email,\n paymentId: response.payload.payment_id,\n amount: response.payload.amount\n }\n } catch (err) {\n const error = /** @type {Error} */ (err)\n logger.error(\n error,\n `[payment] Error getting payment status for paymentId=${paymentId}: ${error.message}`\n )\n throw err\n }\n }\n\n /**\n * Captures a payment that is in 'capturable' status\n * @param {string} paymentId\n * @returns {Promise<boolean>}\n */\n async capturePayment(paymentId) {\n try {\n const response = await post(\n `${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}/${paymentId}/capture`,\n {\n headers: getAuthHeaders(this.#apiKey)\n }\n )\n\n const statusCode = response.res.statusCode\n\n if (\n statusCode === StatusCodes.OK ||\n statusCode === StatusCodes.NO_CONTENT\n ) {\n logger.info(\n `[payment] Successfully captured payment for paymentId=${paymentId}`\n )\n return true\n }\n\n logger.error(\n `[payment] Capture failed for paymentId=${paymentId}: HTTP ${statusCode}`\n )\n return false\n } catch (err) {\n const error = /** @type {Error} */ (err)\n logger.error(\n error,\n `[payment] Error capturing payment for paymentId=${paymentId}: ${error.message}`\n )\n throw err\n }\n }\n\n /**\n * @param {CreatePaymentRequest} payload\n */\n async postToPayProvider(payload) {\n const postJsonByType =\n /** @type {typeof postJson<CreatePaymentResponse>} */ (postJson)\n\n try {\n const response = await postJsonByType(\n `${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}`,\n {\n payload,\n headers: getAuthHeaders(this.#apiKey)\n }\n )\n\n if (response.payload?.state.status !== 'created') {\n throw new Error(\n `Failed to create payment for reference=${payload.reference}`\n )\n }\n\n return response.payload\n } catch (err) {\n const error = /** @type {Error} */ (err)\n logger.error(\n error,\n `[payment] Error creating payment for reference=${payload.reference}: ${error.message}`\n )\n throw err\n }\n }\n}\n\n/**\n * @import { CreatePaymentRequest, CreatePaymentResponse, GetPaymentApiResponse, GetPaymentResponse } from '~/src/server/plugins/payment/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -67,9 +67,9 @@ describe('SummaryViewModel', () => {
|
|
|
67
67
|
} satisfies FormState,
|
|
68
68
|
keys: [
|
|
69
69
|
'How would you like to receive your pizza?',
|
|
70
|
-
'
|
|
70
|
+
'Pizza',
|
|
71
71
|
'How you would like to receive your pizza',
|
|
72
|
-
'
|
|
72
|
+
'Pizza',
|
|
73
73
|
'Pizza'
|
|
74
74
|
],
|
|
75
75
|
values: ['Collection', 'Not provided'],
|
|
@@ -91,13 +91,13 @@ describe('SummaryViewModel', () => {
|
|
|
91
91
|
} satisfies FormState,
|
|
92
92
|
keys: [
|
|
93
93
|
'How would you like to receive your pizza?',
|
|
94
|
-
'Pizza
|
|
94
|
+
'Pizza',
|
|
95
95
|
'How you would like to receive your pizza',
|
|
96
|
-
'
|
|
96
|
+
'Pizza',
|
|
97
97
|
'Pizza'
|
|
98
98
|
],
|
|
99
|
-
values: ['Delivery', 'You added 1
|
|
100
|
-
answers: ['Delivery', 'You added 1
|
|
99
|
+
values: ['Delivery', 'You have added 1 answer'],
|
|
100
|
+
answers: ['Delivery', 'You have added 1 answer'],
|
|
101
101
|
names: ['orderType', 'pizza']
|
|
102
102
|
},
|
|
103
103
|
{
|
|
@@ -120,13 +120,13 @@ describe('SummaryViewModel', () => {
|
|
|
120
120
|
} satisfies FormState,
|
|
121
121
|
keys: [
|
|
122
122
|
'How would you like to receive your pizza?',
|
|
123
|
-
'
|
|
123
|
+
'Pizza',
|
|
124
124
|
'How you would like to receive your pizza',
|
|
125
|
-
'
|
|
125
|
+
'Pizza',
|
|
126
126
|
'Pizza'
|
|
127
127
|
],
|
|
128
|
-
values: ['Delivery', 'You added 2
|
|
129
|
-
answers: ['Delivery', 'You added 2
|
|
128
|
+
values: ['Delivery', 'You have added 2 answers'],
|
|
129
|
+
answers: ['Delivery', 'You have added 2 answers'],
|
|
130
130
|
names: ['orderType', 'pizza']
|
|
131
131
|
}
|
|
132
132
|
])(
|
|
@@ -326,7 +326,7 @@ describe('SummaryViewModel', () => {
|
|
|
326
326
|
expect(details1.items[0]).toMatchObject({
|
|
327
327
|
name: 'pizza',
|
|
328
328
|
value: '',
|
|
329
|
-
title: '
|
|
329
|
+
title: 'Pizza',
|
|
330
330
|
label: 'Pizza'
|
|
331
331
|
})
|
|
332
332
|
|
|
@@ -186,13 +186,13 @@ function ItemRepeat(
|
|
|
186
186
|
const { name, title } = repeat.options
|
|
187
187
|
|
|
188
188
|
const values = page.getListFromState(state)
|
|
189
|
-
const unit = values.length === 1 ?
|
|
189
|
+
const unit = values.length === 1 ? 'answer' : 'answers'
|
|
190
190
|
|
|
191
191
|
return {
|
|
192
192
|
name,
|
|
193
193
|
label: title,
|
|
194
|
-
title
|
|
195
|
-
value: values.length ? `You added ${values.length} ${unit}` : '',
|
|
194
|
+
title,
|
|
195
|
+
value: values.length ? `You have added ${values.length} ${unit}` : '',
|
|
196
196
|
href: getPageHref(page, options.path, {
|
|
197
197
|
returnUrl: getPageHref(page, page.getSummaryPath())
|
|
198
198
|
}),
|
|
@@ -5,7 +5,7 @@ exports[`SummaryViewModel Check answers (0 items) should use correct summary lab
|
|
|
5
5
|
{
|
|
6
6
|
"label": "Pizza",
|
|
7
7
|
"name": "pizza",
|
|
8
|
-
"title": "
|
|
8
|
+
"title": "Pizza",
|
|
9
9
|
"value": "",
|
|
10
10
|
},
|
|
11
11
|
{
|
|
@@ -22,8 +22,8 @@ exports[`SummaryViewModel Check answers (1 item) should use correct summary labe
|
|
|
22
22
|
{
|
|
23
23
|
"label": "Pizza",
|
|
24
24
|
"name": "pizza",
|
|
25
|
-
"title": "Pizza
|
|
26
|
-
"value": "You added 1
|
|
25
|
+
"title": "Pizza",
|
|
26
|
+
"value": "You have added 1 answer",
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"label": "How would you like to receive your pizza?",
|
|
@@ -39,8 +39,8 @@ exports[`SummaryViewModel Check answers (2 items) should use correct summary lab
|
|
|
39
39
|
{
|
|
40
40
|
"label": "Pizza",
|
|
41
41
|
"name": "pizza",
|
|
42
|
-
"title": "
|
|
43
|
-
"value": "You added 2
|
|
42
|
+
"title": "Pizza",
|
|
43
|
+
"value": "You have added 2 answers",
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"label": "How would you like to receive your pizza?",
|
|
@@ -149,7 +149,7 @@ describe('RepeatPageController', () => {
|
|
|
149
149
|
description: 'No items',
|
|
150
150
|
list: [] satisfies RepeatListState,
|
|
151
151
|
viewModel: {
|
|
152
|
-
pageTitle: 'You have added 0
|
|
152
|
+
pageTitle: 'You have added 0 answers',
|
|
153
153
|
showTitle: true,
|
|
154
154
|
sectionTitle: 'Food'
|
|
155
155
|
}
|
|
@@ -164,7 +164,7 @@ describe('RepeatPageController', () => {
|
|
|
164
164
|
}
|
|
165
165
|
] satisfies RepeatListState,
|
|
166
166
|
viewModel: {
|
|
167
|
-
pageTitle: 'You have added 1
|
|
167
|
+
pageTitle: 'You have added 1 answer',
|
|
168
168
|
showTitle: true,
|
|
169
169
|
sectionTitle: 'Food'
|
|
170
170
|
}
|
|
@@ -184,7 +184,7 @@ describe('RepeatPageController', () => {
|
|
|
184
184
|
}
|
|
185
185
|
] satisfies RepeatListState,
|
|
186
186
|
viewModel: {
|
|
187
|
-
pageTitle: 'You have added 2
|
|
187
|
+
pageTitle: 'You have added 2 answers',
|
|
188
188
|
showTitle: true,
|
|
189
189
|
sectionTitle: 'Food'
|
|
190
190
|
}
|
|
@@ -207,7 +207,7 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
207
207
|
) => {
|
|
208
208
|
const { path, repeat } = this
|
|
209
209
|
const { query } = request
|
|
210
|
-
const { schema
|
|
210
|
+
const { schema } = repeat
|
|
211
211
|
const { state } = context
|
|
212
212
|
|
|
213
213
|
const list = this.getListFromState(state)
|
|
@@ -232,7 +232,7 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
232
232
|
// Show error if repeat limits apply
|
|
233
233
|
if (hasErrorMin || hasErrorMax) {
|
|
234
234
|
const count = hasErrorMax ? schema.max : schema.min
|
|
235
|
-
const itemTitle =
|
|
235
|
+
const itemTitle = `answer${count === 1 ? '' : 's'}`
|
|
236
236
|
|
|
237
237
|
context.errors = [
|
|
238
238
|
{
|
|
@@ -295,9 +295,9 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
295
295
|
...viewModel,
|
|
296
296
|
context,
|
|
297
297
|
backLink: this.getBackLink(request, context),
|
|
298
|
-
pageTitle:
|
|
298
|
+
pageTitle: 'Are you sure you want to remove this answer?',
|
|
299
299
|
itemTitle: `${title} ${list.indexOf(item) + 1}`,
|
|
300
|
-
buttonConfirm: { text:
|
|
300
|
+
buttonConfirm: { text: 'Remove' },
|
|
301
301
|
buttonCancel: { text: 'Cancel' }
|
|
302
302
|
} satisfies ItemDeletePageViewModel)
|
|
303
303
|
}
|
|
@@ -431,11 +431,13 @@ export class RepeatPageController extends QuestionPageController {
|
|
|
431
431
|
})
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
const unit = count === 1 ? 'answer' : 'answers'
|
|
435
|
+
|
|
434
436
|
return {
|
|
435
437
|
...this.viewModel,
|
|
436
438
|
backLink: this.getBackLink(request, context),
|
|
437
439
|
repeatTitle: title,
|
|
438
|
-
pageTitle: `You have added ${count} ${
|
|
440
|
+
pageTitle: `You have added ${count} ${unit}`,
|
|
439
441
|
showTitle: true,
|
|
440
442
|
context,
|
|
441
443
|
errors,
|
|
@@ -20,7 +20,7 @@ export function getPaymentApiKey(isLivePayment, formId) {
|
|
|
20
20
|
|
|
21
21
|
if (!apiKeyValue) {
|
|
22
22
|
throw new Error(
|
|
23
|
-
`Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`
|
|
23
|
+
`[payment] Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`
|
|
24
24
|
)
|
|
25
25
|
}
|
|
26
26
|
return apiKeyValue
|
|
@@ -47,6 +47,10 @@ export class PaymentService {
|
|
|
47
47
|
delayed_capture: true
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
+
logger.info(
|
|
51
|
+
`[payment] Created payment and user taken to enter pre-auth details for paymentId=${response.payment_id}`
|
|
52
|
+
)
|
|
53
|
+
|
|
50
54
|
return {
|
|
51
55
|
paymentId: response.payment_id,
|
|
52
56
|
paymentUrl: response._links.next_url.href
|
|
@@ -77,8 +81,13 @@ export class PaymentService {
|
|
|
77
81
|
throw new Error(`Failed to get payment status: ${errorMessage}`)
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
const state = response.payload.state
|
|
85
|
+
logger.info(
|
|
86
|
+
`[payment] Got payment status for paymentId=${paymentId}: ${state.status} message:${state.message ?? 'N/A'} code:${state.code ?? 'N/A'}`
|
|
87
|
+
)
|
|
88
|
+
|
|
80
89
|
return {
|
|
81
|
-
state
|
|
90
|
+
state,
|
|
82
91
|
_links: response.payload._links,
|
|
83
92
|
email: response.payload.email,
|
|
84
93
|
paymentId: response.payload.payment_id,
|
|
@@ -114,7 +123,9 @@ export class PaymentService {
|
|
|
114
123
|
statusCode === StatusCodes.OK ||
|
|
115
124
|
statusCode === StatusCodes.NO_CONTENT
|
|
116
125
|
) {
|
|
117
|
-
logger.info(
|
|
126
|
+
logger.info(
|
|
127
|
+
`[payment] Successfully captured payment for paymentId=${paymentId}`
|
|
128
|
+
)
|
|
118
129
|
return true
|
|
119
130
|
}
|
|
120
131
|
|