From a660da839bd2ed8fc6298d42263526e51cfe52ce Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Wed, 3 Apr 2024 11:57:24 +0100 Subject: [PATCH 1/6] Merge pull request #386 from johnfisherman/pt-email-translations-v2 Update copy for PT email translations after Maria's edits. --- src/core/data/email/confirm-email_pt.yfm | 14 ++++++++------ src/core/data/email/email-exists-login_pt.yfm | 14 +++++++------- .../data/email/email-exists-set-password_pt.yfm | 14 +++++++++----- src/core/data/email/manual-to-automatic_pt.yfm | 15 ++++++--------- src/core/data/email/reset-device_pt.yfm | 17 +++++++++-------- src/core/data/email/reset-password_pt.yfm | 13 +++++++------ 6 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/core/data/email/confirm-email_pt.yfm b/src/core/data/email/confirm-email_pt.yfm index c87bfc1ca..5bde15f18 100644 --- a/src/core/data/email/confirm-email_pt.yfm +++ b/src/core/data/email/confirm-email_pt.yfm @@ -1,19 +1,21 @@ --- -subject: Confirme por favor o seu email +subject: Confirma o teu email ---

Olá *|FNAME|*,


-

Para confirmar o seu email siga o seguinte link:

+

+ Precisamos só de confirmar o teu email. Para confirmares, basta que carregues + neste link: +


- Confirmar morada de email + Confirmar email


- Nota: Se este email não foi solicitado por si pode simplesmente - ignorá-lo; alguém pode ter inserido o seu email por engano e a sua morada não - será gravada. + Nota: Se este email não foi pedido por ti, podes ignorá-lo. Alguém pode + ter inserido o teu email por engano e não o vamos gravar.


Até já!

diff --git a/src/core/data/email/email-exists-login_pt.yfm b/src/core/data/email/email-exists-login_pt.yfm index 1a626e51f..f3274272c 100644 --- a/src/core/data/email/email-exists-login_pt.yfm +++ b/src/core/data/email/email-exists-login_pt.yfm @@ -1,12 +1,12 @@ --- -subject: Já tem uma conta connosco +subject: Já tens uma conta criada ---

Olá *|FNAME|*,


- Tentou criar uma conta nova, mas já tem uma connosco. - Pode fazer login usando o seguinte link: + Tentaste criar uma nova conta, mas reparámos que já tens uma criada. + Podes fazer o login através deste link:


@@ -14,13 +14,13 @@ subject: Já tem uma conta connosco


- Nesse mesmo link vai poder escolher uma nova password, caso se tenha esquecido - dela. + Se te esqueceste da tua palavra-passe, vais poder criar uma nova através do + link acima.


- Nota: Os detalhes de pagamento que inseriu não foram gravados. Assim - que fizer login vai poder mudar os seus detalhes na hora. + Nota: Os dados de pagamento que inseriste não foram gravados. Assim que + fizeres login vais poder mudar os teus dados.


Até já!

diff --git a/src/core/data/email/email-exists-set-password_pt.yfm b/src/core/data/email/email-exists-set-password_pt.yfm index 64cae6147..135734665 100644 --- a/src/core/data/email/email-exists-set-password_pt.yfm +++ b/src/core/data/email/email-exists-set-password_pt.yfm @@ -1,12 +1,12 @@ --- -subject: Já tem uma conta connosco +subject: Já tens uma conta criada ---

Olá *|FNAME|*,


- Tentou criar uma conta nova, mas já tem uma connosco. - Pode usar o seguinte link para definir uma nova password e fazer login: + Tentaste criar uma nova conta, mas reparámos que já tens uma criada. + Podes fazer o login através deste link:


@@ -14,8 +14,12 @@ subject: Já tem uma conta connosco


- Nota: Os detalhes de pagamento que inseriu não foram gravados. Assim - que fizer login vai poder mudar os seus detalhes na hora. + Se te esqueceste da tua palavra-passe, vais poder criar uma nova através do + link acima. +

+

+ Nota: Os dados de pagamento que inseriste não foram gravados. Assim que + fizeres login vais poder mudar os teus dados.


Até já!

diff --git a/src/core/data/email/manual-to-automatic_pt.yfm b/src/core/data/email/manual-to-automatic_pt.yfm index e65791843..66218bbc5 100644 --- a/src/core/data/email/manual-to-automatic_pt.yfm +++ b/src/core/data/email/manual-to-automatic_pt.yfm @@ -1,22 +1,19 @@ --- -subject: O seu método de pagamento foi actualizado +subject: O teu método de pagamento foi actualizado ---

Olá *|FNAME|*,


-

- Obrigado por mudar para um pagamento automático no nosso sistema de - contribuições. -

+

Obrigada por atualizares o teu pagamento.


- Vamos cobrar a sua contribuição no próximo ciclo de pagamento. Lembre-se por - favor de cancelar outros pagamentos que tenha activos para evitar que pague - a dobrar. + A tua contribuição vai ser cobrada no próximo ciclo de pagamento. Lembra-te + de cancelar outras contribuições que possas ter ativas para evitar que + pagues a dobrar.


-

Caso tenha alguma pergunta não hesite em entrar em contacto connosco.

+

Se tiveres dúvidas, fala connosco.


Até já!

diff --git a/src/core/data/email/reset-device_pt.yfm b/src/core/data/email/reset-device_pt.yfm index be5cd64b1..b68bcabd2 100644 --- a/src/core/data/email/reset-device_pt.yfm +++ b/src/core/data/email/reset-device_pt.yfm @@ -1,23 +1,24 @@ --- -subject: Fazer reset da sua autenticação de múltiplos fatores +subject: Reset da tua autenticação de múltiplos fatores ---

Olá *|FNAME|*,


- Solicitou o reset da sua autenticação de múltiplos fatores. Para fazer reset - do seu dispositivo actual e definir um novoa, carregue no link seguinte: + Pediste um reset da tua autenticação de múltiplos fatores. Para fazeres reset + do teu dispositivo atual e definires um novo, carrega neste link:


Fazer reset do dispositivo MFA


-

- Nota: Caso não tenho solicitado este email, ou se for um erro, por - favor ignore o email ou entre em contacto connosco caso precise de ajuda. -

+

Nota: O link é válido por um curto espaço de tempo. Por favor não demore.


-

PS: O link é válido por um curto espaço de tempo. Por favor não demore.


Até já!

+


+

+ Nota: Se este reset não foi pedido por ti, ou se foi um erro, ignora + este email. Se tiveres dúvidas, entra em contacto connosco. +

diff --git a/src/core/data/email/reset-password_pt.yfm b/src/core/data/email/reset-password_pt.yfm index f2f800d7b..df39f2cd8 100644 --- a/src/core/data/email/reset-password_pt.yfm +++ b/src/core/data/email/reset-password_pt.yfm @@ -1,21 +1,22 @@ --- -subject: Fazer reset da sua password +subject: Define a nova palavra-passe ---

Olá *|FNAME|*,


- Recebemos o seu pedido para fazer reset da sua password. - Para o fazer, clique no seguinte link: + Recebemos o teu pedido para definires uma nova palavra-passe. + Para a atualizarmos, basta que carregues neste link:


- Fazer reset da password + Atualizar palavra-passe


- Nota: Se este email não foi solicitado por si pode simplesmente - ignorá-lo; alguém pode ter inserido o seu email por engano. + Nota: Se este email não foi pedido por ti, podes ignorá-lo. Alguém pode + ter inserido o teu email por engano.

+

Se tiveres dúvidas, fala connosco. :)


Até já!

From ec56c5bedb113eea55859c19972740337632a80c Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Fri, 15 Mar 2024 17:59:52 +0000 Subject: [PATCH 2/6] Merge pull request #382 from beabee-communityrm/fix/remove-console-log fix: remove console log --- src/apps/share/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/apps/share/app.ts b/src/apps/share/app.ts index c671128a4..2dfdb04b3 100644 --- a/src/apps/share/app.ts +++ b/src/apps/share/app.ts @@ -26,8 +26,6 @@ async function getCalloutShareSettings( .map((q) => q.split("=")) .find(([k]) => k === "lang")?.[1] || "default"; - console.log("slug", slug, "locale", locale); - const callout = await createQueryBuilder(Callout, "c") .innerJoinAndSelect("c.variants", "v", "v.name = :locale", { locale }) .where("c.slug = :slug", { slug }) From fdd6a321888387f3a9a5f530eebf109dffca01a4 Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Wed, 3 Apr 2024 16:31:12 +0100 Subject: [PATCH 3/6] Support passing VAT and billing address to Stripe --- src/api/controllers/SignupController.ts | 11 +++-- src/api/dto/SignupFlowDto.ts | 18 +++++++- src/core/providers/payment-flow/GCProvider.ts | 2 +- .../providers/payment-flow/StripeProvider.ts | 7 ++-- src/core/providers/payment-flow/index.ts | 5 +-- src/core/providers/payment/StripeProvider.ts | 42 ++++++++++++------- src/core/services/PaymentFlowService.ts | 2 +- src/core/services/PaymentService.ts | 2 +- src/models/JoinForm.ts | 19 ++++++--- 9 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/api/controllers/SignupController.ts b/src/api/controllers/SignupController.ts index 15e603c35..c3f56f32d 100644 --- a/src/api/controllers/SignupController.ts +++ b/src/api/controllers/SignupController.ts @@ -68,9 +68,14 @@ export class SignupController { throw new NotFoundError(); } - if (data.firstname || data.lastname) { - joinFlow.joinForm.firstname = data.firstname || null; - joinFlow.joinForm.lastname = data.lastname || null; + // Merge additional data into the join form + if ( + data.firstname || + data.lastname || + data.billingAddress || + data.vatNumber + ) { + Object.assign(joinFlow.joinForm, data); await getRepository(JoinFlow).save(joinFlow); } diff --git a/src/api/dto/SignupFlowDto.ts b/src/api/dto/SignupFlowDto.ts index adda78ab7..86ebda5c0 100644 --- a/src/api/dto/SignupFlowDto.ts +++ b/src/api/dto/SignupFlowDto.ts @@ -7,11 +7,14 @@ import { IsString } from "class-validator"; +import { UpdateAddressDto } from "@api/dto/AddressDto"; import { StartContributionDto } from "@api/dto/ContributionDto"; import { CompleteJoinFlowDto } from "@api/dto/JoinFlowDto"; import IsPassword from "@api/validators/IsPassword"; import IsUrl from "@api/validators/IsUrl"; +import type JoinForm from "@models/JoinForm"; + import { CompleteUrls } from "@type/complete-urls"; export class StartSignupFlowDto implements CompleteUrls { @@ -37,7 +40,11 @@ export class StartSignupFlowDto implements CompleteUrls { contribution?: StartContributionDto; } -export class CompleteSignupFlowDto extends CompleteJoinFlowDto { +export class CompleteSignupFlowDto + extends CompleteJoinFlowDto + implements + Pick +{ @IsOptional() @IsString() firstname?: string; @@ -45,4 +52,13 @@ export class CompleteSignupFlowDto extends CompleteJoinFlowDto { @IsOptional() @IsString() lastname?: string; + + @IsOptional() + @ValidateNested() + @Type(() => UpdateAddressDto) + billingAddress?: UpdateAddressDto; + + @IsOptional() + @IsString() + vatNumber?: string; } diff --git a/src/core/providers/payment-flow/GCProvider.ts b/src/core/providers/payment-flow/GCProvider.ts index 87eb1caf5..a4b5851e9 100644 --- a/src/core/providers/payment-flow/GCProvider.ts +++ b/src/core/providers/payment-flow/GCProvider.ts @@ -48,7 +48,7 @@ class GCProvider implements PaymentFlowProvider { log.info("Completed redirect flow " + redirectFlow.id); return { - paymentMethod: joinFlow.joinForm.paymentMethod, + joinForm: joinFlow.joinForm, customerId: redirectFlow.links!.customer!, mandateId: redirectFlow.links!.mandate! }; diff --git a/src/core/providers/payment-flow/StripeProvider.ts b/src/core/providers/payment-flow/StripeProvider.ts index 215338aa3..a07ca32f1 100644 --- a/src/core/providers/payment-flow/StripeProvider.ts +++ b/src/core/providers/payment-flow/StripeProvider.ts @@ -38,12 +38,13 @@ class StripeProvider implements PaymentFlowProvider { log.info("Fetched setup intent " + setupIntent.id); - const paymentMethod = setupIntent.payment_method as string; + const siPaymentMethodId = setupIntent.payment_method as string; return { - paymentMethod: joinFlow.joinForm.paymentMethod, + // paymentMethod: joinFlow.joinForm.paymentMethod, + joinForm: joinFlow.joinForm, customerId: "", // Not needed - mandateId: paymentMethod + mandateId: siPaymentMethodId }; } diff --git a/src/core/providers/payment-flow/index.ts b/src/core/providers/payment-flow/index.ts index 50700d167..663f98c53 100644 --- a/src/core/providers/payment-flow/index.ts +++ b/src/core/providers/payment-flow/index.ts @@ -1,7 +1,6 @@ -import { PaymentMethod } from "@beabee/beabee-common"; - import Address from "@models/Address"; import JoinFlow from "@models/JoinFlow"; +import JoinForm from "@models/JoinForm"; export interface PaymentFlow { id: string; @@ -20,7 +19,7 @@ export interface PaymentFlowData { } export interface CompletedPaymentFlow { - paymentMethod: PaymentMethod; + joinForm: JoinForm; customerId: string; mandateId: string; } diff --git a/src/core/providers/payment/StripeProvider.ts b/src/core/providers/payment/StripeProvider.ts index 03097d547..170438c80 100644 --- a/src/core/providers/payment/StripeProvider.ts +++ b/src/core/providers/payment/StripeProvider.ts @@ -80,28 +80,42 @@ export default class StripeProvider extends PaymentProvider { await this.updateData(); } - async updatePaymentMethod( - completedPaymentFlow: CompletedPaymentFlow - ): Promise { + async updatePaymentMethod(flow: CompletedPaymentFlow): Promise { + const customerData: Stripe.CustomerUpdateParams = { + invoice_settings: { + default_payment_method: flow.mandateId + }, + ...(flow.joinForm.billingAddress && { + address: { + line1: flow.joinForm.billingAddress.line1, + line2: flow.joinForm.billingAddress.line2 || "", + city: flow.joinForm.billingAddress.city, + postal_code: flow.joinForm.billingAddress.postcode + } + }) + }; + if (this.data.customerId) { log.info("Attach new payment source to " + this.data.customerId); - await stripe.paymentMethods.attach(completedPaymentFlow.mandateId, { + await stripe.paymentMethods.attach(flow.mandateId, { customer: this.data.customerId }); - await stripe.customers.update(this.data.customerId, { - invoice_settings: { - default_payment_method: completedPaymentFlow.mandateId - } - }); + await stripe.customers.update(this.data.customerId, customerData); } else { log.info("Create new customer"); const customer = await stripe.customers.create({ email: this.contact.email, name: `${this.contact.firstname} ${this.contact.lastname}`, - payment_method: completedPaymentFlow.mandateId, - invoice_settings: { - default_payment_method: completedPaymentFlow.mandateId - } + payment_method: flow.mandateId, + ...(flow.joinForm.vatNumber && { + tax_id_data: [ + { + type: "eu_vat", + value: flow.joinForm.vatNumber + } + ] + }), + ...customerData }); this.data.customerId = customer.id; } @@ -111,7 +125,7 @@ export default class StripeProvider extends PaymentProvider { await stripe.paymentMethods.detach(this.data.mandateId); } - this.data.mandateId = completedPaymentFlow.mandateId; + this.data.mandateId = flow.mandateId; await this.updateData(); } diff --git a/src/core/services/PaymentFlowService.ts b/src/core/services/PaymentFlowService.ts index 91fcf554c..acbcf3ec5 100644 --- a/src/core/services/PaymentFlowService.ts +++ b/src/core/services/PaymentFlowService.ts @@ -224,7 +224,7 @@ class PaymentFlowService implements PaymentFlowProvider { completedPaymentFlow: CompletedPaymentFlow ): Promise { return paymentProviders[ - completedPaymentFlow.paymentMethod + completedPaymentFlow.joinForm.paymentMethod ].getCompletedPaymentFlowData(completedPaymentFlow); } } diff --git a/src/core/services/PaymentService.ts b/src/core/services/PaymentService.ts index 2894ae1dd..8149546ed 100644 --- a/src/core/services/PaymentService.ts +++ b/src/core/services/PaymentService.ts @@ -176,7 +176,7 @@ class PaymentService { }); const data = await this.getData(contact); - const newMethod = completedPaymentFlow.paymentMethod; + const newMethod = completedPaymentFlow.joinForm.paymentMethod; if (data.method !== newMethod) { log.info( "Changing payment method, cancelling any previous contribution", diff --git a/src/models/JoinForm.ts b/src/models/JoinForm.ts index 6e3d05e44..96b39efdc 100644 --- a/src/models/JoinForm.ts +++ b/src/models/JoinForm.ts @@ -2,6 +2,7 @@ import { ContributionPeriod, PaymentMethod } from "@beabee/beabee-common"; import { Column } from "typeorm"; import { PaymentForm } from "@core/utils"; import Password from "./Password"; +import Address from "./Address"; export interface ReferralGiftForm { referralGift?: string | null; @@ -15,12 +16,6 @@ export default class JoinForm implements PaymentForm, ReferralGiftForm { @Column(() => Password) password!: Password; - @Column({ type: String, nullable: true }) - firstname?: string | null; - - @Column({ type: String, nullable: true }) - lastname?: string | null; - @Column({ type: "real" }) monthlyAmount!: number; @@ -36,6 +31,18 @@ export default class JoinForm implements PaymentForm, ReferralGiftForm { @Column() paymentMethod!: PaymentMethod; + @Column({ type: String, nullable: true }) + firstname?: string | null; + + @Column({ type: String, nullable: true }) + lastname?: string | null; + + @Column({ type: "jsonb", nullable: true }) + billingAddress?: Address | null; + + @Column({ nullable: true }) + vatNumber?: string | null; + @Column({ type: String, nullable: true }) referralCode?: string | null; From e261659af8ec8d3ab40d387e2f770c5d40cd11ac Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Wed, 3 Apr 2024 16:35:51 +0100 Subject: [PATCH 4/6] Move Address to type --- src/api/dto/AddressDto.ts | 3 ++- src/api/transformers/AddressTransformer.ts | 6 +++--- src/apps/gift/app.ts | 3 ++- src/apps/tools/apps/exports/exports/GiftsExport.ts | 3 ++- src/core/providers/payment-flow/index.ts | 3 ++- src/core/services/GiftService.ts | 7 ++++--- src/core/services/PaymentFlowService.ts | 3 +-- src/models/ContactProfile.ts | 3 ++- src/models/GiftFlow.ts | 3 ++- src/models/JoinForm.ts | 4 +++- src/tools/database/import-steady.ts | 3 ++- src/{models/Address.ts => type/address.ts} | 2 +- 12 files changed, 26 insertions(+), 17 deletions(-) rename src/{models/Address.ts => type/address.ts} (70%) diff --git a/src/api/dto/AddressDto.ts b/src/api/dto/AddressDto.ts index 0d4ba83ed..4eb3da926 100644 --- a/src/api/dto/AddressDto.ts +++ b/src/api/dto/AddressDto.ts @@ -1,6 +1,7 @@ -import Address from "@models/Address"; import { IsDefined, IsOptional, IsString } from "class-validator"; +import { Address } from "@type/address"; + export class UpdateAddressDto implements Address { @IsDefined() @IsString() diff --git a/src/api/transformers/AddressTransformer.ts b/src/api/transformers/AddressTransformer.ts index 76829b1d3..e35f979b6 100644 --- a/src/api/transformers/AddressTransformer.ts +++ b/src/api/transformers/AddressTransformer.ts @@ -1,8 +1,8 @@ -import { GetAddressDto } from "@api/dto/AddressDto"; - -import Address from "@models/Address"; import { TransformPlainToInstance } from "class-transformer"; +import { GetAddressDto } from "@api/dto/AddressDto"; +import { Address } from "@type/address"; + // TODO: make Address into a proper model class AddressTransformer { @TransformPlainToInstance(GetAddressDto) diff --git a/src/apps/gift/app.ts b/src/apps/gift/app.ts index dfeda504f..193452337 100644 --- a/src/apps/gift/app.ts +++ b/src/apps/gift/app.ts @@ -11,9 +11,10 @@ import GiftService from "@core/services/GiftService"; import ContactsService from "@core/services/ContactsService"; import OptionsService from "@core/services/OptionsService"; -import Address from "@models/Address"; import GiftFlow, { GiftForm } from "@models/GiftFlow"; +import { Address } from "@type/address"; + import { createGiftSchema, updateGiftAddressSchema } from "./schema.json"; const app = express(); diff --git a/src/apps/tools/apps/exports/exports/GiftsExport.ts b/src/apps/tools/apps/exports/exports/GiftsExport.ts index 1d481cb55..8b3e618e2 100644 --- a/src/apps/tools/apps/exports/exports/GiftsExport.ts +++ b/src/apps/tools/apps/exports/exports/GiftsExport.ts @@ -3,9 +3,10 @@ import { SelectQueryBuilder } from "typeorm"; import { createQueryBuilder } from "@core/database"; -import Address from "@models/Address"; import GiftFlow from "@models/GiftFlow"; +import { Address } from "@type/address"; + import BaseExport, { ExportResult } from "./BaseExport"; function addressFields(address: Address | null) { diff --git a/src/core/providers/payment-flow/index.ts b/src/core/providers/payment-flow/index.ts index 663f98c53..b9d76f68a 100644 --- a/src/core/providers/payment-flow/index.ts +++ b/src/core/providers/payment-flow/index.ts @@ -1,7 +1,8 @@ -import Address from "@models/Address"; import JoinFlow from "@models/JoinFlow"; import JoinForm from "@models/JoinForm"; +import { Address } from "@type/address"; + export interface PaymentFlow { id: string; params: PaymentFlowParams; diff --git a/src/core/services/GiftService.ts b/src/core/services/GiftService.ts index aa21618fb..332b78f3b 100644 --- a/src/core/services/GiftService.ts +++ b/src/core/services/GiftService.ts @@ -1,22 +1,23 @@ import { ContributionType, NewsletterStatus } from "@beabee/beabee-common"; import muhammara from "muhammara"; import moment from "moment"; -import { getRepository } from "typeorm"; +import { getRepository } from "@core/database"; import { log as mainLogger } from "@core/logging"; import stripe from "@core/lib/stripe"; import { isDuplicateIndex } from "@core/utils"; +import { generateContactCode } from "@core/utils/contact"; import EmailService from "@core/services/EmailService"; import ContactsService from "@core/services/ContactsService"; import OptionsService from "@core/services/OptionsService"; -import Address from "@models/Address"; import GiftFlow, { GiftForm } from "@models/GiftFlow"; import ContactRole from "@models/ContactRole"; import config from "@config"; -import { generateContactCode } from "@core/utils/contact"; + +import { Address } from "@type/address"; const log = mainLogger.child({ app: "gift-service" }); diff --git a/src/core/services/PaymentFlowService.ts b/src/core/services/PaymentFlowService.ts index acbcf3ec5..1fe4e58e6 100644 --- a/src/core/services/PaymentFlowService.ts +++ b/src/core/services/PaymentFlowService.ts @@ -8,8 +8,6 @@ import ContactsService from "@core/services/ContactsService"; import OptionsService from "@core/services/OptionsService"; import PaymentService from "@core/services/PaymentService"; import ResetSecurityFlowService from "./ResetSecurityFlowService"; - -import Address from "@models/Address"; import JoinFlow from "@models/JoinFlow"; import JoinForm from "@models/JoinForm"; import Contact from "@models/Contact"; @@ -29,6 +27,7 @@ import DuplicateEmailError from "@api/errors/DuplicateEmailError"; import { RESET_SECURITY_FLOW_TYPE } from "@enums/reset-security-flow-type"; +import { Address } from "@type/address"; import { CompleteUrls } from "@type/complete-urls"; const paymentProviders = { diff --git a/src/models/ContactProfile.ts b/src/models/ContactProfile.ts index ad52af0da..7c3a68951 100644 --- a/src/models/ContactProfile.ts +++ b/src/models/ContactProfile.ts @@ -1,9 +1,10 @@ import { NewsletterStatus } from "@beabee/beabee-common"; import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from "typeorm"; -import type Address from "./Address"; import type Contact from "./Contact"; +import { Address } from "@type/address"; + @Entity() export default class ContactProfile { @PrimaryColumn() diff --git a/src/models/GiftFlow.ts b/src/models/GiftFlow.ts index 654a1a78b..36aa94099 100644 --- a/src/models/GiftFlow.ts +++ b/src/models/GiftFlow.ts @@ -6,9 +6,10 @@ import { PrimaryGeneratedColumn } from "typeorm"; -import type Address from "./Address"; import type Contact from "./Contact"; +import { Address } from "@type/address"; + export class GiftForm { @Column() firstname!: string; diff --git a/src/models/JoinForm.ts b/src/models/JoinForm.ts index 96b39efdc..25fa0801a 100644 --- a/src/models/JoinForm.ts +++ b/src/models/JoinForm.ts @@ -1,8 +1,10 @@ import { ContributionPeriod, PaymentMethod } from "@beabee/beabee-common"; import { Column } from "typeorm"; + import { PaymentForm } from "@core/utils"; import Password from "./Password"; -import Address from "./Address"; + +import { Address } from "@type/address"; export interface ReferralGiftForm { referralGift?: string | null; diff --git a/src/tools/database/import-steady.ts b/src/tools/database/import-steady.ts index 563d8f1ec..b1330fe2c 100644 --- a/src/tools/database/import-steady.ts +++ b/src/tools/database/import-steady.ts @@ -14,10 +14,11 @@ import { cleanEmailAddress } from "@core/utils"; import ContactsService from "@core/services/ContactsService"; -import Address from "@models/Address"; import Contact from "@models/Contact"; import ContactRole from "@models/ContactRole"; +import { Address } from "@type/address"; + const headers = [ "first_name", "last_name", diff --git a/src/models/Address.ts b/src/type/address.ts similarity index 70% rename from src/models/Address.ts rename to src/type/address.ts index b9c0b30c2..44c1e1bea 100644 --- a/src/models/Address.ts +++ b/src/type/address.ts @@ -1,4 +1,4 @@ -export default interface Address { +export interface Address { line1: string; line2?: string | undefined; city: string; From 500d22fb0c7752b0a916c0e416adb238f76d133d Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Wed, 3 Apr 2024 16:40:35 +0100 Subject: [PATCH 5/6] Add migration --- .../1712158796671-AddExtraJoinFormFields.ts | 23 +++++++++++++++++++ src/models/JoinForm.ts | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/migrations/1712158796671-AddExtraJoinFormFields.ts diff --git a/src/migrations/1712158796671-AddExtraJoinFormFields.ts b/src/migrations/1712158796671-AddExtraJoinFormFields.ts new file mode 100644 index 000000000..d554eddaf --- /dev/null +++ b/src/migrations/1712158796671-AddExtraJoinFormFields.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddExtraJoinFormFields1712158796671 implements MigrationInterface { + name = "AddExtraJoinFormFields1712158796671"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "join_flow" ADD "joinFormBillingaddress" jsonb` + ); + await queryRunner.query( + `ALTER TABLE "join_flow" ADD "joinFormVatnumber" character varying` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "join_flow" DROP COLUMN "joinFormVatnumber"` + ); + await queryRunner.query( + `ALTER TABLE "join_flow" DROP COLUMN "joinFormBillingaddress"` + ); + } +} diff --git a/src/models/JoinForm.ts b/src/models/JoinForm.ts index 25fa0801a..b70d240bd 100644 --- a/src/models/JoinForm.ts +++ b/src/models/JoinForm.ts @@ -42,7 +42,7 @@ export default class JoinForm implements PaymentForm, ReferralGiftForm { @Column({ type: "jsonb", nullable: true }) billingAddress?: Address | null; - @Column({ nullable: true }) + @Column({ type: String, nullable: true }) vatNumber?: string | null; @Column({ type: String, nullable: true }) From 0be11c1e5285c9406fb8fdc594ac9cd7c60f5292 Mon Sep 17 00:00:00 2001 From: Will Franklin Date: Wed, 3 Apr 2024 17:55:42 +0100 Subject: [PATCH 6/6] Get billing address from Stripe --- src/api/controllers/SignupController.ts | 7 +------ src/api/dto/SignupFlowDto.ts | 8 +------ src/core/providers/payment/StripeProvider.ts | 21 ++++++++++++------- ...2163090314-RemoveJoinFormBillingAddress.ts | 19 +++++++++++++++++ src/models/JoinForm.ts | 3 --- 5 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 src/migrations/1712163090314-RemoveJoinFormBillingAddress.ts diff --git a/src/api/controllers/SignupController.ts b/src/api/controllers/SignupController.ts index c3f56f32d..010a73c73 100644 --- a/src/api/controllers/SignupController.ts +++ b/src/api/controllers/SignupController.ts @@ -69,12 +69,7 @@ export class SignupController { } // Merge additional data into the join form - if ( - data.firstname || - data.lastname || - data.billingAddress || - data.vatNumber - ) { + if (data.firstname || data.lastname || data.vatNumber) { Object.assign(joinFlow.joinForm, data); await getRepository(JoinFlow).save(joinFlow); } diff --git a/src/api/dto/SignupFlowDto.ts b/src/api/dto/SignupFlowDto.ts index 86ebda5c0..e4d5b51cf 100644 --- a/src/api/dto/SignupFlowDto.ts +++ b/src/api/dto/SignupFlowDto.ts @@ -42,8 +42,7 @@ export class StartSignupFlowDto implements CompleteUrls { export class CompleteSignupFlowDto extends CompleteJoinFlowDto - implements - Pick + implements Pick { @IsOptional() @IsString() @@ -53,11 +52,6 @@ export class CompleteSignupFlowDto @IsString() lastname?: string; - @IsOptional() - @ValidateNested() - @Type(() => UpdateAddressDto) - billingAddress?: UpdateAddressDto; - @IsOptional() @IsString() vatNumber?: string; diff --git a/src/core/providers/payment/StripeProvider.ts b/src/core/providers/payment/StripeProvider.ts index 170438c80..c933048cf 100644 --- a/src/core/providers/payment/StripeProvider.ts +++ b/src/core/providers/payment/StripeProvider.ts @@ -81,18 +81,23 @@ export default class StripeProvider extends PaymentProvider { } async updatePaymentMethod(flow: CompletedPaymentFlow): Promise { + const paymentMethod = await stripe.paymentMethods.retrieve(flow.mandateId); + const address = paymentMethod.billing_details.address; + const customerData: Stripe.CustomerUpdateParams = { invoice_settings: { default_payment_method: flow.mandateId }, - ...(flow.joinForm.billingAddress && { - address: { - line1: flow.joinForm.billingAddress.line1, - line2: flow.joinForm.billingAddress.line2 || "", - city: flow.joinForm.billingAddress.city, - postal_code: flow.joinForm.billingAddress.postcode - } - }) + address: address + ? { + line1: address.line1 || "", + ...(address.city && { city: address.city }), + ...(address.country && { country: address.country }), + ...(address.line2 && { line2: address.line2 }), + ...(address.postal_code && { postal_code: address.postal_code }), + ...(address.state && { state: address.state }) + } + : null }; if (this.data.customerId) { diff --git a/src/migrations/1712163090314-RemoveJoinFormBillingAddress.ts b/src/migrations/1712163090314-RemoveJoinFormBillingAddress.ts new file mode 100644 index 000000000..0039f2eee --- /dev/null +++ b/src/migrations/1712163090314-RemoveJoinFormBillingAddress.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class RemoveJoinFormBillingAddress1712163090314 + implements MigrationInterface +{ + name = "RemoveJoinFormBillingAddress1712163090314"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "join_flow" DROP COLUMN "joinFormBillingaddress"` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "join_flow" ADD "joinFormBillingaddress" jsonb` + ); + } +} diff --git a/src/models/JoinForm.ts b/src/models/JoinForm.ts index b70d240bd..fabdae268 100644 --- a/src/models/JoinForm.ts +++ b/src/models/JoinForm.ts @@ -39,9 +39,6 @@ export default class JoinForm implements PaymentForm, ReferralGiftForm { @Column({ type: String, nullable: true }) lastname?: string | null; - @Column({ type: "jsonb", nullable: true }) - billingAddress?: Address | null; - @Column({ type: String, nullable: true }) vatNumber?: string | null;