diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json
index 53058b55bb..15af6d8ab8 100644
--- a/src/app/i18n/locales/de.json
+++ b/src/app/i18n/locales/de.json
@@ -287,7 +287,12 @@
"cryptoRequiresSignIn": "Melde dich an, um mit Kryptowährung zu bezahlen.",
"resendEmail": "E-Mail erneut senden",
"emailResent": "Wir haben dir die E-Mail erneut gesendet",
- "resendEmailError": "Wir konnten die E-Mail nicht senden. Bitte versuche es später erneut."
+ "resendEmailError": "Wir konnten die E-Mail nicht senden. Bitte versuche es später erneut.",
+ "checkEmail": {
+ "title": "Prüfe deine E-Mails",
+ "description": "Deine Zahlung war erfolgreich. Wir haben einen Link zum Abschließen der Kontoeinrichtung gesendet an:",
+ "spamHint": "Nicht gefunden? Sieh in deinem Spam-Ordner nach."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json
index 19273f6e47..46ea8a29ce 100644
--- a/src/app/i18n/locales/en.json
+++ b/src/app/i18n/locales/en.json
@@ -308,7 +308,12 @@
"cryptoRequiresSignIn": "Log in to pay with crypto.",
"resendEmail": "Resend email",
"emailResent": "We've sent you the email again",
- "resendEmailError": "We couldn't send the email. Please try again later."
+ "resendEmailError": "We couldn't send the email. Please try again later.",
+ "checkEmail": {
+ "title": "Check your email",
+ "description": "Your payment was successful. We've sent a link to finish setting up your account to:",
+ "spamHint": "Can't find it? Check your spam folder."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json
index 79211c9184..5d986651ff 100644
--- a/src/app/i18n/locales/es.json
+++ b/src/app/i18n/locales/es.json
@@ -287,7 +287,12 @@
"cryptoRequiresSignIn": "Inicia sesión para pagar con criptomonedas.",
"resendEmail": "Reenviar correo",
"emailResent": "Te hemos vuelto a enviar el correo",
- "resendEmailError": "No hemos podido enviar el correo. Inténtalo de nuevo más tarde."
+ "resendEmailError": "No hemos podido enviar el correo. Inténtalo de nuevo más tarde.",
+ "checkEmail": {
+ "title": "Revisa tu correo",
+ "description": "El pago se ha completado. Hemos enviado un enlace para terminar de configurar tu cuenta a:",
+ "spamHint": "¿No lo encuentras? Revisa la carpeta de spam."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json
index 7c4d971e90..a90f36deda 100644
--- a/src/app/i18n/locales/fr.json
+++ b/src/app/i18n/locales/fr.json
@@ -287,7 +287,12 @@
"cryptoRequiresSignIn": "Connectez-vous pour payer en cryptomonnaie.",
"resendEmail": "Renvoyer l'e-mail",
"emailResent": "Nous vous avons renvoyé l'e-mail",
- "resendEmailError": "Nous n'avons pas pu envoyer l'e-mail. Veuillez réessayer plus tard."
+ "resendEmailError": "Nous n'avons pas pu envoyer l'e-mail. Veuillez réessayer plus tard.",
+ "checkEmail": {
+ "title": "Consultez vos e-mails",
+ "description": "Votre paiement a été effectué. Nous avons envoyé un lien pour finaliser la configuration de votre compte à :",
+ "spamHint": "Vous ne le trouvez pas ? Vérifiez votre dossier de spam."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json
index 7dbee2fb86..be17f2da81 100644
--- a/src/app/i18n/locales/it.json
+++ b/src/app/i18n/locales/it.json
@@ -308,7 +308,12 @@
"cryptoRequiresSignIn": "Accedi per pagare con criptovalute.",
"resendEmail": "Invia di nuovo l'email",
"emailResent": "Ti abbiamo inviato di nuovo l'email",
- "resendEmailError": "Non siamo riusciti a inviare l'email. Riprova più tardi."
+ "resendEmailError": "Non siamo riusciti a inviare l'email. Riprova più tardi.",
+ "checkEmail": {
+ "title": "Controlla la tua email",
+ "description": "Il pagamento è andato a buon fine. Abbiamo inviato un link per completare la configurazione del tuo account a:",
+ "spamHint": "Non la trovi? Controlla la cartella dello spam."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/pt.json b/src/app/i18n/locales/pt.json
index 224acb5bd9..ce35ea0818 100644
--- a/src/app/i18n/locales/pt.json
+++ b/src/app/i18n/locales/pt.json
@@ -308,7 +308,12 @@
"cryptoRequiresSignIn": "Faça login para pagar com criptomoedas.",
"resendEmail": "Reenviar e-mail",
"emailResent": "Enviamos o e-mail novamente",
- "resendEmailError": "Não foi possível enviar o e-mail. Tente novamente mais tarde."
+ "resendEmailError": "Não foi possível enviar o e-mail. Tente novamente mais tarde.",
+ "checkEmail": {
+ "title": "Verifique seu e-mail",
+ "description": "Seu pagamento foi concluído. Enviamos um link para concluir a configuração da sua conta para:",
+ "spamHint": "Não encontrou? Verifique sua pasta de spam."
+ }
}
},
"urgentCheckout": {
@@ -1582,6 +1587,30 @@
}
}
},
+ "accountSetup": {
+ "form": {
+ "title": "Configure sua conta",
+ "description": "Crie uma senha para concluir a configuração da sua conta Internxt.",
+ "confirmPassword": "Confirmar senha",
+ "passwordsDoNotMatch": "As senhas não coincidem",
+ "submit": "Criar conta",
+ "genericError": "Algo deu errado ao configurar sua conta. Tente novamente."
+ },
+ "invalidLink": {
+ "title": "Este link não é mais válido",
+ "description": "O link expirou ou já foi usado. Informe seu e-mail e enviaremos um novo.",
+ "emailLabel": "E-mail",
+ "resendButton": "Enviar um novo link",
+ "resendError": "Não foi possível enviar o e-mail. Tente novamente.",
+ "emailSentTitle": "Verifique seu e-mail",
+ "emailSentDescription": "Se {{email}} tiver uma conta aguardando configuração, você receberá um novo link em breve."
+ },
+ "accountAlreadyExists": {
+ "title": "Sua conta já está configurada",
+ "description": "Esta conta já existe. Faça login com seu e-mail e senha para acessá-la.",
+ "loginButton": "Ir para o login"
+ }
+ },
"blockedAccount": {
"title": "Confirme que é você",
"text1": "Houve tentativas de login malsucedidas demais.",
diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json
index 09ec045291..a8ff02225b 100644
--- a/src/app/i18n/locales/ru.json
+++ b/src/app/i18n/locales/ru.json
@@ -287,7 +287,12 @@
"cryptoRequiresSignIn": "Войдите, чтобы оплатить криптовалютой.",
"resendEmail": "Отправить письмо ещё раз",
"emailResent": "Мы отправили письмо ещё раз",
- "resendEmailError": "Не удалось отправить письмо. Повторите попытку позже."
+ "resendEmailError": "Не удалось отправить письмо. Повторите попытку позже.",
+ "checkEmail": {
+ "title": "Проверьте почту",
+ "description": "Оплата прошла успешно. Мы отправили ссылку для завершения настройки аккаунта на адрес:",
+ "spamHint": "Не нашли письмо? Проверьте папку «Спам»."
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json
index 3d804dd383..952bd37002 100644
--- a/src/app/i18n/locales/tw.json
+++ b/src/app/i18n/locales/tw.json
@@ -308,7 +308,12 @@
"cryptoRequiresSignIn": "請登入後使用加密貨幣付款。",
"resendEmail": "重新寄送郵件",
"emailResent": "我們已重新寄送郵件",
- "resendEmailError": "無法寄送郵件,請稍後再試。"
+ "resendEmailError": "無法寄送郵件,請稍後再試。",
+ "checkEmail": {
+ "title": "請查看你的電子郵件",
+ "description": "付款成功。我們已將完成帳戶設定的連結寄送至:",
+ "spamHint": "找不到郵件?請檢查垃圾郵件資料夾。"
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json
index 180341a9ec..ab159b1688 100644
--- a/src/app/i18n/locales/zh.json
+++ b/src/app/i18n/locales/zh.json
@@ -308,7 +308,12 @@
"cryptoRequiresSignIn": "请登录后使用加密货币付款。",
"resendEmail": "重新发送邮件",
"emailResent": "我们已重新发送邮件",
- "resendEmailError": "无法发送邮件,请稍后重试。"
+ "resendEmailError": "无法发送邮件,请稍后重试。",
+ "checkEmail": {
+ "title": "请查看你的电子邮件",
+ "description": "付款成功。我们已将完成账户设置的链接发送至:",
+ "spamHint": "找不到邮件?请检查垃圾邮件文件夹。"
+ }
}
},
"urgentCheckout": {
diff --git a/src/app/routes/paths.json b/src/app/routes/paths.json
index f21ed58f5e..ce78233a25 100644
--- a/src/app/routes/paths.json
+++ b/src/app/routes/paths.json
@@ -154,7 +154,7 @@
"layout": "empty",
"path": "/checkout/success",
"exact": false,
- "auth": true
+ "auth": false
},
{
"id": "checkout-cancel",
diff --git a/src/views/AccountSetup/AccountSetupView.test.tsx b/src/views/AccountSetup/AccountSetupView.test.tsx
index cfbe145a9b..9062fd1337 100644
--- a/src/views/AccountSetup/AccountSetupView.test.tsx
+++ b/src/views/AccountSetup/AccountSetupView.test.tsx
@@ -27,7 +27,7 @@ vi.mock('services/encrypted-storage.service', () => ({
default: { setToken: vi.fn(), getToken: vi.fn(), getUser: vi.fn(), clear: vi.fn() },
}));
vi.mock('services/local-storage.service', () => ({
- default: { get: vi.fn(), set: vi.fn(), clear: vi.fn() },
+ default: { get: vi.fn(), set: vi.fn(), clear: vi.fn(), clearExcept: vi.fn() },
}));
vi.mock('app/store/slices/user', () => ({
initializeUserThunk: vi.fn(),
diff --git a/src/views/AccountSetup/services/completeAccountSetup.test.ts b/src/views/AccountSetup/services/completeAccountSetup.test.ts
index 6322e71fe3..2401bdab0e 100644
--- a/src/views/AccountSetup/services/completeAccountSetup.test.ts
+++ b/src/views/AccountSetup/services/completeAccountSetup.test.ts
@@ -1,5 +1,6 @@
import { CompleteAccountSetupPayload } from '@internxt/sdk/dist/auth/types';
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
+import { LocalStorageItem } from 'app/core/types';
import { decryptText, decryptTextWithKey, passToHash } from 'app/crypto/services/utils';
import { planThunks } from 'app/store/slices/plan';
import { userThunks } from 'app/store/slices/user';
@@ -7,6 +8,7 @@ import { validateMnemonic } from 'bip39';
import { Buffer } from 'node:buffer';
import encryptedStorageService from 'services/encrypted-storage.service';
import envService from 'services/env.service';
+import localStorageService from 'services/local-storage.service';
import { getCompleteAccountSetupResponse } from 'testUtils/fixtures/accountSetup.fixtures';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { completeAccountSetup } from './completeAccountSetup';
@@ -23,7 +25,7 @@ vi.mock('services/encrypted-storage.service', () => ({
default: { setToken: vi.fn(), getToken: vi.fn(), getUser: vi.fn(), clear: vi.fn() },
}));
vi.mock('services/local-storage.service', () => ({
- default: { get: vi.fn(), set: vi.fn(), clear: vi.fn(), clearExcept: vi.fn() },
+ default: { get: vi.fn(), set: vi.fn(), clear: vi.fn(), clearExcept: vi.fn(), removeItem: vi.fn() },
}));
vi.mock('app/store/slices/user', () => ({
initializeUserThunk: vi.fn(),
@@ -101,5 +103,13 @@ describe('Completing the setup of a paid account', () => {
);
expect(encryptedStorageService.setToken).not.toHaveBeenCalled();
expect(userThunks.setUserThunk).not.toHaveBeenCalled();
+ expect(localStorageService.clearExcept).not.toHaveBeenCalled();
+ });
+
+ test('When the account is set up, then the checkout "check your email" screen is no longer shown on a later visit', async () => {
+ await completeAccountSetup({ setupToken: SETUP_TOKEN, password: PASSWORD, dispatch: vi.fn() });
+
+ const [keysToPreserve] = vi.mocked(localStorageService.clearExcept).mock.calls[0];
+ expect(keysToPreserve).not.toContain(LocalStorageItem.CheckoutAccountSetupEmail);
});
});
diff --git a/src/views/AccountSetup/services/completeAccountSetup.ts b/src/views/AccountSetup/services/completeAccountSetup.ts
index 17df26a60c..6595992209 100644
--- a/src/views/AccountSetup/services/completeAccountSetup.ts
+++ b/src/views/AccountSetup/services/completeAccountSetup.ts
@@ -20,7 +20,7 @@ export const completeAccountSetup = async ({ setupToken, password, dispatch }: C
const { user, token, newToken } = await authClient.completeAccountSetup({ token: setupToken, ...credentials });
const mnemonic = decryptTextWithKey(user.mnemonic, password);
- return startNewAccountSession({
+ const session = await startNewAccountSession({
registeredUser: {
xUser: { ...user, mnemonic } as unknown as UserSettings,
xToken: token,
@@ -31,4 +31,6 @@ export const completeAccountSetup = async ({ setupToken, password, dispatch }: C
redeemCodeObject: false,
dispatch,
});
+
+ return session;
};
diff --git a/src/views/Checkout/views/CheckoutSuccessView.test.tsx b/src/views/Checkout/views/CheckoutSuccessView.test.tsx
index 30f12e29c6..a3ca896958 100644
--- a/src/views/Checkout/views/CheckoutSuccessView.test.tsx
+++ b/src/views/Checkout/views/CheckoutSuccessView.test.tsx
@@ -1,11 +1,13 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
-import { render, waitFor } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
import CheckoutSuccessView from './CheckoutSuccessView';
-import { AppView } from 'app/core/types';
+import { AppView, LocalStorageItem } from 'app/core/types';
import { PURCHASE_LOCAL_STORAGE_ITEMS } from 'services/storage-keys';
const mocks = vi.hoisted(() => ({
localStorageRemoveItem: vi.fn(),
+ localStorageGet: vi.fn(),
+ getSessionToken: vi.fn(),
navigationPush: vi.fn(),
retrievePaymentIntent: vi.fn(),
retrieveSetupIntent: vi.fn(),
@@ -19,7 +21,13 @@ const mocks = vi.hoisted(() => ({
vi.mock('app/store/hooks', () => ({ useAppDispatch: () => vi.fn() }));
vi.mock('services/navigation.service', () => ({ default: { push: mocks.navigationPush } }));
-vi.mock('services/local-storage.service', () => ({ default: { removeItem: mocks.localStorageRemoveItem } }));
+vi.mock('services/local-storage.service', () => ({
+ default: { removeItem: mocks.localStorageRemoveItem, get: mocks.localStorageGet },
+}));
+vi.mock('services/encrypted-storage.service', () => ({ default: { getToken: mocks.getSessionToken } }));
+vi.mock('../components/ResendAccountSetupEmailButton', () => ({
+ ResendAccountSetupEmailButton: () => ,
+}));
vi.mock('app/analytics/meta.service', () => ({ default: { trackPurchase: mocks.metaTrackPurchase } }));
vi.mock('app/analytics/ga.service', () => ({ default: { trackPurchase: mocks.gaTrackPurchase } }));
vi.mock('app/analytics/impact.service', () => ({ trackPaymentConversion: mocks.trackPaymentConversion }));
@@ -94,9 +102,44 @@ const expectCheckoutFinished = () => {
describe('Checkout success view', () => {
beforeEach(() => {
vi.clearAllMocks();
+ mocks.getSessionToken.mockReturnValue('session_token');
+ mocks.localStorageGet.mockReturnValue(null);
landOn(SUCCESS_PATH);
});
+ describe('When a buyer without an account finishes paying', () => {
+ beforeEach(() => {
+ mocks.getSessionToken.mockReturnValue(undefined);
+ mocks.localStorageGet.mockImplementation((key: string) =>
+ key === LocalStorageItem.CheckoutAccountSetupEmail ? 'new.buyer@internxt.com' : null,
+ );
+ });
+
+ test('then they are asked to check the email the setup link was sent to, and they stay on the page', async () => {
+ render(
{translate('checkout.accountSetup.checkEmail.description')}
+{email}
+{translate('checkout.accountSetup.checkEmail.spamHint')}
+