Integrate BTCPay Server: auto-settling invoice deposits
- lib/btcpay.ts: Greenfield API client (createBtcPayInvoice, getBtcPayInvoiceStatus) - /api/btcpay/invoice: creates per-deposit invoice (USD amount + handle in metadata) - /api/btcpay/status/[id]: polls invoice status + returns BTC address/amount - add-funds page: BTCPay tab with amount picker, live address display, 10s auto-poll, settlement auto-credits localStorage ledger - WalletContext: creditBtcPayInvoice() with duplicate-invoice guard - .env.example: BTCPAY_URL, BTCPAY_API_KEY, BTCPAY_STORE_ID, NEXT_PUBLIC_BTCPAY_ENABLED Made-with: Cursor
This commit is contained in:
32
app/api/btcpay/status/[invoiceId]/route.ts
Normal file
32
app/api/btcpay/status/[invoiceId]/route.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getBtcPayInvoiceStatus, isBtcPayConfigured } from "@/lib/btcpay";
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
{ params }: { params: { invoiceId: string } },
|
||||
) {
|
||||
if (!isBtcPayConfigured()) {
|
||||
return NextResponse.json({ ok: false, error: "BTCPay not configured" }, { status: 503 });
|
||||
}
|
||||
|
||||
const { invoiceId } = params;
|
||||
if (!invoiceId || invoiceId.length < 4) {
|
||||
return NextResponse.json({ ok: false, error: "Invalid invoice ID" }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await getBtcPayInvoiceStatus(invoiceId);
|
||||
if (!result.ok) {
|
||||
return NextResponse.json({ ok: false, error: result.error }, { status: 502 });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: result.status,
|
||||
usdAmount: result.usdAmount,
|
||||
btcAddress: result.paymentMethod?.destination ?? null,
|
||||
btcAmount: result.paymentMethod?.due ?? null,
|
||||
btcRate: result.paymentMethod?.rate ?? null,
|
||||
totalPaid: result.paymentMethod?.totalPaid ?? null,
|
||||
settled: result.status === "Settled",
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user