Official documentation for adding the Ezzocard live checkout widget to StreamElements. This page explains the setup process, the supported flow, referral tracking with manual or automatic StreamElements user detection, chat command integration, and provides a full copy-ready widget code block.
Useful public resources related to this integration.
The user can choose from the supported Ezzocard products.
Only amounts aligned with the configured sell prices are displayed.
The widget supports BTC, ETH, USDT networks, DOGE, LTC, TRX, SOL and BNB.
The widget creates the order and displays payment instructions.
Users can scan the QR code directly from the overlay.
Once payment is confirmed, delivered card data is shown in the widget.
Each order can be attributed using a manual ID or automatic StreamElements channel detection.
The PRO widget can react to supported chat commands and show engagement prompts.
StreamElements → My Overlays → Add Widget → Custom Widget
YOUR_STREAMELEMENTS_USER_ID with your own ID, or leave it unchanged if you want to use automatic detection.The PRO widget supports two referral tracking modes.
Replace YOUR_STREAMELEMENTS_USER_ID in the widget code with your own
StreamElements user ID.
If you leave the placeholder unchanged, the widget will try to automatically detect your StreamElements channel username and use it as the referral ID.
const EZZO_REF = "YOUR_STREAMELEMENTS_USER_ID";
const EZZO_CREATOR = "MyChannel";
EZZO_REF, or leave the placeholder for automatic detection.const EZZO_REF = "streamer123";
The PRO widget supports chat-triggered engagement actions through StreamElements event listeners.
Shows a call-to-action prompt and highlights the creator link.
Promotes the card checkout widget to the audience.
Can preselect the Visa card option in the PRO widget.
Displays the supported crypto payment methods to the viewer.
Select a supported card type.
Select an available amount.
Select the crypto payment method.
Click Generate Payment to create the order.
Show the user the wallet address, QR code and exact crypto amount.
Poll for payment status until the cards are delivered.
Copy the full widget code below and paste it into your StreamElements Custom Widget.
<div id="ezzo-widget-v2">
<div class="ezzo-v2-shell">
<div class="ezzo-v2-top">
<div class="ezzo-v2-brand">EZZOCARD LIVE</div>
<div class="ezzo-v2-title">Virtual Card Checkout</div>
<div class="ezzo-v2-sub">Fast crypto payment overlay</div>
<div class="ezzo-v2-promo">
Creator: <strong id="v2-refBadge">creator</strong> | Instant delivery | Visa / Mastercard
</div>
<div class="v2-top-actions">
<a id="v2-creatorUrl" class="v2-top-link" href="https://ezzocard.finance/" target="_blank" rel="noopener">Open creator link</a>
<button id="v2-copyCreatorBtn" class="v2-mini-btn" type="button">Copy ref link</button>
</div>
<div id="v2-alertStack" class="v2-alert-stack"></div>
</div>
<div id="v2-step-select" class="v2-step active">
<div class="v2-label">Card type</div>
<select id="v2-cardType"></select>
<div class="v2-label">Amount</div>
<select id="v2-amount"></select>
<div id="v2-cardInfo" class="v2-card-info"></div>
<div class="v2-label">Crypto</div>
<div id="v2-cryptoGrid" class="v2-crypto-grid"></div>
<div class="v2-summary">
<div><span>Card</span><strong id="v2-sumCard">-</strong></div>
<div><span>Amount</span><strong id="v2-sumAmount">-</strong></div>
<div><span>Crypto</span><strong id="v2-sumCrypto">-</strong></div>
</div>
<div class="v2-chat-panel">
<div class="v2-chat-head">
<strong>Chat bot triggers</strong>
<span>Viewer engagement</span>
</div>
<div class="v2-chat-commands">
<span id="v2-cmd-buy">!buy</span>
<span id="v2-cmd-card">!card</span>
<span id="v2-cmd-visa">!visa</span>
<span id="v2-cmd-crypto">!crypto</span>
</div>
<div id="v2-chatHint" class="v2-chat-hint">
Viewers can trigger promo prompts directly from chat.
</div>
</div>
<button id="v2-createBtn" class="v2-main-btn" disabled>
Generate Payment
</button>
<div id="v2-createMsg" class="v2-msg"></div>
</div>
<div id="v2-step-pay" class="v2-step">
<div class="v2-pay-box">
<div class="v2-pay-head">Payment Details</div>
<div class="v2-pay-card-brand" id="v2-payCardBrand"></div>
<div class="v2-pay-row"><span>Order ID</span><strong id="v2-orderId">-</strong></div>
<div class="v2-pay-row"><span>Secret</span><strong id="v2-secret">-</strong></div>
<div class="v2-pay-row">
<span>Crypto</span>
<strong id="v2-crypto" class="v2-pay-crypto">-</strong>
</div>
<div class="v2-pay-row"><span>Total</span><strong id="v2-total">-</strong></div>
</div>
<div class="v2-label">Wallet address</div>
<div class="v2-address-box">
<div id="v2-address" class="v2-address-text">-</div>
<button id="v2-copyBtn" class="v2-copy-btn">Copy</button>
</div>
<div class="v2-qr-wrap">
<div class="v2-qr-box">
<div id="v2-qrCanvas"></div>
</div>
<div class="v2-qr-note" id="v2-qrNote">Scan to pay</div>
</div>
<div class="v2-link-box">
<a id="v2-paymentUrl" href="#" target="_blank" rel="noopener">Open payment page</a>
</div>
<div class="v2-timer-wrap">
<div class="v2-timer-head">
<span id="v2-statusText">Waiting for payment...</span>
<strong id="v2-timeText">30:00</strong>
</div>
<div class="v2-progress">
<div id="v2-progressBar" class="v2-progress-bar"></div>
</div>
</div>
<div class="v2-actions">
<button id="v2-checkBtn" class="v2-sec-btn">Check now</button>
<button id="v2-resetBtn" class="v2-cancel-btn">Cancel</button>
</div>
<div id="v2-payMsg" class="v2-msg"></div>
</div>
<div id="v2-step-done" class="v2-step">
<div class="v2-done-title">Payment Confirmed</div>
<div id="v2-doneMeta" class="v2-msg ok">Card delivered successfully.</div>
<div id="v2-cardsWrap" class="v2-cards-wrap"></div>
<button id="v2-resetBtn2" class="v2-main-btn">New Order</button>
</div>
</div>
</div>
<style>
#ezzo-widget-v2 {
width: 390px;
max-width: 390px;
font-family: Inter, Arial, sans-serif;
color: #eef3ff;
}
* {
box-sizing: border-box;
}
.ezzo-v2-shell {
border-radius: 24px;
overflow: hidden;
background:
radial-gradient(circle at top right, rgba(110, 124, 255, 0.22), transparent 32%),
linear-gradient(180deg, rgba(10,14,24,0.98), rgba(5,8,16,0.98));
border: 1px solid rgba(255,255,255,0.08);
box-shadow: 0 20px 70px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
.ezzo-v2-top {
padding: 16px;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ezzo-v2-brand {
display: inline-block;
padding: 5px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 800;
letter-spacing: 0.12em;
background: rgba(110,124,255,0.14);
border: 1px solid rgba(110,124,255,0.24);
color: #cfd7ff;
}
.ezzo-v2-title {
margin-top: 10px;
font-size: 22px;
font-weight: 800;
}
.ezzo-v2-sub {
margin-top: 4px;
font-size: 12px;
color: #9fb0d0;
}
.ezzo-v2-promo {
margin-top: 10px;
font-size: 11px;
line-height: 1.45;
color: #dbe4ff;
}
.v2-top-actions {
display: flex;
gap: 8px;
margin-top: 10px;
}
.v2-top-link,
.v2-mini-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 34px;
padding: 8px 10px;
border-radius: 12px;
font-size: 11px;
font-weight: 800;
text-decoration: none;
}
.v2-top-link {
flex: 1;
color: #eef3ff;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
}
.v2-top-link:hover {
background: rgba(255,255,255,0.08);
}
.v2-mini-btn {
cursor: pointer;
border: 1px solid rgba(110,124,255,0.25);
color: #fff;
background: rgba(110,124,255,0.18);
}
.v2-mini-btn:hover {
background: rgba(110,124,255,0.28);
}
.v2-alert-stack {
display: grid;
gap: 8px;
margin-top: 12px;
}
.v2-alert {
padding: 10px 12px;
border-radius: 14px;
font-size: 11px;
line-height: 1.45;
background: rgba(17,22,35,0.96);
border: 1px solid rgba(255,255,255,0.08);
animation: v2SlideIn 0.25s ease;
}
.v2-alert strong {
display: block;
font-size: 12px;
margin-bottom: 3px;
}
.v2-alert.info {
border-color: rgba(60,123,255,0.25);
}
.v2-alert.success {
border-color: rgba(32,211,138,0.28);
}
.v2-alert.warn {
border-color: rgba(255,179,71,0.32);
}
@keyframes v2SlideIn {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
.v2-step {
display: none;
padding: 14px;
}
.v2-step.active {
display: block;
}
.v2-label {
font-size: 12px;
color: #9fb0d0;
margin: 10px 0 6px;
}
#ezzo-widget-v2 select {
width: 100%;
background: rgba(20,26,42,0.95);
border: 1px solid rgba(255,255,255,0.08);
color: #eef3ff;
border-radius: 14px;
padding: 12px;
outline: none;
}
.v2-card-info,
.v2-summary,
.v2-pay-box,
.v2-address-box,
.v2-link-box,
.v2-timer-wrap,
.v2-card-box,
.v2-qr-box {
background: rgba(17,22,35,0.96);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 16px;
}
.v2-card-info {
padding: 12px;
color: #bfd0f2;
font-size: 12px;
line-height: 1.55;
margin-top: 10px;
}
.v2-card-info-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 10px;
margin-bottom: 8px;
}
.v2-card-brand-line {
margin-top: 8px;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.v2-card-brand-label {
font-size: 11px;
color: #9fb0d0;
font-weight: 700;
}
.v2-brand-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 999px;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
color: #eef3ff;
font-size: 11px;
font-weight: 800;
}
.v2-brand-chip-large {
margin-bottom: 10px;
font-size: 12px;
}
.v2-brand-logo {
height: 34px;
max-width: 34px;
object-fit: contain;
border-radius: 6px;
padding: 2px 4px;
}
.v2-pay-card-brand {
margin-bottom: 10px;
}
.v2-tag {
display: inline-block;
margin: 3px 4px 0 0;
padding: 4px 8px;
border-radius: 999px;
background: rgba(255,255,255,0.05);
font-size: 10px;
color: #d9e1ff;
}
.v2-crypto-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.v2-crypto-btn {
border: 1px solid rgba(255,255,255,0.08);
background: rgba(20,26,42,0.96);
color: #eef3ff;
border-radius: 12px;
padding: 10px 8px;
font-size: 11px;
font-weight: 700;
cursor: pointer;
transition: 0.18s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 92px;
gap: 4px;
}
.v2-crypto-btn:hover {
transform: translateY(-1px);
border-color: rgba(110,124,255,0.4);
}
.v2-crypto-btn.active {
background: rgba(110,124,255,0.18);
border-color: rgba(110,124,255,0.45);
}
.v2-crypto-icon {
width: 28px;
height: 28px;
object-fit: contain;
background: #fff;
border-radius: 50%;
padding: 2px;
}
.v2-crypto-symbol {
font-size: 11px;
font-weight: 800;
line-height: 1.1;
}
.v2-crypto-network {
font-size: 10px;
color: #9fb0d0;
background: rgba(255,255,255,0.06);
border-radius: 999px;
padding: 2px 6px;
line-height: 1.1;
}
.v2-summary {
margin-top: 12px;
padding: 12px;
}
.v2-chat-panel {
margin-top: 12px;
padding: 12px;
background: rgba(17,22,35,0.96);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 16px;
}
.v2-chat-head {
display: flex;
justify-content: space-between;
gap: 10px;
align-items: center;
}
.v2-chat-head strong {
font-size: 12px;
}
.v2-chat-head span,
.v2-chat-hint {
font-size: 11px;
color: #9fb0d0;
}
.v2-chat-commands {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.v2-chat-commands span {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 7px 10px;
border-radius: 999px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
font-size: 11px;
font-weight: 800;
color: #eef3ff;
}
.v2-summary div,
.v2-pay-row {
display: flex;
justify-content: space-between;
gap: 10px;
padding: 6px 0;
font-size: 12px;
}
.v2-summary span,
.v2-pay-row span {
color: #9fb0d0;
}
.v2-pay-crypto span {
display: inline-flex;
align-items: center;
gap: 8px;
}
.v2-pay-crypto img {
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
padding: 1px;
}
.v2-main-btn,
.v2-sec-btn,
.v2-cancel-btn,
.v2-copy-btn,
.v2-toggle-btn {
cursor: pointer;
border: 0;
font-weight: 800;
transition: 0.18s ease;
}
.v2-main-btn {
width: 100%;
margin-top: 12px;
padding: 13px;
border-radius: 14px;
color: white;
background: linear-gradient(135deg, #14b86a, #21d07a);
box-shadow: 0 10px 30px rgba(20,184,106,0.18);
}
.v2-main-btn:hover {
transform: translateY(-1px);
background: linear-gradient(135deg, #12a45f, #1cc46f);
box-shadow: 0 14px 34px rgba(20,184,106,0.28);
}
.v2-main-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.v2-pay-box {
padding: 12px;
}
.v2-pay-head {
font-size: 14px;
font-weight: 800;
margin-bottom: 8px;
}
.v2-address-box {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
}
.v2-address-text {
flex: 1;
font-family: monospace;
font-size: 12px;
word-break: break-all;
}
.v2-copy-btn {
padding: 8px 12px;
border-radius: 12px;
color: #fff;
background: rgba(110,124,255,0.18);
border: 1px solid rgba(110,124,255,0.25);
}
.v2-copy-btn:hover {
background: rgba(110,124,255,0.28);
}
.v2-qr-wrap {
margin-top: 12px;
text-align: center;
}
.v2-qr-box {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px;
}
#v2-qrCanvas {
width: 180px;
height: 180px;
background: #fff;
border-radius: 12px;
overflow: hidden;
}
#v2-qrCanvas img,
#v2-qrCanvas canvas {
display: block;
width: 180px !important;
height: 180px !important;
border-radius: 12px;
}
.v2-qr-note {
margin-top: 8px;
font-size: 12px;
color: #9fb0d0;
}
.v2-link-box {
margin-top: 10px;
padding: 10px 12px;
}
.v2-link-box a {
color: #cfd7ff;
text-decoration: none;
font-size: 12px;
}
.v2-timer-wrap {
margin-top: 12px;
padding: 12px;
}
.v2-timer-head {
display: flex;
justify-content: space-between;
gap: 10px;
font-size: 12px;
}
.v2-progress {
margin-top: 10px;
height: 10px;
border-radius: 999px;
background: rgba(255,255,255,0.06);
overflow: hidden;
}
.v2-progress-bar {
height: 100%;
width: 100%;
background: linear-gradient(90deg, #20d38a, #8fdc6b);
transition: width 0.8s linear;
}
.v2-actions {
display: flex;
gap: 10px;
margin-top: 12px;
}
.v2-sec-btn,
.v2-cancel-btn {
flex: 1;
padding: 12px;
border-radius: 12px;
}
.v2-sec-btn {
color: #fff;
background: linear-gradient(135deg, #2667ff, #3c7bff);
border: 1px solid rgba(60,123,255,0.35);
box-shadow: 0 10px 28px rgba(38,103,255,0.16);
}
.v2-sec-btn:hover {
transform: translateY(-1px);
background: linear-gradient(135deg, #1f59e2, #2f6df0);
box-shadow: 0 14px 32px rgba(38,103,255,0.28);
}
.v2-cancel-btn {
color: #d9e1ff;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
}
.v2-cancel-btn:hover {
transform: translateY(-1px);
color: #fff;
background: linear-gradient(135deg, #c9304f, #ef476f);
border-color: rgba(239,71,111,0.4);
box-shadow: 0 14px 32px rgba(239,71,111,0.28);
}
.v2-msg {
margin-top: 10px;
font-size: 12px;
color: #aebddd;
line-height: 1.5;
min-height: 18px;
}
.v2-msg.ok {
color: #9af0c9;
}
.v2-done-title {
font-size: 16px;
font-weight: 800;
color: #20d38a;
}
.v2-cards-wrap {
display: grid;
gap: 10px;
margin-top: 12px;
}
.v2-card-box {
padding: 12px;
}
.v2-card-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.v2-card-head strong {
font-size: 13px;
}
.v2-toggle-btn {
padding: 7px 10px;
border-radius: 10px;
color: #fff;
background: rgba(110,124,255,0.18);
border: 1px solid rgba(110,124,255,0.22);
}
.v2-toggle-btn:hover {
background: rgba(110,124,255,0.28);
}
.v2-card-line {
display: flex;
justify-content: space-between;
gap: 10px;
padding: 6px 0;
font-size: 12px;
}
.v2-card-line span {
color: #9fb0d0;
}
.v2-secret {
filter: blur(7px);
user-select: none;
transition: 0.18s ease;
}
.v2-secret.show {
filter: blur(0);
user-select: text;
}
.v2-status-ok {
color: #9af0c9;
}
.v2-status-bad {
color: #ff9aaa;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script>
const V2_CREATE = 'https://ezzocard.finance/ai-agent/process.php';
const V2_STATUS = 'https://ezzocard.finance/api/ai-check.php?secret=';
/***********************************************************
###########################
# YOUR STREAMER ID #
###########################
Replace with your StreamElements User ID.
If you leave the placeholder unchanged, the widget will try
to auto-detect the channel username from StreamElements.
Examples:
const EZZO_REF = "my_stream_id";
const EZZO_CREATOR = "MyChannel";
***********************************************************/
const EZZO_REF = "YOUR_STREAMELEMENTS_USER_ID";
const EZZO_CREATOR = "";
const EZZO_CAMPAIGN = "streamelements";
const EZZO_SOURCE = "streamelements-widget";
const V2_CRYPTOS = [
'BTC',
'ETH',
'USDT.ERC20',
'USDT.TRC20',
'USDT.BEP20',
'USDT.SOL',
'DOGE',
'LTC',
'TRX',
'SOL',
'BNB'
];
const V2_CRYPTO_META = {
'BTC': { name: 'Bitcoin', symbol: 'BTC', image: 'https://ezzocard.finance/images/bitcoin.svg', network: 'Bitcoin' },
'ETH': { name: 'Ethereum', symbol: 'ETH', image: 'https://ezzocard.finance/images/ethereum.svg', network: 'ERC20' },
'USDT.ERC20': { name: 'Tether', symbol: 'USDT', image: 'https://ezzocard.finance/images/usdt_erc20.svg', network: 'ERC20' },
'USDT.TRC20': { name: 'Tether', symbol: 'USDT', image: 'https://ezzocard.finance/images/usdt_trc20.svg', network: 'TRC20' },
'USDT.BEP20': { name: 'Tether', symbol: 'USDT', image: 'https://ezzocard.finance/images/usdt_bep20.svg', network: 'BEP20' },
'USDT.SOL': { name: 'Tether', symbol: 'USDT', image: 'https://ezzocard.finance/images/usdt_sol.svg', network: 'SOL' },
'DOGE': { name: 'Dogecoin', symbol: 'DOGE', image: 'https://ezzocard.finance/images/dogecoin.svg', network: 'Dogecoin' },
'LTC': { name: 'Litecoin', symbol: 'LTC', image: 'https://ezzocard.finance/images/litecoin.svg', network: 'Litecoin' },
'TRX': { name: 'Tron', symbol: 'TRX', image: 'https://ezzocard.finance/images/tron.svg', network: 'TRC20' },
'SOL': { name: 'Solana', symbol: 'SOL', image: 'https://ezzocard.finance/images/solana.svg', network: 'Solana' },
'BNB': { name: 'BNB', symbol: 'BNB', image: 'https://ezzocard.finance/images/bnb.svg', network: 'BEP20' }
};
const V2_CARD_BRANDS = {
'gold_visa': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'gold_mastercard': { brand: 'Mastercard', logo: 'https://ezzocard.finance/images/s-mastercard.svg' },
'violet': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'lime-7': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'lime-30': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'brown': { brand: 'Mastercard', logo: 'https://ezzocard.finance/images/s-mastercard.svg' },
'orange': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'yellow': { brand: 'Mastercard', logo: 'https://ezzocard.finance/images/s-mastercard.svg' },
'maroon': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' },
'teal': { brand: 'Visa', logo: 'https://ezzocard.finance/images/s-visa.svg' }
};
const V2_CARDS = {
'gold_visa': {
title: 'Gold Visa',
currency: 'USD',
validity: '24 months',
registration: 'Required',
tags: ['Visa', '3D Secure', 'Wallet'],
pricing: { 100: 125.00, 200: 215.00, 500: 509.99, 1000: 1029.99, 2000: 2029.99, 5000: 5029.99, 10000: 10229.99 }
},
'gold_mastercard': {
title: 'Gold Mastercard',
currency: 'USD',
validity: '24 months',
registration: 'Required',
tags: ['Mastercard', '3D Secure', 'Wallet'],
pricing: { 100: 125.00, 200: 219.00, 500: 509.99, 1000: 1029.99, 2000: 2029.99, 5000: 5029.99, 10000: 10229.99 }
},
'violet': {
title: 'Violet',
currency: 'USD',
validity: '24 months',
registration: 'No',
tags: ['Anonymous', 'USD'],
pricing: { 50: 74.99, 100: 119.99, 250: 275.00, 500: 505.99, 1000: 1019.99, 2000: 2179.99, 5000: 5179.99, 10000: 10179.99 }
},
'lime-7': {
title: 'Lime-7',
currency: 'USD',
validity: '24 months',
registration: 'No',
tags: ['Anonymous', 'Wallet'],
pricing: { 100: 109.99, 200: 209.99, 250: 259.99, 500: 504.99, 2000: 2009.99, 5000: 5009.99 }
},
'lime-30': {
title: 'Lime-30',
currency: 'USD',
validity: '24 months',
registration: 'No',
tags: ['Anonymous', 'US Only'],
pricing: { 100: 114.99, 200: 219.99, 250: 269.99, 500: 504.99, 2000: 2019.99, 10000: 10019.99 }
},
'brown': {
title: 'Brown',
currency: 'CAD',
validity: '6 months - 3 years',
registration: 'Required',
tags: ['CAD', 'Mastercard'],
pricing: { 25: 51.99, 50: 57.99, 100: 99.99, 200: 189.99 }
},
'orange': {
title: 'Orange',
currency: 'USD',
validity: '5-8 years',
registration: 'Required',
tags: ['US Only', 'Visa'],
pricing: { 25: 56.99, 50: 79.99, 100: 124.99, 200: 259.99 }
},
'yellow': {
title: 'Yellow',
currency: 'USD',
validity: '12 months',
registration: 'No',
tags: ['Anonymous', 'Mastercard'],
pricing: { 50: 54.00, 100: 110.00, 200: 215.00, 250: 260.00, 500: 505.00 }
},
'maroon': {
title: 'Maroon',
currency: 'USD',
validity: '6 months',
registration: 'No',
tags: ['US Only', 'Visa'],
pricing: { 25: 49.99, 50: 77.99, 100: 122.99, 200: 249.99, 500: 506.99, 1000: 1199.99 }
},
'teal': {
title: 'Teal',
currency: 'CAD',
validity: '6 months',
registration: 'No',
tags: ['CAD', 'Anonymous'],
pricing: { 25: 44.99, 50: 52.99, 100: 89.99, 250: 209.99 }
}
};
const v2 = {
cardType: 'gold_visa',
amount: 100,
crypto: '',
order: null,
expiresIn: 1800,
startedAt: null,
poller: null,
timer: null,
ref: EZZO_REF,
campaign: EZZO_CAMPAIGN,
source: EZZO_SOURCE,
creatorName: EZZO_CREATOR || 'creator',
creatorUrl: EZZO_REF && EZZO_REF !== 'YOUR_STREAMELEMENTS_USER_ID'
? `https://ezzocard.finance/?ref=${encodeURIComponent(EZZO_REF)}`
: 'https://ezzocard.finance/',
commands: {
buy: '!buy',
card: '!card',
visa: '!visa',
crypto: '!crypto'
},
alerts: [],
activity: [],
autoDetectedRef: false
};
const $ = (id) => document.getElementById(id);
function esc(v) {
return String(v ?? '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function money(v) {
const n = Number(v || 0);
return n.toFixed(2);
}
function pickNonEmpty(...values) {
return values.find(v => String(v || '').trim() !== '') || '';
}
function isPlaceholderRef(value) {
const v = String(value || '').trim();
return !v || v === 'YOUR_STREAMELEMENTS_USER_ID' || v === 'creator';
}
function normalizeChannelRef(value) {
return String(value || '')
.trim()
.replace(/^@+/, '')
.replace(/\s+/g, '');
}
function buildCreatorUrl(refValue) {
const ref = normalizeChannelRef(refValue);
return ref ? `https://ezzocard.finance/?ref=${encodeURIComponent(ref)}` : 'https://ezzocard.finance/';
}
function getCardConfig() {
return V2_CARDS[v2.cardType];
}
function getSellPrice() {
const cfg = getCardConfig();
return cfg?.pricing?.[v2.amount] ?? null;
}
function getAmountsForCard(cardType) {
const cfg = V2_CARDS[cardType];
return Object.keys(cfg.pricing).map(Number).sort((a, b) => a - b);
}
function v2SetStep(stepId) {
['v2-step-select', 'v2-step-pay', 'v2-step-done'].forEach(id => {
$(id).classList.remove('active');
});
$(stepId).classList.add('active');
}
function v2InitCardTypes() {
$('v2-cardType').innerHTML = Object.entries(V2_CARDS)
.map(([k, c]) => `<option value="${k}">${c.title} (${c.currency})</option>`)
.join('');
}
function v2InitAmounts() {
const cfg = getCardConfig();
const amounts = getAmountsForCard(v2.cardType);
$('v2-amount').innerHTML = amounts.map(a => {
const sellPrice = cfg.pricing[a];
return `<option value="${a}">${cfg.currency} ${a} - Sell ${cfg.currency} ${money(sellPrice)}</option>`;
}).join('');
v2.amount = Number($('v2-amount').value);
}
function v2RenderInfo() {
const c = getCardConfig();
const brandMeta = V2_CARD_BRANDS[v2.cardType] || null;
$('v2-cardInfo').innerHTML = `
<div class="v2-card-info-top">
<div>
<div><strong>${esc(c.title)}</strong></div>
<div class="v2-card-brand-line">
<span class="v2-card-brand-label">Card type</span>
${brandMeta ? `
<span class="v2-brand-chip">
<img src="${brandMeta.logo}" alt="${esc(brandMeta.brand)}" class="v2-brand-logo">
<span>${esc(brandMeta.brand)}</span>
</span>
` : ''}
</div>
</div>
</div>
<div>Currency: ${esc(c.currency)}</div>
<div>Validity: ${esc(c.validity)}</div>
<div>Registration: ${esc(c.registration)}</div>
<div style="margin-top:6px;">${c.tags.map(t => `<span class="v2-tag">${esc(t)}</span>`).join('')}</div>
`;
}
function v2RenderCryptos() {
$('v2-cryptoGrid').innerHTML = V2_CRYPTOS.map(c => {
const meta = V2_CRYPTO_META[c] || { symbol: c, image: '', network: '' };
return `
<button class="v2-crypto-btn ${v2.crypto === c ? 'active' : ''}" data-crypto="${c}">
${meta.image ? `<img src="${meta.image}" alt="${esc(meta.symbol)}" class="v2-crypto-icon">` : ''}
<span class="v2-crypto-symbol">${esc(meta.symbol)}</span>
${meta.network ? `<span class="v2-crypto-network">${esc(meta.network)}</span>` : ''}
</button>
`;
}).join('');
[...document.querySelectorAll('.v2-crypto-btn')].forEach(btn => {
btn.onclick = () => {
v2.crypto = btn.dataset.crypto;
v2RenderCryptos();
v2UpdateSummary();
};
});
}
function v2UpdateSummary() {
const cfg = getCardConfig();
const sellPrice = getSellPrice();
const cryptoMeta = V2_CRYPTO_META[v2.crypto] || null;
const brandMeta = V2_CARD_BRANDS[v2.cardType] || null;
if (brandMeta) {
$('v2-sumCard').innerHTML = `
<span style="display:inline-flex;align-items:center;gap:8px;">
<img src="${brandMeta.logo}" alt="${esc(brandMeta.brand)}" style="height:34px;max-width:34px;object-fit:contain;border-radius:6px;padding:2px;">
${esc(cfg.title)}
</span>
`;
} else {
$('v2-sumCard').textContent = cfg.title;
}
$('v2-sumAmount').textContent = `${cfg.currency} ${v2.amount} -> ${cfg.currency} ${money(sellPrice)}`;
if (v2.crypto && cryptoMeta) {
$('v2-sumCrypto').textContent = `${cryptoMeta.symbol} (${cryptoMeta.network})`;
} else {
$('v2-sumCrypto').textContent = '-';
}
$('v2-createBtn').disabled = !v2.crypto || !sellPrice;
}
function v2BuildQrValue(address, crypto, amount) {
const c = String(crypto || '').toUpperCase();
if (!address) return '';
if (c === 'BTC') return `bitcoin:${address}?amount=${amount || ''}`;
if (c === 'ETH') return `ethereum:${address}`;
if (c === 'LTC') return `litecoin:${address}?amount=${amount || ''}`;
if (c === 'DOGE') return `dogecoin:${address}?amount=${amount || ''}`;
if (c === 'TRX') return address;
if (c === 'SOL') return address;
if (c === 'BNB') return address;
if (c.indexOf('USDT') === 0) return address;
return address;
}
function v2RenderQr(address, crypto, amount) {
const qrBox = $('v2-qrCanvas');
const qrNote = $('v2-qrNote');
if (!qrBox) return;
qrBox.innerHTML = '';
const qrValue = v2BuildQrValue(address, crypto, amount);
if (!qrValue) {
qrNote.textContent = 'QR unavailable';
return;
}
new QRCode(qrBox, {
text: qrValue,
width: 180,
height: 180
});
qrNote.textContent = `Scan to pay with ${crypto}`;
}
function v2Notify(type, title, message) {
const stack = $('v2-alertStack');
if (!stack) return;
const box = document.createElement('div');
box.className = `v2-alert ${type || 'info'}`;
box.innerHTML = `<strong>${esc(title)}</strong><div>${esc(message)}</div>`;
stack.prepend(box);
while (stack.children.length > 3) {
stack.removeChild(stack.lastChild);
}
setTimeout(() => {
if (box.parentNode) box.parentNode.removeChild(box);
}, 7000);
}
function v2CopyCreatorUrl() {
if (!v2.creatorUrl) return;
navigator.clipboard.writeText(v2.creatorUrl).then(() => {
v2Notify('success', 'Referral link copied', 'Share this creator link in chat, panels, or bot replies.');
}).catch(() => {
v2Notify('warn', 'Copy failed', 'Clipboard is not available in this browser context.');
});
}
function v2SetCardType(cardType) {
if (!V2_CARDS[cardType]) return;
v2.cardType = cardType;
$('v2-cardType').value = cardType;
v2InitAmounts();
v2RenderInfo();
v2UpdateSummary();
}
function v2HandleChatCommand(rawText, username = 'Viewer') {
const text = String(rawText || '').trim().toLowerCase();
if (!text) return;
if (text === v2.commands.buy.toLowerCase() || text.startsWith(v2.commands.buy.toLowerCase() + ' ')) {
v2Notify('info', `${username} used ${v2.commands.buy}`, `CTA pushed for ${v2.creatorName}. Link: ${v2.creatorUrl}`);
return;
}
if (text === v2.commands.card.toLowerCase() || text.startsWith(v2.commands.card.toLowerCase() + ' ')) {
v2Notify('info', `${username} asked for cards`, 'Overlay highlighted the active card checkout funnel.');
return;
}
if (text === v2.commands.visa.toLowerCase() || text.startsWith(v2.commands.visa.toLowerCase() + ' ')) {
v2SetCardType('gold_visa');
v2Notify('info', `${username} requested Visa`, 'Gold Visa was preselected in the widget.');
return;
}
if (text === v2.commands.crypto.toLowerCase() || text.startsWith(v2.commands.crypto.toLowerCase() + ' ')) {
v2Notify('warn', `${username} asked for crypto options`, V2_CRYPTOS.join(', '));
}
}
function v2GetRuntimeConfig(detail = null) {
const params = new URLSearchParams(window.location.search);
const seData = detail?.detail?.fieldData || window.fieldData || {};
const channelData = detail?.detail?.channel || {};
const custom = window.EZZO_WIDGET_CONFIG || {};
const channelUsername = normalizeChannelRef(channelData.username || '');
const manualRef = normalizeChannelRef(EZZO_REF);
const manualCreator = String(EZZO_CREATOR || '').trim();
const runtimeRef = normalizeChannelRef(
pickNonEmpty(seData.ref, custom.ref, params.get('ref'))
);
if (!isPlaceholderRef(manualRef)) {
v2.ref = manualRef;
} else if (runtimeRef) {
v2.ref = runtimeRef;
} else if (channelUsername) {
v2.ref = channelUsername;
v2.autoDetectedRef = true;
} else {
v2.ref = 'creator';
}
const runtimeCampaign = pickNonEmpty(seData.campaign, custom.campaign, params.get('campaign'));
const runtimeSource = pickNonEmpty(seData.source, custom.source, params.get('source'));
const runtimeCreator = pickNonEmpty(seData.creatorName, custom.creatorName, params.get('creator'));
v2.campaign = runtimeCampaign || EZZO_CAMPAIGN;
v2.source = runtimeSource || EZZO_SOURCE;
v2.creatorName = runtimeCreator || manualCreator || v2.ref || 'creator';
const runtimeClickUrl = pickNonEmpty(seData.clickUrl, custom.clickUrl, params.get('url'));
v2.creatorUrl = runtimeClickUrl || buildCreatorUrl(v2.ref);
v2.commands.buy = pickNonEmpty(seData.cmdBuy, custom.cmdBuy, params.get('cmd_buy')) || '!buy';
v2.commands.card = pickNonEmpty(seData.cmdCard, custom.cmdCard, params.get('cmd_card')) || '!card';
v2.commands.visa = pickNonEmpty(seData.cmdVisa, custom.cmdVisa, params.get('cmd_visa')) || '!visa';
v2.commands.crypto = pickNonEmpty(seData.cmdCrypto, custom.cmdCrypto, params.get('cmd_crypto')) || '!crypto';
}
function v2RenderRuntimeMeta() {
$('v2-refBadge').textContent = v2.creatorName;
$('v2-creatorUrl').href = v2.creatorUrl;
$('v2-cmd-buy').textContent = v2.commands.buy;
$('v2-cmd-card').textContent = v2.commands.card;
$('v2-cmd-visa').textContent = v2.commands.visa;
$('v2-cmd-crypto').textContent = v2.commands.crypto;
$('v2-chatHint').textContent = `Affiliate ref: ${v2.ref} | Campaign: ${v2.campaign}`;
}
async function v2CreateOrder() {
$('v2-createMsg').textContent = 'Creating order...';
const cfg = getCardConfig();
const sellPrice = getSellPrice();
if (!sellPrice) {
$('v2-createMsg').textContent = 'Selected amount is not available.';
return;
}
const payload = {
card_type: v2.cardType,
amount: Number(v2.amount),
currency: cfg.currency,
crypto: v2.crypto,
ref: v2.ref,
campaign: v2.campaign,
source: v2.source,
creator_name: v2.creatorName,
landing_url: v2.creatorUrl
};
try {
const res = await fetch(V2_CREATE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await res.json();
if (!res.ok || !data.success || !data.secret) {
throw new Error(data.message || data.error || 'Could not create order.');
}
v2.order = data;
v2.expiresIn = Number(data.expires_in || 1800);
v2.startedAt = Date.now();
$('v2-orderId').textContent = data.order_id || '-';
$('v2-secret').textContent = data.secret || '-';
const payBrand = $('v2-payCardBrand');
const brandMeta = V2_CARD_BRANDS[v2.cardType] || null;
if (brandMeta) {
payBrand.innerHTML = `
<span class="v2-brand-chip v2-brand-chip-large">
<img src="${brandMeta.logo}" alt="${esc(brandMeta.brand)}" class="v2-brand-logo">
<span>${esc(getCardConfig().title)} | ${esc(brandMeta.brand)}</span>
</span>
`;
} else {
payBrand.innerHTML = '';
}
const payCrypto = $('v2-crypto');
const cryptoMeta = V2_CRYPTO_META[data.crypto] || null;
if (cryptoMeta) {
payCrypto.innerHTML = `
<span>
<img src="${cryptoMeta.image}" alt="${esc(cryptoMeta.symbol)}">
${esc(data.crypto)}
</span>
`;
} else {
payCrypto.textContent = data.crypto || '-';
}
$('v2-total').textContent = data.amount_crypto ? `${data.amount_crypto} ${data.crypto}` : '-';
$('v2-address').textContent = data.address || '-';
v2RenderQr(data.address || '', data.crypto || '', data.amount_crypto || '');
const link = $('v2-paymentUrl');
if (data.payment_url) {
link.href = data.payment_url;
link.textContent = 'Open payment page';
} else {
link.href = '#';
link.textContent = 'Payment page unavailable';
}
$('v2-statusText').textContent = 'Waiting for payment...';
$('v2-payMsg').textContent = `Selected sell price: ${cfg.currency} ${money(sellPrice)}. Send the exact crypto amount.`;
v2Notify('info', 'Order created', `${cfg.title} | ${cfg.currency} ${v2.amount} | ${data.crypto} | ref ${v2.ref}`);
v2SetStep('v2-step-pay');
$('v2-createMsg').textContent = '';
v2StartPolling();
} catch (e) {
$('v2-createMsg').textContent = e.message || 'Order creation failed.';
}
}
function v2TimeFormat(sec) {
sec = Math.max(0, Number(sec || 0));
const m = String(Math.floor(sec / 60)).padStart(2, '0');
const s = String(sec % 60).padStart(2, '0');
return `${m}:${s}`;
}
function v2UpdateCountdown(remaining = null) {
if (remaining === null && v2.startedAt) {
const elapsed = Math.floor((Date.now() - v2.startedAt) / 1000);
remaining = Math.max(0, v2.expiresIn - elapsed);
}
if (remaining === null) remaining = v2.expiresIn;
$('v2-timeText').textContent = v2TimeFormat(remaining);
const pct = v2.expiresIn ? (remaining / v2.expiresIn) * 100 : 0;
$('v2-progressBar').style.width = `${Math.max(0, Math.min(100, pct))}%`;
if (pct <= 25) {
$('v2-progressBar').style.background = 'linear-gradient(90deg, #ffb347, #ff6b81)';
} else {
$('v2-progressBar').style.background = 'linear-gradient(90deg, #20d38a, #8fdc6b)';
}
}
function v2StartPolling() {
v2StopPolling();
v2CheckStatus();
v2.poller = setInterval(() => {
v2CheckStatus();
}, 30000);
v2.timer = setInterval(() => {
v2UpdateCountdown();
}, 1000);
}
function v2StopPolling() {
if (v2.poller) {
clearInterval(v2.poller);
v2.poller = null;
}
if (v2.timer) {
clearInterval(v2.timer);
v2.timer = null;
}
}
async function v2CheckStatus() {
if (!v2.order?.secret) return;
try {
const res = await fetch(V2_STATUS + encodeURIComponent(v2.order.secret), {
method: 'GET'
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || 'Status check failed.');
}
if (typeof data.time_remaining_seconds !== 'undefined') {
v2UpdateCountdown(data.time_remaining_seconds);
}
const status = String(data.status || '').toLowerCase();
if (status === 'confirmed') {
$('v2-statusText').textContent = 'Payment confirmed';
$('v2-payMsg').innerHTML = `<span class="v2-status-ok">Card delivered successfully.</span>`;
v2Notify('success', 'Payment confirmed', `${v2.creatorName} conversion completed successfully.`);
v2StopPolling();
v2RenderCards(data);
v2SetStep('v2-step-done');
return;
}
if (status === 'expired') {
$('v2-statusText').textContent = 'Order expired';
$('v2-payMsg').innerHTML = `<span class="v2-status-bad">This order expired. Create a new one.</span>`;
v2StopPolling();
return;
}
if (status === 'awaiting_payment') {
$('v2-statusText').textContent = 'Waiting for payment...';
$('v2-payMsg').textContent = 'No blockchain payment detected yet.';
return;
}
$('v2-statusText').textContent = data.status || 'Waiting for payment...';
$('v2-payMsg').textContent = data.next_step || 'Polling order status...';
} catch (e) {
$('v2-payMsg').innerHTML = `<span class="v2-status-bad">${esc(e.message || 'Status check failed.')}</span>`;
}
}
function v2RenderCards(data) {
const cards = Array.isArray(data.cards) ? data.cards : [];
$('v2-doneMeta').innerHTML = `
Status: ${esc(data.status || 'confirmed')}<br>
Delivery: ${esc(data.delivery_status || 'completed')}<br>
TX: ${esc(data.tx_hash || '-')}
`;
if (!cards.length) {
$('v2-cardsWrap').innerHTML = `
<div class="v2-card-box">
No card data found in response.
</div>
`;
return;
}
$('v2-cardsWrap').innerHTML = cards.map((card, i) => {
const number = card.number || '';
const expiry = card.expiry || '';
const cvv = card.cvv || '';
const displayName = card.display_name || `Card ${i + 1}`;
const system = card.system || 'CARD';
return `
<div class="v2-card-box">
<div class="v2-card-head">
<strong>${esc(displayName)}</strong>
<button class="v2-toggle-btn" onclick="v2ToggleCard(${i}, this)">Show</button>
</div>
<div class="v2-card-line">
<span>System</span>
<strong>${esc(system)}</strong>
</div>
<div class="v2-card-line">
<span>Number</span>
<strong id="v2-card-number-${i}" class="v2-secret">${esc(number)}</strong>
</div>
<div class="v2-card-line">
<span>Expiry</span>
<strong id="v2-card-expiry-${i}" class="v2-secret">${esc(expiry)}</strong>
</div>
<div class="v2-card-line">
<span>CVV</span>
<strong id="v2-card-cvv-${i}" class="v2-secret">${esc(cvv)}</strong>
</div>
</div>
`;
}).join('');
}
function v2ToggleCard(i, btn) {
const fields = [
document.getElementById(`v2-card-number-${i}`),
document.getElementById(`v2-card-expiry-${i}`),
document.getElementById(`v2-card-cvv-${i}`)
];
const showing = fields[0]?.classList.contains('show');
fields.forEach(el => {
if (!el) return;
if (showing) el.classList.remove('show');
else el.classList.add('show');
});
if (btn) btn.textContent = showing ? 'Show' : 'Hide';
}
async function v2CopyAddress() {
const value = $('v2-address').textContent.trim();
if (!value || value === '-') return;
try {
await navigator.clipboard.writeText(value);
$('v2-payMsg').innerHTML = `<span class="v2-status-ok">Address copied.</span>`;
} catch (e) {
$('v2-payMsg').innerHTML = `<span class="v2-status-bad">Copy failed in this browser context.</span>`;
}
}
function v2Reset() {
v2StopPolling();
v2.order = null;
v2.expiresIn = 1800;
v2.startedAt = null;
v2.crypto = '';
$('v2-createMsg').textContent = '';
$('v2-payMsg').textContent = '';
$('v2-cardsWrap').innerHTML = '';
$('v2-doneMeta').textContent = 'Card delivered successfully.';
$('v2-qrCanvas').innerHTML = '';
$('v2-qrNote').textContent = 'Scan to pay';
$('v2-payCardBrand').innerHTML = '';
$('v2-crypto').textContent = '-';
v2SetStep('v2-step-select');
v2RenderCryptos();
v2UpdateSummary();
}
function v2Bind() {
$('v2-cardType').addEventListener('change', (e) => {
v2.cardType = e.target.value;
v2InitAmounts();
v2RenderInfo();
v2UpdateSummary();
});
$('v2-amount').addEventListener('change', (e) => {
v2.amount = Number(e.target.value);
v2UpdateSummary();
});
$('v2-createBtn').addEventListener('click', v2CreateOrder);
$('v2-copyBtn').addEventListener('click', v2CopyAddress);
$('v2-copyCreatorBtn').addEventListener('click', v2CopyCreatorUrl);
$('v2-checkBtn').addEventListener('click', v2CheckStatus);
$('v2-resetBtn').addEventListener('click', v2Reset);
$('v2-resetBtn2').addEventListener('click', v2Reset);
}
function v2Init(detail = null) {
v2GetRuntimeConfig(detail);
v2InitCardTypes();
v2InitAmounts();
v2RenderInfo();
v2RenderCryptos();
v2UpdateSummary();
v2RenderRuntimeMeta();
v2Bind();
if (v2.autoDetectedRef) {
v2Notify('success', 'Auto-detected StreamElements user', `Using channel username as ref: ${v2.ref}`);
} else if (!isPlaceholderRef(EZZO_REF)) {
v2Notify('info', 'Manual ref loaded', `Tracking ref ${v2.ref} from ${v2.source}.`);
} else {
v2Notify('warn', 'Set your Streamer ID', 'Replace YOUR_STREAMELEMENTS_USER_ID or let StreamElements auto-detect your channel username.');
}
}
window.addEventListener('onWidgetLoad', function(obj) {
v2Init(obj);
});
window.addEventListener('onEventReceived', function(obj) {
const listener = obj?.detail?.listener || '';
const event = obj?.detail?.event || {};
if (listener !== 'message') return;
const data = event.data || {};
const text = data.text || event.text || '';
const username = data.displayName || data.nick || event.nick || 'Viewer';
v2HandleChatCommand(text, username);
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
if (!window.SE_API) v2Init();
});
} else {
if (!window.SE_API) v2Init();
}
</script>
ref value.cards array exists after confirmation.
YOUR_STREAMELEMENTS_USER_ID correctly in the widget code,
or confirm that automatic StreamElements username detection is working properly.
EZZO_REF constant.