@defra/forms-engine-plugin 4.7.3 → 4.9.0
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/.public/javascripts/shared.min.js +1 -1
- package/.public/javascripts/shared.min.js.map +1 -1
- package/.server/client/javascripts/map.d.ts +4 -0
- package/.server/client/javascripts/map.js +9 -1
- package/.server/client/javascripts/map.js.map +1 -1
- package/.server/server/forms/payment-v2-test.yaml +341 -0
- package/.server/server/plugins/engine/components/PaymentField.d.ts +7 -0
- package/.server/server/plugins/engine/components/PaymentField.js +58 -6
- package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +2 -0
- package/.server/server/plugins/engine/models/SummaryViewModel.js +2 -0
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +24 -2
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +10 -0
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +57 -13
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/errors.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/errors.js +2 -2
- package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -1
- package/.server/server/plugins/engine/routes/index.js +5 -2
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/routes/payment.js +6 -1
- package/.server/server/plugins/engine/routes/payment.js.map +1 -1
- package/.server/server/plugins/engine/routes/payment.test.js +3 -3
- package/.server/server/plugins/engine/routes/payment.test.js.map +1 -1
- package/.server/server/plugins/engine/services/localFormsService.js +6 -0
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
- package/.server/server/plugins/engine/views/summary.html +2 -1
- package/.server/server/plugins/map/routes/vts/esri-aerial.json +23 -0
- package/.server/server/plugins/payment/service.d.ts +2 -1
- package/.server/server/plugins/payment/service.js +11 -3
- package/.server/server/plugins/payment/service.js.map +1 -1
- package/.server/server/plugins/payment/types.d.ts +4 -0
- package/.server/server/plugins/payment/types.js +1 -0
- package/.server/server/plugins/payment/types.js.map +1 -1
- package/package.json +2 -2
- package/src/client/javascripts/map.js +10 -1
- package/src/server/forms/payment-v2-test.yaml +341 -0
- package/src/server/plugins/engine/components/PaymentField.ts +70 -6
- package/src/server/plugins/engine/models/SummaryViewModel.ts +2 -0
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +32 -1
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +99 -17
- package/src/server/plugins/engine/pageControllers/errors.ts +2 -2
- package/src/server/plugins/engine/routes/index.ts +9 -2
- package/src/server/plugins/engine/routes/payment.js +7 -1
- package/src/server/plugins/engine/services/localFormsService.js +7 -0
- package/src/server/plugins/engine/views/summary.html +2 -1
- package/src/server/plugins/map/routes/vts/esri-aerial.json +23 -0
- package/src/server/plugins/payment/service.js +13 -3
- package/src/server/plugins/payment/types.js +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":["StatusCodes","createLogger","buildPaymentInfo","convertPenceToPounds","get","post","postJson","PAYMENT_BASE_URL","PAYMENT_ENDPOINT","logger","getAuthHeaders","apiKey","Authorization","PaymentService","constructor","createPayment","amount","description","returnUrl","reference","isLivePayment","metadata","response","postToPayProvider","return_url","delayed_capture","info","payment_id","paymentId","paymentUrl","_links","next_url","href","err","error","output","payload","message","undefined","getPaymentStatus","getByType","headers","json","errorMessage","Error","JSON","stringify","state","status","code","email","capturePayment","statusCode","res","OK","NO_CONTENT","event","category","action","outcome","reason","postJsonByType","includes"],"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 {\n buildPaymentInfo,\n convertPenceToPounds\n} from '~/src/server/plugins/engine/routes/payment-helper.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 {boolean} isLivePayment\n * @param {{ formId: string, slug: string } | undefined } metadata\n */\n async createPayment(\n amount,\n description,\n returnUrl,\n reference,\n isLivePayment,\n metadata\n ) {\n try {\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 buildPaymentInfo(\n 'create-payment',\n 'success',\n `amount=${convertPenceToPounds(amount)}`,\n isLivePayment,\n response.payment_id\n ),\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 } catch (err) {\n const error =\n /** @type {{ output?: { payload?: any }, message?: any }} */ (err)\n if (isLivePayment) {\n logger.error(\n error.output?.payload ?? error.message,\n `[payment] Failed to create payment session for reference ${reference}`\n )\n }\n }\n return undefined\n }\n\n /**\n * @param {string} paymentId\n * @param {boolean} isLivePayment\n * @returns {Promise<GetPaymentResponse>}\n */\n async getPaymentStatus(paymentId, isLivePayment) {\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 buildPaymentInfo(\n 'get-payment-status',\n state.status === 'capturable' || state.status === 'success'\n ? 'success'\n : 'failure',\n `status:${state.status} code:${state.code ?? 'N/A'} message:${state.message ?? 'N/A'}`,\n isLivePayment,\n paymentId\n ),\n `[payment] Got payment status for paymentId=${paymentId}: status=${state.status}`\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 * @param {number} amount\n * @returns {Promise<boolean>}\n */\n async capturePayment(paymentId, amount) {\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 {\n event: {\n category: 'payment',\n action: 'capture-payment',\n outcome: 'success',\n reason: `amount=${convertPenceToPounds(amount)}`,\n reference: paymentId\n }\n },\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 if (!error.message.includes('401 Unauthorized')) {\n logger.error(\n error,\n `[payment] Error creating payment for reference=${payload.reference}: ${error.message}`\n )\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":[]}
|
|
1
|
+
{"version":3,"file":"service.js","names":["StatusCodes","createLogger","buildPaymentInfo","convertPenceToPounds","get","post","postJson","PAYMENT_BASE_URL","PAYMENT_ENDPOINT","logger","getAuthHeaders","apiKey","Authorization","PaymentService","constructor","createPayment","amount","description","returnUrl","reference","isLivePayment","metadata","email","payload","return_url","delayed_capture","response","postToPayProvider","info","payment_id","paymentId","paymentUrl","_links","next_url","href","err","error","output","message","undefined","getPaymentStatus","getByType","headers","json","errorMessage","Error","JSON","stringify","state","status","code","capturePayment","statusCode","res","OK","NO_CONTENT","event","category","action","outcome","reason","postJsonByType","includes"],"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 {\n buildPaymentInfo,\n convertPenceToPounds\n} from '~/src/server/plugins/engine/routes/payment-helper.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 {boolean} isLivePayment\n * @param {{ formId: string, slug: string } | undefined } metadata\n * @param {string} [email] - optional email to prepopulate on GOV.UK Pay\n */\n async createPayment(\n amount,\n description,\n returnUrl,\n reference,\n isLivePayment,\n metadata,\n email\n ) {\n try {\n /** @type {CreatePaymentRequest} */\n const payload = {\n amount,\n description,\n reference,\n metadata,\n return_url: returnUrl,\n delayed_capture: true\n }\n\n // Prepopulate email on GOV.UK Pay if provided\n if (email) {\n payload.email = email\n }\n\n const response = await this.postToPayProvider(payload)\n\n logger.info(\n buildPaymentInfo(\n 'create-payment',\n 'success',\n `amount=${convertPenceToPounds(amount)}`,\n isLivePayment,\n response.payment_id\n ),\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 } catch (err) {\n const error =\n /** @type {{ output?: { payload?: any }, message?: any }} */ (err)\n if (isLivePayment) {\n logger.error(\n error.output?.payload ?? error.message,\n `[payment] Failed to create payment session for reference ${reference}`\n )\n }\n }\n return undefined\n }\n\n /**\n * @param {string} paymentId\n * @param {boolean} isLivePayment\n * @returns {Promise<GetPaymentResponse>}\n */\n async getPaymentStatus(paymentId, isLivePayment) {\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 buildPaymentInfo(\n 'get-payment-status',\n state.status === 'capturable' || state.status === 'success'\n ? 'success'\n : 'failure',\n `status:${state.status} code:${state.code ?? 'N/A'} message:${state.message ?? 'N/A'}`,\n isLivePayment,\n paymentId\n ),\n `[payment] Got payment status for paymentId=${paymentId}: status=${state.status}`\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 * @param {number} amount\n * @returns {Promise<boolean>}\n */\n async capturePayment(paymentId, amount) {\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 {\n event: {\n category: 'payment',\n action: 'capture-payment',\n outcome: 'success',\n reason: `amount=${convertPenceToPounds(amount)}`,\n reference: paymentId\n }\n },\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 if (!error.message.includes('401 Unauthorized')) {\n logger.error(\n error,\n `[payment] Error creating payment for reference=${payload.reference}: ${error.message}`\n )\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":"AAAA,SAASA,WAAW,QAAQ,mBAAmB;AAE/C,SAASC,YAAY;AACrB,SACEC,gBAAgB,EAChBC,oBAAoB;AAEtB,SAASC,GAAG,EAAEC,IAAI,EAAEC,QAAQ;AAE5B,MAAMC,gBAAgB,GAAG,2CAA2C;AACpE,MAAMC,gBAAgB,GAAG,cAAc;AAEvC,MAAMC,MAAM,GAAGR,YAAY,CAAC,CAAC;;AAE7B;AACA;AACA;AACA;AACA,SAASS,cAAcA,CAACC,MAAM,EAAE;EAC9B,OAAO;IACLC,aAAa,EAAE,UAAUD,MAAM;EACjC,CAAC;AACH;AAEA,OAAO,MAAME,cAAc,CAAC;EAC1B;EACA,CAACF,MAAM;;EAEP;AACF;AACA;EACEG,WAAWA,CAACH,MAAM,EAAE;IAClB,IAAI,CAAC,CAACA,MAAM,GAAGA,MAAM;EACvB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE,MAAMI,aAAaA,CACjBC,MAAM,EACNC,WAAW,EACXC,SAAS,EACTC,SAAS,EACTC,aAAa,EACbC,QAAQ,EACRC,KAAK,EACL;IACA,IAAI;MACF;MACA,MAAMC,OAAO,GAAG;QACdP,MAAM;QACNC,WAAW;QACXE,SAAS;QACTE,QAAQ;QACRG,UAAU,EAAEN,SAAS;QACrBO,eAAe,EAAE;MACnB,CAAC;;MAED;MACA,IAAIH,KAAK,EAAE;QACTC,OAAO,CAACD,KAAK,GAAGA,KAAK;MACvB;MAEA,MAAMI,QAAQ,GAAG,MAAM,IAAI,CAACC,iBAAiB,CAACJ,OAAO,CAAC;MAEtDd,MAAM,CAACmB,IAAI,CACT1B,gBAAgB,CACd,gBAAgB,EAChB,SAAS,EACT,UAAUC,oBAAoB,CAACa,MAAM,CAAC,EAAE,EACxCI,aAAa,EACbM,QAAQ,CAACG,UACX,CAAC,EACD,oFAAoFH,QAAQ,CAACG,UAAU,EACzG,CAAC;MAED,OAAO;QACLC,SAAS,EAAEJ,QAAQ,CAACG,UAAU;QAC9BE,UAAU,EAAEL,QAAQ,CAACM,MAAM,CAACC,QAAQ,CAACC;MACvC,CAAC;IACH,CAAC,CAAC,OAAOC,GAAG,EAAE;MACZ,MAAMC,KAAK,GACT,4DAA8DD,GAAI;MACpE,IAAIf,aAAa,EAAE;QACjBX,MAAM,CAAC2B,KAAK,CACVA,KAAK,CAACC,MAAM,EAAEd,OAAO,IAAIa,KAAK,CAACE,OAAO,EACtC,4DAA4DnB,SAAS,EACvE,CAAC;MACH;IACF;IACA,OAAOoB,SAAS;EAClB;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMC,gBAAgBA,CAACV,SAAS,EAAEV,aAAa,EAAE;IAC/C,MAAMqB,SAAS,GAAG,gDAAkDrC,GAAI;IAExE,IAAI;MACF,MAAMsB,QAAQ,GAAG,MAAMe,SAAS,CAC9B,GAAGlC,gBAAgB,GAAGC,gBAAgB,IAAIsB,SAAS,EAAE,EACrD;QACEY,OAAO,EAAEhC,cAAc,CAAC,IAAI,CAAC,CAACC,MAAM,CAAC;QACrCgC,IAAI,EAAE;MACR,CACF,CAAC;MAED,IAAIjB,QAAQ,CAACU,KAAK,EAAE;QAClB,MAAMQ,YAAY,GAChBlB,QAAQ,CAACU,KAAK,YAAYS,KAAK,GAC3BnB,QAAQ,CAACU,KAAK,CAACE,OAAO,GACtBQ,IAAI,CAACC,SAAS,CAACrB,QAAQ,CAACU,KAAK,CAAC;QACpC,MAAM,IAAIS,KAAK,CAAC,iCAAiCD,YAAY,EAAE,CAAC;MAClE;MAEA,MAAMI,KAAK,GAAGtB,QAAQ,CAACH,OAAO,CAACyB,KAAK;MACpCvC,MAAM,CAACmB,IAAI,CACT1B,gBAAgB,CACd,oBAAoB,EACpB8C,KAAK,CAACC,MAAM,KAAK,YAAY,IAAID,KAAK,CAACC,MAAM,KAAK,SAAS,GACvD,SAAS,GACT,SAAS,EACb,UAAUD,KAAK,CAACC,MAAM,SAASD,KAAK,CAACE,IAAI,IAAI,KAAK,YAAYF,KAAK,CAACV,OAAO,IAAI,KAAK,EAAE,EACtFlB,aAAa,EACbU,SACF,CAAC,EACD,8CAA8CA,SAAS,YAAYkB,KAAK,CAACC,MAAM,EACjF,CAAC;MAED,OAAO;QACLD,KAAK;QACLhB,MAAM,EAAEN,QAAQ,CAACH,OAAO,CAACS,MAAM;QAC/BV,KAAK,EAAEI,QAAQ,CAACH,OAAO,CAACD,KAAK;QAC7BQ,SAAS,EAAEJ,QAAQ,CAACH,OAAO,CAACM,UAAU;QACtCb,MAAM,EAAEU,QAAQ,CAACH,OAAO,CAACP;MAC3B,CAAC;IACH,CAAC,CAAC,OAAOmB,GAAG,EAAE;MACZ,MAAMC,KAAK,GAAG,oBAAsBD,GAAI;MACxC1B,MAAM,CAAC2B,KAAK,CACVA,KAAK,EACL,wDAAwDN,SAAS,KAAKM,KAAK,CAACE,OAAO,EACrF,CAAC;MACD,MAAMH,GAAG;IACX;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;EACE,MAAMgB,cAAcA,CAACrB,SAAS,EAAEd,MAAM,EAAE;IACtC,IAAI;MACF,MAAMU,QAAQ,GAAG,MAAMrB,IAAI,CACzB,GAAGE,gBAAgB,GAAGC,gBAAgB,IAAIsB,SAAS,UAAU,EAC7D;QACEY,OAAO,EAAEhC,cAAc,CAAC,IAAI,CAAC,CAACC,MAAM;MACtC,CACF,CAAC;MAED,MAAMyC,UAAU,GAAG1B,QAAQ,CAAC2B,GAAG,CAACD,UAAU;MAE1C,IACEA,UAAU,KAAKpD,WAAW,CAACsD,EAAE,IAC7BF,UAAU,KAAKpD,WAAW,CAACuD,UAAU,EACrC;QACA9C,MAAM,CAACmB,IAAI,CACT;UACE4B,KAAK,EAAE;YACLC,QAAQ,EAAE,SAAS;YACnBC,MAAM,EAAE,iBAAiB;YACzBC,OAAO,EAAE,SAAS;YAClBC,MAAM,EAAE,UAAUzD,oBAAoB,CAACa,MAAM,CAAC,EAAE;YAChDG,SAAS,EAAEW;UACb;QACF,CAAC,EACD,yDAAyDA,SAAS,EACpE,CAAC;QACD,OAAO,IAAI;MACb;MAEArB,MAAM,CAAC2B,KAAK,CACV,0CAA0CN,SAAS,UAAUsB,UAAU,EACzE,CAAC;MACD,OAAO,KAAK;IACd,CAAC,CAAC,OAAOjB,GAAG,EAAE;MACZ,MAAMC,KAAK,GAAG,oBAAsBD,GAAI;MACxC1B,MAAM,CAAC2B,KAAK,CACVA,KAAK,EACL,mDAAmDN,SAAS,KAAKM,KAAK,CAACE,OAAO,EAChF,CAAC;MACD,MAAMH,GAAG;IACX;EACF;;EAEA;AACF;AACA;EACE,MAAMR,iBAAiBA,CAACJ,OAAO,EAAE;IAC/B,MAAMsC,cAAc,GAClB,qDAAuDvD,QAAS;IAElE,IAAI;MACF,MAAMoB,QAAQ,GAAG,MAAMmC,cAAc,CACnC,GAAGtD,gBAAgB,GAAGC,gBAAgB,EAAE,EACxC;QACEe,OAAO;QACPmB,OAAO,EAAEhC,cAAc,CAAC,IAAI,CAAC,CAACC,MAAM;MACtC,CACF,CAAC;MAED,IAAIe,QAAQ,CAACH,OAAO,EAAEyB,KAAK,CAACC,MAAM,KAAK,SAAS,EAAE;QAChD,MAAM,IAAIJ,KAAK,CACb,0CAA0CtB,OAAO,CAACJ,SAAS,EAC7D,CAAC;MACH;MAEA,OAAOO,QAAQ,CAACH,OAAO;IACzB,CAAC,CAAC,OAAOY,GAAG,EAAE;MACZ,MAAMC,KAAK,GAAG,oBAAsBD,GAAI;MACxC,IAAI,CAACC,KAAK,CAACE,OAAO,CAACwB,QAAQ,CAAC,kBAAkB,CAAC,EAAE;QAC/CrD,MAAM,CAAC2B,KAAK,CACVA,KAAK,EACL,kDAAkDb,OAAO,CAACJ,SAAS,KAAKiB,KAAK,CAACE,OAAO,EACvF,CAAC;MACH;MACA,MAAMH,GAAG;IACX;EACF;AACF;;AAEA;AACA;AACA","ignoreList":[]}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* @property {string} return_url - URL to redirect the user to after payment
|
|
21
21
|
* @property {boolean} [delayed_capture] - Whether to delay capturing the payment
|
|
22
22
|
* @property {{ formId: string, slug: string }} [metadata] - Additional metadata for the payment
|
|
23
|
+
* @property {string} [email] - Email to prepopulate on GOV.UK Pay (max 254 chars)
|
|
23
24
|
*/
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/payment/types.js"],"sourcesContent":["/**\n * @typedef {object} PaymentResponseState\n * @property {'created' | 'started' | 'submitted' | 'capturable' | 'success' | 'failed' | 'cancelled' | 'error'} status - Current status of the payment\n * @property {boolean} finished - Whether the payment process has completed\n * @property {string} [message] - Human-readable message about the payment state\n * @property {string} [code] - Error or status code for the payment state\n */\n\n/**\n * @typedef {object} PaymentLink\n * @property {string} href - URL of the linked resource\n * @property {string} method - HTTP method to use for the link\n */\n\n/**\n * @typedef {object} CreatePaymentRequest\n * @property {number} amount - Payment amount in pence\n * @property {string} reference - Unique reference for the payment\n * @property {string} description - Human-readable description of the payment\n * @property {string} return_url - URL to redirect the user to after payment\n * @property {boolean} [delayed_capture] - Whether to delay capturing the payment\n * @property {{ formId: string, slug: string }} [metadata] - Additional metadata for the payment\n */\n\n/**\n * @typedef {object} CreatePaymentResponse\n * @property {string} payment_id - Unique identifier for the created payment\n * @property {PaymentResponseState} state - Current state of the payment\n * @property {{ next_url: PaymentLink }} _links - HATEOAS links for the payment\n */\n\n/**\n * Base response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint\n * @typedef {object} GetPaymentResponseBase\n * @property {PaymentResponseState} state - Current state of the payment\n * @property {{ self: PaymentLink, next_url?: PaymentLink }} _links - HATEOAS links for the payment\n * @property {string} [email] - The paying user's email address\n */\n\n/**\n * Response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint - not underscore in property name\n * @typedef {object} GetPaymentApiResponsePaymentProp\n * @property {string} payment_id - Unique identifier for the payment\n * @property {number} amount - amount of the payment\n */\n\n/**\n * Response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint\n * @typedef {GetPaymentResponseBase & GetPaymentApiResponsePaymentProp} GetPaymentApiResponse\n */\n\n/**\n * Response returned from getPaymentStatus - subtley different from GetPaymentApiResponse\n * @typedef {object} GetPaymentResponsePaymentProp\n * @property {string} paymentId - Unique identifier for the payment - note no underscore in property name\n * @property {number} amount - amount of the payment\n */\n\n/**\n * Response returned from getPaymentStatus - subtley different from GetPaymentApiResponse\n * @typedef {GetPaymentResponseBase & GetPaymentResponsePaymentProp} GetPaymentResponse\n */\n\n/**\n * Payment session data stored when dispatching to GOV.UK Pay\n * @typedef {object} PaymentSessionData\n * @property {string} uuid - unique identifier for this payment attempt\n * @property {string} formId - id of the form\n * @property {string} reference - form reference number\n * @property {number} amount - amount in pounds\n * @property {string} description - payment description\n * @property {string} paymentId - GOV.UK Pay payment ID\n * @property {string} componentName - name of the PaymentField component\n * @property {string} returnUrl - URL to redirect to after successful payment\n * @property {string} failureUrl - URL to redirect to after failed/cancelled payment\n * @property {boolean} isLivePayment - whether the payment is using live API key\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/payment/types.js"],"sourcesContent":["/**\n * @typedef {object} PaymentResponseState\n * @property {'created' | 'started' | 'submitted' | 'capturable' | 'success' | 'failed' | 'cancelled' | 'error'} status - Current status of the payment\n * @property {boolean} finished - Whether the payment process has completed\n * @property {string} [message] - Human-readable message about the payment state\n * @property {string} [code] - Error or status code for the payment state\n */\n\n/**\n * @typedef {object} PaymentLink\n * @property {string} href - URL of the linked resource\n * @property {string} method - HTTP method to use for the link\n */\n\n/**\n * @typedef {object} CreatePaymentRequest\n * @property {number} amount - Payment amount in pence\n * @property {string} reference - Unique reference for the payment\n * @property {string} description - Human-readable description of the payment\n * @property {string} return_url - URL to redirect the user to after payment\n * @property {boolean} [delayed_capture] - Whether to delay capturing the payment\n * @property {{ formId: string, slug: string }} [metadata] - Additional metadata for the payment\n * @property {string} [email] - Email to prepopulate on GOV.UK Pay (max 254 chars)\n */\n\n/**\n * @typedef {object} CreatePaymentResponse\n * @property {string} payment_id - Unique identifier for the created payment\n * @property {PaymentResponseState} state - Current state of the payment\n * @property {{ next_url: PaymentLink }} _links - HATEOAS links for the payment\n */\n\n/**\n * Base response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint\n * @typedef {object} GetPaymentResponseBase\n * @property {PaymentResponseState} state - Current state of the payment\n * @property {{ self: PaymentLink, next_url?: PaymentLink }} _links - HATEOAS links for the payment\n * @property {string} [email] - The paying user's email address\n */\n\n/**\n * Response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint - not underscore in property name\n * @typedef {object} GetPaymentApiResponsePaymentProp\n * @property {string} payment_id - Unique identifier for the payment\n * @property {number} amount - amount of the payment\n */\n\n/**\n * Response from GOV.UK Pay GET /v1/payments/{PAYMENT_ID} endpoint\n * @typedef {GetPaymentResponseBase & GetPaymentApiResponsePaymentProp} GetPaymentApiResponse\n */\n\n/**\n * Response returned from getPaymentStatus - subtley different from GetPaymentApiResponse\n * @typedef {object} GetPaymentResponsePaymentProp\n * @property {string} paymentId - Unique identifier for the payment - note no underscore in property name\n * @property {number} amount - amount of the payment\n */\n\n/**\n * Response returned from getPaymentStatus - subtley different from GetPaymentApiResponse\n * @typedef {GetPaymentResponseBase & GetPaymentResponsePaymentProp} GetPaymentResponse\n */\n\n/**\n * Payment session data stored when dispatching to GOV.UK Pay\n * @typedef {object} PaymentSessionData\n * @property {string} uuid - unique identifier for this payment attempt\n * @property {string} formId - id of the form\n * @property {string} reference - form reference number\n * @property {number} amount - amount in pounds\n * @property {string} description - payment description\n * @property {string} paymentId - GOV.UK Pay payment ID\n * @property {string} componentName - name of the PaymentField component\n * @property {string} returnUrl - URL to redirect to after successful payment\n * @property {string} failureUrl - URL to redirect to after failed/cancelled payment\n * @property {boolean} isLivePayment - whether the payment is using live API key\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/forms-engine-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "Defra forms engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"license": "SEE LICENSE IN LICENSE",
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@defra/forms-model": "^3.0.
|
|
89
|
+
"@defra/forms-model": "^3.0.648",
|
|
90
90
|
"@defra/hapi-tracing": "^1.29.0",
|
|
91
91
|
"@defra/interactive-map": "^0.0.17-alpha",
|
|
92
92
|
"@elastic/ecs-pino-format": "^1.5.0",
|
|
@@ -13,7 +13,8 @@ const COMPANY_SYMBOL_CODE = 169
|
|
|
13
13
|
const defaultData = {
|
|
14
14
|
VTS_OUTDOOR_URL: '/api/maps/vts/OS_VTS_3857_Outdoor.json',
|
|
15
15
|
VTS_DARK_URL: '/api/maps/vts/OS_VTS_3857_Dark.json',
|
|
16
|
-
VTS_BLACK_AND_WHITE_URL: '/api/maps/vts/OS_VTS_3857_Black_and_White.json'
|
|
16
|
+
VTS_BLACK_AND_WHITE_URL: '/api/maps/vts/OS_VTS_3857_Black_and_White.json',
|
|
17
|
+
VTS_AERIAL_URL: '/api/maps/vts/esri-aerial.json'
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -317,6 +318,13 @@ export function createMap(mapId, initConfig, mapsConfig) {
|
|
|
317
318
|
logo: `${assetPath}/interactive-map/assets/images/os-logo-black.svg`,
|
|
318
319
|
logoAltText,
|
|
319
320
|
attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
id: 'aerial',
|
|
324
|
+
label: 'Aerial',
|
|
325
|
+
url: data.VTS_AERIAL_URL,
|
|
326
|
+
thumbnail: `${assetPath}/interactive-map/assets/images/aerial-map-thumb.jpg`,
|
|
327
|
+
attribution: `Tiles ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Esri — Source: Esri, Maxar, Earthstar Geographics, and the GIS User Community ${new Date().getFullYear()}`
|
|
320
328
|
}
|
|
321
329
|
]
|
|
322
330
|
}),
|
|
@@ -388,6 +396,7 @@ export function centerMap(map, mapProvider, center) {
|
|
|
388
396
|
* @property {string} VTS_OUTDOOR_URL - the outdoor tile URL
|
|
389
397
|
* @property {string} VTS_DARK_URL - the dark tile URL
|
|
390
398
|
* @property {string} VTS_BLACK_AND_WHITE_URL - the black and white tile URL
|
|
399
|
+
* @property {string} VTS_AERIAL_URL - the aerial tile URL
|
|
391
400
|
*/
|
|
392
401
|
|
|
393
402
|
/**
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Based on "Apply for a lock and weir fishing permit" production form.
|
|
3
|
+
# Extended with duration and site access to demonstrate complex compound conditions.
|
|
4
|
+
# Pricing matrix: duration x permit type x site access
|
|
5
|
+
schema: 2
|
|
6
|
+
name: Apply for a lock and weir fishing permit (v2 payment test)
|
|
7
|
+
engine: V2
|
|
8
|
+
declaration: I apply for permission to fish at the sites listed on this application for the duration of the permit, subject to the normal closed seasons.
|
|
9
|
+
startPage: '/fishing-sites'
|
|
10
|
+
options:
|
|
11
|
+
showReferenceNumber: true
|
|
12
|
+
pages:
|
|
13
|
+
- title: Fishing sites
|
|
14
|
+
path: '/fishing-sites'
|
|
15
|
+
components:
|
|
16
|
+
- id: '1fb84634-86f2-477b-affa-c7ace61aec26'
|
|
17
|
+
type: Markdown
|
|
18
|
+
content: "The fishing sites include:\n\n* Buscot\n* Grafton\n* Rushey\n* Sandford\n* Abingdon\n* Benson\n* Goring\n* Hurley\n* Bell Weir\n* Molesey"
|
|
19
|
+
options: {}
|
|
20
|
+
schema: {}
|
|
21
|
+
name: fishingSites
|
|
22
|
+
next: []
|
|
23
|
+
- title: Contact details
|
|
24
|
+
path: '/contact-details'
|
|
25
|
+
components:
|
|
26
|
+
- id: '3598ed25-1b9a-4ce6-8432-5676063b96ec'
|
|
27
|
+
type: TextField
|
|
28
|
+
title: What is your full name?
|
|
29
|
+
name: fullName
|
|
30
|
+
shortDescription: Full name
|
|
31
|
+
options:
|
|
32
|
+
required: true
|
|
33
|
+
schema: {}
|
|
34
|
+
- id: '9c4f0158-8f87-4cbd-a3a1-960166f015e4'
|
|
35
|
+
type: TelephoneNumberField
|
|
36
|
+
title: What is your phone number?
|
|
37
|
+
name: phoneNumber
|
|
38
|
+
shortDescription: Phone number
|
|
39
|
+
options:
|
|
40
|
+
required: true
|
|
41
|
+
schema: {}
|
|
42
|
+
- id: '9b83dc1e-e385-4cd3-b642-dcc247f3fc89'
|
|
43
|
+
type: EmailAddressField
|
|
44
|
+
title: What is your email address?
|
|
45
|
+
name: emailAddress
|
|
46
|
+
shortDescription: Email address
|
|
47
|
+
options:
|
|
48
|
+
required: true
|
|
49
|
+
next: []
|
|
50
|
+
- title: ''
|
|
51
|
+
path: '/rod-licence-number'
|
|
52
|
+
components:
|
|
53
|
+
- id: '22405838-becd-48ab-b984-c3c886822412'
|
|
54
|
+
type: TextField
|
|
55
|
+
title: What is your rod licence number (current or previous)?
|
|
56
|
+
name: rodLicenceNumber
|
|
57
|
+
shortDescription: Rod licence number
|
|
58
|
+
hint: The permit must be used in conjunction with a valid rod licence.
|
|
59
|
+
options:
|
|
60
|
+
required: true
|
|
61
|
+
schema: {}
|
|
62
|
+
next: []
|
|
63
|
+
- title: ''
|
|
64
|
+
path: '/permit-duration'
|
|
65
|
+
components:
|
|
66
|
+
- id: 'a1a1a1a1-1111-4aaa-aaaa-000000000001'
|
|
67
|
+
type: RadiosField
|
|
68
|
+
title: What duration of permit do you need?
|
|
69
|
+
name: permitDuration
|
|
70
|
+
shortDescription: Permit duration
|
|
71
|
+
options:
|
|
72
|
+
required: true
|
|
73
|
+
list: 'b1b1b1b1-1111-4bbb-bbbb-000000000001'
|
|
74
|
+
next: []
|
|
75
|
+
- title: ''
|
|
76
|
+
path: '/what-kind-of-permit-do-you-require'
|
|
77
|
+
components:
|
|
78
|
+
- id: 'f7663ac4-61e7-4b64-a157-70f002818493'
|
|
79
|
+
type: RadiosField
|
|
80
|
+
title: What kind of permit do you require?
|
|
81
|
+
name: permitType
|
|
82
|
+
shortDescription: Permit type
|
|
83
|
+
options:
|
|
84
|
+
required: true
|
|
85
|
+
list: 'aac4ee00-fb82-4a37-88ad-b9e10ded92e9'
|
|
86
|
+
next: []
|
|
87
|
+
- title: ''
|
|
88
|
+
path: '/site-access'
|
|
89
|
+
condition: 'c1c1c1c1-6666-4ccc-cccc-000000000005'
|
|
90
|
+
components:
|
|
91
|
+
- id: 'a1a1a1a1-1111-4aaa-aaaa-000000000003'
|
|
92
|
+
type: RadiosField
|
|
93
|
+
title: Which site access do you need?
|
|
94
|
+
name: siteAccess
|
|
95
|
+
shortDescription: Site access
|
|
96
|
+
hint: Single site permits are valid for one named site only.
|
|
97
|
+
options:
|
|
98
|
+
required: true
|
|
99
|
+
list: 'b1b1b1b1-1111-4bbb-bbbb-000000000002'
|
|
100
|
+
next: []
|
|
101
|
+
- title: ''
|
|
102
|
+
path: '/payment-required'
|
|
103
|
+
components:
|
|
104
|
+
- id: '6522e3f7-f414-42e5-9dbf-84e5868fbbd3'
|
|
105
|
+
type: PaymentField
|
|
106
|
+
title: Payment required
|
|
107
|
+
name: fishingPermitPayment
|
|
108
|
+
options:
|
|
109
|
+
required: true
|
|
110
|
+
amount: 0
|
|
111
|
+
description: Lock and weir fishing permit
|
|
112
|
+
emailField: emailAddress
|
|
113
|
+
conditionalAmounts:
|
|
114
|
+
# === 3-way: 12-month + type + site (most specific first) ===
|
|
115
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000001'
|
|
116
|
+
amount: 38
|
|
117
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000002'
|
|
118
|
+
amount: 25
|
|
119
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000003'
|
|
120
|
+
amount: 24
|
|
121
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000004'
|
|
122
|
+
amount: 16
|
|
123
|
+
# === 2-way: 8-day + type (site doesn't matter) ===
|
|
124
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000005'
|
|
125
|
+
amount: 12
|
|
126
|
+
- condition: 'd1d1d1d1-8888-4ddd-dddd-000000000006'
|
|
127
|
+
amount: 8
|
|
128
|
+
# === Simple: 1-day flat rate ===
|
|
129
|
+
- condition: 'c1c1c1c1-6666-4ccc-cccc-000000000003'
|
|
130
|
+
amount: 5
|
|
131
|
+
# === Simple: Junior free ===
|
|
132
|
+
- condition: 'c1c1c1c1-6666-4ccc-cccc-000000000004'
|
|
133
|
+
amount: 0
|
|
134
|
+
next: []
|
|
135
|
+
- title: ''
|
|
136
|
+
path: '/summary'
|
|
137
|
+
controller: SummaryPageController
|
|
138
|
+
conditions:
|
|
139
|
+
# =================================================================
|
|
140
|
+
# Simple atomic conditions
|
|
141
|
+
# =================================================================
|
|
142
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000001'
|
|
143
|
+
displayName: Is 12-month permit
|
|
144
|
+
items:
|
|
145
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000001'
|
|
146
|
+
componentId: 'a1a1a1a1-1111-4aaa-aaaa-000000000001'
|
|
147
|
+
operator: is
|
|
148
|
+
type: ListItemRef
|
|
149
|
+
value:
|
|
150
|
+
itemId: 'f1f1f1f1-1111-4fff-ffff-000000000001'
|
|
151
|
+
listId: 'b1b1b1b1-1111-4bbb-bbbb-000000000001'
|
|
152
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000002'
|
|
153
|
+
displayName: Is 8-day permit
|
|
154
|
+
items:
|
|
155
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000002'
|
|
156
|
+
componentId: 'a1a1a1a1-1111-4aaa-aaaa-000000000001'
|
|
157
|
+
operator: is
|
|
158
|
+
type: ListItemRef
|
|
159
|
+
value:
|
|
160
|
+
itemId: 'f1f1f1f1-1111-4fff-ffff-000000000002'
|
|
161
|
+
listId: 'b1b1b1b1-1111-4bbb-bbbb-000000000001'
|
|
162
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000003'
|
|
163
|
+
displayName: Is 1-day permit
|
|
164
|
+
items:
|
|
165
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000003'
|
|
166
|
+
componentId: 'a1a1a1a1-1111-4aaa-aaaa-000000000001'
|
|
167
|
+
operator: is
|
|
168
|
+
type: ListItemRef
|
|
169
|
+
value:
|
|
170
|
+
itemId: 'f1f1f1f1-1111-4fff-ffff-000000000003'
|
|
171
|
+
listId: 'b1b1b1b1-1111-4bbb-bbbb-000000000001'
|
|
172
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000004'
|
|
173
|
+
displayName: Is Junior permit type
|
|
174
|
+
items:
|
|
175
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000004'
|
|
176
|
+
componentId: 'f7663ac4-61e7-4b64-a157-70f002818493'
|
|
177
|
+
operator: is
|
|
178
|
+
type: ListItemRef
|
|
179
|
+
value:
|
|
180
|
+
itemId: 'f1f1f1f1-2222-4fff-ffff-000000000003'
|
|
181
|
+
listId: 'aac4ee00-fb82-4a37-88ad-b9e10ded92e9'
|
|
182
|
+
- id: 'dcaa3b3d-5cbc-4be9-b5ce-b2be5c72ccd1'
|
|
183
|
+
displayName: Is Adult permit type
|
|
184
|
+
items:
|
|
185
|
+
- id: '13b5e86b-2eb5-4a6c-8ab6-9fdc35907f18'
|
|
186
|
+
componentId: 'f7663ac4-61e7-4b64-a157-70f002818493'
|
|
187
|
+
operator: is
|
|
188
|
+
type: ListItemRef
|
|
189
|
+
value:
|
|
190
|
+
itemId: 'b6034236-63cf-44af-bb6c-d5d4d3825973'
|
|
191
|
+
listId: 'aac4ee00-fb82-4a37-88ad-b9e10ded92e9'
|
|
192
|
+
- id: 'c5177318-61ec-44ec-b5c2-18c1be1f1e42'
|
|
193
|
+
displayName: Is Concession permit type
|
|
194
|
+
items:
|
|
195
|
+
- id: 'fb75f1be-d471-4020-ac13-f7a2246078bb'
|
|
196
|
+
componentId: 'f7663ac4-61e7-4b64-a157-70f002818493'
|
|
197
|
+
operator: is
|
|
198
|
+
type: ListItemRef
|
|
199
|
+
value:
|
|
200
|
+
itemId: '86baf02e-0db7-4fad-8fce-fa9a30c1b7f0'
|
|
201
|
+
listId: 'aac4ee00-fb82-4a37-88ad-b9e10ded92e9'
|
|
202
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000007'
|
|
203
|
+
displayName: Is all sites access
|
|
204
|
+
items:
|
|
205
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000007'
|
|
206
|
+
componentId: 'a1a1a1a1-1111-4aaa-aaaa-000000000003'
|
|
207
|
+
operator: is
|
|
208
|
+
type: ListItemRef
|
|
209
|
+
value:
|
|
210
|
+
itemId: 'f1f1f1f1-3333-4fff-ffff-000000000001'
|
|
211
|
+
listId: 'b1b1b1b1-1111-4bbb-bbbb-000000000002'
|
|
212
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000008'
|
|
213
|
+
displayName: Is single site access
|
|
214
|
+
items:
|
|
215
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000008'
|
|
216
|
+
componentId: 'a1a1a1a1-1111-4aaa-aaaa-000000000003'
|
|
217
|
+
operator: is
|
|
218
|
+
type: ListItemRef
|
|
219
|
+
value:
|
|
220
|
+
itemId: 'f1f1f1f1-3333-4fff-ffff-000000000002'
|
|
221
|
+
listId: 'b1b1b1b1-1111-4bbb-bbbb-000000000002'
|
|
222
|
+
# =================================================================
|
|
223
|
+
# Page visibility: site access only shown for 12-month permits
|
|
224
|
+
# =================================================================
|
|
225
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000005'
|
|
226
|
+
displayName: Is 12-month (show site access page)
|
|
227
|
+
items:
|
|
228
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000005'
|
|
229
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000001'
|
|
230
|
+
# =================================================================
|
|
231
|
+
# 2-way compound AND: duration + type
|
|
232
|
+
# =================================================================
|
|
233
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000010'
|
|
234
|
+
displayName: 12-month AND Adult
|
|
235
|
+
coordinator: and
|
|
236
|
+
items:
|
|
237
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000010'
|
|
238
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000001'
|
|
239
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000011'
|
|
240
|
+
conditionId: 'dcaa3b3d-5cbc-4be9-b5ce-b2be5c72ccd1'
|
|
241
|
+
- id: 'c1c1c1c1-6666-4ccc-cccc-000000000011'
|
|
242
|
+
displayName: 12-month AND Concession
|
|
243
|
+
coordinator: and
|
|
244
|
+
items:
|
|
245
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000012'
|
|
246
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000001'
|
|
247
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000013'
|
|
248
|
+
conditionId: 'c5177318-61ec-44ec-b5c2-18c1be1f1e42'
|
|
249
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000005'
|
|
250
|
+
displayName: 8-day AND Adult
|
|
251
|
+
coordinator: and
|
|
252
|
+
items:
|
|
253
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000014'
|
|
254
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000002'
|
|
255
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000015'
|
|
256
|
+
conditionId: 'dcaa3b3d-5cbc-4be9-b5ce-b2be5c72ccd1'
|
|
257
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000006'
|
|
258
|
+
displayName: 8-day AND Concession
|
|
259
|
+
coordinator: and
|
|
260
|
+
items:
|
|
261
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000016'
|
|
262
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000002'
|
|
263
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000017'
|
|
264
|
+
conditionId: 'c5177318-61ec-44ec-b5c2-18c1be1f1e42'
|
|
265
|
+
# =================================================================
|
|
266
|
+
# 3-way compound AND: duration + type + site (chains 2-way refs)
|
|
267
|
+
# Same pattern as "Bats chargeable use" in protected species form
|
|
268
|
+
# =================================================================
|
|
269
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000001'
|
|
270
|
+
displayName: 12-month AND Adult AND All sites
|
|
271
|
+
coordinator: and
|
|
272
|
+
items:
|
|
273
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000020'
|
|
274
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000010'
|
|
275
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000021'
|
|
276
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000007'
|
|
277
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000002'
|
|
278
|
+
displayName: 12-month AND Adult AND Single site
|
|
279
|
+
coordinator: and
|
|
280
|
+
items:
|
|
281
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000022'
|
|
282
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000010'
|
|
283
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000023'
|
|
284
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000008'
|
|
285
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000003'
|
|
286
|
+
displayName: 12-month AND Concession AND All sites
|
|
287
|
+
coordinator: and
|
|
288
|
+
items:
|
|
289
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000024'
|
|
290
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000011'
|
|
291
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000025'
|
|
292
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000007'
|
|
293
|
+
- id: 'd1d1d1d1-8888-4ddd-dddd-000000000004'
|
|
294
|
+
displayName: 12-month AND Concession AND Single site
|
|
295
|
+
coordinator: and
|
|
296
|
+
items:
|
|
297
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000026'
|
|
298
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000011'
|
|
299
|
+
- id: 'e1e1e1e1-7777-4eee-eeee-000000000027'
|
|
300
|
+
conditionId: 'c1c1c1c1-6666-4ccc-cccc-000000000008'
|
|
301
|
+
sections: []
|
|
302
|
+
lists:
|
|
303
|
+
- id: 'b1b1b1b1-1111-4bbb-bbbb-000000000001'
|
|
304
|
+
title: Permit durations
|
|
305
|
+
name: permitDurations
|
|
306
|
+
type: string
|
|
307
|
+
items:
|
|
308
|
+
- id: 'f1f1f1f1-1111-4fff-ffff-000000000001'
|
|
309
|
+
text: 12-month permit
|
|
310
|
+
value: 12-month
|
|
311
|
+
- id: 'f1f1f1f1-1111-4fff-ffff-000000000002'
|
|
312
|
+
text: 8-day permit
|
|
313
|
+
value: 8-day
|
|
314
|
+
- id: 'f1f1f1f1-1111-4fff-ffff-000000000003'
|
|
315
|
+
text: 1-day permit
|
|
316
|
+
value: 1-day
|
|
317
|
+
- id: 'aac4ee00-fb82-4a37-88ad-b9e10ded92e9'
|
|
318
|
+
title: Permit types
|
|
319
|
+
name: permitTypes
|
|
320
|
+
type: string
|
|
321
|
+
items:
|
|
322
|
+
- id: 'b6034236-63cf-44af-bb6c-d5d4d3825973'
|
|
323
|
+
text: Adult
|
|
324
|
+
value: Adult
|
|
325
|
+
- id: '86baf02e-0db7-4fad-8fce-fa9a30c1b7f0'
|
|
326
|
+
text: Concession (65+ or disabled)
|
|
327
|
+
value: Concession
|
|
328
|
+
- id: 'f1f1f1f1-2222-4fff-ffff-000000000003'
|
|
329
|
+
text: Junior (13-16 years)
|
|
330
|
+
value: Junior
|
|
331
|
+
- id: 'b1b1b1b1-1111-4bbb-bbbb-000000000002'
|
|
332
|
+
title: Site access
|
|
333
|
+
name: siteAccess
|
|
334
|
+
type: string
|
|
335
|
+
items:
|
|
336
|
+
- id: 'f1f1f1f1-3333-4fff-ffff-000000000001'
|
|
337
|
+
text: All sites
|
|
338
|
+
value: all-sites
|
|
339
|
+
- id: 'f1f1f1f1-3333-4fff-ffff-000000000002'
|
|
340
|
+
text: Single site
|
|
341
|
+
value: single-site
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { StatusCodes } from 'http-status-codes'
|
|
8
8
|
import joi, { type ObjectSchema } from 'joi'
|
|
9
9
|
|
|
10
|
+
import { createLogger } from '../../../common/helpers/logging/logger.js'
|
|
10
11
|
import { COMPONENT_STATE_ERROR } from '../../../constants.js'
|
|
11
12
|
import { FormComponent } from './FormComponent.js'
|
|
12
13
|
import { type PaymentState } from './PaymentField.types.js'
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
createError,
|
|
15
16
|
getPluginOptions
|
|
16
17
|
} from '../helpers.js'
|
|
18
|
+
import { type FormModel } from '../models/index.js'
|
|
17
19
|
import {
|
|
18
20
|
PaymentErrorTypes,
|
|
19
21
|
PaymentPreAuthError,
|
|
@@ -38,6 +40,8 @@ import {
|
|
|
38
40
|
formatCurrency
|
|
39
41
|
} from '../../payment/helper.js'
|
|
40
42
|
|
|
43
|
+
const logger = createLogger()
|
|
44
|
+
|
|
41
45
|
export class PaymentField extends FormComponent {
|
|
42
46
|
declare options: PaymentFieldComponent['options']
|
|
43
47
|
declare formSchema: ObjectSchema
|
|
@@ -109,7 +113,9 @@ export class PaymentField extends FormComponent {
|
|
|
109
113
|
? (payload[this.name] as unknown as PaymentState)
|
|
110
114
|
: undefined
|
|
111
115
|
|
|
112
|
-
//
|
|
116
|
+
// Use payment state amount if pre-authorized, otherwise use default.
|
|
117
|
+
// The page controller overrides this with the resolved conditional amount
|
|
118
|
+
// using the full form state (which getViewModel doesn't have access to).
|
|
113
119
|
const amount = paymentState?.amount ?? this.options.amount
|
|
114
120
|
|
|
115
121
|
return {
|
|
@@ -184,6 +190,37 @@ export class PaymentField extends FormComponent {
|
|
|
184
190
|
}
|
|
185
191
|
}
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Resolves the payment amount from conditional amounts configuration.
|
|
195
|
+
* Evaluates conditions in order; first true condition wins.
|
|
196
|
+
* Falls back to the default options.amount.
|
|
197
|
+
*/
|
|
198
|
+
static resolveAmount(
|
|
199
|
+
options: PaymentFieldComponent['options'],
|
|
200
|
+
model: FormModel,
|
|
201
|
+
state: FormState
|
|
202
|
+
): number {
|
|
203
|
+
const { conditionalAmounts } = options
|
|
204
|
+
|
|
205
|
+
if (!conditionalAmounts?.length) {
|
|
206
|
+
return options.amount
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const { condition, amount } of conditionalAmounts) {
|
|
210
|
+
if (!model.conditions[condition]) {
|
|
211
|
+
logger.warn(
|
|
212
|
+
`[payment] Condition '${condition}' not found in form conditions. Skipping.`
|
|
213
|
+
)
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
if (model.conditions[condition].fn(state)) {
|
|
217
|
+
return amount
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return options.amount
|
|
222
|
+
}
|
|
223
|
+
|
|
187
224
|
/**
|
|
188
225
|
* Dispatcher for external redirect to GOV.UK Pay
|
|
189
226
|
*/
|
|
@@ -219,7 +256,12 @@ export class PaymentField extends FormComponent {
|
|
|
219
256
|
const uuid = randomUUID()
|
|
220
257
|
|
|
221
258
|
const reference = state.$$__referenceNumber as string
|
|
222
|
-
const
|
|
259
|
+
const resolvedAmount = PaymentField.resolveAmount(options, model, state)
|
|
260
|
+
|
|
261
|
+
// Zero-amount safety net (page skip should prevent this, but defensive)
|
|
262
|
+
if (resolvedAmount === 0) {
|
|
263
|
+
return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)
|
|
264
|
+
}
|
|
223
265
|
|
|
224
266
|
const description = options.description
|
|
225
267
|
|
|
@@ -228,14 +270,26 @@ export class PaymentField extends FormComponent {
|
|
|
228
270
|
const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`
|
|
229
271
|
const paymentPageUrl = args.sourceUrl
|
|
230
272
|
|
|
231
|
-
|
|
273
|
+
// Prepopulate GOV.UK Pay email if emailField is configured.
|
|
274
|
+
// The referenced EmailAddressField validates with joi.string().email()
|
|
275
|
+
// at input time, so the value in state is already validated.
|
|
276
|
+
let prefilledEmail: string | undefined
|
|
277
|
+
if (options.emailField) {
|
|
278
|
+
const emailValue = state[options.emailField]
|
|
279
|
+
if (typeof emailValue === 'string' && emailValue) {
|
|
280
|
+
prefilledEmail = emailValue
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const amountInPence = Math.round(resolvedAmount * 100)
|
|
232
285
|
const payment = await paymentService.createPayment(
|
|
233
286
|
amountInPence,
|
|
234
287
|
description,
|
|
235
288
|
payCallbackUrl,
|
|
236
289
|
reference,
|
|
237
290
|
isLivePayment,
|
|
238
|
-
{ formId, slug }
|
|
291
|
+
{ formId, slug },
|
|
292
|
+
prefilledEmail
|
|
239
293
|
)
|
|
240
294
|
|
|
241
295
|
if (!payment) {
|
|
@@ -253,7 +307,7 @@ export class PaymentField extends FormComponent {
|
|
|
253
307
|
uuid,
|
|
254
308
|
formId,
|
|
255
309
|
reference,
|
|
256
|
-
amount,
|
|
310
|
+
amount: resolvedAmount,
|
|
257
311
|
description,
|
|
258
312
|
paymentId: payment.paymentId,
|
|
259
313
|
componentName,
|
|
@@ -276,6 +330,16 @@ export class PaymentField extends FormComponent {
|
|
|
276
330
|
_metadata: FormMetadata,
|
|
277
331
|
context: FormContext
|
|
278
332
|
): Promise<void> {
|
|
333
|
+
// Zero-amount bypass — no capture needed
|
|
334
|
+
const resolvedAmount = PaymentField.resolveAmount(
|
|
335
|
+
this.options,
|
|
336
|
+
this.model,
|
|
337
|
+
context.state
|
|
338
|
+
)
|
|
339
|
+
if (resolvedAmount === 0) {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
279
343
|
const paymentState = this.getPaymentStateFromState(context.state)
|
|
280
344
|
|
|
281
345
|
if (!paymentState) {
|
|
@@ -309,7 +373,7 @@ export class PaymentField extends FormComponent {
|
|
|
309
373
|
|
|
310
374
|
PaymentSubmissionError.checkPaymentAmount(
|
|
311
375
|
status.amount,
|
|
312
|
-
|
|
376
|
+
resolvedAmount,
|
|
313
377
|
this
|
|
314
378
|
)
|
|
315
379
|
|