Skip to content

Commit

Permalink
Fully featured KYC support
Browse files Browse the repository at this point in the history
  • Loading branch information
jophish committed Jan 23, 2024
1 parent cd1c005 commit 86d2e41
Show file tree
Hide file tree
Showing 15 changed files with 560 additions and 47 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"buffer": "^6.0.3",
"ethers": "^6.8.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
17 changes: 17 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,20 @@
font-weight: bold;
font-size: 12px;
}

#KYCBottomSection {
font-size: 12px;
flex: 1;
justify-content: flex-end;
align-self: flex-end;
}

#KYCFormContainer {
max-height: 60%;
overflow-y: scroll;
width: 100%;
flex-grow: 1;
align-self: center;
background: white;
border: 0;
}
22 changes: 18 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ import {
ObfuscatedFiatAccountData,
TransferResponse,
TransferType,
KycStatus,
} from '@fiatconnect/fiatconnect-types'
import { loadConfig } from './config'
import { queryParamsSchema, QueryParams } from './schema'
import { DoneSection } from './components/DoneSection'
import { SendCrypto } from './components/SendCrypto'
import { KYCInfoScreen } from './components/KYCInfoScreen'

function useQueryParams() {
const [searchParams] = useSearchParams()
Expand All @@ -50,7 +52,7 @@ function useAppState(
finishedSignIn: boolean,
finishedUserActionDetails: boolean,
finishedSendCrypto: boolean,
kycApproved: boolean,
kycStatus: undefined | KycStatus,
linkedAccount: ObfuscatedFiatAccountData | undefined,
transferResponse: TransferResponse | undefined,
): AppState | undefined {
Expand All @@ -66,7 +68,7 @@ function useAppState(
screens,
}
}
if (queryParams.kycSchema && !kycApproved) {
if (queryParams.kycSchema && kycStatus !== KycStatus.KycApproved) {
return {
currentScreen: Screens.KYCScreen,
screens,
Expand Down Expand Up @@ -150,14 +152,14 @@ function App() {
const [finishedUserActionDetails, setFinishedUserActionDetails] =
useState(false)
const [finishedSendCrypto, setFinishedSendCrypto] = useState(false)
const [kycApproved, setKycApproved] = useState(false)
const [kycStatus, setKycStatus] = useState<undefined | KycStatus>(undefined)

const appState = useAppState(
queryParamsResults.success ? queryParamsResults.data : undefined,
finishedSignIn,
finishedUserActionDetails,
finishedSendCrypto,
kycApproved,
kycStatus,
linkedAccount,
transferResponse,
)
Expand Down Expand Up @@ -195,13 +197,25 @@ function App() {
case Screens.SignInScreen: {
return (
<SignInScreen
setKycStatus={setKycStatus}
onError={onError}
onNext={() => setFinishedSignIn(true)}
params={queryParamsResults.data}
setLinkedAccount={setLinkedAccount}
/>
)
}
case Screens.KYCScreen: {
return (
<KYCInfoScreen
setKycStatus={setKycStatus}
kycStatus={kycStatus}
onError={onError}
onNext={() => {}}
params={queryParamsResults.data}
/>
)
}
case Screens.PaymentInfoScreen: {
return (
<PaymentInfoScreen
Expand Down
81 changes: 79 additions & 2 deletions src/FiatConnectClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
FiatAccountType,
ObfuscatedFiatAccountData,
TransferRequestBody,
KycRequestParams,
KycStatusResponse,
KycStatus,
KycSchema,
} from '@fiatconnect/fiatconnect-types'
import { generateNonce, SiweMessage } from 'siwe'
import { getAddress } from 'ethers'
Expand All @@ -15,9 +19,82 @@ import {
} from '@fiatconnect/fiatconnect-sdk'
import { createSiweConfig } from '@fiatconnect/fiatconnect-sdk/dist/fiat-connect-client'
import { fiatConnectNetworkToChainId } from './constants'
import { ProviderIds } from './types'
import { ProviderIds, AddKycParams } from './types'

export function addFiatAccount(
export async function addKyc<T extends KycSchema>(
params: AddKycParams<T>,
clientConfig: FiatConnectClientConfig,
): Promise<KycStatus> {
const addKycResponse = await fetch(
`${clientConfig.baseUrl}/kyc/${params.kycSchemaName}`,
{
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${clientConfig.apiKey}`,
},
body: JSON.stringify(params.data),
},
)
if (!addKycResponse.ok) {
throw new Error(
'Non-404 error from provider while fetching linked accounts',
)
}
const response = (await addKycResponse.json()) as KycStatusResponse
return response.kycStatus
}

export async function deleteKyc(
params: KycRequestParams,
clientConfig: FiatConnectClientConfig,
): Promise<void> {
const deleteKycStatusResponse = await fetch(
`${clientConfig.baseUrl}/kyc/${params.kycSchema}`,
{
method: 'DELETE',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${clientConfig.apiKey}`,
},
},
)
if (!deleteKycStatusResponse.ok) {
if (deleteKycStatusResponse.status !== 404) {
throw new Error('Non-404 error from provider while deleting KYC')
}
}
}

export async function getKycStatus(
params: KycRequestParams,
clientConfig: FiatConnectClientConfig,
): Promise<KycStatus | undefined> {
const getKycStatusResponse = await fetch(
`${clientConfig.baseUrl}/kyc/${params.kycSchema}/status`,
{
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${clientConfig.apiKey}`,
},
},
)
if (!getKycStatusResponse.ok) {
if (getKycStatusResponse.status === 404) {
return undefined
} else {
throw new Error('Non-404 error from provider while getting KYC status')
}
}
const response = (await getKycStatusResponse.json()) as KycStatusResponse
return response.kycStatus
}

export async function addFiatAccount(
params: PostFiatAccountRequestBody,
clientConfig: FiatConnectClientConfig,
) {
Expand Down
26 changes: 19 additions & 7 deletions src/components/KYCInfoFieldSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,23 @@ function KYCInfoFieldSection({
const requiredFields = Object.entries(kycSchemaMetadata)
.filter(([_, metadata]) => metadata.required)
.map(([field, _]) => field)
const fieldValues = requiredFields.map((field) => kycInfo[field])
if (!fieldValues.every((value) => !!value)) {
setErrorMessage(undefined)
setSubmitDisabled(true)
return
for (const requiredField of requiredFields) {
const fieldMetadata = kycSchemaMetadata[requiredField]
if (fieldMetadata.group) {
const groupValue = kycInfo[fieldMetadata.group]
if (typeof groupValue === 'object' && !groupValue[requiredField]) {
setErrorMessage(undefined)
setSubmitDisabled(true)
return
}
} else {
if (!kycInfo[requiredField]) {
setErrorMessage(undefined)
setSubmitDisabled(true)
return
}
}
}

for (const field of requiredFields) {
const fieldMetadata = kycSchemaMetadata[field]
if (!fieldMetadata.validator) {
Expand All @@ -81,7 +91,8 @@ function KYCInfoFieldSection({
}, [kycInfo])

const setKycInfoWrapper = (newDetails: Record<string, string>) => {
const formattedInfo: Record<string, string | Record<string, string>> = {}
const formattedInfo: Record<string, string | Record<string, string>> =
kycInfo
for (const [field, value] of Object.entries(newDetails)) {
const fieldMetadata = kycSchemaMetadata[field]
const formattedValue = fieldMetadata?.formatter
Expand Down Expand Up @@ -112,6 +123,7 @@ function KYCInfoFieldSection({
return (
<UserInfoLineItem
key={field}
photo={fieldMetadata.photo}
title={fieldMetadata.displayInfo?.title ?? ''}
placeholder={fieldMetadata.displayInfo?.placeholder ?? ''}
onChange={(value) => setKycInfoWrapper({ [field]: value })}
Expand Down
Loading

0 comments on commit 86d2e41

Please sign in to comment.