@defra/forms-engine-plugin 4.0.42 → 4.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.public/javascripts/shared.min.js +1 -1
  2. package/.public/javascripts/shared.min.js.map +1 -1
  3. package/.public/stylesheets/application.min.css +1 -1
  4. package/.public/stylesheets/application.min.css.map +1 -1
  5. package/.server/client/javascripts/location-map.js +8 -4
  6. package/.server/client/javascripts/location-map.js.map +1 -1
  7. package/.server/client/stylesheets/_payment-field.scss +8 -0
  8. package/.server/client/stylesheets/application.scss +2 -0
  9. package/.server/index.js +3 -1
  10. package/.server/index.js.map +1 -1
  11. package/.server/server/constants.d.ts +1 -0
  12. package/.server/server/constants.js +1 -0
  13. package/.server/server/constants.js.map +1 -1
  14. package/.server/server/forms/payment-test.yaml +42 -0
  15. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  16. package/.server/server/plugins/engine/components/FormComponent.d.ts +1 -0
  17. package/.server/server/plugins/engine/components/FormComponent.js +1 -0
  18. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  19. package/.server/server/plugins/engine/components/PaymentField.d.ts +135 -0
  20. package/.server/server/plugins/engine/components/PaymentField.js +228 -0
  21. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -0
  22. package/.server/server/plugins/engine/components/PaymentField.types.d.ts +21 -0
  23. package/.server/server/plugins/engine/components/PaymentField.types.js +2 -0
  24. package/.server/server/plugins/engine/components/PaymentField.types.js.map +1 -0
  25. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -1
  26. package/.server/server/plugins/engine/components/UkAddressField.js +3 -1
  27. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  28. package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
  29. package/.server/server/plugins/engine/components/helpers/components.js +3 -0
  30. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  31. package/.server/server/plugins/engine/components/index.d.ts +1 -0
  32. package/.server/server/plugins/engine/components/index.js +1 -0
  33. package/.server/server/plugins/engine/components/index.js.map +1 -1
  34. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  35. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -2
  36. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  37. package/.server/server/plugins/engine/helpers.d.ts +1 -0
  38. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +3 -0
  39. package/.server/server/plugins/engine/models/SummaryViewModel.js +7 -0
  40. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  41. package/.server/server/plugins/engine/options.js +2 -1
  42. package/.server/server/plugins/engine/options.js.map +1 -1
  43. package/.server/server/plugins/engine/outputFormatters/human/v1.js +34 -1
  44. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  45. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +22 -0
  46. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +43 -1
  47. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  48. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +29 -8
  49. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  50. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -0
  51. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +17 -0
  52. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +173 -51
  53. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  54. package/.server/server/plugins/engine/pageControllers/errors.d.ts +31 -0
  55. package/.server/server/plugins/engine/pageControllers/errors.js +59 -2
  56. package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -1
  57. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +27 -0
  58. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +77 -0
  59. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -0
  60. package/.server/server/plugins/engine/plugin.js +10 -5
  61. package/.server/server/plugins/engine/plugin.js.map +1 -1
  62. package/.server/server/plugins/engine/routes/index.js +8 -4
  63. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  64. package/.server/server/plugins/engine/routes/payment-helper.d.ts +14 -0
  65. package/.server/server/plugins/engine/routes/payment-helper.js +41 -0
  66. package/.server/server/plugins/engine/routes/payment-helper.js.map +1 -0
  67. package/.server/server/plugins/engine/routes/payment-helper.test.js +81 -0
  68. package/.server/server/plugins/engine/routes/payment-helper.test.js.map +1 -0
  69. package/.server/server/plugins/engine/routes/payment.d.ts +8 -0
  70. package/.server/server/plugins/engine/routes/payment.js +140 -0
  71. package/.server/server/plugins/engine/routes/payment.js.map +1 -0
  72. package/.server/server/plugins/engine/routes/payment.test.js +187 -0
  73. package/.server/server/plugins/engine/routes/payment.test.js.map +1 -0
  74. package/.server/server/plugins/engine/services/localFormsService.js +6 -0
  75. package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
  76. package/.server/server/plugins/engine/types/schema.js +7 -0
  77. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  78. package/.server/server/plugins/engine/types.d.ts +20 -1
  79. package/.server/server/plugins/engine/types.js +4 -0
  80. package/.server/server/plugins/engine/types.js.map +1 -1
  81. package/.server/server/plugins/engine/validationHelpers.d.ts +1 -1
  82. package/.server/server/plugins/engine/validationHelpers.js.map +1 -1
  83. package/.server/server/plugins/engine/views/components/paymentfield.html +42 -0
  84. package/.server/server/plugins/engine/views/index.html +9 -1
  85. package/.server/server/plugins/engine/views/partials/form.html +20 -5
  86. package/.server/server/plugins/engine/views/summary.html +17 -1
  87. package/.server/server/plugins/map/routes/get-os-token.d.ts +6 -0
  88. package/.server/server/plugins/map/routes/get-os-token.js +41 -0
  89. package/.server/server/plugins/map/routes/get-os-token.js.map +1 -0
  90. package/.server/server/plugins/map/routes/get-os-token.test.js +49 -0
  91. package/.server/server/plugins/map/routes/get-os-token.test.js.map +1 -0
  92. package/.server/server/plugins/map/routes/index.d.ts +1 -11
  93. package/.server/server/plugins/map/routes/index.js +60 -16
  94. package/.server/server/plugins/map/routes/index.js.map +1 -1
  95. package/.server/server/plugins/map/types.d.ts +1 -0
  96. package/.server/server/plugins/map/types.js +1 -0
  97. package/.server/server/plugins/map/types.js.map +1 -1
  98. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  99. package/.server/server/plugins/payment/helper.d.ts +30 -0
  100. package/.server/server/plugins/payment/helper.js +49 -0
  101. package/.server/server/plugins/payment/helper.js.map +1 -0
  102. package/.server/server/plugins/payment/helper.test.js +37 -0
  103. package/.server/server/plugins/payment/helper.test.js.map +1 -0
  104. package/.server/server/plugins/payment/service.d.ts +40 -0
  105. package/.server/server/plugins/payment/service.js +129 -0
  106. package/.server/server/plugins/payment/service.js.map +1 -0
  107. package/.server/server/plugins/payment/service.test.js +162 -0
  108. package/.server/server/plugins/payment/service.test.js.map +1 -0
  109. package/.server/server/plugins/payment/types.d.ts +172 -0
  110. package/.server/server/plugins/payment/types.js +78 -0
  111. package/.server/server/plugins/payment/types.js.map +1 -0
  112. package/.server/server/types.d.ts +3 -0
  113. package/.server/server/types.js.map +1 -1
  114. package/.server/typings/hapi/index.d.js.map +1 -1
  115. package/README.md +12 -9
  116. package/package.json +2 -2
  117. package/src/client/javascripts/location-map.js +12 -4
  118. package/src/client/stylesheets/_payment-field.scss +8 -0
  119. package/src/client/stylesheets/application.scss +2 -0
  120. package/src/index.ts +5 -1
  121. package/src/server/constants.js +1 -0
  122. package/src/server/forms/payment-test.yaml +42 -0
  123. package/src/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  124. package/src/server/plugins/engine/components/FormComponent.ts +1 -0
  125. package/src/server/plugins/engine/components/PaymentField.test.ts +611 -0
  126. package/src/server/plugins/engine/components/PaymentField.ts +367 -0
  127. package/src/server/plugins/engine/components/PaymentField.types.ts +21 -0
  128. package/src/server/plugins/engine/components/UkAddressField.ts +2 -1
  129. package/src/server/plugins/engine/components/helpers/components.ts +5 -0
  130. package/src/server/plugins/engine/components/index.ts +1 -0
  131. package/src/server/plugins/engine/configureEnginePlugin.ts +4 -2
  132. package/src/server/plugins/engine/models/SummaryViewModel.ts +8 -0
  133. package/src/server/plugins/engine/options.js +2 -1
  134. package/src/server/plugins/engine/outputFormatters/human/v1.payment.test.ts +147 -0
  135. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +105 -103
  136. package/src/server/plugins/engine/outputFormatters/human/v1.ts +61 -2
  137. package/src/server/plugins/engine/outputFormatters/machine/v2.payment.test.ts +115 -0
  138. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +60 -1
  139. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +32 -6
  140. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +247 -72
  141. package/src/server/plugins/engine/pageControllers/errors.test.ts +13 -1
  142. package/src/server/plugins/engine/pageControllers/errors.ts +79 -4
  143. package/src/server/plugins/engine/pageControllers/helpers/submission.test.ts +299 -0
  144. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +110 -0
  145. package/src/server/plugins/engine/plugin.ts +17 -10
  146. package/src/server/plugins/engine/routes/index.ts +17 -16
  147. package/src/server/plugins/engine/routes/payment-helper.js +39 -0
  148. package/src/server/plugins/engine/routes/payment-helper.test.js +90 -0
  149. package/src/server/plugins/engine/routes/payment.js +151 -0
  150. package/src/server/plugins/engine/routes/payment.test.js +180 -0
  151. package/src/server/plugins/engine/services/localFormsService.js +7 -0
  152. package/src/server/plugins/engine/types/schema.ts +9 -0
  153. package/src/server/plugins/engine/types.ts +25 -1
  154. package/src/server/plugins/engine/validationHelpers.ts +1 -1
  155. package/src/server/plugins/engine/views/components/paymentfield.html +42 -0
  156. package/src/server/plugins/engine/views/index.html +9 -1
  157. package/src/server/plugins/engine/views/partials/form.html +20 -5
  158. package/src/server/plugins/engine/views/summary.html +17 -1
  159. package/src/server/plugins/map/routes/get-os-token.js +41 -0
  160. package/src/server/plugins/map/routes/get-os-token.test.js +55 -0
  161. package/src/server/plugins/map/routes/index.js +70 -24
  162. package/src/server/plugins/map/types.js +1 -0
  163. package/src/server/plugins/payment/helper.js +56 -0
  164. package/src/server/plugins/payment/helper.test.js +52 -0
  165. package/src/server/plugins/payment/service.js +171 -0
  166. package/src/server/plugins/payment/service.test.js +205 -0
  167. package/src/server/plugins/payment/types.js +77 -0
  168. package/src/server/types.ts +3 -0
  169. package/src/typings/hapi/index.d.ts +1 -0
@@ -3,5 +3,5 @@
3
3
  * @this {NunjucksContext}
4
4
  * @param {string} name - The name of the component
5
5
  */
6
- export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | undefined;
6
+ export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/PaymentField.js").PaymentField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | undefined;
7
7
  import type { NunjucksContext } from '~/src/server/plugins/nunjucks/types.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Determine which payment API key value to use.
3
+ * If a draft preview form or a live preview form, read the TEST API key value specific to that form.
4
+ * If a live (non-preview) form, read the LIVE API key value specific to that form.
5
+ * @param {boolean} isLivePayment - true if this is a live payment (as opposed to a test one)
6
+ * @param {string} formId - id of the form
7
+ * @returns {string}
8
+ */
9
+ export function getPaymentApiKey(isLivePayment: boolean, formId: string): string;
10
+ /**
11
+ * Creates a PaymentService instance with the appropriate API key
12
+ * @param {boolean} isLivePayment - true if this is a live payment
13
+ * @param {string} formId - id of the form
14
+ * @returns {PaymentService}
15
+ */
16
+ export function createPaymentService(isLivePayment: boolean, formId: string): PaymentService;
17
+ /**
18
+ * Formats a payment date for display
19
+ * @param {string} isoString - ISO date string
20
+ * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
21
+ */
22
+ export function formatPaymentDate(isoString: string): string;
23
+ /**
24
+ * Formats a payment amount with two decimal places
25
+ * @param {number} amount - amount in pounds
26
+ * @returns {string} Formatted amount (e.g., "£10.00")
27
+ */
28
+ export function formatPaymentAmount(amount: number): string;
29
+ export const DEFAULT_PAYMENT_HELP_URL: "https://www.gov.uk/government/organisations/department-for-environment-food-rural-affairs";
30
+ import { PaymentService } from '~/src/server/plugins/payment/service.js';
@@ -0,0 +1,49 @@
1
+ import { format } from 'date-fns';
2
+ import { PaymentService } from "./service.js";
3
+ export const DEFAULT_PAYMENT_HELP_URL = 'https://www.gov.uk/government/organisations/department-for-environment-food-rural-affairs';
4
+
5
+ /**
6
+ * Determine which payment API key value to use.
7
+ * If a draft preview form or a live preview form, read the TEST API key value specific to that form.
8
+ * If a live (non-preview) form, read the LIVE API key value specific to that form.
9
+ * @param {boolean} isLivePayment - true if this is a live payment (as opposed to a test one)
10
+ * @param {string} formId - id of the form
11
+ * @returns {string}
12
+ */
13
+ export function getPaymentApiKey(isLivePayment, formId) {
14
+ const apiKeyValue = isLivePayment ? process.env[`PAYMENT_PROVIDER_API_KEY_LIVE_${formId}`] : process.env[`PAYMENT_PROVIDER_API_KEY_TEST_${formId}`];
15
+ if (!apiKeyValue) {
16
+ throw new Error(`Missing payment api key for ${isLivePayment ? 'live' : 'test'} form id ${formId}`);
17
+ }
18
+ return apiKeyValue;
19
+ }
20
+
21
+ /**
22
+ * Creates a PaymentService instance with the appropriate API key
23
+ * @param {boolean} isLivePayment - true if this is a live payment
24
+ * @param {string} formId - id of the form
25
+ * @returns {PaymentService}
26
+ */
27
+ export function createPaymentService(isLivePayment, formId) {
28
+ const apiKey = getPaymentApiKey(isLivePayment, formId);
29
+ return new PaymentService(apiKey);
30
+ }
31
+
32
+ /**
33
+ * Formats a payment date for display
34
+ * @param {string} isoString - ISO date string
35
+ * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
36
+ */
37
+ export function formatPaymentDate(isoString) {
38
+ return format(new Date(isoString), 'd MMMM yyyy h:mmaaa');
39
+ }
40
+
41
+ /**
42
+ * Formats a payment amount with two decimal places
43
+ * @param {number} amount - amount in pounds
44
+ * @returns {string} Formatted amount (e.g., "£10.00")
45
+ */
46
+ export function formatPaymentAmount(amount) {
47
+ return `£${amount.toFixed(2)}`;
48
+ }
49
+ //# sourceMappingURL=helper.js.map
@@ -0,0 +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,+BAA+BL,aAAa,GAAG,MAAM,GAAG,MAAM,YAAYC,MAAM,EAClF,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":[]}
@@ -0,0 +1,37 @@
1
+ import { config } from "../../../config/index.js";
2
+ import { formatPaymentAmount, formatPaymentDate, getPaymentApiKey } from "./helper.js";
3
+ describe('getPaymentApiKey', () => {
4
+ config.set('paymentProviderApiKeyTest', 'TEST-API-KEY');
5
+ const formId = 'form-id';
6
+ process.env['PAYMENT_PROVIDER_API_KEY_LIVE_form-id'] = 'LIVE-API-KEY';
7
+ process.env['PAYMENT_PROVIDER_API_KEY_TEST_form-id'] = 'TEST-API-KEY';
8
+ it('should read test key when non-live form', () => {
9
+ const apiKey = getPaymentApiKey(false, formId);
10
+ expect(apiKey).toBe('TEST-API-KEY');
11
+ });
12
+ it('should read live key when live form', () => {
13
+ const apiKey = getPaymentApiKey(true, formId);
14
+ expect(apiKey).toBe('LIVE-API-KEY');
15
+ });
16
+ it('should throw if TEST key is missing', () => {
17
+ expect(() => getPaymentApiKey(false, 'form-id-missing')).toThrow('Missing payment api key for test form id form-id-missing');
18
+ });
19
+ it('should throw if LIVE key is missing', () => {
20
+ expect(() => getPaymentApiKey(true, 'form-id-missing')).toThrow('Missing payment api key for live form id form-id-missing');
21
+ });
22
+ });
23
+ describe('formatPaymentDate', () => {
24
+ it('should format ISO date string to en-GB format', () => {
25
+ const result = formatPaymentDate('2025-11-10T17:01:29.000Z');
26
+ expect(result).toBe('10 November 2025 5:01pm');
27
+ });
28
+ });
29
+ describe('formatPaymentAmount', () => {
30
+ it('should format whole number with two decimal places', () => {
31
+ expect(formatPaymentAmount(10)).toBe('£10.00');
32
+ });
33
+ it('should format decimal amount', () => {
34
+ expect(formatPaymentAmount(99.5)).toBe('£99.50');
35
+ });
36
+ });
37
+ //# sourceMappingURL=helper.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.test.js","names":["config","formatPaymentAmount","formatPaymentDate","getPaymentApiKey","describe","set","formId","process","env","it","apiKey","expect","toBe","toThrow","result"],"sources":["../../../../src/server/plugins/payment/helper.test.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport {\n formatPaymentAmount,\n formatPaymentDate,\n getPaymentApiKey\n} from '~/src/server/plugins/payment/helper.js'\n\ndescribe('getPaymentApiKey', () => {\n config.set('paymentProviderApiKeyTest', 'TEST-API-KEY')\n const formId = 'form-id'\n process.env['PAYMENT_PROVIDER_API_KEY_LIVE_form-id'] = 'LIVE-API-KEY'\n process.env['PAYMENT_PROVIDER_API_KEY_TEST_form-id'] = 'TEST-API-KEY'\n\n it('should read test key when non-live form', () => {\n const apiKey = getPaymentApiKey(false, formId)\n expect(apiKey).toBe('TEST-API-KEY')\n })\n\n it('should read live key when live form', () => {\n const apiKey = getPaymentApiKey(true, formId)\n expect(apiKey).toBe('LIVE-API-KEY')\n })\n\n it('should throw if TEST key is missing', () => {\n expect(() => getPaymentApiKey(false, 'form-id-missing')).toThrow(\n 'Missing payment api key for test form id form-id-missing'\n )\n })\n\n it('should throw if LIVE key is missing', () => {\n expect(() => getPaymentApiKey(true, 'form-id-missing')).toThrow(\n 'Missing payment api key for live form id form-id-missing'\n )\n })\n})\n\ndescribe('formatPaymentDate', () => {\n it('should format ISO date string to en-GB format', () => {\n const result = formatPaymentDate('2025-11-10T17:01:29.000Z')\n expect(result).toBe('10 November 2025 5:01pm')\n })\n})\n\ndescribe('formatPaymentAmount', () => {\n it('should format whole number with two decimal places', () => {\n expect(formatPaymentAmount(10)).toBe('£10.00')\n })\n\n it('should format decimal amount', () => {\n expect(formatPaymentAmount(99.5)).toBe('£99.50')\n })\n})\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SACEC,mBAAmB,EACnBC,iBAAiB,EACjBC,gBAAgB;AAGlBC,QAAQ,CAAC,kBAAkB,EAAE,MAAM;EACjCJ,MAAM,CAACK,GAAG,CAAC,2BAA2B,EAAE,cAAc,CAAC;EACvD,MAAMC,MAAM,GAAG,SAAS;EACxBC,OAAO,CAACC,GAAG,CAAC,uCAAuC,CAAC,GAAG,cAAc;EACrED,OAAO,CAACC,GAAG,CAAC,uCAAuC,CAAC,GAAG,cAAc;EAErEC,EAAE,CAAC,yCAAyC,EAAE,MAAM;IAClD,MAAMC,MAAM,GAAGP,gBAAgB,CAAC,KAAK,EAAEG,MAAM,CAAC;IAC9CK,MAAM,CAACD,MAAM,CAAC,CAACE,IAAI,CAAC,cAAc,CAAC;EACrC,CAAC,CAAC;EAEFH,EAAE,CAAC,qCAAqC,EAAE,MAAM;IAC9C,MAAMC,MAAM,GAAGP,gBAAgB,CAAC,IAAI,EAAEG,MAAM,CAAC;IAC7CK,MAAM,CAACD,MAAM,CAAC,CAACE,IAAI,CAAC,cAAc,CAAC;EACrC,CAAC,CAAC;EAEFH,EAAE,CAAC,qCAAqC,EAAE,MAAM;IAC9CE,MAAM,CAAC,MAAMR,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAACU,OAAO,CAC9D,0DACF,CAAC;EACH,CAAC,CAAC;EAEFJ,EAAE,CAAC,qCAAqC,EAAE,MAAM;IAC9CE,MAAM,CAAC,MAAMR,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAACU,OAAO,CAC7D,0DACF,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC;AAEFT,QAAQ,CAAC,mBAAmB,EAAE,MAAM;EAClCK,EAAE,CAAC,+CAA+C,EAAE,MAAM;IACxD,MAAMK,MAAM,GAAGZ,iBAAiB,CAAC,0BAA0B,CAAC;IAC5DS,MAAM,CAACG,MAAM,CAAC,CAACF,IAAI,CAAC,yBAAyB,CAAC;EAChD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEFR,QAAQ,CAAC,qBAAqB,EAAE,MAAM;EACpCK,EAAE,CAAC,oDAAoD,EAAE,MAAM;IAC7DE,MAAM,CAACV,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAACW,IAAI,CAAC,QAAQ,CAAC;EAChD,CAAC,CAAC;EAEFH,EAAE,CAAC,8BAA8B,EAAE,MAAM;IACvCE,MAAM,CAACV,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAACW,IAAI,CAAC,QAAQ,CAAC;EAClD,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,40 @@
1
+ export class PaymentService {
2
+ /**
3
+ * @param {string} apiKey - API key to use (global config for test value, per-form config for live value)
4
+ */
5
+ constructor(apiKey: string);
6
+ /**
7
+ * Creates a payment with delayed capture (pre-authorisation)
8
+ * @param {number} amount - in pence
9
+ * @param {string} description
10
+ * @param {string} returnUrl
11
+ * @param {string} reference
12
+ * @param {{ formId: string, slug: string }} metadata
13
+ */
14
+ createPayment(amount: number, description: string, returnUrl: string, reference: string, metadata: {
15
+ formId: string;
16
+ slug: string;
17
+ }): Promise<{
18
+ paymentId: string;
19
+ paymentUrl: string;
20
+ }>;
21
+ /**
22
+ * @param {string} paymentId
23
+ * @returns {Promise<GetPaymentResponse>}
24
+ */
25
+ getPaymentStatus(paymentId: string): Promise<GetPaymentResponse>;
26
+ /**
27
+ * Captures a payment that is in 'capturable' status
28
+ * @param {string} paymentId
29
+ * @returns {Promise<boolean>}
30
+ */
31
+ capturePayment(paymentId: string): Promise<boolean>;
32
+ /**
33
+ * @param {CreatePaymentRequest} payload
34
+ */
35
+ postToPayProvider(payload: CreatePaymentRequest): Promise<CreatePaymentResponse>;
36
+ #private;
37
+ }
38
+ import type { GetPaymentResponse } from '~/src/server/plugins/payment/types.js';
39
+ import type { CreatePaymentRequest } from '~/src/server/plugins/payment/types.js';
40
+ import type { CreatePaymentResponse } from '~/src/server/plugins/payment/types.js';
@@ -0,0 +1,129 @@
1
+ import { StatusCodes } from 'http-status-codes';
2
+ import { createLogger } from "../../common/helpers/logging/logger.js";
3
+ import { get, post, postJson } from "../../services/httpService.js";
4
+ const PAYMENT_BASE_URL = 'https://publicapi.payments.service.gov.uk';
5
+ const PAYMENT_ENDPOINT = '/v1/payments';
6
+ const logger = createLogger();
7
+
8
+ /**
9
+ * @param {string} apiKey
10
+ * @returns {{ Authorization: string }}
11
+ */
12
+ function getAuthHeaders(apiKey) {
13
+ return {
14
+ Authorization: `Bearer ${apiKey}`
15
+ };
16
+ }
17
+ export class PaymentService {
18
+ /** @type {string} */
19
+ #apiKey;
20
+
21
+ /**
22
+ * @param {string} apiKey - API key to use (global config for test value, per-form config for live value)
23
+ */
24
+ constructor(apiKey) {
25
+ this.#apiKey = apiKey;
26
+ }
27
+
28
+ /**
29
+ * Creates a payment with delayed capture (pre-authorisation)
30
+ * @param {number} amount - in pence
31
+ * @param {string} description
32
+ * @param {string} returnUrl
33
+ * @param {string} reference
34
+ * @param {{ formId: string, slug: string }} metadata
35
+ */
36
+ async createPayment(amount, description, returnUrl, reference, metadata) {
37
+ const response = await this.postToPayProvider({
38
+ amount,
39
+ description,
40
+ reference,
41
+ metadata,
42
+ return_url: returnUrl,
43
+ delayed_capture: true
44
+ });
45
+ return {
46
+ paymentId: response.payment_id,
47
+ paymentUrl: response._links.next_url.href
48
+ };
49
+ }
50
+
51
+ /**
52
+ * @param {string} paymentId
53
+ * @returns {Promise<GetPaymentResponse>}
54
+ */
55
+ async getPaymentStatus(paymentId) {
56
+ const getByType = /** @type {typeof get<GetPaymentApiResponse>} */get;
57
+ try {
58
+ const response = await getByType(`${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}/${paymentId}`, {
59
+ headers: getAuthHeaders(this.#apiKey),
60
+ json: true
61
+ });
62
+ if (response.error) {
63
+ const errorMessage = response.error instanceof Error ? response.error.message : JSON.stringify(response.error);
64
+ throw new Error(`Failed to get payment status: ${errorMessage}`);
65
+ }
66
+ return {
67
+ state: response.payload.state,
68
+ _links: response.payload._links,
69
+ email: response.payload.email,
70
+ paymentId: response.payload.payment_id,
71
+ amount: response.payload.amount
72
+ };
73
+ } catch (err) {
74
+ const error = /** @type {Error} */err;
75
+ logger.error(error, `[payment] Error getting payment status for paymentId=${paymentId}: ${error.message}`);
76
+ throw err;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Captures a payment that is in 'capturable' status
82
+ * @param {string} paymentId
83
+ * @returns {Promise<boolean>}
84
+ */
85
+ async capturePayment(paymentId) {
86
+ try {
87
+ const response = await post(`${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}/${paymentId}/capture`, {
88
+ headers: getAuthHeaders(this.#apiKey)
89
+ });
90
+ const statusCode = response.res.statusCode;
91
+ if (statusCode === StatusCodes.OK || statusCode === StatusCodes.NO_CONTENT) {
92
+ logger.info(`[payment] Successfully captured payment ${paymentId}`);
93
+ return true;
94
+ }
95
+ logger.error(`[payment] Capture failed for paymentId=${paymentId}: HTTP ${statusCode}`);
96
+ return false;
97
+ } catch (err) {
98
+ const error = /** @type {Error} */err;
99
+ logger.error(error, `[payment] Error capturing payment for paymentId=${paymentId}: ${error.message}`);
100
+ throw err;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {CreatePaymentRequest} payload
106
+ */
107
+ async postToPayProvider(payload) {
108
+ const postJsonByType = /** @type {typeof postJson<CreatePaymentResponse>} */postJson;
109
+ try {
110
+ const response = await postJsonByType(`${PAYMENT_BASE_URL}${PAYMENT_ENDPOINT}`, {
111
+ payload,
112
+ headers: getAuthHeaders(this.#apiKey)
113
+ });
114
+ if (response.payload?.state.status !== 'created') {
115
+ throw new Error(`Failed to create payment for reference=${payload.reference}`);
116
+ }
117
+ return response.payload;
118
+ } catch (err) {
119
+ const error = /** @type {Error} */err;
120
+ logger.error(error, `[payment] Error creating payment for reference=${payload.reference}: ${error.message}`);
121
+ throw err;
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * @import { CreatePaymentRequest, CreatePaymentResponse, GetPaymentApiResponse, GetPaymentResponse } from '~/src/server/plugins/payment/types.js'
128
+ */
129
+ //# sourceMappingURL=service.js.map
@@ -0,0 +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","paymentId","payment_id","paymentUrl","_links","next_url","href","getPaymentStatus","getByType","headers","json","error","errorMessage","Error","message","JSON","stringify","state","payload","email","err","capturePayment","statusCode","res","OK","NO_CONTENT","info","postJsonByType","status"],"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 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 return {\n state: response.payload.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(`[payment] Successfully captured payment ${paymentId}`)\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":[]}
@@ -0,0 +1,162 @@
1
+ import { PaymentService } from "./service.js";
2
+ import { get, post, postJson } from "../../services/httpService.js";
3
+ jest.mock("../../services/httpService.ts");
4
+ describe('payment service', () => {
5
+ const service = new PaymentService('my-api-key');
6
+ describe('constructor', () => {
7
+ it('should create instance', () => {
8
+ expect(service).toBeDefined();
9
+ });
10
+ });
11
+ describe('createPayment', () => {
12
+ it('should create a payment', async () => {
13
+ const createPaymentResult = {
14
+ payment_id: 'payment-id-12345',
15
+ _links: {
16
+ next_url: {
17
+ href: 'http://next-url-href/payment'
18
+ }
19
+ },
20
+ state: {
21
+ status: 'created'
22
+ }
23
+ };
24
+ jest.mocked(postJson).mockResolvedValueOnce({
25
+ res: (/** @type {IncomingMessage} */{
26
+ statusCode: 200,
27
+ headers: {}
28
+ }),
29
+ payload: createPaymentResult,
30
+ error: undefined
31
+ });
32
+ const referenceNumber = 'ABC-DEF-123';
33
+ const returnUrl = 'http://localhost:3009/payment-callback-handler';
34
+ const metadata = {
35
+ formId: 'form-id',
36
+ slug: 'my-form-slug'
37
+ };
38
+ const payment = await service.createPayment(100, 'Payment description', returnUrl, referenceNumber, metadata);
39
+ expect(payment.paymentId).toBe('payment-id-12345');
40
+ expect(payment.paymentUrl).toBe('http://next-url-href/payment');
41
+ });
42
+ it('should throw if fails to create a payment - failed API call', async () => {
43
+ jest.mocked(postJson).mockRejectedValueOnce(new Error('internal creation error'));
44
+ const referenceNumber = 'ABC-DEF-123';
45
+ const returnUrl = 'http://localhost:3009/payment-callback-handler';
46
+ const metadata = {
47
+ formId: 'form-id',
48
+ slug: 'my-form-slug'
49
+ };
50
+ await expect(() => service.createPayment(100, 'Payment description', returnUrl, referenceNumber, metadata)).rejects.toThrow('internal creation error');
51
+ });
52
+ it('should throw if fails to create a payment - bad result from API call', async () => {
53
+ const createPaymentResult = {
54
+ state: {
55
+ status: 'failed'
56
+ }
57
+ };
58
+ jest.mocked(postJson).mockResolvedValueOnce({
59
+ res: (/** @type {IncomingMessage} */{
60
+ statusCode: 200,
61
+ headers: {}
62
+ }),
63
+ payload: createPaymentResult,
64
+ error: undefined
65
+ });
66
+ const referenceNumber = 'ABC-DEF-123';
67
+ const returnUrl = 'http://localhost:3009/payment-callback-handler';
68
+ const metadata = {
69
+ formId: 'form-id',
70
+ slug: 'my-form-slug'
71
+ };
72
+ await expect(() => service.createPayment(100, 'Payment description', returnUrl, referenceNumber, metadata)).rejects.toThrow('Failed to create payment');
73
+ });
74
+ });
75
+ describe('getPaymentStatus', () => {
76
+ it('should get payment status if exists', async () => {
77
+ const getPaymentStatusResult = {
78
+ payment_id: 'payment-id-12345',
79
+ _links: {
80
+ next_url: {
81
+ href: 'http://next-url-href/payment'
82
+ }
83
+ },
84
+ state: {
85
+ status: 'created'
86
+ }
87
+ };
88
+ jest.mocked(get).mockResolvedValueOnce({
89
+ res: (/** @type {IncomingMessage} */{
90
+ statusCode: 200,
91
+ headers: {}
92
+ }),
93
+ payload: getPaymentStatusResult,
94
+ error: undefined
95
+ });
96
+ const paymentStatus = await service.getPaymentStatus('payment-id-12345');
97
+ expect(paymentStatus.paymentId).toBe('payment-id-12345');
98
+ expect(paymentStatus._links.next_url?.href).toBe('http://next-url-href/payment');
99
+ });
100
+ it('should handle payment status error', async () => {
101
+ jest.mocked(get).mockResolvedValueOnce({
102
+ res: (/** @type {IncomingMessage} */{
103
+ statusCode: 200,
104
+ headers: {}
105
+ }),
106
+ payload: undefined,
107
+ error: new Error('some-error')
108
+ });
109
+ await expect(() => service.getPaymentStatus('payment-id-12345')).rejects.toThrow('Failed to get payment status: some-error');
110
+ });
111
+ });
112
+ describe('capturePayment', () => {
113
+ it('should return true when successful capture with statusCode 200', async () => {
114
+ const capturePaymentResult = {};
115
+ jest.mocked(post).mockResolvedValueOnce({
116
+ res: (/** @type {IncomingMessage} */{
117
+ statusCode: 200,
118
+ headers: {}
119
+ }),
120
+ payload: capturePaymentResult,
121
+ error: undefined
122
+ });
123
+ const captureResult = await service.capturePayment('payment-id-12345');
124
+ expect(captureResult).toBe(true);
125
+ });
126
+ it('should return true when successful capture with statusCode 204', async () => {
127
+ const capturePaymentResult = {};
128
+ jest.mocked(post).mockResolvedValueOnce({
129
+ res: (/** @type {IncomingMessage} */{
130
+ statusCode: 204,
131
+ headers: {}
132
+ }),
133
+ payload: capturePaymentResult,
134
+ error: undefined
135
+ });
136
+ const captureResult = await service.capturePayment('payment-id-12345');
137
+ expect(captureResult).toBe(true);
138
+ });
139
+ it('should return false when status code not 200 or 204', async () => {
140
+ const capturePaymentResult = {};
141
+ jest.mocked(post).mockResolvedValueOnce({
142
+ res: (/** @type {IncomingMessage} */{
143
+ statusCode: 500,
144
+ headers: {}
145
+ }),
146
+ payload: capturePaymentResult,
147
+ error: undefined
148
+ });
149
+ const captureResult = await service.capturePayment('payment-id-12345');
150
+ expect(captureResult).toBe(false);
151
+ });
152
+ it('should throw when internal error', async () => {
153
+ jest.mocked(post).mockRejectedValueOnce(new Error('internal capture error'));
154
+ await expect(() => service.capturePayment('payment-id-12345')).rejects.toThrow('internal capture error');
155
+ });
156
+ });
157
+ });
158
+
159
+ /**
160
+ * @import { IncomingMessage } from 'node:http'
161
+ */
162
+ //# sourceMappingURL=service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.test.js","names":["PaymentService","get","post","postJson","jest","mock","describe","service","it","expect","toBeDefined","createPaymentResult","payment_id","_links","next_url","href","state","status","mocked","mockResolvedValueOnce","res","statusCode","headers","payload","error","undefined","referenceNumber","returnUrl","metadata","formId","slug","payment","createPayment","paymentId","toBe","paymentUrl","mockRejectedValueOnce","Error","rejects","toThrow","getPaymentStatusResult","paymentStatus","getPaymentStatus","capturePaymentResult","captureResult","capturePayment"],"sources":["../../../../src/server/plugins/payment/service.test.js"],"sourcesContent":["import { PaymentService } from '~/src/server/plugins/payment/service.js'\nimport { get, post, postJson } from '~/src/server/services/httpService.js'\n\njest.mock('~/src/server/services/httpService.ts')\n\ndescribe('payment service', () => {\n const service = new PaymentService('my-api-key')\n describe('constructor', () => {\n it('should create instance', () => {\n expect(service).toBeDefined()\n })\n })\n\n describe('createPayment', () => {\n it('should create a payment', async () => {\n const createPaymentResult = {\n payment_id: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n }\n jest.mocked(postJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: createPaymentResult,\n error: undefined\n })\n\n const referenceNumber = 'ABC-DEF-123'\n const returnUrl = 'http://localhost:3009/payment-callback-handler'\n const metadata = { formId: 'form-id', slug: 'my-form-slug' }\n const payment = await service.createPayment(\n 100,\n 'Payment description',\n returnUrl,\n referenceNumber,\n metadata\n )\n expect(payment.paymentId).toBe('payment-id-12345')\n expect(payment.paymentUrl).toBe('http://next-url-href/payment')\n })\n\n it('should throw if fails to create a payment - failed API call', async () => {\n jest\n .mocked(postJson)\n .mockRejectedValueOnce(new Error('internal creation error'))\n\n const referenceNumber = 'ABC-DEF-123'\n const returnUrl = 'http://localhost:3009/payment-callback-handler'\n const metadata = { formId: 'form-id', slug: 'my-form-slug' }\n await expect(() =>\n service.createPayment(\n 100,\n 'Payment description',\n returnUrl,\n referenceNumber,\n metadata\n )\n ).rejects.toThrow('internal creation error')\n })\n\n it('should throw if fails to create a payment - bad result from API call', async () => {\n const createPaymentResult = {\n state: {\n status: 'failed'\n }\n }\n jest.mocked(postJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: createPaymentResult,\n error: undefined\n })\n\n const referenceNumber = 'ABC-DEF-123'\n const returnUrl = 'http://localhost:3009/payment-callback-handler'\n const metadata = { formId: 'form-id', slug: 'my-form-slug' }\n await expect(() =>\n service.createPayment(\n 100,\n 'Payment description',\n returnUrl,\n referenceNumber,\n metadata\n )\n ).rejects.toThrow('Failed to create payment')\n })\n })\n\n describe('getPaymentStatus', () => {\n it('should get payment status if exists', async () => {\n const getPaymentStatusResult = {\n payment_id: 'payment-id-12345',\n _links: {\n next_url: {\n href: 'http://next-url-href/payment'\n }\n },\n state: {\n status: 'created'\n }\n }\n\n jest.mocked(get).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: getPaymentStatusResult,\n error: undefined\n })\n\n const paymentStatus = await service.getPaymentStatus('payment-id-12345')\n expect(paymentStatus.paymentId).toBe('payment-id-12345')\n expect(paymentStatus._links.next_url?.href).toBe(\n 'http://next-url-href/payment'\n )\n })\n\n it('should handle payment status error', async () => {\n jest.mocked(get).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: undefined,\n error: new Error('some-error')\n })\n\n await expect(() =>\n service.getPaymentStatus('payment-id-12345')\n ).rejects.toThrow('Failed to get payment status: some-error')\n })\n })\n\n describe('capturePayment', () => {\n it('should return true when successful capture with statusCode 200', async () => {\n const capturePaymentResult = {}\n jest.mocked(post).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: capturePaymentResult,\n error: undefined\n })\n\n const captureResult = await service.capturePayment('payment-id-12345')\n expect(captureResult).toBe(true)\n })\n\n it('should return true when successful capture with statusCode 204', async () => {\n const capturePaymentResult = {}\n jest.mocked(post).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 204,\n headers: {}\n }),\n payload: capturePaymentResult,\n error: undefined\n })\n\n const captureResult = await service.capturePayment('payment-id-12345')\n expect(captureResult).toBe(true)\n })\n\n it('should return false when status code not 200 or 204', async () => {\n const capturePaymentResult = {}\n jest.mocked(post).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 500,\n headers: {}\n }),\n payload: capturePaymentResult,\n error: undefined\n })\n\n const captureResult = await service.capturePayment('payment-id-12345')\n expect(captureResult).toBe(false)\n })\n\n it('should throw when internal error', async () => {\n jest\n .mocked(post)\n .mockRejectedValueOnce(new Error('internal capture error'))\n\n await expect(() =>\n service.capturePayment('payment-id-12345')\n ).rejects.toThrow('internal capture error')\n })\n })\n})\n\n/**\n * @import { IncomingMessage } from 'node:http'\n */\n"],"mappings":"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","ignoreList":[]}