$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: YOUR_WALLET_ADDRESS (BSC BEP-20)
MCRT contract: 0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f
Flow: 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

OptionTypeDefaultDescription
containerSelectorstring'#mcrtpay-container'CSS selector for the mount element
walletAddressstringMCRT walletBEP-20 destination wallet
paymentAmountnumberUSD amount — auto-converts to MCRT live
mcrtAmountnumberFixed MCRT amount (skips price fetch)
currencystring'MCRT'Token symbol
defaultNetworkstring'bep20'Default network tab
planIdstring''Passed to /api/mcrtpay/verify for your DB
validateOnBackendbooleantrueSet false to skip backend verification
backendUrlstring'/api/mcrtpay/verify'Your verification endpoint
priceApiUrlstring'/api/crypto/prices'Your price endpoint
themestring'dark''dark' or 'light'
successCallbackfunctionfunction(txHash, network) — fires on confirmed payment

Wallet & contract

ItemValue
Payment walletYOUR_WALLET_ADDRESS
MCRT token contract0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f
NetworkBNB Chain (BEP-20)
BscScan tokenView on BscScan

Where to buy $MCRT

ExchangeLink
PancakeSwap (DEX)pancakeswap.finance
Gate.io (CEX)gate.io/trade/MCRT_USDT
MagicCraft in-appmagiccraft.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