diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f0b56cd8..7ba3521a14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,38 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v6.92.0](https://github.com/opengovsg/FormSG/compare/v6.92.0...v6.92.0) + +- feat: set secret key input to password type on activation modal [`#6933`](https://github.com/opengovsg/FormSG/pull/6933) + +#### [v6.92.0](https://github.com/opengovsg/FormSG/compare/v6.91.1...v6.92.0) + +> 28 November 2023 + +- fix: add myinfo errors to error map for storage-mode submissions [`#6931`](https://github.com/opengovsg/FormSG/pull/6931) +- feat(FE): set secret key input to password type [`#6930`](https://github.com/opengovsg/FormSG/pull/6930) +- feat: add prefills for variable payments [`#6899`](https://github.com/opengovsg/FormSG/pull/6899) +- chore: update package repo for font-wqy-zenhei package [`#6920`](https://github.com/opengovsg/FormSG/pull/6920) +- feat(payments): add paynow payment method [`#6900`](https://github.com/opengovsg/FormSG/pull/6900) +- build: merge Release 6.91.1 into develop [`#6925`](https://github.com/opengovsg/FormSG/pull/6925) +- fix(deps): bump libphonenumber-js from 1.10.48 to 1.10.51 in /shared [`#6926`](https://github.com/opengovsg/FormSG/pull/6926) +- build: release 6.91.1 hotfix [`#6924`](https://github.com/opengovsg/FormSG/pull/6924) +- fix(deps): bump type-fest from 4.8.1 to 4.8.2 in /shared [`#6923`](https://github.com/opengovsg/FormSG/pull/6923) +- chore(deps-dev): bump @types/lodash from 4.14.201 to 4.14.202 in /shared [`#6921`](https://github.com/opengovsg/FormSG/pull/6921) +- build: release v6.91.0 (#6918) [`#6919`](https://github.com/opengovsg/FormSG/pull/6919) +- fix(markdown): refine regex to handle newlines after indentation groups [`#6917`](https://github.com/opengovsg/FormSG/pull/6917) +- fix: omit isVisible property from webhook response [`#6907`](https://github.com/opengovsg/FormSG/pull/6907) +- feat: charts [`#6790`](https://github.com/opengovsg/FormSG/pull/6790) +- build: merge release 6.90.0 to develop [`#6914`](https://github.com/opengovsg/FormSG/pull/6914) +- chore: bump version to v6.92.0 [`72fac02`](https://github.com/opengovsg/FormSG/commit/72fac021a92df588be577c25690b49e96796387d) + #### [v6.91.1](https://github.com/opengovsg/FormSG/compare/v6.91.0...v6.91.1) +> 23 November 2023 + - build: release v6.91.0 [`#6918`](https://github.com/opengovsg/FormSG/pull/6918) - Revert "fix: refine regex to handle newlines after indentation groups" [`10955ed`](https://github.com/opengovsg/FormSG/commit/10955ed7bbac0fcb7872ab0b49be43ba3dd3acb5) +- chore: bump version to 6.91.1 [`e74904b`](https://github.com/opengovsg/FormSG/commit/e74904ba118db029b876590edbdd3d53a04fa6e9) #### [v6.91.0](https://github.com/opengovsg/FormSG/compare/v6.90.0...v6.91.0) diff --git a/Dockerfile.development b/Dockerfile.development index 8cabbf9716..091b3f500b 100644 --- a/Dockerfile.development +++ b/Dockerfile.development @@ -49,8 +49,8 @@ RUN apk update && apk upgrade && \ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser -# Chinese fonts -RUN echo @edge http://dl-cdn.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories && apk add wqy-zenhei@edge +# This package is needed to render Chinese characters in autoreply PDFs +RUN apk add font-wqy-zenhei --repository https://dl-cdn.alpinelinux.org/alpine/edge/community # Avoid using globs as there seems to be some inconsistency in the way dockerfile handles globs # * https://github.com/moby/moby/issues/15858 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 809c3c8e0c..f81a0f2e38 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "form-frontend", - "version": "6.91.0", + "version": "6.92.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "form-frontend", - "version": "6.91.0", + "version": "6.92.0", "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^1.8.6", diff --git a/frontend/package.json b/frontend/package.json index b80b5cf820..7d5729d712 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "form-frontend", - "version": "6.91.0", + "version": "6.92.0", "homepage": ".", "private": true, "dependencies": { diff --git a/frontend/src/features/admin-form/responses/components/SecretKeyVerification/SecretKeyVerification.tsx b/frontend/src/features/admin-form/responses/components/SecretKeyVerification/SecretKeyVerification.tsx index 8555e6708d..357193ffc6 100644 --- a/frontend/src/features/admin-form/responses/components/SecretKeyVerification/SecretKeyVerification.tsx +++ b/frontend/src/features/admin-form/responses/components/SecretKeyVerification/SecretKeyVerification.tsx @@ -165,6 +165,7 @@ export const SecretKeyVerification = ({ diff --git a/frontend/src/features/admin-form/settings/components/SecretKeyActivationModal.tsx b/frontend/src/features/admin-form/settings/components/SecretKeyActivationModal.tsx index 0f9ea2f5d2..a06dcfff50 100644 --- a/frontend/src/features/admin-form/settings/components/SecretKeyActivationModal.tsx +++ b/frontend/src/features/admin-form/settings/components/SecretKeyActivationModal.tsx @@ -196,6 +196,7 @@ export const SecretKeyActivationModal = ({ Enter or upload Secret Key 0) { + const paymentAmount = centsToDollars(Number(paymentParamValue)) + defaultFormValues[PAYMENT_VARIABLE_INPUT_AMOUNT_FIELD_ID] = paymentAmount + } + } + const formMethods = useForm({ defaultValues: defaultFormValues, mode: 'onTouched', diff --git a/package-lock.json b/package-lock.json index c94581e347..1bb3b41107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "FormSG", - "version": "6.91.1", + "version": "6.92.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "FormSG", - "version": "6.91.1", + "version": "6.92.0", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.347.1", diff --git a/package.json b/package.json index a7bd7f13f3..d44c754cc3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FormSG", "description": "Form Manager for Government", - "version": "6.91.1", + "version": "6.92.0", "homepage": "https://form.gov.sg", "authors": [ "FormSG " diff --git a/shared/package-lock.json b/shared/package-lock.json index 1ed07b0416..43124680f2 100644 --- a/shared/package-lock.json +++ b/shared/package-lock.json @@ -102,9 +102,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.201", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", - "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==", + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", "dev": true }, "node_modules/@types/semver": { @@ -611,9 +611,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.48", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.48.tgz", - "integrity": "sha512-Vvcgt4+o8+puIBJZLdMshPYx9nRN3/kTT7HPtOyfYrSQuN9PGBF1KUv0g07fjNzt4E4GuA7FnsLb+WeAMzyRQg==" + "version": "1.10.51", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.51.tgz", + "integrity": "sha512-vY2I+rQwrDQzoPds0JeTEpeWzbUJgqoV0O4v31PauHBb/e+1KCXKylHcDnBMgJZ9fH9mErsEbROJY3Z3JtqEmg==" }, "node_modules/lie": { "version": "3.3.0", @@ -865,9 +865,9 @@ } }, "node_modules/type-fest": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.1.tgz", - "integrity": "sha512-ShaaYnjf+0etG8W/FumARKMjjIToy/haCaTjN2dvcewOSoNqCQzdgG7m2JVOlM5qndGTHjkvsrWZs+k/2Z7E0Q==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.2.tgz", + "integrity": "sha512-mcvrCjixA5166hSrUoJgGb9gBQN4loMYyj9zxuMs/66ibHNEFd5JXMw37YVDx58L4/QID9jIzdTBB4mDwDJ6KQ==", "engines": { "node": ">=16" }, @@ -960,9 +960,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.201", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", - "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==", + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", "dev": true }, "@types/semver": { @@ -1295,9 +1295,9 @@ } }, "libphonenumber-js": { - "version": "1.10.48", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.48.tgz", - "integrity": "sha512-Vvcgt4+o8+puIBJZLdMshPYx9nRN3/kTT7HPtOyfYrSQuN9PGBF1KUv0g07fjNzt4E4GuA7FnsLb+WeAMzyRQg==" + "version": "1.10.51", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.51.tgz", + "integrity": "sha512-vY2I+rQwrDQzoPds0JeTEpeWzbUJgqoV0O4v31PauHBb/e+1KCXKylHcDnBMgJZ9fH9mErsEbROJY3Z3JtqEmg==" }, "lie": { "version": "3.3.0", @@ -1478,9 +1478,9 @@ } }, "type-fest": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.1.tgz", - "integrity": "sha512-ShaaYnjf+0etG8W/FumARKMjjIToy/haCaTjN2dvcewOSoNqCQzdgG7m2JVOlM5qndGTHjkvsrWZs+k/2Z7E0Q==" + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.2.tgz", + "integrity": "sha512-mcvrCjixA5166hSrUoJgGb9gBQN4loMYyj9zxuMs/66ibHNEFd5JXMw37YVDx58L4/QID9jIzdTBB4mDwDJ6KQ==" }, "util-deprecate": { "version": "1.0.2", diff --git a/shared/types/form/form.ts b/shared/types/form/form.ts index 2e7113d1ff..88c4b24498 100644 --- a/shared/types/form/form.ts +++ b/shared/types/form/form.ts @@ -12,7 +12,7 @@ import { } from '../../constants/form' import { DateString } from '../generic' import { FormLogic, LogicDto } from './form_logic' -import { PaymentChannel, PaymentType } from '../payment' +import { PaymentChannel, PaymentMethodType, PaymentType } from '../payment' import { Product } from './product' export type FormId = Opaque @@ -72,6 +72,7 @@ export enum FormResponseMode { } export type FormPaymentsChannel = { + payment_methods?: PaymentMethodType[] channel: PaymentChannel target_account_id: string publishable_key: string diff --git a/shared/types/payment.ts b/shared/types/payment.ts index eba2003d31..5920999e4a 100644 --- a/shared/types/payment.ts +++ b/shared/types/payment.ts @@ -18,6 +18,11 @@ export enum PaymentChannel { Stripe = 'Stripe', // for extensibility to future payment options } + +export enum PaymentMethodType { + Paynow = 'Paynow', +} + export enum PaymentType { Fixed = 'Fixed', Variable = 'Variable', diff --git a/src/app/models/__tests__/form.server.model.spec.ts b/src/app/models/__tests__/form.server.model.spec.ts index 4760c83612..64f94228d8 100644 --- a/src/app/models/__tests__/form.server.model.spec.ts +++ b/src/app/models/__tests__/form.server.model.spec.ts @@ -96,6 +96,7 @@ const PAYMENTS_DEFAULTS = { channel: PaymentChannel.Unconnected, target_account_id: '', publishable_key: '', + payment_methods: [], }, payments_field: { enabled: false, diff --git a/src/app/models/form.server.model.ts b/src/app/models/form.server.model.ts index 4526073c4c..255584eb0d 100644 --- a/src/app/models/form.server.model.ts +++ b/src/app/models/form.server.model.ts @@ -216,6 +216,10 @@ const EncryptedFormSchema = new Schema({ default: '', validate: [/^\S*$/i, 'publishable_key must not contain whitespace.'], }, + payment_methods: { + type: [String], + default: [], + }, }, payments_field: formPaymentsFieldSchema, @@ -244,6 +248,7 @@ EncryptedFormDocumentSchema.methods.addPaymentAccountId = async function ({ channel: PaymentChannel.Stripe, target_account_id: accountId, publishable_key: publishableKey, + payment_methods: [], } } return this.save() @@ -254,6 +259,7 @@ EncryptedFormDocumentSchema.methods.removePaymentAccount = async function () { channel: PaymentChannel.Unconnected, target_account_id: '', publishable_key: '', + payment_methods: [], } if (this.payments_field) { this.payments_field.enabled = false diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts index 0c3a4a6649..d56c4b31e2 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts @@ -88,6 +88,7 @@ import { createEncryptedSubmissionDto, getPaymentAmount, getPaymentIntentDescription, + getStripePaymentMethod, mapRouteError, } from './encrypt-submission.utils' @@ -487,10 +488,7 @@ const _createPaymentSubmission = async ({ const createPaymentIntentParams: Stripe.PaymentIntentCreateParams = { amount, currency: paymentConfig.defaultCurrency, - // determine payment methods available based on stripe settings - automatic_payment_methods: { - enabled: true, - }, + ...getStripePaymentMethod(form), description: getPaymentIntentDescription(form, paymentProducts), receipt_email: paymentReceiptEmail, metadata, diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts index e03f99c8f8..ac5d1a551e 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts @@ -1,9 +1,12 @@ import { StatusCodes } from 'http-status-codes' import moment from 'moment-timezone' +import Stripe from 'stripe' import { FormPaymentsField, + PaymentChannel, PaymentFieldsDto, + PaymentMethodType, PaymentType, StorageModeSubmissionContentDto, StorageModeSubmissionDto, @@ -53,6 +56,14 @@ import { FormNotFoundError, PrivateFormError, } from '../../form/form.errors' +import { + MyInfoCookieStateError, + MyInfoHashDidNotMatchError, + MyInfoHashingError, + MyInfoInvalidLoginCookieError, + MyInfoMissingHashError, + MyInfoMissingLoginCookieError, +} from '../../myinfo/myinfo.errors' import { MyInfoKey } from '../../myinfo/myinfo.types' import { PaymentNotFoundError } from '../../payments/payments.errors' import { @@ -120,12 +131,32 @@ const errorMapper: MapRouteError = ( case MissingJwtError: case VerifyJwtError: case InvalidJwtError: + case MyInfoMissingLoginCookieError: + case MyInfoCookieStateError: + case MyInfoInvalidLoginCookieError: case MalformedVerifiedContentError: return { statusCode: StatusCodes.UNAUTHORIZED, errorMessage: 'Something went wrong with your login. Please try logging in and submitting again.', } + case MyInfoMissingHashError: + return { + statusCode: StatusCodes.GONE, + errorMessage: + 'MyInfo verification expired, please refresh and try again.', + } + case MyInfoHashDidNotMatchError: + return { + statusCode: StatusCodes.UNAUTHORIZED, + errorMessage: 'MyInfo verification failed.', + } + case MyInfoHashingError: + return { + statusCode: StatusCodes.SERVICE_UNAVAILABLE, + errorMessage: + 'MyInfo verification unavailable, please try again later.', + } case MissingUserError: return { statusCode: StatusCodes.UNPROCESSABLE_ENTITY, @@ -405,3 +436,24 @@ export const formatMyInfoStorageResponseData = ( }) } } + +export const getStripePaymentMethod = ( + form: IPopulatedEncryptedForm, +): Omit => { + const isPaynowOnly = + form.payments_channel.payment_methods?.includes(PaymentMethodType.Paynow) && + form.payments_channel.payment_methods?.length === 1 + const stripePaynowOnly = + form.payments_channel.channel === PaymentChannel.Stripe && isPaynowOnly + + if (stripePaynowOnly) { + return { + payment_method_types: ['paynow'], + } + } + return { + automatic_payment_methods: { + enabled: true, + }, + } +}