$MCRT · BEP-20 · BNB Chain
MCRTPay Integration Guide
Add $MCRT payments to any website or app in minutes. Copy-paste lib files, API routes, and React UI — no payment processor account needed.
Wallet:
MCRT contract:
Flow: User sends MCRT → pastes tx hash → API verifies on-chain via BscScan → you activate whatever you sell.
YOUR_WALLET_ADDRESS (BSC BEP-20)MCRT contract:
0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4fFlow: User sends MCRT → pastes tx hash → API verifies on-chain via BscScan → you activate whatever you sell.
Step 1 — Required env vars
Add to .env.local / .env.production:
BSCSCAN_API_KEY=your_key_here # free at bscscan.com/apis
MCRT_PRICE_USD=0.00013 # optional fallback if price APIs are down
MCRTPAY_WALLET_ADDRESS= # optional override; default is already set in mcrtpay-config.ts
BSCSCAN_API_KEY is required for on-chain verification. Get a free key at bscscan.com/apis.
Option A — HTML / Vanilla JS (any stack)
No build step. Drop into any HTML page.
1. Include the scripts
<!-- QR code library -->
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<!-- MCRTPay widget -->
<script src="https://mcrtpay.com/js/mcrtpay.js"></script>
2. Add a container
<div id="mcrtpay-container"></div>
3. Initialize
<script>
new MCRTPay({
containerSelector: '#mcrtpay-container',
paymentAmount: 25, // USD — widget converts to MCRT live
planId: 'premium',
successCallback: function(txHash, network) {
// fired after on-chain verification succeeds
activateYourPlan(txHash);
}
});
</script>
Tip: If you don't have a backend verification endpoint, set
validateOnBackend: false. The widget will still verify format and fire the callback — you handle confirmation manually.
Option B — Next.js (3 lib files, no changes needed)
Copy these 3 files into your project. They work for both Pages Router and App Router.
lib/mcrtpay-config.ts
const DEFAULT_WALLET = 'YOUR_WALLET_ADDRESS'
export function getMCRTPayWalletAddress(): string {
return process.env.MCRTPAY_WALLET_ADDRESS || DEFAULT_WALLET
}
export const MCRTPAY_NETWORKS = [
{
id: 'bep20' as const,
name: 'BSC (BEP-20)',
prefix: 'binance:',
explorerUrl: 'https://bscscan.com/tx/',
tokenAddress: '0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f'
},
{
id: 'erc20' as const,
name: 'Ethereum (ERC-20)',
prefix: 'ethereum:',
explorerUrl: 'https://etherscan.io/tx/',
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDT on ETH
}
] as const
export const MCRTPAY_CONFIG = {
defaultNetwork: 'bep20',
currencies: ['MCRT', 'USDT'] as const,
defaultCurrency: 'MCRT' as const
} as const
export type MCRTPayNetwork = typeof MCRTPAY_NETWORKS[number]
export type MCRTPayCurrency = typeof MCRTPAY_CONFIG.currencies[number]
lib/crypto-pricing.ts
export interface CryptoPricing {
currency: string
priceUSD: number
lastUpdated: number
source: string
}
const CACHE_TTL = 15 * 60 * 1000
const cache = new Map<string, { data: CryptoPricing; ts: number }>()
const pending = new Map<string, Promise<CryptoPricing>>()
export async function getMCRTPrice(): Promise<CryptoPricing> {
const key = 'MCRT-USD'
if (pending.has(key)) return pending.get(key)!
const req = (async () => {
const cached = cache.get(key)
if (cached && Date.now() - cached.ts < CACHE_TTL) return cached.data
const sources = [
async () => {
const r = await fetch('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=magiccraft')
if (!r.ok) return null
const a = await r.json()
const p = Array.isArray(a) ? a[0]?.current_price : null
return p > 0 ? { currency: 'MCRT', priceUSD: +p, lastUpdated: Date.now(), source: 'CoinGecko' } : null
},
async () => {
const r = await fetch('https://api.dexscreener.com/latest/dex/search?q=magiccraft')
if (!r.ok) return null
const d = await r.json()
const pairs = (d?.pairs || []).map((p: any) => ({ p: +p.priceUsd, l: +(p.liquidity?.usd || 0) })).filter((x: any) => x.p > 0)
if (!pairs.length) return null
const total = pairs.reduce((s: number, x: any) => s + x.l, 0) || 1
const weighted = pairs.reduce((s: number, x: any) => s + x.p * x.l, 0) / total
return weighted > 0 ? { currency: 'MCRT', priceUSD: weighted, lastUpdated: Date.now(), source: 'DexScreener' } : null
}
]
for (const src of sources) {
try { const r = await src(); if (r) { cache.set(key, { data: r, ts: Date.now() }); return r } } catch {}
}
const fallback = { currency: 'MCRT', priceUSD: +(process.env.MCRT_PRICE_USD || '0.00013'), lastUpdated: Date.now(), source: 'Fallback' }
cache.set(key, { data: fallback, ts: Date.now() })
return fallback
})()
pending.set(key, req)
req.finally(() => pending.delete(key))
return req
}
export async function getUSDTPrice(): Promise<CryptoPricing> {
return { currency: 'USDT', priceUSD: 1.0, lastUpdated: Date.now(), source: 'Fixed' }
}
export async function getAllTokenPrices() {
const [mcrt, usdt] = await Promise.all([getMCRTPrice(), getUSDTPrice()])
return { MCRT: mcrt, USDT: usdt }
}
lib/blockchain-verification.ts
import { getMCRTPayWalletAddress } from './mcrtpay-config'
const BSCSCAN_URL = 'https://api.bscscan.com/api'
const MCRT_CONTRACT = '0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f'.toLowerCase()
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
function bigintPow(base: bigint, exp: number): bigint {
let r = BigInt(1)
for (let i = 0; i < exp; i++) r *= base
return r
}
function toUnits(amount: number, decimals: number): bigint {
if (amount <= 0) return BigInt(0)
const parts = amount.toString().split('.')
const frac = ((parts[1] || '') + '0'.repeat(decimals)).slice(0, decimals)
return BigInt(parts[0] || '0') * bigintPow(BigInt(10), decimals) + BigInt(frac || '0')
}
export interface TransactionVerification {
isValid: boolean
txHash: string
network: 'bep20' | 'erc20'
amount?: number
currency: 'MCRT' | 'USDT'
error?: string
}
export async function verifyTransaction(
txHash: string,
network: 'bep20' | 'erc20',
expectedAmount: number,
currency: 'MCRT' | 'USDT'
): Promise<TransactionVerification> {
const apiKey = process.env.BSCSCAN_API_KEY
if (!apiKey) return { isValid: false, txHash, network, currency, error: 'BSCSCAN_API_KEY not set' }
const url = `${BSCSCAN_URL}?module=logs&action=getLogs&txhash=${txHash}&apikey=${apiKey}`
const res = await fetch(url)
if (!res.ok) throw new Error('BscScan unreachable')
const data = await res.json() as any
if (data.status !== '1' || !Array.isArray(data.result) || !data.result.length)
return { isValid: false, txHash, network, currency, error: 'Transaction not found on BSC' }
const decimals = 18
const expectedUnits = toUnits(expectedAmount, decimals)
const tolerance = expectedUnits * BigInt(95) / BigInt(100)
const recipient = '0x000000000000000000000000' + getMCRTPayWalletAddress().toLowerCase().slice(2)
for (const log of data.result) {
if (log.address?.toLowerCase() !== MCRT_CONTRACT) continue
if (log.topics?.[0]?.toLowerCase() !== TRANSFER_TOPIC) continue
if (log.topics?.[2]?.toLowerCase() !== recipient) continue
const sent = BigInt(log.data || '0')
if (sent >= tolerance) {
const amount = Number(sent / bigintPow(BigInt(10), decimals - 4)) / 10000
return { isValid: true, txHash, network, currency, amount }
}
}
return { isValid: false, txHash, network, currency, error: `Payment not found or too low. Expected ~${expectedAmount} MCRT to ${getMCRTPayWalletAddress()}` }
}
export function getMinimumConfirmations(network: 'bep20' | 'erc20'): number {
return network === 'bep20' ? 3 : 6
}
Step 2 — API Routes
Pages Router: pages/api/crypto/prices.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { getAllTokenPrices } from '@/lib/crypto-pricing'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') return res.status(405).end()
try {
const prices = await getAllTokenPrices()
res.setHeader('Cache-Control', 'public, max-age=300, s-maxage=300')
return res.status(200).json(prices)
} catch {
return res.status(200).json({
USDT: { currency: 'USDT', priceUSD: 1.0, source: 'Fallback' },
MCRT: { currency: 'MCRT', priceUSD: +(process.env.MCRT_PRICE_USD || '0.00013'), source: 'Fallback' }
})
}
}
Pages Router: pages/api/mcrtpay/verify.ts
Replace the activatePlan block with your own auth + DB logic.
import type { NextApiRequest, NextApiResponse } from 'next'
import { verifyTransaction } from '@/lib/blockchain-verification'
import { getMCRTPayWalletAddress } from '@/lib/mcrtpay-config'
// TODO: import { getSession } from '@/lib/your-auth'
// TODO: import { activatePlan } from '@/lib/your-db'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end()
// const session = await getSession(req, res)
// if (!session?.user) return res.status(401).json({ error: 'Not authenticated' })
const { txHash, network, expectedAmount, currency, planId } = req.body || {}
if (!txHash || !network || !expectedAmount || !currency)
return res.status(400).json({ error: 'Missing: txHash, network, expectedAmount, currency' })
try {
const v = await verifyTransaction(txHash, network, +expectedAmount, currency)
if (!v.isValid) return res.status(400).json({ success: false, error: v.error, verification: v })
// --- Replace this with your DB logic ---
// await activatePlan(session.user.id, planId, { via: 'mcrt', txHash })
console.log(`[mcrtpay/verify] ${txHash} — ${expectedAmount} ${currency} — plan: ${planId}`)
return res.status(200).json({ success: true, verification: v, walletAddress: getMCRTPayWalletAddress() })
} catch (err: any) {
return res.status(500).json({ error: err.message || 'Verification error' })
}
}
App Router: Same logic — swap
NextApiRequest/NextApiResponse for NextRequest/NextResponse and use export async function POST(request: NextRequest). See merlintheai.com for App Router reference.
Step 3 — React Payment Component
Drop this into your pricing or checkout page. Replace onSuccess with a redirect or state update.
import { useState, useEffect, useCallback } from 'react'
const MCRT_WALLET = 'YOUR_WALLET_ADDRESS'
const MCRT_CONTRACT = '0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f'
export function McrtPaymentSection({
planUsd,
planId,
onSuccess,
}: {
planUsd: number
planId: string
onSuccess: () => void
}) {
const [open, setOpen] = useState(false)
const [mcrtPrice, setMcrtPrice] = useState<number | null>(null)
const [txHash, setTxHash] = useState('')
const [verifying, setVerifying] = useState(false)
const [status, setStatus] = useState<{ type: 'success' | 'error' | 'pending'; msg: string } | null>(null)
const [copied, setCopied] = useState(false)
useEffect(() => {
if (!open) return
fetch('/api/crypto/prices')
.then(r => r.json())
.then(d => setMcrtPrice(d?.MCRT?.priceUSD ?? null))
}, [open])
const mcrtAmount = mcrtPrice ? Math.ceil(planUsd / mcrtPrice) : null
const copy = useCallback(() => {
navigator.clipboard.writeText(MCRT_WALLET).then(() => {
setCopied(true)
setTimeout(() => setCopied(false), 2000)
})
}, [])
const verify = async () => {
if (!txHash.trim()) return
setVerifying(true); setStatus(null)
try {
const res = await fetch('/api/mcrtpay/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ txHash: txHash.trim(), network: 'bep20', expectedAmount: mcrtAmount, currency: 'MCRT', planId }),
})
const data = await res.json()
if (res.status === 202) {
setStatus({ type: 'pending', msg: data.error || 'Waiting for confirmations. Try again in ~30s.' })
} else if (data.success) {
setStatus({ type: 'success', msg: 'Payment confirmed! Activating…' })
setTimeout(onSuccess, 1500)
} else {
setStatus({ type: 'error', msg: data.error || 'Verification failed. Check tx hash.' })
}
} catch {
setStatus({ type: 'error', msg: 'Network error. Please retry.' })
} finally {
setVerifying(false)
}
}
return (
<div className="border-t pt-4 mt-4">
<button onClick={() => { setOpen(v => !v); setStatus(null) }}
className="w-full flex justify-between items-center text-sm font-semibold py-1">
<span>⚡ Pay with $MCRT (instant, no trial)</span>
<span>{open ? '▲' : '▼'}</span>
</button>
{open && (
<div className="mt-4 p-4 border border-yellow-300 bg-yellow-50 rounded-xl space-y-3 text-sm">
<p>Send <strong>{mcrtAmount ? mcrtAmount.toLocaleString() : '…'} $MCRT</strong> on BNB Chain (BEP-20) to:</p>
<div className="flex items-center gap-2 bg-white border rounded-lg px-3 py-2">
<code className="flex-1 text-xs break-all">{MCRT_WALLET}</code>
<button onClick={copy} className="text-xs text-blue-600">{copied ? '✓ Copied' : 'Copy'}</button>
</div>
{mcrtPrice && (
<p className="text-xs text-gray-500">
Live price: ${mcrtPrice.toFixed(8)} · ≈ {mcrtAmount?.toLocaleString()} MCRT for ${planUsd} USD ·{' '}
Token: <a href={`https://bscscan.com/token/${MCRT_CONTRACT}`} target="_blank" rel="noopener noreferrer">BscScan</a>
</p>
)}
<div>
<label className="text-xs font-semibold block mb-1">
Paste your BscScan transaction hash after sending:
</label>
<input type="text" value={txHash} onChange={e => setTxHash(e.target.value)}
placeholder="0x…"
className="w-full px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-yellow-400" />
</div>
{status && (
<div className={`p-3 rounded-lg text-sm font-medium ${
status.type === 'success' ? 'bg-green-100 text-green-800' :
status.type === 'pending' ? 'bg-blue-100 text-blue-800' :
'bg-red-100 text-red-800'}`}>
{status.msg}
</div>
)}
<button onClick={verify} disabled={verifying || !txHash.trim()}
className="w-full py-2.5 bg-yellow-400 hover:bg-yellow-500 text-black font-bold rounded-lg disabled:opacity-50">
{verifying ? 'Verifying on-chain…' : 'Verify $MCRT payment'}
</button>
<p className="text-xs text-gray-400 text-center">
Need $MCRT?{' '}
<a href="https://pancakeswap.finance" target="_blank" rel="noopener noreferrer">PancakeSwap</a>
{' '}or{' '}
<a href="https://www.gate.io/trade/MCRT_USDT" target="_blank" rel="noopener noreferrer">Gate.io</a>
</p>
</div>
)}
</div>
)
}
Usage:
<McrtPaymentSection
planUsd={selectedPlan.amount} // e.g. 25
planId={selectedPlan.id} // e.g. "premium"
onSuccess={() => router.push('/success?via=mcrt')}
/>
Widget options reference
| Option | Type | Default | Description |
|---|---|---|---|
containerSelector | string | '#mcrtpay-container' | CSS selector for the mount element |
walletAddress | string | MCRT wallet | BEP-20 destination wallet |
paymentAmount | number | — | USD amount — auto-converts to MCRT live |
mcrtAmount | number | — | Fixed MCRT amount (skips price fetch) |
currency | string | 'MCRT' | Token symbol |
defaultNetwork | string | 'bep20' | Default network tab |
planId | string | '' | Passed to /api/mcrtpay/verify for your DB |
validateOnBackend | boolean | true | Set false to skip backend verification |
backendUrl | string | '/api/mcrtpay/verify' | Your verification endpoint |
priceApiUrl | string | '/api/crypto/prices' | Your price endpoint |
theme | string | 'dark' | 'dark' or 'light' |
successCallback | function | — | function(txHash, network) — fires on confirmed payment |
Wallet & contract
| Item | Value |
|---|---|
| Payment wallet | YOUR_WALLET_ADDRESS |
| MCRT token contract | 0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f |
| Network | BNB Chain (BEP-20) |
| BscScan token | View on BscScan |
Where to buy $MCRT
| Exchange | Link |
|---|---|
| PancakeSwap (DEX) | pancakeswap.finance |
| Gate.io (CEX) | gate.io/trade/MCRT_USDT |
| MagicCraft in-app | magiccraft.io |
Reference implementations:
DocAI.live (Pages Router): github.com/creationaff/DocAI.live
Merlintheai.com (App Router): github.com/creationaff/merlintheai.com
MCRTPay source: github.com/creationaff/mcrtpay
DocAI.live (Pages Router): github.com/creationaff/DocAI.live
Merlintheai.com (App Router): github.com/creationaff/merlintheai.com
MCRTPay source: github.com/creationaff/mcrtpay