Follow these steps to integrate using our pre-built React component.
Step 1: Obtain Access Token
Before using the Phoenix component, you need to obtain an access token from your backend.
// Example of fetching the access token from your backend
const [ accessToken , setAccessToken ] = useState ( '' );
useEffect (() => {
// Replace with your actual backend endpoint that implements the token generation
fetch ( '/api/token' )
. then ( response => response . json ())
. then ( data => setAccessToken ( data . access_token ))
. catch ( error => console . error ( 'Error fetching access token:' , error ));
}, []);
See the Obtaining Access Token page for detailed instructions on setting up the backend endpoint.
This is example code for obtaining an access token. You will need to adapt the endpoint URL and request format based on your specific backend implementation. Refer to the Obtaining Access Token page for a complete example of setting up a backend service.
Step 2: Download the Component
You can view the component code or download it directly:
import React , { useEffect , useRef } from 'react' ;
interface PhoenixPaymentProps {
// Required parameters
walletAddress : string ;
amount : string ;
accessToken : string ; // Access token from your backend
onClose : () => void ;
onComplete : ( success : boolean , transactionId ?: string , data ?: any ) => void ;
onDone ?: () => void ; // New callback for handling done events
onOrderCreated ?: ( data : any ) => void ; // Callback for handling order creation
theme ?: 'light' | 'dark' ; // Optional theme parameter
}
export const PhoenixPayment : React . FC < PhoenixPaymentProps > = ({
walletAddress ,
amount ,
accessToken ,
onClose ,
onComplete ,
onDone ,
onOrderCreated ,
theme ,
}) => {
const iframeRef = useRef < HTMLIFrameElement >( null );
useEffect (() => {
// Handle messages from the iframe
const handleMessage = ( event : MessageEvent ) => {
// Verify origin for security
if ( event . origin !== 'https://app-partner.phoenix.market' ) return ;
// Handle transaction success message
if ( event . data ?. type === 'TRANSACTION_SUCCESS' ) {
const { amount , token , chain , walletAddress , sendAmount , transactionHash , basescanLink } = event . data . data ;
onComplete ( true , transactionHash , {
amount , token , chain , walletAddress , sendAmount , transactionHash , basescanLink
});
}
// Handle order created message
else if ( event . data ?. type === 'ORDER_CREATED' ) {
const { order_id , ... data } = event . data . data ;
console . log ( 'Order created:' , event . data . data );
if ( onOrderCreated ) {
onOrderCreated ({ order_id , data });
}
}
// Handle cancel message
else if ( event . data ?. type === 'CANCEL' ) {
console . log ( 'Payment canceled by user' );
onClose ();
}
// Handle open explorer URL message
else if ( event . data ?. type === 'OPEN_EXPLORER_URL' ) {
const url = event . data . url ;
console . log ( 'Opening explorer URL:' , url );
// Always open in a new tab
window . open ( url , '_blank' );
}
// Handle done message
else if ( event . data ?. type === 'USER_DONE' ) {
console . log ( 'Payment process completed' );
if ( onDone ) {
onDone ();
} else {
// Default behavior: close the window
onClose ();
}
}
};
window . addEventListener ( 'message' , handleMessage );
// Clean up
return () => {
window . removeEventListener ( 'message' , handleMessage );
};
}, [ onComplete , onClose , onDone , onOrderCreated ]);
// Build iframe URL with all parameters
const getIframeUrl = () => {
let url = `https://app-partner.phoenix.market/?address= ${ walletAddress } &amount= ${ amount } &access_token= ${ accessToken } ` ;
if ( theme ) {
url += `&theme= ${ theme } ` ;
}
return url ;
};
return (
< div className = "phoenix-payment-container" >
< div className = "payment-overlay" >
< div className = "payment-modal" >
< button
className = "close-button"
onClick = { onClose }
aria-label = "Close payment window"
>
×
</ button >
< iframe
ref = { iframeRef }
src = { getIframeUrl () }
className = "teller-iframe"
title = "Phoenix Payment"
allow = "clipboard-write"
/>
</ div >
</ div >
< style > { `
.phoenix-payment-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
.payment-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.payment-modal {
position: relative;
background-color: transparent;
border-radius: 12px;
padding: 0;
overflow: visible;
box-shadow: none;
}
.close-button {
position: absolute;
top: -15px;
right: -15px;
background-color: #333;
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
padding: 0;
line-height: 1;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.teller-iframe {
width: 350px;
height: 600px;
border: none;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
` } </ style >
</ div >
);
};
export default PhoenixPayment ;
Save this file to your project’s component directory, for example:
/src/components/PhoenixPayment.tsx
The component includes all necessary styling internally via the <style>
tag, so you don’t need to add any additional CSS.
Step 3: Implement the Component
Follow these steps to implement the component in your application:
Step 3.1: Import the Component
import { PhoenixPayment } from './components/PhoenixPayment' ;
Step 3.2: Prepare Required and Optional Parameters
Before adding the component, you need to collect the following information:
Required Parameters
walletAddress
: The Ethereum wallet address where USDC will be sent
amount
: The amount in USD to convert to USDC
accessToken
: The access token retrieved from your backend
onClose
: Function to handle when the user closes the payment modal
onComplete
: Callback function to handle payment completion events
Optional Parameters
onDone
: Callback function to handle when the payment process is complete
onOrderCreated
: Optional callback function to handle when an order is created
theme
: Optional UI theme, can be set to either ‘light’ or ‘dark’
You should create form inputs in your UI to collect the required information:
View Input Collection Example
Step 3.3: Add the Component to Your JSX
Once you have collected these values, you can pass them to the PhoenixPayment component:
< PhoenixPayment
walletAddress = { walletAddress }
amount = { amount }
accessToken = { accessToken }
onClose = { () => setShowPayment ( false ) }
onComplete = { handlePaymentComplete }
onDone = { handlePaymentDone }
onOrderCreated = { handleOrderCreated } // Optional: handle order creation to receive order details
theme = "dark" // Optional: specify 'light' or 'dark' theme
/>
Step 3.4: Handle Order Created Events
The onOrderCreated
callback is triggered when a new order is created in the Phoenix system. This happens before the payment process begins. The callback receives an object containing:
order_id
: Unique identifier for the order
data
: Object containing additional order details:
created_at
: Timestamp of order creation
buyer_address
: The buyer’s wallet address
seller_address
: The seller’s wallet address
send_amount
: The amount to be sent
receive_amount
: The amount to be received
uniqueness_amount
: The amount of cents added to the order for uniqueness
receiving_chain_id
: The chain ID where the payment will be received
zelle_address
: The Zelle address for payment
const handleOrderCreated = ({ order_id , data }) => {
console . log ( 'Order created:' , { order_id , data });
// You can store the order details for reference
};
Step 3.5: Handle Payment Completion
The onComplete
callback receives three parameters:
success
: Boolean indicating if the operation succeeded
transactionId
: Transaction hash (only for successful payments)
data
: Object containing additional information
Transaction Success Data
When a transaction is successful, the data object will contain:
amount
: The USD amount
token
: The token received (e.g., “USDC”)
chain
: The blockchain (e.g., “Base”)
walletAddress
: The receiving wallet address
sendAmount
: The amount of tokens sent
transactionHash
: The blockchain transaction hash
basescanLink
: Link to view the transaction on Basescan
const handlePaymentComplete = ( success , transactionId , data ) => {
if ( success ) {
// For transaction success
if ( data ?. transactionHash ) {
console . log ( 'Transaction successful:' , data );
// You might want to redirect to a success page or show transaction details
// Uncomment the following line if you want to close the window after transaction success
// setShowPayment(false);
}
} else {
console . log ( 'Payment failed or cancelled' );
// Uncomment the following line if you want to close the window after payment failure/cancellation
// setShowPayment(false);
}
};
Step 3.6: Handle Done Events
const handlePaymentDone = () => {
// Close the payment window
setShowPayment ( false );
};
Full Implementation Example
Here’s a complete example showing how all the pieces fit together:
import { useState , useEffect } from 'react' ;
import { PhoenixPayment } from './components/PhoenixPayment' ;
function PaymentPage () {
const [ walletAddress , setWalletAddress ] = useState ( '' );
const [ amount , setAmount ] = useState ( '' );
const [ showPayment , setShowPayment ] = useState ( false );
const [ accessToken , setAccessToken ] = useState ( '' );
useEffect (() => {
// Fetch the access token from your backend
fetch ( '/api/token' )
. then ( response => response . json ())
. then ( data => setAccessToken ( data . access_token ))
. catch ( error => console . error ( 'Error fetching access token:' , error ));
}, []);
const handlePaymentComplete = ( success , transactionId , data ) => {
if ( success ) {
// For transaction success
if ( data ?. transactionHash ) {
console . log ( 'Transaction successful:' , data );
// You might want to redirect to a success page or show transaction details
// Uncomment the following line if you want to close the window after transaction success
// setShowPayment(false);
}
} else {
console . log ( 'Payment failed or cancelled' );
// Uncomment the following line if you want to close the window after payment failure/cancellation
// setShowPayment(false);
}
};
const handlePaymentDone = () => {
// Close the payment window
setShowPayment ( false );
};
const handleOrderCreated = ({ order_id , data }) => {
console . log ( 'Order created:' , { order_id , data });
// You can store the order details for reference
};
return (
<>
{ /* Form to collect wallet address and amount */ }
< div >
< label htmlFor = "wallet" > Wallet Address: </ label >
< input
id = "wallet"
type = "text"
value = { walletAddress }
onChange = { ( e ) => setWalletAddress ( e . target . value ) }
placeholder = "0x..."
/>
</ div >
< div >
< label htmlFor = "amount" > Amount (USD): </ label >
< input
id = "amount"
type = "number"
value = { amount }
onChange = { ( e ) => setAmount ( e . target . value ) }
placeholder = "100"
/>
</ div >
< button
onClick = { () => setShowPayment ( true ) }
disabled = { ! accessToken } // Disable if access token isn't loaded
>
Make Payment
</ button >
{ showPayment && accessToken && (
< PhoenixPayment
walletAddress = { walletAddress }
amount = { amount }
accessToken = { accessToken }
onClose = { () => setShowPayment ( false ) }
onComplete = { handlePaymentComplete }
onDone = { handlePaymentDone }
onOrderCreated = { handleOrderCreated }
theme = "dark" // Optional: specify 'light' or 'dark' theme
/>
) }
</>
);
}