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:
@@ -13,6 +13,7 @@ interface WalletContextType {
|
||||
earnLuxCredits: (amount: number) => void;
|
||||
spendUsdStoreCredit: (amount: number) => boolean;
|
||||
verifyBtcDeposit: (txid: string) => Promise<{ ok: boolean; error?: string; creditedUsd?: number }>;
|
||||
creditBtcPayInvoice: (invoiceId: string, usdAmount: number) => { ok: boolean; error?: string };
|
||||
vault: VaultState;
|
||||
collectKey: (key: string) => boolean;
|
||||
addVaultReceipt: (receipt: VaultReceipt) => void;
|
||||
@@ -141,6 +142,23 @@ export const WalletProvider = ({ children }: WalletProviderProps) => {
|
||||
[handle],
|
||||
);
|
||||
|
||||
const creditBtcPayInvoice = useCallback(
|
||||
(invoiceId: string, usdAmount: number) => {
|
||||
if (!handle) return { ok: false, error: "Sign in first" };
|
||||
const id = String(invoiceId || "").trim();
|
||||
if (!id) return { ok: false, error: "Invalid invoice ID" };
|
||||
const safe = Number.isFinite(usdAmount) ? Math.max(0, Math.round(usdAmount * 100) / 100) : 0;
|
||||
if (safe <= 0) return { ok: false, error: "Invalid amount" };
|
||||
const row = getLedgerRow(handle);
|
||||
if (row.claimedTxids.includes(id)) return { ok: false, error: "Invoice already credited" };
|
||||
const nextUsd = Math.round((row.usd + safe) * 100) / 100;
|
||||
setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: [...row.claimedTxids, id] });
|
||||
setUsdStoreCredit(nextUsd);
|
||||
return { ok: true };
|
||||
},
|
||||
[handle],
|
||||
);
|
||||
|
||||
const collectKey = (key: string) => {
|
||||
const trimmed = String(key || "").trim();
|
||||
if (!trimmed) return false;
|
||||
@@ -167,6 +185,7 @@ export const WalletProvider = ({ children }: WalletProviderProps) => {
|
||||
earnLuxCredits,
|
||||
spendUsdStoreCredit,
|
||||
verifyBtcDeposit,
|
||||
creditBtcPayInvoice,
|
||||
vault,
|
||||
collectKey,
|
||||
addVaultReceipt,
|
||||
@@ -179,6 +198,7 @@ export const WalletProvider = ({ children }: WalletProviderProps) => {
|
||||
earnLuxCredits,
|
||||
spendUsdStoreCredit,
|
||||
verifyBtcDeposit,
|
||||
creditBtcPayInvoice,
|
||||
vault,
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user