如何将BTCPay Server接入MERN应用以创建发票?
在MERN栈中接入BTCPay Server生成发票
1. 后端(Express)配置与接口开发
首先在BTCPay Server后台生成API密钥,权限仅勾选invoices:create,遵循最小权限原则。
安装依赖
在Express项目中安装请求工具:
npm install axios
编写创建发票的后端接口
通过后端中转调用BTCPay API,避免密钥暴露在前端:
const express = require('express'); const axios = require('axios'); const router = express.Router(); // 用环境变量存储敏感配置,禁止硬编码 const BTCPAY_URL = process.env.BTCPAY_URL; const BTCPAY_API_KEY = process.env.BTCPAY_API_KEY; const STORE_ID = process.env.BTCPAY_STORE_ID; router.post('/api/create-invoice', async (req, res) => { try { const { amount, currency, description } = req.body; const invoiceData = { amount, currency, description, checkout: { redirectURL: `${process.env.FRONTEND_URL}/payment-success`, cancelURL: `${process.env.FRONTEND_URL}/payment-cancel` } }; const response = await axios.post( `${BTCPAY_URL}/api/v1/stores/${STORE_ID}/invoices`, invoiceData, { headers: { 'Authorization': `token ${BTCPAY_API_KEY}`, 'Content-Type': 'application/json' } } ); res.json({ invoiceId: response.data.id, paymentUrl: response.data.checkoutLink, qrCodeUrl: response.data.qrCode }); } catch (error) { console.error('创建发票失败:', error.response?.data || error.message); res.status(500).json({ error: '创建发票失败,请稍后重试' }); } }); module.exports = router;
2. 前端(React)调用接口并展示发票
在React组件中实现触发创建发票的逻辑,展示支付链接与二维码:
import { useState } from 'react'; import axios from 'axios'; const PaymentForm = () => { const [amount, setAmount] = useState(''); const [currency, setCurrency] = useState('USD'); const [description, setDescription] = useState('测试订单'); const [paymentUrl, setPaymentUrl] = useState(''); const [qrCodeUrl, setQrCodeUrl] = useState(''); const handleCreateInvoice = async (e) => { e.preventDefault(); try { const response = await axios.post('/api/create-invoice', { amount, currency, description }); setPaymentUrl(response.data.paymentUrl); setQrCodeUrl(response.data.qrCodeUrl); } catch (error) { alert('创建发票失败,请检查输入或稍后重试'); } }; return ( <div> <form onSubmit={handleCreateInvoice}> <div> <label>金额:</label> <input type="number" step="0.01" value={amount} onChange={(e) => setAmount(e.target.value)} required /> </div> <div> <label>货币:</label> <select value={currency} onChange={(e) => setCurrency(e.target.value)}> <option value="USD">USD</option> <option value="BTC">BTC</option> <option value="EUR">EUR</option> </select> </div> <div> <label>订单描述:</label> <input type="text" value={description} onChange={(e) => setDescription(e.target.value)} /> </div> <button type="submit">生成支付发票</button> </form> {paymentUrl && ( <div className="payment-options"> <h3>请完成支付:</h3> <a href={paymentUrl} target="_blank" rel="noopener noreferrer"> 点击跳转BTCPay支付页面 </a> {qrCodeUrl && <img src={qrCodeUrl} alt="支付二维码" />} </div> )} </div> ); }; export default PaymentForm;
3. 额外优化:支付状态回调处理
在BTCPay后台配置Webhook指向后端接口(如/api/webhook/btcpay),实时更新订单状态:
router.post('/api/webhook/btcpay', express.raw({ type: 'application/json' }), async (req, res) => { // 必须验证Webhook签名,防止恶意请求 try { const invoiceData = JSON.parse(req.body); const invoiceId = invoiceData.id; const status = invoiceData.status; // 根据invoiceId更新MongoDB中的订单记录 // 示例:await Order.findOneAndUpdate({ invoiceId }, { status }); res.sendStatus(200); } catch (error) { console.error('处理Webhook失败:', error); res.sendStatus(500); } });
核心注意事项
- 敏感配置必须用环境变量存储,禁止暴露在前端代码中。
- Webhook必须验证签名,参考BTCPay官方文档的签名验证逻辑。
- 可根据业务需求扩展发票参数,比如自定义元数据、过期时间等。
内容的提问来源于stack exchange,提问作者Jack Hillyer
相关产品推荐
相关产品推荐

