閃電網路支付實戰:LNURLw 完整教學
深入理解 LNURLw 標準,包含 Channels.open()、Invoice.create() 等實際操作流程。
閃電網路支付實戰:LNURLw 完整教學
LNURLw 是閃電網路的重要標準之一,它提供了一種標準化的方式來發起和接收閃電支付。本教學將深入解析 LNURLw 標準,並說明 Channels.open()、Invoice.create() 等實際操作流程。
什麼是 LNURLw?
LNURLw (Lightning URL with Withdrawal) 是一個基於 LNURL 的協議擴展,允許:
- 提現請求:服務端生成 LNURLw,用戶掃描後可以發起閃電支付
- 支付請求:標準化的支付請求格式
- 認證機制:通過 HTTP GET 請求進行身份驗證
LNURLw 工作流程
LNURLw 的完整工作流程如下:
┌─────────────┐ 1. GET ┌─────────────┐
│ 用戶端 │ ──────────────→│ 服務端 │
│ (錢包) │ │ (LNURLw) │
│ │ ←────────────── │ │
└─────────────┘ 2. JSON └─────────────┘
│
│ 3. 解析 LNURLw
↓
┌─────────────┐
│ 比特幣網路 │
└─────────────┘
│
│ 4. 發起閃電支付
↓
┌─────────────┐ 5. Invoice ┌─────────────┐
│ 用戶端 │ ←─────────────│ 服務端 │
│ │ │ │
└─────────────┘ └─────────────┘
LNURLw 格式
基礎 LNURLw
LNURLw 本質上是一個編碼的 URL:
// 原始 URL
https://example.com/lnurlw?tag=withdrawal& idempotency_key=abc123& min=1000& max=50000
// LNURLw 編碼(Base64url)
lnurl1EXAMPLE...
典型回應
當錢包請求 LNURLw 時,服務端返回 JSON:
{
"tag": "withdrawal",
"callback": "https://example.com/lnurlw/callback",
"k1": "random_idempotency_key_12345",
"min": 1000,
"max": 50000,
"defaultDescription": "Withdraw from Example Service"
}
Channels.open() 詳解
Channels.open() 是 LND (Lightning Network Daemon) API 的一部分,用於打開新的閃電通道。
基本語法
// LND gRPC 調用
const lnd = require('@lightninglabs/ln-rpc');
const rpc = lnd();
async function openChannel() {
const response = await rpc.channelOpen({
local_funding_amount: '50000', // 頻道金額(satoshis)
push_sats: '0', // 給對方的初始餘額
target_conf: 6, // 目標區塊確認數
sat_per_byte: '10', // 每字節手續費
private: false, // 是否私有頻道
min_htlc_msat: '1', // 最小 HTLC 金額
remote_csv_delay: 144, // 對方的 CSV 延遲
});
return response;
}
回應格式
{
"funding_txid": "abc123...",
"output_index": 0,
"channel_point": "abc123...:0"
}
Invoice.create() 詳解
Invoice.create() 用於創建閃電發票(Payment Request)。
基本語法
async function createInvoice() {
const response = await rpc.addInvoice({
value: '1000', // 金額(satoshis)
memo: 'Payment for Coffee', // 備註
expiry: '3600', // 過期時間(秒)
route_hints: [], // 路由提示
private: false, // 是否私有
});
return response;
}
回應格式
{
"payment_request": "lnbc10n1...",
"add_index": "12345",
"payment_addr": "abcdef..."
}
實戰:實現 LNURLw 提現服務
後端實現 (```javascript
const crypto = require('Node.js)
crypto');
const Lnurl = require('lnurl');
// 1. 生成 LNURLw 端點
app.get('/api/lnurlw/withdraw', async (req, res) => {
const idempotencyKey = crypto.randomBytes(16).toString('hex');
const k1 = crypto.randomBytes(32).toString('hex');
// 儲存提現請求
await redis.setex(
lnurlw:${k1},
3600, // 1小時過期
JSON.stringify({
idempotencyKey,
maxAmount: 50000,
minAmount: 1000,
used: false
})
);
// 生成 LNURLw
const lnurlw = https://${req.host}/api/lnurlw/callback?tag=withdrawal&k1=${k1};
const encoded = Lnurl.encode(lnurlw);
res.json({
lnurl: encoded,
min: 1000,
max: 50000
});
});
// 2. 處理回調
app.get('/api/lnurlw/callback', async (req, res) => {
const { k1, pr } = req.query;
// 驗證請求
const withdrawRequest = JSON.parse(
await redis.get(lnurlw:${k1})
);
if (!withdrawRequest || withdrawRequest.used) {
return res.json({ status: 'ERROR', reason: 'Invalid or used LNURLw' });
}
// 解析支付請求
const decoded = Lnurl.decode(pr);
const invoice = await decodeInvoice(decoded);
// 驗證金額
if (invoice.satoshis > withdrawRequest.maxAmount ||
invoice.satoshis < withdrawRequest.minAmount) {
return res.json({ status: 'ERROR', reason: 'Amount out of range' });
}
// 標記為已使用
await redis.setex(
lnurlw:${k1},
60, // 短過期
JSON.stringify({ ...withdrawRequest, used: true })
);
// 廣播閃電支付
await sendPayment(pr);
res.json({ status: 'OK' });
});
### 前端實現 (錢包端)
// 1. 解析 LNURLw
async function handleLNURLw(lnurlwString) {
const decodedUrl = Lnurl.decode(lnurlwString);
// 請求服務端
const response = await fetch(decodedUrl);
const data = await response.json();
// 顯示提現金額選擇 UI
showWithdrawalUI(data.min, data.max, data.callback, data.k1);
}
// 2. 發起支付
async function initiateWithdrawal(callback, k1, amount) {
// 創建發票
const invoice = await createInvoice(amount);
// 發送給服務端
const response = await fetch(${callback}&amount=${amount}&pr=${encodeURIComponent(invoice)});
const result = await response.json();
if (result.status === 'OK') {
showSuccess('提現成功!');
} else {
showError(result.reason);
}
}
## LNURLw 安全考量
### 服務端安全
1. **HTTPS 強制**:所有 LNURLw 通信必須使用 HTTPS
2. **速率限制**:防止濫用和暴力攻擊
3. **金額驗證**:嚴格驗證請求金額範圍
4. ** idempotency key**:防止重放攻擊
### 客戶端安全
1. **驗證服務身份**:確認 LNURLw 來自可信服務
2. **金額確認**:支付前確認金額正確
3. **過期時間**:注意發票的過期時間
## 常見問題
### Q: LNURLw 和 LNURL 有什麼區別?
A: LNURLw 是 LNURL 的擴展,專門用於提現場景。LNURL 更通用,可以用於登入、認證等場景。
### Q: 為什麼我的 LNURLw 支付失敗?
A: 常見原因包括:頻道餘額不足、網路擁塞、發票過期、金額超出範圍等。
### Q: LNURLw 支付有手續費嗎?
A: 是的,閃電網路支付會有路由費用,通常遠低於鏈上交易。
### Q: 如何確保 LNURLw 的安全性?
A: 始終通過 HTTPS 連接、驗證服務端身份、使用知名錢包軟體。
## 結論
LNURLw 為閃電網路支付提供了標準化且用戶友好的方式。通過理解其工作原理和實際操作流程,開發者可以構建各種閃電網路應用,用戶也能更安全地使用閃電支付功能。
## 相關資源
- [LNURL 規範](https://github.com/lnurl/luds)
- [LND API 文檔](https://api.lightning.community/)
- [閃電網路官方網站](https://lightning.network/)
相關文章
- 小額支付實驗 — 體驗快速且低費用的支付方式。
- 閃電網路 Channels 詳解 — 深入理解 HTLC、通道狀態與流動性管理。
- Eltoo 通道機制 — 理解 Eltoo 協議設計與閃電網路升級機制。
- 閃電網路路由機制 — 閃電網路路由演算法與機制
- 閃電網路費用計算完全指南 — 深入理解閃電費用結構,學習如何計算與優化費用。
延伸閱讀與來源
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!