HTML Integration
Learn how to integrate Phoenix using vanilla HTML and JavaScript
For non-React frameworks or custom implementations, follow this HTML integration guide.
Step 1: Obtain Access Token
Before integrating Phoenix, you need to obtain an access token from your backend.
// Example function to fetch access token from your backend
async function getAccessToken() {
try {
const response = await fetch('/api/token');
const data = await response.json();
return data.access_token;
} catch (error) {
console.error('Error fetching access token:', error);
return null;
}
}
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: Add the iframe to your HTML
<div id="payment-container" style="display: none;">
<div class="payment-overlay">
<div class="payment-modal">
<button
id="cancel-payment-button"
class="close-button"
aria-label="Close payment window"
>
×
</button>
<iframe
id="phoenix-payment-iframe"
src="https://app-partner.phoenix.market/"
class="teller-iframe"
title="Phoenix Payment"
allow="clipboard-write"
></iframe>
</div>
</div>
</div>
Step 3: Add Essential CSS
Add this CSS to style the payment container and iframe:
/* Phoenix Payment Styles */
#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;
box-shadow: none;
overflow: visible;
}
.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);
}
Step 4: Add JavaScript for Communication
You need to add JavaScript functions to handle the integration. Here’s a breakdown of the required parameters and functions:
Required Parameters
walletAddress
: The Ethereum wallet address where USDC will be sentamount
: The amount in USD to convert to USDCaccessToken
: The access token from your backendonClose
: Function to handle when the user closes the payment modalhandlePaymentMessage
: Function to process payment response messagesonDone
: Function to handle when the payment process is complete (new)
Core Functions
Transaction Success Data
When a transaction is successful, the data object will contain:
amount
: The USD amounttoken
: The token received (e.g., “USDC”)chain
: The blockchain (e.g., “Base”)walletAddress
: The receiving wallet addresssendAmount
: The amount of tokens senttransactionHash
: The blockchain transaction hashbasescanLink
: Link to view the transaction on Basescan
Order Created Data
When an order is created, the data object will contain:
order_id
: Unique identifier for the orderdata
: Object containing additional order details:created_at
: Timestamp of order creationbuyer_address
: The buyer’s wallet addressseller_address
: The seller’s wallet addresssend_amount
: The amount to be sentreceive_amount
: The amount to be receiveduniqueness_amount
: The amount of cents added to the order for uniquenessreceiving_chain_id
: The chain ID where the payment will be receivedzelle_address
: The Zelle address for payment
// Function to get access token from your backend
async function getAccessToken() {
try {
const response = await fetch('/api/token');
const data = await response.json();
return data.access_token;
} catch (error) {
console.error('Error fetching access token:', error);
return null;
}
}
// Function to show payment iframe with parameters
async function showPayment(walletAddress, amount, theme) {
const iframe = document.getElementById('phoenix-payment-iframe');
const container = document.getElementById('payment-container');
// Get the access token from your backend
const accessToken = await getAccessToken();
if (!accessToken) {
alert('Unable to obtain access token. Please try again later.');
return;
}
// Build iframe URL with all parameters
let url = `https://app-partner.phoenix.market/?address=${walletAddress}&amount=${amount}&access_token=${accessToken}`;
if (theme) {
url += `&theme=${theme}`;
}
// Set iframe src
iframe.src = url;
// Display the container that holds the iframe
container.style.display = 'block';
// Set up event listener for messages from the iframe
window.addEventListener('message', handlePaymentMessage);
// Set up cancel button functionality
document.getElementById('cancel-payment-button').onclick = hidePayment;
}
// Function to handle messages received from the iframe
function handlePaymentMessage(event) {
// IMPORTANT: Verify origin for security
if (event.origin !== 'https://app-partner.phoenix.market') return;
// Handle order created message
if (event.data?.type === 'ORDER_CREATED') {
const { order_id, ...data } = event.data.data;
console.log('Order created:', event.data.data);
// Handle the order creation event as needed
// You should store order details in your database, especially the order_id
}
// Handle transaction success message
else if (event.data?.type === 'TRANSACTION_SUCCESS') {
const { amount, token, chain, walletAddress, sendAmount, transactionHash, basescanLink } = event.data.data;
console.log('Transaction successful:', transactionHash);
// You should store transaction details in your database
// and update your UI to show the payment was successful
// Uncomment the following line if you want to close the window after transaction success
// hidePayment();
}
// Handle cancel message
else if (event.data?.type === 'CANCEL') {
console.log('Payment canceled by user');
hidePayment();
}
// Handle open explorer URL message
else if (event.data?.type === 'OPEN_EXPLORER_URL') {
const url = event.data.url;
console.log('Opening explorer URL:', url);
// Open the URL in a new tab
window.open(url, '_blank');
}
// Handle done message
else if (event.data?.type === 'USER_DONE') {
console.log('Payment process completed');
hidePayment();
}
// Handle payment failure or cancellation
else if (event.data?.type === 'PAYMENT_FAILED' || event.data?.type === 'PAYMENT_CANCELLED') {
console.log('Payment failed or cancelled');
// Uncomment the following line if you want to close the window after payment failure/cancellation
// hidePayment();
}
}
// Function to hide the payment iframe
function hidePayment() {
document.getElementById('payment-container').style.display = 'none';
// IMPORTANT: Remove event listener to prevent memory leaks
window.removeEventListener('message', handlePaymentMessage);
}
Connecting Everything
Add this script to connect your form to the payment flow:
// Set up the button click handler to start the payment process
document.getElementById('start-payment').addEventListener('click', function() {
// Get values from form inputs
const walletAddress = document.getElementById('wallet').value;
const amount = document.getElementById('amount').value;
const theme = document.getElementById('theme').value; // Get selected theme
// Validate inputs (basic validation)
if (!walletAddress || !amount) {
alert('Please enter both wallet address and amount');
return;
}
// Start payment process with collected values
showPayment(walletAddress, amount, theme);
});
Complete Implementation Example
Here’s a complete example showing how to implement the Phoenix payment in an HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phoenix Payment Demo</title>
<style>
/* Basic page styling */
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #16A34A;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
/* Phoenix Payment Styles */
#payment-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
display: none;
}
.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;
box-shadow: none;
overflow: visible;
}
.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>
</head>
<body>
<h1>Phoenix Payment Demo</h1>
<div class="input-group">
<label for="wallet">Wallet Address:</label>
<input id="wallet" type="text" placeholder="0x...">
</div>
<div class="input-group">
<label for="amount">Amount (USD):</label>
<input id="amount" type="number" placeholder="100">
</div>
<!-- Add this new input group -->
<div class="input-group">
<label for="theme">Theme:</label>
<select id="theme">
<option value="">Default</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
<button id="start-payment">Make Payment</button>
<!-- Payment Container (Hidden by default) -->
<div id="payment-container">
<div class="payment-overlay">
<div class="payment-modal">
<button
id="cancel-payment-button"
class="close-button"
aria-label="Close payment window"
>
×
</button>
<iframe
id="phoenix-payment-iframe"
src="about:blank"
class="teller-iframe"
title="Phoenix Payment"
allow="clipboard-write"
></iframe>
</div>
</div>
</div>
<script>
// Function to get access token from your backend
async function getAccessToken() {
try {
const response = await fetch('/api/token');
const data = await response.json();
return data.access_token;
} catch (error) {
console.error('Error fetching access token:', error);
return null;
}
}
// Function to show payment iframe with parameters
async function showPayment(walletAddress, amount, theme) {
const iframe = document.getElementById('phoenix-payment-iframe');
const container = document.getElementById('payment-container');
// Get the access token from your backend
const accessToken = await getAccessToken();
if (!accessToken) {
alert('Unable to obtain access token. Please try again later.');
return;
}
// Build iframe URL with all parameters
let url = `https://app-partner.phoenix.market/?address=${walletAddress}&amount=${amount}&access_token=${accessToken}`;
if (theme) {
url += `&theme=${theme}`;
}
// Set iframe src
iframe.src = url;
// Display the container that holds the iframe
container.style.display = 'block';
// Set up event listener for messages from the iframe
window.addEventListener('message', handlePaymentMessage);
// Set up cancel button functionality
document.getElementById('cancel-payment-button').onclick = hidePayment;
}
// Function to handle messages received from the iframe
function handlePaymentMessage(event) {
// IMPORTANT: Verify origin for security
if (event.origin !== 'https://app-partner.phoenix.market') return;
// Handle order created message
if (event.data?.type === 'ORDER_CREATED') {
const { order_id, ...data } = event.data.data;
console.log('Order created:', event.data.data);
// Handle the order creation event as needed
// You should store order details in your database, especially the order_id
}
// Handle transaction success message
else if (event.data?.type === 'TRANSACTION_SUCCESS') {
const { amount, token, chain, walletAddress, sendAmount, transactionHash, basescanLink } = event.data.data;
console.log('Transaction successful:', transactionHash);
// You should store transaction details in your database
// and update your UI to show the payment was successful
// Uncomment the following line if you want to close the window after transaction success
// hidePayment();
}
// Handle cancel message
else if (event.data?.type === 'CANCEL') {
console.log('Payment canceled by user');
hidePayment();
}
// Handle open explorer URL message
else if (event.data?.type === 'OPEN_EXPLORER_URL') {
const url = event.data.url;
console.log('Opening explorer URL:', url);
// Open the URL in a new tab
window.open(url, '_blank');
}
// Handle done message
else if (event.data?.type === 'USER_DONE') {
console.log('Payment process completed');
hidePayment();
}
// Handle payment failure or cancellation
else if (event.data?.type === 'PAYMENT_FAILED' || event.data?.type === 'PAYMENT_CANCELLED') {
console.log('Payment failed or cancelled');
// Uncomment the following line if you want to close the window after payment failure/cancellation
// hidePayment();
}
}
// Function to hide the payment iframe
function hidePayment() {
document.getElementById('payment-container').style.display = 'none';
// IMPORTANT: Remove event listener to prevent memory leaks
window.removeEventListener('message', handlePaymentMessage);
}
// Set up the button click handler
document.getElementById('start-payment').addEventListener('click', function() {
// Get values from form inputs
const walletAddress = document.getElementById('wallet').value;
const amount = document.getElementById('amount').value;
const theme = document.getElementById('theme').value; // Get selected theme
// Validate inputs (basic validation)
if (!walletAddress || !amount) {
alert('Please enter both wallet address and amount');
return;
}
// Start payment process with collected values
showPayment(walletAddress, amount, theme);
});
</script>
</body>
</html>