Skip to content

Commit 13c6da6

Browse files
committed
added fetchForteAccessToken and createPaymentIntent endpoint
1 parent f0c8444 commit 13c6da6

File tree

9 files changed

+210
-6
lines changed

9 files changed

+210
-6
lines changed

examples/react/src/components/Connected.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ export const Connected = () => {
395395
recipientAddress: address,
396396
currencyAddress,
397397
collectionAddress,
398-
creditCardProviders: [checkoutProvider || 'transak'],
398+
creditCardProviders: ['forte', 'sardine', 'transak'],
399399
onRampProvider: onRampProvider ? (onRampProvider as TransactionOnRampProvider) : TransactionOnRampProvider.transak,
400400
transakConfig: {
401401
contractId,

packages/checkout/src/api/data.ts

+135-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SequenceAPIClient } from '@0xsequence/api'
22
import { TokenMetadata } from '@0xsequence/metadata'
3-
import { ChainId, networks } from '@0xsequence/network'
3+
import { ChainId, networks, findSupportedNetwork } from '@0xsequence/network'
44

55
import { CreditCardCheckout } from '../contexts'
66

@@ -233,3 +233,137 @@ export const fetchSardineOnRampLink = async ({
233233

234234
return url.href
235235
}
236+
237+
export interface FetchForteAccessTokenReturn {
238+
accessToken: string
239+
expiresIn: number
240+
tokenType: string
241+
}
242+
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> => {
247+
const clientId = '5tpnj5869vs3jpgtpif2ci8v08'
248+
const clientSecret = 'jpkbg3e2ho9rbd0959qe5l6ke238d4bca2nptstfga2i9hant5e'
249+
250+
const url = `${FORTE_URL}/auth/v1/oauth2/token`
251+
252+
const res = await fetch(url, {
253+
method: 'POST',
254+
headers: {
255+
'Content-Type': 'application/json'
256+
},
257+
body: JSON.stringify({
258+
client_id: clientId,
259+
client_secret: clientSecret
260+
})
261+
})
262+
263+
const { data } = await res.json()
264+
265+
return {
266+
accessToken: data.access_token,
267+
expiresIn: data.expires_in,
268+
tokenType: data.token_type
269+
}
270+
}
271+
272+
export interface CreateFortePaymentIntentArgs {
273+
accessToken: string
274+
tokenType: string
275+
nonce: string
276+
nftQuantity: string
277+
recipientAddress: string
278+
chainId: string
279+
signature: string
280+
tokenAddress: string
281+
protocolAddress: string
282+
nftName: string
283+
imageUrl: string
284+
tokenId: string
285+
}
286+
287+
export interface CreateFortePaymentIntentReturn {
288+
errorCode: string | null
289+
flow: string
290+
notes: string[]
291+
paymentIntentId: string
292+
widgetData: string
293+
}
294+
295+
export const createFortePaymentIntent = async (args: CreateFortePaymentIntentArgs): Promise<CreateFortePaymentIntentReturn> => {
296+
const {
297+
accessToken,
298+
tokenType,
299+
recipientAddress,
300+
chainId,
301+
signature,
302+
protocolAddress,
303+
nftName,
304+
nftQuantity,
305+
imageUrl,
306+
tokenId,
307+
nonce
308+
} = args
309+
const network = findSupportedNetwork(chainId)
310+
311+
if (!network) {
312+
throw new Error('Invalid chainId')
313+
}
314+
315+
const url = `${FORTE_URL}/payments/v2/intent`
316+
const forteBlockchainName = network.name.toLowerCase().replace('-', '_')
317+
const idempotencyKey = `${recipientAddress}-${tokenId}-${protocolAddress}-${nftName}-${new Date().getTime()}`
318+
319+
const res = await fetch(url, {
320+
method: 'POST',
321+
headers: {
322+
'Content-Type': 'application/json',
323+
Authorization: `${tokenType} ${accessToken}`
324+
},
325+
body: JSON.stringify({
326+
blockchain: forteBlockchainName,
327+
currency: 'USD',
328+
idempotency_key: idempotencyKey,
329+
transaction_type: 'BUY_NFT_MINT',
330+
buyer: {
331+
id: recipientAddress,
332+
wallet: {
333+
address: recipientAddress,
334+
blockchain: forteBlockchainName
335+
}
336+
},
337+
items: [
338+
{
339+
name: nftName,
340+
quantity: nftQuantity,
341+
price: {
342+
amount: nftQuantity,
343+
image_url: imageUrl,
344+
title: nftName,
345+
mint_data: {
346+
nonce,
347+
signature: signature,
348+
token_ids: [tokenId],
349+
protocol_address: protocolAddress,
350+
protocol: 'protocol-mint'
351+
}
352+
}
353+
}
354+
]
355+
})
356+
})
357+
358+
const {
359+
data: { error_code, flow, notes, payment_intent_id, widget_data }
360+
} = await res.json()
361+
362+
return {
363+
errorCode: error_code,
364+
flow,
365+
notes,
366+
paymentIntentId: payment_intent_id,
367+
widgetData: widget_data
368+
}
369+
}

packages/checkout/src/contexts/CheckoutModal.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface TransakConfig {
2323
callDataOverride?: string
2424
}
2525

26+
export interface ForteConfig {}
27+
2628
export interface CreditCardCheckout {
2729
chainId: number
2830
contractAddress: string
@@ -36,8 +38,9 @@ export interface CreditCardCheckout {
3638
nftQuantity: string
3739
nftDecimals?: string
3840
calldata: string
39-
provider?: 'sardine' | 'transak'
41+
provider?: 'sardine' | 'transak' | 'forte'
4042
transakConfig?: TransakConfig
43+
forteConfig?: ForteConfig
4144
onSuccess?: (transactionHash: string, settings: CreditCardCheckout) => void
4245
onError?: (error: Error, settings: CreditCardCheckout) => void
4346
onClose?: () => void

packages/checkout/src/contexts/SelectPaymentModal.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { TransakConfig } from '../contexts/CheckoutModal'
55

66
import { createGenericContext } from './genericContext'
77

8-
export type CreditCardProviders = 'sardine' | 'transak'
8+
export type CreditCardProviders = 'sardine' | 'transak' | 'forte'
99

1010
export interface Collectible {
1111
tokenId: string

packages/checkout/src/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export * from './useCheckoutOptionsSalesContract'
1212
export * from './useERC1155SaleContractCheckout'
1313
export * from './useSkipOnCloseCallback'
1414
export * from './useSardineOnRampLink'
15+
export * from './useForteAccessToken'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useQuery } from '@tanstack/react-query'
2+
3+
import { fetchForteAccessToken } from '../api/data'
4+
5+
export const useForteAccessToken = () => {
6+
return useQuery({
7+
queryKey: ['useForteAccessToken'],
8+
queryFn: async () => {
9+
const res = await fetchForteAccessToken()
10+
11+
return res
12+
},
13+
retry: false,
14+
staleTime: 60 * 1000,
15+
refetchOnWindowFocus: false
16+
})
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useQuery } from '@tanstack/react-query'
2+
3+
import { createFortePaymentIntent, CreateFortePaymentIntentArgs } from '../api/data'
4+
5+
export const useFortePaymentIntent = (args: CreateFortePaymentIntentArgs) => {
6+
return useQuery({
7+
queryKey: ['useFortePaymentIntent', args],
8+
queryFn: async () => {
9+
const res = await createFortePaymentIntent(args)
10+
11+
return res
12+
},
13+
retry: false,
14+
staleTime: 60 * 1000,
15+
refetchOnWindowFocus: false
16+
})
17+
}

packages/checkout/src/views/PaymentSelection/PayWithCreditCard/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface PayWithCreditCardProps {
1414
skipOnCloseCallback: () => void
1515
}
1616

17-
type BasePaymentProviderOptions = 'sardine' | 'transak'
17+
type BasePaymentProviderOptions = 'sardine' | 'transak' | 'forte'
1818
type CustomPaymentProviderOptions = 'custom'
1919
type PaymentProviderOptions = BasePaymentProviderOptions | CustomPaymentProviderOptions
2020

@@ -64,6 +64,7 @@ export const PayWithCreditCard = ({ settings, disableButtons, skipOnCloseCallbac
6464
return
6565
case 'sardine':
6666
case 'transak':
67+
case 'forte':
6768
onPurchase()
6869
return
6970
default:
@@ -134,6 +135,7 @@ export const PayWithCreditCard = ({ settings, disableButtons, skipOnCloseCallbac
134135
switch (creditCardProvider) {
135136
case 'sardine':
136137
case 'transak':
138+
case 'forte':
137139
case 'custom':
138140
return (
139141
<Card

packages/checkout/src/views/PendingCreditCardTransaction.tsx

+31-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
useCheckoutModal,
1515
useSardineClientToken,
1616
useTransactionStatusModal,
17-
useSkipOnCloseCallback
17+
useSkipOnCloseCallback,
18+
useForteAccessToken
1819
} from '../hooks'
1920
import { TRANSAK_PROXY_ADDRESS } from '../utils/transak'
2021

@@ -35,6 +36,8 @@ export const PendingCreditCardTransaction = () => {
3536
const { skipOnCloseCallback } = useSkipOnCloseCallback(onClose)
3637

3738
switch (provider) {
39+
case 'forte':
40+
return <PendingCreditCardTransactionForte skipOnCloseCallback={skipOnCloseCallback} />
3841
case 'transak':
3942
return <PendingCreditCardTransactionTransak skipOnCloseCallback={skipOnCloseCallback} />
4043
case 'sardine':
@@ -431,3 +434,30 @@ export const PendingCreditCardTransactionSardine = ({ skipOnCloseCallback }: Pen
431434
</div>
432435
)
433436
}
437+
438+
export const PendingCreditCardTransactionForte = ({ skipOnCloseCallback }: PendingCreditTransactionProps) => {
439+
const { data: accessTokenData, isLoading: isLoadingAccessToken, isError: isErrorAccessToken } = useForteAccessToken()
440+
441+
if (isLoadingAccessToken) {
442+
return (
443+
<div className="flex items-center justify-center" style={{ height: '770px' }}>
444+
<Spinner size="lg" />
445+
</div>
446+
)
447+
}
448+
449+
if (isErrorAccessToken) {
450+
return (
451+
<div className="flex items-center justify-center" style={{ height: '770px' }}>
452+
<Text color="primary">An error has occurred</Text>
453+
</div>
454+
)
455+
}
456+
457+
return (
458+
<div className="flex items-center justify-center" style={{ height: '770px' }}>
459+
<Text color="primary">Forte</Text>
460+
<Text color="primary">{accessTokenData?.accessToken}</Text>
461+
</div>
462+
)
463+
}

0 commit comments

Comments
 (0)