Skip to content

Commit 15041a5

Browse files
committed
add usePaymentIntent flow
1 parent 13c6da6 commit 15041a5

File tree

8 files changed

+122
-20
lines changed

8 files changed

+122
-20
lines changed

examples/react/src/config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ export const checkoutConfig: SequenceCheckoutConfig = {
167167
sardineCheckoutUrl: 'https://sardine-checkout-sandbox.sequence.info',
168168
sardineOnRampUrl: 'https://crypto.sandbox.sardine.ai/',
169169
transakApiUrl: 'https://global-stg.transak.com',
170-
transakApiKey: 'c20f2a0e-fe6a-4133-8fa7-77e9f84edf98'
170+
transakApiKey: 'c20f2a0e-fe6a-4133-8fa7-77e9f84edf98',
171+
fortePaymentUrl: 'https://staging-api.pti-dev.cloud',
172+
forteWidgetUrl: 'https://dev-forte-payments-cdn.pti-dev.cloud/forte-payments-widget.js'
171173
}
172174
: undefined
173175
}

packages/checkout/src/api/data.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -240,14 +240,11 @@ export interface FetchForteAccessTokenReturn {
240240
tokenType: string
241241
}
242242

243-
//TODO: remove once part of the sequence api
244-
const FORTE_URL = 'https://staging-api.pti-dev.cloud'
245-
246-
export const fetchForteAccessToken = async (): Promise<FetchForteAccessTokenReturn> => {
243+
export const fetchForteAccessToken = async (forteApiUrl: string): Promise<FetchForteAccessTokenReturn> => {
247244
const clientId = '5tpnj5869vs3jpgtpif2ci8v08'
248245
const clientSecret = 'jpkbg3e2ho9rbd0959qe5l6ke238d4bca2nptstfga2i9hant5e'
249246

250-
const url = `${FORTE_URL}/auth/v1/oauth2/token`
247+
const url = `${forteApiUrl}/auth/v1/oauth2/token`
251248

252249
const res = await fetch(url, {
253250
method: 'POST',
@@ -292,7 +289,10 @@ export interface CreateFortePaymentIntentReturn {
292289
widgetData: string
293290
}
294291

295-
export const createFortePaymentIntent = async (args: CreateFortePaymentIntentArgs): Promise<CreateFortePaymentIntentReturn> => {
292+
export const createFortePaymentIntent = async (
293+
forteApiUrl: string,
294+
args: CreateFortePaymentIntentArgs
295+
): Promise<CreateFortePaymentIntentReturn> => {
296296
const {
297297
accessToken,
298298
tokenType,
@@ -312,7 +312,7 @@ export const createFortePaymentIntent = async (args: CreateFortePaymentIntentArg
312312
throw new Error('Invalid chainId')
313313
}
314314

315-
const url = `${FORTE_URL}/payments/v2/intent`
315+
const url = `${forteApiUrl}/payments/v2/intent`
316316
const forteBlockchainName = network.name.toLowerCase().replace('-', '_')
317317
const idempotencyKey = `${recipientAddress}-${tokenId}-${protocolAddress}-${nftName}-${new Date().getTime()}`
318318

packages/checkout/src/components/SequenceCheckoutProvider/SequenceCheckoutProvider.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ export const SequenceCheckoutProvider = ({ children, config }: SequenceCheckoutP
204204
sardineCheckoutUrl: config?.env?.sardineCheckoutUrl ?? 'https://sardine-checkout.sequence.info',
205205
sardineOnRampUrl: config?.env?.sardineOnRampUrl ?? 'https://crypto.sardine.ai/',
206206
transakApiUrl: config?.env?.transakApiUrl ?? 'https://global.transak.com',
207-
transakApiKey: config?.env?.transakApiKey ?? '5911d9ec-46b5-48fa-a755-d59a715ff0cf'
207+
transakApiKey: config?.env?.transakApiKey ?? '5911d9ec-46b5-48fa-a755-d59a715ff0cf',
208+
fortePaymentUrl: config?.env?.fortePaymentUrl ?? 'https://api.payments.forte.io',
209+
forteWidgetUrl: config?.env?.forteWidgetUrl ?? 'https://client.payments.forte.io/forte-payments-widget.js'
208210
}}
209211
>
210212
<SwapModalContextProvider

packages/checkout/src/contexts/Environment.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export interface EnvironmentOverrides {
88
transakApiKey: string
99
sardineCheckoutUrl: string
1010
sardineOnRampUrl: string
11+
fortePaymentUrl: string
12+
forteWidgetUrl: string
1113
}
1214

1315
const [useEnvironmentContext, EnvironmentContextProvider] = createGenericContext<EnvironmentOverrides>()

packages/checkout/src/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from './useERC1155SaleContractCheckout'
1313
export * from './useSkipOnCloseCallback'
1414
export * from './useSardineOnRampLink'
1515
export * from './useForteAccessToken'
16+
export * from './useFortePaymentIntent'

packages/checkout/src/hooks/useForteAccessToken.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { useQuery } from '@tanstack/react-query'
22

33
import { fetchForteAccessToken } from '../api/data'
4+
import { useEnvironmentContext } from '../contexts/Environment'
45

56
export const useForteAccessToken = () => {
7+
const { fortePaymentUrl } = useEnvironmentContext()
8+
69
return useQuery({
710
queryKey: ['useForteAccessToken'],
811
queryFn: async () => {
9-
const res = await fetchForteAccessToken()
12+
const res = await fetchForteAccessToken(fortePaymentUrl)
1013

1114
return res
1215
},
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import { useQuery } from '@tanstack/react-query'
22

33
import { createFortePaymentIntent, CreateFortePaymentIntentArgs } from '../api/data'
4+
import { useEnvironmentContext } from '../contexts/Environment'
5+
interface UseFortePaymentIntentOptions {
6+
disabled?: boolean
7+
}
8+
9+
export const useFortePaymentIntent = (args: CreateFortePaymentIntentArgs, options?: UseFortePaymentIntentOptions) => {
10+
const { fortePaymentUrl } = useEnvironmentContext()
411

5-
export const useFortePaymentIntent = (args: CreateFortePaymentIntentArgs) => {
612
return useQuery({
713
queryKey: ['useFortePaymentIntent', args],
814
queryFn: async () => {
9-
const res = await createFortePaymentIntent(args)
15+
const res = await createFortePaymentIntent(fortePaymentUrl, args)
1016

1117
return res
1218
},
1319
retry: false,
1420
staleTime: 60 * 1000,
15-
refetchOnWindowFocus: false
21+
refetchOnWindowFocus: false,
22+
enabled: !options?.disabled
1623
})
1724
}

packages/checkout/src/views/PendingCreditCardTransaction.tsx

+92-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { useAnalyticsContext, useProjectAccessKey } from '@0xsequence/connect'
2-
import { Spinner, Text } from '@0xsequence/design-system'
2+
import { Button, Spinner, Text } from '@0xsequence/design-system'
33
import { useConfig, useGetTokenMetadata, useGetContractInfo } from '@0xsequence/hooks'
44
import { findSupportedNetwork } from '@0xsequence/network'
55
import pako from 'pako'
6-
import { useEffect, useRef } from 'react'
6+
import { useEffect, useRef, useState } from 'react'
77
import { formatUnits } from 'viem'
8+
import { useSignMessage, usePublicClient, useAccount } from 'wagmi'
89

910
import { fetchSardineOrderStatus } from '../api'
1011
import { NFT_CHECKOUT_SOURCE } from '../constants'
@@ -15,7 +16,8 @@ import {
1516
useSardineClientToken,
1617
useTransactionStatusModal,
1718
useSkipOnCloseCallback,
18-
useForteAccessToken
19+
useForteAccessToken,
20+
useFortePaymentIntent
1921
} from '../hooks'
2022
import { TRANSAK_PROXY_ADDRESS } from '../utils/transak'
2123

@@ -436,28 +438,111 @@ export const PendingCreditCardTransactionSardine = ({ skipOnCloseCallback }: Pen
436438
}
437439

438440
export const PendingCreditCardTransactionForte = ({ skipOnCloseCallback }: PendingCreditTransactionProps) => {
441+
const { forteWidgetUrl } = useEnvironmentContext()
439442
const { data: accessTokenData, isLoading: isLoadingAccessToken, isError: isErrorAccessToken } = useForteAccessToken()
443+
const { data: signatureData, signMessage } = useSignMessage()
444+
const { address } = useAccount()
445+
const nav = useNavigation()
446+
const {
447+
params: { creditCardCheckout }
448+
} = nav.navigation as TransactionPendingNavigation
449+
const publicClient = usePublicClient({ chainId: creditCardCheckout.chainId })
450+
const isMessageSigned = signatureData !== undefined
440451

441-
if (isLoadingAccessToken) {
452+
const {
453+
data: tokenMetadatas,
454+
isLoading: isLoadingTokenMetadata,
455+
isError: isErrorTokenMetadata
456+
} = useGetTokenMetadata({
457+
chainID: String(creditCardCheckout.chainId),
458+
contractAddress: creditCardCheckout.nftAddress,
459+
tokenIDs: [creditCardCheckout.nftId]
460+
})
461+
462+
const tokenMetadata = tokenMetadatas ? tokenMetadatas[0] : undefined
463+
464+
const nftQuantity = formatUnits(BigInt(creditCardCheckout.nftQuantity), Number(creditCardCheckout.nftDecimals || 0))
465+
466+
const {
467+
data: paymentIntentData,
468+
isLoading: isLoadingPaymentIntent,
469+
isError: isErrorPaymentIntent
470+
} = useFortePaymentIntent(
471+
{
472+
accessToken: accessTokenData?.accessToken || '',
473+
tokenType: accessTokenData?.tokenType || '',
474+
nonce: `${address || ''}-${Date.now()}`,
475+
nftQuantity,
476+
recipientAddress: creditCardCheckout.recipientAddress,
477+
chainId: creditCardCheckout.chainId.toString(),
478+
signature: signatureData || '',
479+
tokenAddress: creditCardCheckout.nftAddress,
480+
protocolAddress: creditCardCheckout.contractAddress,
481+
nftName: tokenMetadata?.name || '',
482+
imageUrl: tokenMetadata?.image || '',
483+
tokenId: creditCardCheckout.nftId
484+
},
485+
{
486+
disabled: !isMessageSigned || isLoadingTokenMetadata
487+
}
488+
)
489+
490+
const isLoading = isLoadingTokenMetadata || isLoadingAccessToken || isLoadingPaymentIntent
491+
const isError = isErrorTokenMetadata || isErrorAccessToken || isErrorPaymentIntent
492+
493+
const onClickSignMessage = async () => {
494+
if (!publicClient || !address) {
495+
console.error('No public client or address')
496+
return
497+
}
498+
499+
try {
500+
await signMessage({ message: creditCardCheckout.calldata })
501+
} catch (e) {
502+
console.error('An error occurred while signing the message')
503+
}
504+
}
505+
506+
if (!isMessageSigned) {
507+
return (
508+
<div className="flex items-center justify-center" style={{ height: '770px' }}>
509+
<Button variant="primary" onClick={onClickSignMessage}>
510+
Approve and purchase
511+
</Button>
512+
</div>
513+
)
514+
}
515+
516+
if (isLoading) {
442517
return (
443518
<div className="flex items-center justify-center" style={{ height: '770px' }}>
444519
<Spinner size="lg" />
445520
</div>
446521
)
447522
}
448523

449-
if (isErrorAccessToken) {
524+
if (isError) {
450525
return (
451526
<div className="flex items-center justify-center" style={{ height: '770px' }}>
452527
<Text color="primary">An error has occurred</Text>
453528
</div>
454529
)
455530
}
456531

532+
const initializeFortePayment = () => {
533+
// @ts-ignore-next-line
534+
window.initFortePaymentsWidget({
535+
containerId: 'forte-payments-widget-container',
536+
data: {
537+
...paymentIntentData
538+
}
539+
})
540+
}
541+
457542
return (
458543
<div className="flex items-center justify-center" style={{ height: '770px' }}>
459-
<Text color="primary">Forte</Text>
460-
<Text color="primary">{accessTokenData?.accessToken}</Text>
544+
<div id="forte-payments-widget-container"></div>
545+
<script onLoad={initializeFortePayment} type="module" async src={forteWidgetUrl}></script>
461546
</div>
462547
)
463548
}

0 commit comments

Comments
 (0)