ChainPay Gateway
Self-hosted, multi-chain crypto payment gateway. One codebase to accept Bitcoin, Ethereum, BNB, USDT, USDC, Litecoin, Dogecoin and more — plus Stripe + PayPal alongside.
17+ chains
BTC, ETH, BNB, ERC-20, BEP-20, TRC-20, LTC, DOGE, BCH, XRP, SOL, ALGO, XMR, Lightning.
Drop-in widget
~80 kB JavaScript bundle. One <script> tag, one host element. Inline or popup.
Hosted checkout
Every checkout gets a public URL: /c/{slug}. Share by email, QR, social.
Full admin
Dashboard, ledger, customers, custom fields, branding, security, audit log.
Fiat fallback
Stripe + PayPal next to crypto in the same picker. PCI burden stays with the provider.
Self-hosted
Your server, your keys. No middleman, no platform fees, no rate limits beyond your RPC.
Quick start
If you already have PHP 8.3+, MySQL, and Composer:
git clone <your-purchase> chainpay
cd chainpay
cp .env.example .env
composer install --no-dev --optimize-autoloader
npm install && npm run build
php artisan key:generate
php artisan migrate --seed
php artisan serve
Open http://localhost:8000, finish the setup wizard, log in, you're live.
Installation
System requirements
| Component | Minimum | Recommended |
|---|---|---|
| PHP | 8.3 | 8.4 |
| MySQL / MariaDB | 8.0 / 10.6 | 8.4 |
| Composer | 2.5 | latest |
| Node.js (build only) | 20 | 22 |
| Web server | Nginx or Apache | Nginx + PHP-FPM |
| Disk | 250 MB | 1 GB+ (logs, QR cache) |
| Memory | 256 MB PHP | 512 MB |
PHP extensions required: pdo_mysql, mbstring, bcmath, gmp, openssl, curl, intl, xml, zip, gd (or imagick).
gmp and bcmath? ChainPay handles arbitrary-precision crypto amounts (18-decimal ETH wei, 8-decimal BTC satoshi). Native int overflows and loses precision; both extensions keep the math exact.
Install wizard (recommended)
- Upload the package to your web root (or a subdirectory).
- Set the document root to the
public/directory. - Visit the URL in your browser. The install wizard auto-launches if
.envis missing. - Walk through 4 steps:
- Requirements check — wizard verifies PHP version, extensions, writable
storage/. - Database — enter MySQL host, database, user, password. Wizard tests connection, runs migrations + seeds.
- Owner account — name, email, password.
- Branding — product name, tagline, primary colour. (Editable later in admin.)
- Requirements check — wizard verifies PHP version, extensions, writable
The wizard writes .env for you, generates APP_KEY, and finalises the install.
Manual install
# 1. Files
git clone <your-purchase> chainpay
cd chainpay
cp .env.example .env
# 2. PHP deps
composer install --no-dev --optimize-autoloader
# 3. Front-end build (only on a build host — production server doesn't need Node)
npm install
npm run build
# 4. App key + storage symlink
php artisan key:generate
php artisan storage:link
# 5. Database (edit .env first: DB_DATABASE, DB_USERNAME, DB_PASSWORD)
php artisan migrate --seed
# 6. Caches (production)
php artisan config:cache
php artisan route:cache
php artisan view:cache
Required .env keys
APP_NAME=ChainPay
APP_ENV=production
APP_KEY= # populated by `php artisan key:generate`
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=chainpay
DB_USERNAME=chainpay
DB_PASSWORD=
CACHE_STORE=file
SESSION_DRIVER=database
QUEUE_CONNECTION=database
# Mail via Resend (preferred):
MAIL_MAILER=smtp
MAIL_HOST=smtp.resend.com
MAIL_PORT=465
MAIL_USERNAME=resend
MAIL_PASSWORD=re_xxxxxxxxxxxxxxxxxxxxxxxx
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS="hello@your-domain.com"
MAIL_FROM_NAME="${APP_NAME}"
File permissions
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
chmod 600 .env
Nginx config (recommended)
server {
listen 443 ssl http2;
server_name your-domain.com;
root /var/www/chainpay/public;
index index.php;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* { deny all; }
client_max_body_size 25M;
}
Apache? A .htaccess ships in public/. Point DocumentRoot at it and ensure mod_rewrite is on.
Cron + queue worker
ChainPay needs a heartbeat to monitor the chain, refresh exchange rates, retry webhooks, and prune stale transactions. Two pieces:
1. Cron (required)
On a VPS / dedicated server:
# Laravel scheduler — fires every job at its declared cadence
* * * * * cd /var/www/chainpay && php artisan schedule:run >> /dev/null 2>&1
On Hostinger shared hosting:
- hPanel → Advanced → Cron Jobs
- Click Create a new cron job
- Choose Type → "Custom"
- Run interval: every minute (
* * * * *) - Command:
/opt/alt/php84/usr/bin/php /home/USER/domains/YOURDOMAIN/public_html/chainpay/artisan schedule:run >/dev/null 2>&1
Replace USER with your hosting username and YOURDOMAIN with your domain.
On other shared hosts (cPanel, Plesk, DirectAdmin): same idea — use the panel's cron UI to fire php artisan schedule:run every minute, with the absolute path to your PHP 8.3+ binary.
2. Queue worker — choose one
Option A — Long-running worker (best for VPS, high volume)
php artisan queue:work --sleep=3 --tries=3 --max-time=3600
Run via systemd or supervisor for resilience:
# /etc/systemd/system/chainpay-worker.service
[Unit]
Description=ChainPay queue worker
After=network.target
[Service]
User=www-data
Restart=always
WorkingDirectory=/var/www/chainpay
ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --max-time=3600
[Install]
WantedBy=multi-user.target
sudo systemctl enable chainpay-worker
sudo systemctl start chainpay-worker
Option B — Sync mode (best for shared hosting)
If your host doesn't allow long-running processes (most shared hosts don't), set QUEUE_CONNECTION=sync in .env. Jobs run inline within whatever process triggers them — the cron tick is the worker. Simpler, slightly slower per-request, no daemon needed.
# .env
QUEUE_CONNECTION=sync
What the scheduler does
| Job | Cadence | Purpose |
|---|---|---|
chainpay.transactions.check-pending | every minute | Polls every pending tx against the chain via configured RPCs/explorers |
chainpay.exchange-rates.refresh | every 15 min | Refreshes fiat→crypto rates from Coinbase/Binance/Bitstamp |
chainpay.transactions.purge-stale | every 5 min | Marks expired any pending tx past its expires_at |
chainpay.webhooks.retry-pending | every minute | Retries failed merchant webhooks per their backoff schedule |
chainpay.retention.apply | daily 03:30 | Prunes audit log + webhook delivery history per retention settings |
Verifying the cron is alive
# Should show recent timestamps
tail -f storage/logs/laravel.log
# Should show the scheduler dispatching jobs
php artisan schedule:list
storage/logs/laravel.log has no new entries after 5 minutes of the cron supposedly being on, the cron isn't actually running. Most common cause on shared hosts: wrong PHP binary path, or the panel's cron UI is set to a higher interval than "every minute".
Alchemy API key (required for ETH + BSC + Solana)
Public RPCs can't deliver the address-history lookups the chain monitor needs at production rates. Alchemy's free tier covers Ethereum, BSC, and Solana mainnet.
- Sign up free at alchemy.com — no credit card.
- Create a new app (any name; pick "Ethereum mainnet" — the same key works across all their chains).
- Copy the API key (the part after
/v2/in the HTTPS URL). - In ChainPay admin → Settings → Blockchains:
- Alchemy API key → paste → save (powers ETH + BSC + ERC-20 + BEP-20)
- Solana RPC URL → paste
https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY→ save (uses the same Alchemy key, just a different endpoint per chain)
That single key powers BNB, ETH, USDT-ERC20, USDC-ERC20, USDT-BEP20, BUSD, SOL, any SPL tokens, and any custom ERC-20 / BEP-20 tokens.
Demo mode
For marketplace demos / sales pages where prospective buyers should be able to browse the admin without breaking anything, flip the toggle:
# .env
APP_DEMO_MODE=true
Then php artisan config:clear. The admin UI now:
- Shows a persistent gradient banner: "You're viewing a live demo. Browse anything — saves are disabled."
- Allows all GET requests (browsing, viewing dashboards)
- Blocks every POST/PUT/PATCH/DELETE in
/admin/*with a flash error toast: "This is a live demo — purchase the script for full access." - Whitelists
/logoutso visitors can sign out
Forms still render so visitors see the full feature surface — the save button shows the upgrade message instead of persisting.
To make real admin changes on a demo install: SSH in, set APP_DEMO_MODE=false in .env, run php artisan config:clear, do the work, set it back. No code change.
Admin guide
This is a screen-by-screen walkthrough of the admin panel. Log in at https://<your-domain>/login with the owner account you created during install.
Dashboard /admin
The landing page after login. Six regions:
- Headline tiles — revenue today (with delta vs. yesterday), confirmed transactions, pending transactions, customer count.
- Revenue & volume trend — 30-day dual-series area + line chart. Drag to zoom.
- Status mix donut — pending/confirmed/expired/refunded breakdown.
- Gateway split bar — revenue per gateway (crypto/Stripe/PayPal/etc.).
- Activity feed + Top customers — last 10 transactions and lifetime-spend leaderboard. Each row is clickable.
- Install health strip — 8 indicators: cache, queue, mail, SSL, scheduler, storage, rates, wallets.
Wallets /admin/wallets
The crypto receiving addresses payments will land in.
- Add wallet: pick a cryptocurrency, paste the address, optionally label it. The system validates the address against the chain's encoding (Base58Check for BTC, EIP-55 for ETH, Bech32 for Lightning, etc.).
- Activate / deactivate to gate which wallets the customer-facing widget can route payments to.
- Soft-delete to retire a wallet without losing audit history.
Custom tokens /admin/custom-tokens
For ERC-20 / BEP-20 / TRC-20 tokens not in the default catalogue.
- Network —
ethereum,bsc, ortron. Determines which RPC handles confirmations. - Contract address — must be the token contract (NOT a wallet). EIP-55 checksum auto-applied for EVM.
- Decimals — most ERC-20s are 18 or 6. Confirm against the contract on Etherscan.
- Pricing — either Fixed rate (e.g.
1for a USD-pegged stablecoin), or Rate URL — a JSON endpoint returning{"price": <number>}. The rate engine polls every 5 minutes.
Transactions /admin/transactions
Read-mostly ledger. Filter by status, gateway, date range, customer.
- Show page displays the full lifecycle: created, payment-seen, confirmation count, confirmed, refunded.
- Refund (top-right) — initiates an on-chain refund to the source address (BTC/ETH only in v1.0). Requires admin password confirmation.
- Manually mark confirmed — escape hatch for chains without RPC support. Use sparingly; logged in audit.
Customers /admin/customers
Auto-created from the email a customer enters at checkout.
- Show page lists all transactions for that customer, lifetime spend, and merchant-private notes.
- Notes — internal-only, never shown to the customer. Searchable in the index.
- VAT number — recorded for invoice generation if the merchant operates in EU.
Checkouts /admin/checkouts
Reusable payment links / widget configurations.
- Title, description, image — what the customer sees.
- Price — fixed amount, or "pay what you want" (leave blank), or "fixed currency input".
- Allowed cryptocurrencies — whitelist. Empty = all enabled.
- Custom fields — add text/email/phone/select/checkbox fields to capture extra info at checkout.
- Branding overrides — button text, success/failure copy, optional QR toggle.
- Layout —
inlineorpopup. - Hosted link — every checkout gets a public URL:
https://<your-domain>/c/<slug>. Copy and share.
Settings /admin/settings
Tabbed. Changes are live — no deploy needed.
| Tab | What's there |
|---|---|
| General | Product name, tagline, support email, default fiat currency. |
| Branding | Logo, favicon, colours, custom CSS / JS. |
| SMTP / Resend, FROM address. Test-send button. | |
| Notifications | Admin recipients, alerts for failed logins, oversize refunds, low-rate-source health. |
| Security | Force-HTTPS-on-admin, IP allowlist (CIDR), session timeout, 2FA enforcement. |
| Maintenance | Global checkout pause, custom message, stale-pending cleanup cron. |
| Rates | Preferred source order (Coinbase / Binance / Bitstamp / custom), refresh interval. |
| Gateways | Stripe + PayPal credentials, webhook endpoints, fees. |
Users /admin/users
Owner-only by default. Owner can promote others to:
- Admin — full management except role changes for other admins.
- Agent — read-only access; cannot edit or destroy.
Per-user actions (per-row dropdown):
- Force password reset — flags the user; their next login routes through password change.
- Re-send invitation — for users whose invite email got lost.
- Suspend / Reinstate — disables login without deleting; uses
locked_untilset 1 year ahead. - Login as user — admin steps into another user's session for support. A persistent banner appears across the entire UI; click "Return to your account" to swap back.
- Delete — soft-delete; can be restored from DB.
Widget integration
The ChainPay widget is a standalone JavaScript bundle (~80 kB gzipped) you embed on any website to accept payments. Two layouts:
| Layout | When to use |
|---|---|
inline | Replaces a <div> on your page with the checkout form |
popup | A button opens a centered modal |
1. Get a checkout slug
In admin → Checkouts → "New checkout". Configure price, allowed cryptos, copy. Save. Note the slug (e.g. pro-license-bundle).
2. Drop the script tag
<script src="https://your-chainpay-domain.com/widget.js" defer></script>
3. Add the host element
Inline layout:
<div data-cpg-checkout="pro-license-bundle" data-cpg-layout="inline"></div>
Popup layout:
<button data-cpg-checkout="pro-license-bundle" data-cpg-layout="popup">
Buy now — $19.99
</button>
4. (Optional) Pre-fill customer info
<div
data-cpg-checkout="pro-license-bundle"
data-cpg-layout="inline"
data-cpg-customer-email="customer@example.com"
data-cpg-customer-first-name="Ada"
data-cpg-customer-last-name="Lovelace"
></div>
5. Listen for events
window.addEventListener('chainpay:created', (e) => {
console.log('Transaction created:', e.detail.reference);
});
window.addEventListener('chainpay:confirmed', (e) => {
console.log('Payment confirmed:', e.detail);
// Trigger your success flow — fulfilment, redirect, analytics
});
window.addEventListener('chainpay:failed', (e) => {
console.warn('Payment failed:', e.detail.status);
});
6. Styling
The widget ships with self-contained CSS (CSS variables, no Tailwind). Override via:
/* Brand colours */
.cpg-form { --cpg-accent: #ec4899; --cpg-accent-2: #f43f5e; }
/* Or override individual classes */
.cpg-submit { border-radius: 0; }
Hosted checkout
If you don't want to embed the widget, every checkout you create automatically gets a public URL you can share by email, link, QR code — anywhere.
https://<your-domain>/c/<slug>
Example: https://chainpay.codic.online/c/demo-product
What the page looks like
A polished, single-purpose checkout that mounts the same widget as the embed:
- Branding header (logo, product name, tagline)
- Product image (if uploaded)
- The full widget form (price, crypto picker, custom fields, email)
- "Powered by" footer
After the customer submits, they're redirected to the Pay page (/pay/{reference}) which shows the QR, address, countdown, and confirmation rings.
Use cases
| Scenario | How |
|---|---|
| Email an invoice | Paste the hosted link |
| QR code on a printed receipt | Generate a QR pointing at the hosted URL |
| Twitter / IG bio link | Use the URL directly |
| Donation page | Make a checkout with accepts_currency_input=true |
| Multi-tier pricing | Create 3 checkouts, share three links |
REST API
ChainPay exposes a small REST API for programmatic access. Two surfaces:
- Public widget API — unauthenticated, what
widget.jsitself calls. - Authenticated API — Sanctum bearer tokens for server-to-server use.
Authentication
Generate a token in admin → Settings → API tokens (or via tinker):
$user = App\Models\User::find(1);
$token = $user->createToken('cli', ['transactions:read', 'transactions:refund'])->plainTextToken;
Use it:
Authorization: Bearer <token>
Token scopes (abilities)
| Ability | What it allows |
|---|---|
transactions:read | List + show transactions |
transactions:refund | Issue on-chain refunds |
customers:read | List + show customers |
checkouts:read | List checkouts |
wallets:read | List wallet addresses (no private keys) |
Public widget endpoints
GET /api/checkouts/{slug}
Returns the checkout config + active gateways + active cryptos for the widget to render.
{
"checkout": {
"reference": "01j8a2...",
"slug": "demo-product",
"title": "ChainPay Demo Product",
"price_minor": 1999,
"currency_code": "USD",
"show_qr": true,
"button_text": null,
"custom_fields": []
},
"options": {
"cryptocurrencies": [
{ "code": "btc", "name": "Bitcoin", "ticker": "BTC", "decimals": 8, "network": "bitcoin" }
],
"gateways": ["crypto", "stripe"]
}
}
POST /api/checkouts/{slug}/sessions
{
"gateway": "crypto",
"cryptocurrency_code": "btc",
"customer": {
"email": "buyer@example.com",
"first_name": "Ada",
"last_name": "Lovelace"
},
"custom_fields": { "company": "Acme Inc" },
"amount_fiat_minor": 1999
}
Returns { "redirect_url": "/pay/01k..." }.
GET /pay/{reference}/status
Polled every 5s by the Pay page. Returns the transaction snapshot WITHOUT the QR SVG (kept lean).
Rate limiting
Public widget endpoints are throttled at 60 requests/minute per IP. Authenticated endpoints at 120 req/min per token.
Webhooks
ChainPay POSTs to your endpoint when a transaction's state changes. Use this to fulfil orders, send receipts, update your CRM.
Events
| Event | When |
|---|---|
transaction.created | A new payment session was created |
transaction.payment_seen | The blockchain mempool has the inbound transfer |
transaction.confirming | First confirmation reached |
transaction.confirmed | Min confirmations reached — money is yours |
transaction.expired | Window closed without payment |
transaction.underpaid | Customer sent less than required |
transaction.refunded | Refund issued |
transaction.failed | Generic failure |
Signature verification
Every request carries an X-ChainPay-Signature header — an HMAC-SHA256 of the raw body using your signing secret.
$signature = $_SERVER['HTTP_X_CHAINPAY_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, $secret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('invalid signature');
}
$payload = json_decode($body, true);
// process the event...
http_response_code(200);
hash_equals(), NOT == or ===. Constant-time comparison prevents timing attacks.
Retries
If your endpoint returns anything other than 2xx, ChainPay retries with exponential backoff: 30s → 2 min → 10 min → 1 hour → 6 hours, then gives up.
Blockchain coverage
ChainPay ships drivers for the most-asked-for chains. Coverage breaks into three tiers based on what each driver can do.
Tier 1 — Full custody (deposit + refund + monitoring)
| Chain | Coin | Notes |
|---|---|---|
| Bitcoin | BTC | Native + SegWit (bech32). Tiered fees. Source-address refund. |
| Ethereum | ETH | EIP-155 signing. ERC-20s (USDT, USDC, LINK, SHIB, BAT). EIP-1559 fee fallback. |
| BNB Smart Chain | BNB, BUSD, USDT-BEP20 | EVM-compatible — same code path as Ethereum, different chain ID. |
Tier 2 — Auto-confirm (deposit + monitoring, manual refund)
These chains auto-confirm customer payments end-to-end via their explorer/RPC integrations. The only thing they don't do is push outgoing refunds — refunds happen manually from the merchant's own wallet (or via the v1.1 send-side update).
| Chain | Coin | Auto-confirm | Notes |
|---|---|---|---|
| Solana | SOL, SPL tokens | ✅ | Solana JSON-RPC (getSignaturesForAddress). Use Alchemy/Helius for production. |
| Tron | TRX, USDT-TRC20 | ✅ | TronGrid public API. |
| XRP Ledger | XRP | ✅ | XRPL public RPC. |
| Algorand | ALGO | ✅ | Algorand Indexer. |
| Litecoin | LTC | ✅ | Same UTXO matcher as BTC. |
| Dogecoin | DOGE | ✅ | Via Blockchair. |
| Bitcoin Cash | BCH | ✅ | Via Blockchair. |
| Monero | XMR | ⚠ | Opt-in only — requires view-key on the wallet for incoming detection. |
On-chain refund for these chains is deferred to v1.1. Until then, the merchant initiates refunds manually from their own wallet, then marks the ChainPay transaction refunded in admin.
Tier 3 — Skeleton
| Chain | Coin | Status |
|---|---|---|
| Bitcoin Lightning | BTC-LN | BOLT11 invoices generate; LND/CLN integration in v1.1 |
Confirmation thresholds
| Chain | Default min_confirmations | ~Time |
|---|---|---|
| Bitcoin | 2 | ~20 min |
| Ethereum | 12 | ~3 min |
| BSC | 15 | ~45 sec |
| Tron | 19 | ~1 min |
| Solana | 32 | ~13 sec |
| Litecoin | 4 | ~10 min |
| Dogecoin | 6 | ~6 min |
| XRP | 1 | ~3-5 sec |
Stripe + PayPal
In addition to crypto, ChainPay can route payments through traditional rails.
Stripe
- Create a Stripe account.
- Get your publishable key and secret key from the Stripe dashboard.
- In ChainPay → Settings → Gateways → Stripe, paste keys, pick Live or Test, save.
- Configure the webhook endpoint in Stripe: URL
https://<your-domain>/webhooks/stripe, subscribe topayment_intent.succeeded,payment_intent.payment_failed,charge.refunded. - Copy the signing secret (
whsec_…) into ChainPay's webhook field.
Stripe charges per their published rates (e.g. 2.9% + 30¢ in the US). ChainPay does NOT add markup.
PayPal
- Create a PayPal Business account.
- Create an App in the Developer Dashboard to get a Client ID + Secret.
- In ChainPay → Settings → Gateways → PayPal, paste keys, pick Live or Sandbox, save.
- Webhook URL:
https://<your-domain>/webhooks/paypal— subscribe toPAYMENT.CAPTURE.COMPLETED,PAYMENT.CAPTURE.DENIED,PAYMENT.CAPTURE.REFUNDED.
Test cards (Stripe)
| Card | Behaviour |
|---|---|
4242 4242 4242 4242 | Successful charge |
4000 0000 0000 0002 | Decline |
4000 0027 6000 3184 | 3D Secure required |
Troubleshooting
The first place to look is storage/logs/laravel.log. Most errors include a stack trace and a request ID.
tail -f storage/logs/laravel.log
Common issues
"500 server error" right after install
APP_KEY=empty → runphp artisan key:generatestorage/orbootstrap/cache/not writable →chmod -R 775and chown to web user- DB credentials wrong in
.env→php artisan migrate:statussurfaces the connection error - Cached config out of date →
php artisan config:clear && php artisan config:cache
Customer paid but transaction stuck on "pending"
- Confirm the wallet address in admin matches the chain (not a typo, not a wrong network).
- Look up the transaction hash on the chain's explorer — did it land on chain?
- Check the queue worker is running:
ps aux | grep "queue:work". The chain monitor runs as a queued job. - If the chain RPC is rate-limiting, you'll see retries in
laravel.log. Switch to a paid RPC.
"Crypto picker is empty"
The customer-facing widget only shows cryptos where:
- The crypto is
is_active=truein the catalogue - At least one active, non-deleted wallet exists for it
- The crypto code is in the checkout's
allowed_cryptocurrencieswhitelist (or whitelist is empty)
Forgetting to add a wallet is the #1 cause.
"Email is not arriving"
- Confirm
MAIL_*keys in.envare correct - Test:
php artisan tinker→Mail::raw('test', fn($m) => $m->to('you@x.com')->subject('test')); - If using Resend / Postmark / SES, check the provider's dashboard for bounces / blocks
"I can't log in even with the correct password"
users.locked_until— if set in the future, you're suspended. Clear via tinker.force_password_reset_at— if set, you're routed through reset.email_verified_at— if null and verification is enforced, login redirects to verification.
Forgot owner password
php artisan tinker
$u = User::where('role', App\Enums\UserRole::Owner)->first();
$u->password = Hash::make('new-password-here');
$u->save();
Changelog
1.0.0 — 2026-05-05
Initial public release.
Core
- Multi-chain crypto payment gateway: BTC, ETH, BNB, ERC-20 (USDT/USDC/LINK/SHIB/BAT), BEP-20, TRC-20, TRX, LTC, DOGE, BCH, XRP, SOL, ALGO, XMR, BTC-LN.
- Custom-token admin: add any ERC-20 / BEP-20 / TRC-20 without code changes.
- Multi-source rate engine with fallback (Coinbase / Binance / Bitstamp / custom JSON).
- BIP-21 / EIP-681 wallet-scannable URIs for QR codes.
Widget + checkout
- Embeddable widget (~80 kB gzipped) — popup + inline layouts.
- Hosted checkout page at
/c/{slug}. - Cryptomus-style crypto picker with real coin logos.
- Live amount-aware submit button.
- Pay page with twin progress rings (expiration + confirmations).
- BaconQrCode QR generation, click-to-copy address, polling status updates.
- MetaMask / Web3 wallet connect (EVM chains).
Admin
- Dashboard with charts, activity feed, install health strip.
- Wallets, Transactions, Customers, Checkouts, Custom Tokens, Users, Settings.
- User management: invite, suspend, "Login as user" impersonation, role changes.
- Branding, Security, Maintenance, Rates, Gateways tabs.
Fiat gateways
- Stripe (live + test) with PaymentIntents + webhook validation.
- PayPal (live + sandbox) with order capture + webhook validation.
Roadmap (v1.1)
- On-chain refund for Tron, Solana, XRP, Algorand
- Full Lightning LND/CLN integration
- EIP-1559 fee model
- Verifone driver
- Public API write endpoints
- Audit log UI