Skip to content

Commit

Permalink
fix: Frontend parts for card (#1763)
Browse files Browse the repository at this point in the history
* Frintend parts for card

* Update [accountId].tsx

* feat: send KYC verified email

* add T&C

* feat: add card terms

* Update signup.tsx

* Update signup.tsx

* fix: test

---------

Co-authored-by: dragosp1011 <[email protected]>
  • Loading branch information
Tymmmy and dragosp1011 authored Oct 23, 2024
1 parent bf2cc56 commit e34a0a6
Show file tree
Hide file tree
Showing 25 changed files with 335 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.table('users', function (table) {
table.boolean('acceptedCardTerms').defaultTo(false)
})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.table('users', function (table) {
table.dropColumn('acceptedCardTerms')
})
}
21 changes: 17 additions & 4 deletions packages/wallet/backend/src/auth/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { UserService } from '@/user/service'
import { getRandomToken, hashToken } from '@/utils/helpers'
import { EmailService } from '@/email/service'
import { Logger } from 'winston'
import { Unauthorized, NotVerified } from '@shared/backend'
import { Unauthorized, NotVerified, BadRequest } from '@shared/backend'
import { GateHubClient } from '@/gatehub/client'

interface resendVerifyEmailArgs {
Expand All @@ -17,7 +17,9 @@ interface AuthorizeArgs {
password: string
}

interface SignUpArgs extends AuthorizeArgs {}
interface SignUpArgs extends AuthorizeArgs {
acceptedCardTerms?: boolean
}

interface AuthorizeResult {
user: User
Expand All @@ -38,7 +40,11 @@ export class AuthService implements IAuthService {
private env: Env
) {}

async signUp({ email, password }: SignUpArgs): Promise<User> {
async signUp({
email,
password,
acceptedCardTerms
}: SignUpArgs): Promise<User> {
const domain = email.split('@')[1]
await this.emailService.verifyDomain(domain)

Expand All @@ -54,13 +60,20 @@ export class AuthService implements IAuthService {
if (!gateHubUser) {
throw new Error('You are not allowed to sign up.')
}

if (!acceptedCardTerms) {
throw new BadRequest(
'Additional terms and condition should be accepted'
)
}
}

const token = getRandomToken()
const user = await this.userService.create({
email,
password,
verifyEmailToken: hashToken(token)
verifyEmailToken: hashToken(token),
acceptedCardTerms
})

await this.emailService.sendVerifyEmail(email, token).catch((e) => {
Expand Down
40 changes: 32 additions & 8 deletions packages/wallet/backend/src/email/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import dns from 'dns'
import domains from 'disposable-email-domains'
import { BadRequest } from '@shared/backend'
import { getRejectEmailTemplate } from '@/email/templates/rejectEmail'
import { getActionRequiredEmailTemplate } from './templates/actionRequiredEmail'
import { getKYCVerificationEmailTemplate } from './templates/kycVerifiedEmail'

interface EmailArgs {
to: string
Expand All @@ -23,6 +25,9 @@ interface IEmailService {
export class EmailService implements IEmailService {
private readonly baseUrl: string
private readonly from: MailDataRequired['from']
private imageSrc: string =
'https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerTestWallet.png'
private subjectPrefix: string = 'Test.Wallet'

constructor(
private env: Env,
Expand All @@ -40,6 +45,12 @@ export class EmailService implements IEmailService {
email: this.env.FROM_EMAIL,
name: 'Tech Interledger'
}

if (this.env.GATEHUB_ENV === 'production') {
this.imageSrc =
'https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerWallet.png'
this.subjectPrefix = 'Interledger Wallet'
}
}

private async send(email: EmailArgs): Promise<void> {
Expand All @@ -52,8 +63,8 @@ export class EmailService implements IEmailService {
if (this.env.SEND_EMAIL) {
return this.send({
to,
subject: '[Test.Wallet] Reset your password',
html: getForgotPasswordEmailTemplate(url)
subject: `[${this.subjectPrefix}] Reset your password`,
html: getForgotPasswordEmailTemplate(url, this.imageSrc)
})
}

Expand All @@ -66,8 +77,8 @@ export class EmailService implements IEmailService {
if (this.env.SEND_EMAIL) {
return this.send({
to,
subject: '[Test.Wallet] Verify your account',
html: getVerifyEmailTemplate(url)
subject: `[${this.subjectPrefix}] Verify your account`,
html: getVerifyEmailTemplate(url, this.imageSrc)
})
}

Expand All @@ -78,8 +89,8 @@ export class EmailService implements IEmailService {
if (this.env.SEND_EMAIL) {
return this.send({
to,
subject: '[Test.Wallet] Account rejected',
html: getRejectEmailTemplate(textHtml)
subject: `[${this.subjectPrefix}] Account rejected`,
html: getRejectEmailTemplate(textHtml, this.imageSrc)
})
}

Expand All @@ -90,8 +101,8 @@ export class EmailService implements IEmailService {
if (this.env.SEND_EMAIL) {
return this.send({
to,
subject: '[Test.Wallet] Action required',
html: getRejectEmailTemplate(textHtml)
subject: `[${this.subjectPrefix}] Action required`,
html: getActionRequiredEmailTemplate(textHtml, this.imageSrc)
})
}

Expand All @@ -100,6 +111,19 @@ export class EmailService implements IEmailService {
)
}

async sendKYCVerifiedEmail(to: string): Promise<void> {
if (this.env.SEND_EMAIL) {
const loginUrl = `${this.baseUrl}/auth/login`
return this.send({
to,
subject: `[${this.subjectPrefix}] You are verified`,
html: getKYCVerificationEmailTemplate(loginUrl, this.imageSrc)
})
}

this.logger.info(`Send email is disabled. KYC verified email was not sent`)
}

public async verifyDomain(domain: string): Promise<void> {
try {
if (this.isDisposableDomain(domain)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const getActionRequiredEmailTemplate = (textHtml: string): string => {
export const getActionRequiredEmailTemplate = (
textHtml: string,
imageSrc: string
): string => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<html lang="en">
Expand All @@ -10,7 +13,7 @@ export const getActionRequiredEmailTemplate = (textHtml: string): string => {
<table style="margin-top:32px;text-align:center;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Test Wallet" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerTestWallet.png" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Wallet" src="${imageSrc}" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
</tr>
</tbody>
</table>
Expand Down
9 changes: 6 additions & 3 deletions packages/wallet/backend/src/email/templates/forgotPassword.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const getForgotPasswordEmailTemplate = (url: string): string => {
export const getForgotPasswordEmailTemplate = (
url: string,
imageSrc: string
): string => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<html lang="en">
Expand All @@ -10,7 +13,7 @@ export const getForgotPasswordEmailTemplate = (url: string): string => {
<table style="margin-top:32px;text-align:center;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Test Wallet" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerTestWallet.png" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Wallet" src="${imageSrc}" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
</tr>
</tbody>
</table>
Expand All @@ -19,7 +22,7 @@ export const getForgotPasswordEmailTemplate = (url: string): string => {
<tbody>
<tr>
<td>
<img alt="Test Wallet Password" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/EnvelopeBird.png" width="120" height="96" style="outline:none;border:none;text-decoration:none" />
<img alt="Wallet Password" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/EnvelopeBird.png" width="120" height="96" style="outline:none;border:none;text-decoration:none" />
</td>
</tr>
<tr>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions packages/wallet/backend/src/email/templates/kycVerifiedEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export const getKYCVerificationEmailTemplate = (
loginUrl: string,
imageSrc: string
): string => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<html lang="en">
<head></head>
<body style="background-color:#eefee5;margin:0 auto;font-family:-apple-system, BlinkMacSystemFont, &#x27;Segoe UI&#x27;, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;, &#x27;Helvetica Neue&#x27;, sans-serif">
<table align="center" role="presentation" cellSpacing="0" cellPadding="0" border="0" width="100%" style="max-width:600px;margin:0 auto">
<tr style="width:100%">
<td>
<table style="margin-top:32px;text-align:center;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Wallet" src="${imageSrc}" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
</tr>
</tbody>
</table>
<table style="background:#ffffff;margin:30px 50px 30px 0;padding:23px;text-align:center; border:solid 10px #0e7b31;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td>
<h1 style="color:#0e7b31;font-size:36px;font-weight:500;margin:30px 0;padding:0;line-height:42px">Interledger Wallet Verification</h1>
</td>
</tr>
<tr>
<td>
Thank you for registering with Interledger Wallet. Your KYC data has been successfully verified. You can now <a href="${loginUrl} target="_blank">login to your account</a> and start using the Wallet.
</td>
</tr>
</tbody>
</table>
<table align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td>
<table width="100%" style="margin-bottom:32px;padding-left:8px;padding-right:8px;width:100%" align="center" role="presentation" cellSpacing="0" cellPadding="0" border="0">
<tbody style="width:100%">
<tr style="width:100%">
<td style="width:100%;text-align:center;">
<a href="https://interledger.org" target="_blank">
<img alt="Interledger Foundation" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerFoundation.png" width="202" height="56" style="outline:none;border:none;text-decoration:none;"/>
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</body>
</html>`
}
7 changes: 5 additions & 2 deletions packages/wallet/backend/src/email/templates/rejectEmail.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const getRejectEmailTemplate = (textHtml: string): string => {
export const getRejectEmailTemplate = (
textHtml: string,
imageSrc: string
): string => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<html lang="en">
Expand All @@ -10,7 +13,7 @@ export const getRejectEmailTemplate = (textHtml: string): string => {
<table style="margin-top:32px;text-align:center;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Test Wallet" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerTestWallet.png" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Wallet" src="${imageSrc}" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
</tr>
</tbody>
</table>
Expand Down
7 changes: 5 additions & 2 deletions packages/wallet/backend/src/email/templates/verifyEmail.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const getVerifyEmailTemplate = (url: string): string => {
export const getVerifyEmailTemplate = (
url: string,
imageSrc: string
): string => {
return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<html lang="en">
Expand All @@ -10,7 +13,7 @@ export const getVerifyEmailTemplate = (url: string): string => {
<table style="margin-top:32px;text-align:center;" align="center" border="0" cellPadding="0" cellSpacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Test Wallet" src="https://raw.githubusercontent.com/interledger/testnet/main/packages/wallet/backend/src/email/templates/images/InterledgerTestWallet.png" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
<td><a href="https://wallet.interledger-test.dev" target="_blank"><img alt="Interledger Wallet" src="${imageSrc}" width="250" style="outline:none;border:none;text-decoration:none" /></a></td>
</tr>
</tbody>
</table>
Expand Down
4 changes: 3 additions & 1 deletion packages/wallet/backend/src/gatehub/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export class GateHubService {
id: user.id,
gateHubUserId: user.gateHubUserId
})

if (this.gateHubClient.isProduction) {
await this.emailService.sendKYCVerifiedEmail(user.email)
}
break
}
case 'id.verification.action_required':
Expand Down
1 change: 1 addition & 0 deletions packages/wallet/backend/src/user/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class User extends BaseModel {
public customerId?: string
public cardWalletAddress?: string
public isPinSet!: boolean
public acceptedCardTerms!: boolean

public sessions?: Session[]
public passwordResetToken?: string | null
Expand Down
1 change: 1 addition & 0 deletions packages/wallet/backend/src/user/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface CreateUserArgs {
email: string
password: string
verifyEmailToken: string
acceptedCardTerms?: boolean
}

interface VerifyEmailArgs {
Expand Down
1 change: 1 addition & 0 deletions packages/wallet/backend/tests/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const mockSignUpRequest = (
const result = mockLogInRequest()
return {
body: {
acceptedCardTerms: true,
...result.body,
confirmPassword: result.body.password,
...overrides
Expand Down
9 changes: 7 additions & 2 deletions packages/wallet/frontend/src/components/HeaderLogo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Logo } from '@/ui/Logo'
import { Logo, LogoWallet } from '@/ui/Logo'
import { FEATURES_ENABLED } from '@/utils/constants'
import { cx } from 'class-variance-authority'

type HeaderLogoProps = {
Expand All @@ -8,7 +9,11 @@ type HeaderLogoProps = {
export const HeaderLogo = ({ header }: HeaderLogoProps) => {
return (
<div className="flex flex-col items-center md:flex-col">
<Logo className={cx('h-36 w-60 flex-shrink-0')} />
{FEATURES_ENABLED ? (
<LogoWallet className={cx('h-36 w-60 flex-shrink-0')}></LogoWallet>
) : (
<Logo className={cx('h-36 w-60 flex-shrink-0')} />
)}
<h1 className="mb-5 block space-x-4 text-center text-xl md:mb-0 md:mt-5 md:text-3xl md:font-semibold">
{header}
</h1>
Expand Down
10 changes: 7 additions & 3 deletions packages/wallet/frontend/src/components/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { userService } from '@/lib/api/user'
import { Link } from '@/ui/Link'
import { Logo } from '@/ui/Logo'
import { Logo, LogoWallet } from '@/ui/Logo'
import {
Dialog,
DialogPanel,
Expand Down Expand Up @@ -237,7 +237,11 @@ export const Menu = () => {
<header className="fixed inset-x-0 top-0 block h-[84px] border-b-2 border-dotted bg-white px-6 dark:bg-purple md:hidden">
<nav className="flex items-center justify-between">
<Link className="" href="/">
<Logo className="w-48 py-4 text-black transition-[transform,fill,color] duration-200 dark:text-white" />
{FEATURES_ENABLED ? (
<LogoWallet className="w-48 py-4 text-black transition-[transform,fill,color] duration-200 dark:text-white" />
) : (
<Logo className="w-48 py-4 text-black transition-[transform,fill,color] duration-200 dark:text-white" />
)}
</Link>
<button
className="p-1"
Expand All @@ -255,7 +259,7 @@ export const Menu = () => {
className="group mb-4 rounded-md border border-transparent p-2 focus:border-black dark:focus:border-white dark:focus:shadow-glow-link"
href="/"
>
<Logo className="w-48 py-4 text-black transition-transform duration-200 group-hover:scale-105 group-focus:scale-105 dark:text-white dark:group-hover:scale-100 dark:group-hover:drop-shadow-glow-svg dark:group-focus:scale-100 dark:group-focus:drop-shadow-glow-svg" />
<LogoWallet className="w-48 py-4 text-black transition-transform duration-200 group-hover:scale-105 group-focus:scale-105 dark:text-white dark:group-hover:scale-100 dark:group-hover:drop-shadow-glow-svg dark:group-focus:scale-100 dark:group-focus:drop-shadow-glow-svg" />
</Link>
<div className="w-full space-y-4">
{menuItems.map(({ name, href, id, Icon }) => (
Expand Down
Loading

0 comments on commit e34a0a6

Please sign in to comment.