360 Secure SkillHub
360 安全技能中心

Self-hosted Crypto Payment

为 Next.js 14+ App Router + Supabase 项目添加自托管加密支付:按付费类型派生唯一 HD 钱包地址、展示二维码与倒计时,由定时任务轮询链上余额确认支付,并在确认后原子发放额度、套餐或附加项。支持 EVM、Solana、Bitcoin 多链及稳定币,依赖服务端助记词与 cron 密钥环境变量。

flacko2048
flacko2048
热度 51.61
最近更新 2026-05-18 11:38:38
分类:开发依赖:代码执行 / 读写文件 / 第三方 API应用场景:代码生成 / API 集成

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add self-hosted-crypto
Metadata
name
self-hosted-crypto-payments
description
Add self-custodied crypto payment checkout to a Next.js + Supabase app. Accepts ETH, BTC, SOL, USDC, USDT and 25+ coins across 9 chains. No payment processor fees — HD wallet derivation, on-chain polling, zero third-party dependency. Use when: user asks to add crypto payments, accept Bitcoin/Ethereum/Solana, Web3 checkout, self-hosted crypto, no-middleman payments, blockchain payments, accept USDC/USDT.
allowed-tools
["Bash","Read","Write","Edit","Glob","Grep"]
env
{"required":[{"name":"CRYPTO_MASTER_MNEMONIC","description":"BIP39 mnemonic (12-24 words). Master key for HD wallet derivation. Server-only — never expose to client.","sensitive":true},{"name":"CRON_SECRET","description":"32-char hex string. Protects /api/cron/check-crypto-payments from unauthorized calls.","sensitive":true}]}

Self-Hosted Crypto Payments

What This Skill Does

Adds a complete, production-ready crypto payment system to any Next.js 14+ App Router app backed by Supabase. No Stripe, no CryptAPI, no payment processor — you keep 100% of every payment.

Supported chains & coins: ETH, Base, Polygon, Arbitrum, Optimism, BNB Chain, Avalanche, Solana, Bitcoin — including USDC/USDT/DAI stablecoins on each chain.

Architecture:

  1. User picks coin → server derives a unique HD wallet address (BIP44/84/SLIP-0010)
  2. Server stores address + expected amount in DB with a 1-hour expiry
  3. A cron job polls the actual blockchain every minute, verifies balance, confirms payment
  4. On confirmation, credits/plan/addon is applied atomically to the user's profile

Security model: Mnemonic never touches client or source code. User cannot influence destination address or amount. Payment confirmation is blockchain-verified, not webhook-forgeable.


Files to Copy

All template files are in resources/. Copy them into your project at these paths:

Resource file Destination in your project
crypto-wallets.ts src/lib/crypto-wallets.ts
crypto-checkout-route.ts src/app/api/billing/crypto-checkout/route.ts
crypto-checkout-status-route.ts src/app/api/billing/crypto-checkout/[id]/status/route.ts
check-crypto-payments-route.ts src/app/api/cron/check-crypto-payments/route.ts
crypto-payment-modal.tsx src/components/dashboard/crypto-payment-modal.tsx
005_crypto_payments.sql supabase/migrations/005_crypto_payments.sql
006_crypto_direct.sql supabase/migrations/006_crypto_direct.sql

Installation Steps

1. Install dependencies

npm install ethers @scure/bip32 @scure/bip39 @scure/base @noble/hashes @solana/web3.js qrcode
npm install -D @types/qrcode

2. Run database migrations

In Supabase SQL editor (or via supabase db push), run both migration files in order:

  • 005_crypto_payments.sql
  • 006_crypto_direct.sql

3. Add environment variables

# Generate mnemonic: https://iancoleman.io/bip39/ (12 or 24 words — NEVER commit this)
CRYPTO_MASTER_MNEMONIC=word1 word2 word3 ... word12

# Protects the cron endpoint — generate with:
# node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
CRON_SECRET=your-32-char-hex-string

4. Set up a cron job

The cron endpoint POST /api/cron/check-crypto-payments must be called every 60 seconds.

Vercel Cron (vercel.json):

{
  "crons": [
    {
      "path": "/api/cron/check-crypto-payments",
      "schedule": "* * * * *"
    }
  ]
}

Add the Authorization header in your cron config or Vercel dashboard:

Authorization: Bearer <your CRON_SECRET>

Fly.io machines / self-hosted: Use a scheduled task or external cron (e.g. cron-job.org) hitting the endpoint with Authorization: Bearer <CRON_SECRET>.

5. Customise payment types

The checkout route handles three payment type values — edit to match your app's data model:

Type What it does Required body fields
credits Adds credits to user profile packId
plan Upgrades user plan plan, months (optional for lifetime)
addon Applies a one-time resource addon addonId, botId (optional)

To customise: In crypto-checkout-route.ts, replace the CREDIT_PACKS, PLANS, and validAddons references with your own constants. The amount must always come from server-side constants — never from user input.

In check-crypto-payments-route.ts, update applyPayment() to perform whatever action your app takes when a payment is confirmed (add credits, unlock features, update subscription, etc.).

6. Wire up the modal

import { CryptoPaymentModal } from '@/components/dashboard/crypto-payment-modal'

// Example: pay for a credit pack
<CryptoPaymentModal
  target={{
    type: 'credits',
    packId: '1000',
    label: '1,000 Credits',
    amountUsd: 10,
  }}
  onClose={() => setModalOpen(false)}
/>

The modal handles coin selection, QR display, countdown timer, and polls for confirmation automatically.


How Address Derivation Works

Each payment gets a unique deposit address derived from your master mnemonic:

  • EVM (ETH, Base, Polygon, etc.): m/44'/60'/0'/0/{index} — checksummed Ethereum address, valid on all EVM chains
  • Bitcoin: m/84'/0'/0'/0/{index} — native SegWit P2WPKH (bc1q...)
  • Solana: m/44'/501'/{index}'/0' — SLIP-0010 ed25519 (matches Phantom/Solflare)

The derivation_index increments per payment and is stored in the DB. A UNIQUE constraint prevents two active payments from sharing an address.

Implication: One mnemonic controls all your payment wallets. Back it up securely. If you ever need to sweep funds, import the mnemonic into MetaMask (for EVM), Phantom (for SOL), or a BIP84 wallet (for BTC) and derive the child accounts.


What to Customise

Thing Where
Supported coins list SUPPORTED_COINS in crypto-checkout-route.ts
Quick-pick coins shown first QUICK_PICKS in crypto-payment-modal.tsx
Payment window (default 1hr) expiresAt in crypto-checkout-route.ts
Balance tolerance (default 0.5%) minRequired in check-crypto-payments-route.ts
Payment types + fulfilment logic applyPayment() in check-crypto-payments-route.ts
Rate limit (default 3 req/5min) checkRateLimit call in crypto-checkout-route.ts

Security Notes

  • crypto-wallets.ts is server-only — never import it in client components or pages
  • CRYPTO_MASTER_MNEMONIC must never appear in source code, git history, or logs
  • The cron endpoint uses timingSafeEqual to prevent timing attacks on CRON_SECRET
  • Payment amounts are sourced from server-side constants — users cannot manipulate them
  • Address uniqueness is enforced at the DB level (unique index on wallet_type + derivation_index)

Troubleshooting

"CRYPTO_MASTER_MNEMONIC is not set" — The env var is missing. Add it to .env.local (development) or your hosting provider's environment settings (production).

Payments not confirming — Check that your cron job is running and the Authorization: Bearer <CRON_SECRET> header matches exactly. Check Vercel/server logs for [check-crypto] lines.

"Failed to allocate payment address" — Extremely rare concurrent collision (3 retries exhausted). Safe to retry.

EVM token not detected — Ensure the contract address for that token+chain exists in ERC20_CONTRACTS in crypto-wallets.ts. Check etherscan.io for the canonical USDC/USDT address on your chain.