Skip to main content

التثبيت

npm install l402-kit
المتطلبات: Node.js 18+، Express 4+

البدء السريع

import express from 'express';
import { l402, BlinkProvider } from 'l402-kit';

const app = express();

// تذهب المدفوعات مباشرةً إلى محفظة Blink الخاصة بك — رسوم 0%
const lightning = new BlinkProvider(
  process.env.BLINK_API_KEY!,
  process.env.BLINK_WALLET_ID!,
);

app.get('/api/data', l402({ priceSats: 10, lightning }), (req, res) => {
  res.json({ data: 'premium content' });
});

app.listen(3000);

l402(options) — الوسيط

يُعيد RequestHandler من Express يُطبّق دفع L402 على المسار.

الخيارات

الخيارالنوعالافتراضيالوصف
priceSatsnumberمطلوبالسعر لكل استدعاء بالـ satoshis
lightningLightningProviderمطلوبالواجهة الخلفية لـ Lightning الخاصة بك
supabaseUrlstringمتغير بيئة SUPABASE_URLرابط Supabase لتسجيل المدفوعات
supabaseKeystringمتغير بيئة SUPABASE_ANON_KEYمفتاح Supabase لتسجيل المدفوعات
onPayment(token, amountSats) => voidاستدعاء راجع يُشغَّل بعد كل دفعة مُتحقَّق منها
webhookUrlstringنقطة النهاية الخاصة بك لاستقبال أحداث الدفع الموقَّعة
webhookSecretstringسر HMAC-SHA256 لتوقيع الـ webhook
replayAdapterReplayAdapterفي الذاكرةخلفية قابلة للتوصيل لحماية إعادة التشغيل

السلوك

الطلبالاستجابة
لا يوجد ترويسة Authorization402 Payment Required مع فاتورة BOLT11 + macaroon
L402 <macaroon>:<preimage> صالحnext() — يُنفَّذ المعالج
رمز غير صالح أو منتهي الصلاحية401 Unauthorized
preimage مُعاد استخدامه401 Token already used

نص استجابة 402

{
  "error": "Payment Required",
  "invoice": "lnbc100n1p...",
  "macaroon": "eyJoYXNoIjoiYWJjMTIzIiwiZXhwIjoxNzAwMDAwMDAwfQ==",
  "priceSats": 10
}

ترويسة WWW-Authenticate

WWW-Authenticate: L402 macaroon="eyJ...", invoice="lnbc..."

المزودون

AlbyProvider — موصى به لوضع السيادة الكاملة

Alby — محفظة غير حضانية. أنت تتحكم في المفاتيح.
import { AlbyProvider } from 'l402-kit';

// 1. أنشئ Alby Hub على hub.getalby.com (أو استضفه بنفسك)
// 2. الإعدادات ← رموز الوصول ← أنشئ رمزاً (النطاقات: invoices:create, invoices:read)
const lightning = new AlbyProvider(
  process.env.ALBY_ACCESS_TOKEN!, // رمز وصول Hub
  process.env.ALBY_HUB_URL!,      // مثال: "https://your-name.getalby.com"
);

BTCPayProvider — مستضاف ذاتياً، ثقة صفرية

شغّل BTCPay Server الخاص بك. سيادة كاملة.
import { BTCPayProvider } from 'l402-kit';

const lightning = new BTCPayProvider(
  process.env.BTCPAY_URL!,         // https://your-btcpay.com
  process.env.BTCPAY_API_KEY!,     // مفتاح API الخاص بالمتجر
  process.env.BTCPAY_STORE_ID!,    // معرّف المتجر
);

BlinkProvider — حضاني، أسهل بداية

Blink — مجاني، لا KYC للمبالغ الصغيرة.
import { BlinkProvider } from 'l402-kit';

const lightning = new BlinkProvider(
  process.env.BLINK_API_KEY!,    // dashboard.blink.sv ← مفاتيح API
  process.env.BLINK_WALLET_ID!,  // معرّف محفظة BTC الخاصة بك
);

LNbitsProvider

مستضاف ذاتياً أو عبر legend.lnbits.com.
import { LNbitsProvider } from 'l402-kit';

const lightning = new LNbitsProvider(
  process.env.LNBITS_API_KEY!,
  'https://your-lnbits-instance.com', // اختياري، يُستخدم legend.lnbits.com افتراضياً
);

OpenNodeProvider

import { OpenNodeProvider } from 'l402-kit';

const lightning = new OpenNodeProvider(
  process.env.OPENNODE_API_KEY!,
  false, // testMode — اضبطه على true للبيئة التجريبية
);

ManagedProvider — وضع السحابة (رسوم 0.3%)

يستضيف l402kit.com عقدة Lightning. تستلم 99.7% من كل دفعة. يتطلب الاشتراك الصريح.
import { ManagedProvider } from 'l402-kit';

const lightning = ManagedProvider.fromAddress('you@yourdomain.com');

// اختياري: التسجيل التلقائي في دليل API العام
const lightning = ManagedProvider.fromAddress('you@yourdomain.com', {
  registerDirectory: {
    url: 'https://api.you.com/v1/data',
    name: 'My Data API',
    priceSats: 10,
    category: 'data',          // data | ai | finance | weather | compute | storage | other
    description: 'Optional',
  },
});
يُشغَّل التسجيل مرة واحدة عند بدء التشغيل (fire-and-forget، الأخطاء صامتة). تظهر الواجهة البرمجية على l402kit.com/apis.json ليتمكن الوكلاء من اكتشافها تلقائياً.

حماية إعادة التشغيل

الافتراضي — في الذاكرة (بيئة التطوير)

مدمج. يُعاد ضبطه عند إعادة التشغيل. مناسب للنشر ذي العملية الواحدة.
app.get('/api', l402({ priceSats: 10, lightning }), handler);

Redis (الإنتاج — متعدد النسخ)

import Redis from 'ioredis';
import { l402, RedisReplayAdapter } from 'l402-kit';

const replay = new RedisReplayAdapter(
  new Redis(process.env.REDIS_URL!),
  86400, // مدة الصلاحية بالثواني (24 ساعة)
);

app.get('/api', l402({ priceSats: 10, lightning, replayAdapter: replay }), handler);
يستخدم RedisReplayAdapter الأمر SET key 1 NX EX ttl — ذري وخالٍ من تعارض السباق.

Webhooks الدفع

استلم حدثاً موقَّعاً بعد كل دفعة.
import { l402, verifyWebhook } from 'l402-kit';

app.get('/api/data', l402({
  priceSats: 10,
  lightning,
  webhookUrl: 'https://yourapi.com/webhooks/l402',
  webhookSecret: process.env.L402_WEBHOOK_SECRET!,
}), handler);

// مستقبل الـ webhook
app.post('/webhooks/l402', express.raw({ type: '*/*' }), (req, res) => {
  const valid = verifyWebhook(
    process.env.L402_WEBHOOK_SECRET!,
    req.body.toString(),
    req.headers['l402-signature'] as string,
  );
  if (!valid) return res.status(401).end();

  const event = JSON.parse(req.body.toString());
  console.log('Payment:', event.data.paymentHash, event.data.amountSats);
  res.json({ ok: true });
});
حمولة الـ webhook:
{
  "id": "evt_abc123",
  "type": "payment.received",
  "created": 1700000000,
  "data": {
    "endpoint": "/api/data",
    "amountSats": 10,
    "paymentHash": "sha256-of-preimage"
  }
}

استدعاء onPayment الراجع

خطاف متزامن يُستدعى بعد كل دفعة مُتحقَّق منها، قبل next():
app.get('/api/data', l402({
  priceSats: 10,
  lightning,
  onPayment: async ({ macaroon, preimage }, amountSats) => {
    await myAnalytics.track('payment', { amountSats });
  },
}), handler);

تسجيل المدفوعات عبر Supabase

اضبط SUPABASE_URL + SUPABASE_ANON_KEY في بيئتك لتسجيل المدفوعات تلقائياً.
app.get('/api', l402({ priceSats: 10, lightning }), handler);
// يُقرأ SUPABASE_URL و SUPABASE_ANON_KEY من process.env تلقائياً
مخطط جدول المدفوعات (payments):
create table payments (
  id            uuid primary key default gen_random_uuid(),
  payment_hash  text unique not null,  -- SHA256(preimage) — آمن للتخزين
  endpoint      text,
  amount_sats   integer,
  paid_at       timestamptz default now()
);
يخزّن payment_hash القيمة SHA256(preimage) وليس الـ preimage الخام. الـ preimage هو سر دفع Lightning المؤلف من 32 بايت — وتجزئته متاحة للعموم بالفعل في فاتورة BOLT11.

أدوات مستقلة

import { verifyToken, parseToken, checkAndMarkPreimage } from 'l402-kit';

// التحقق من رمز (يُعيد true/false)
const isValid = await verifyToken('eyJoYXNoIjoi...:deadbeef...');

// تحليل أجزاء الرمز
const { macaroon, preimage } = parseToken(token);

// فحص إعادة التشغيل المخصص (يُعيد true = أول استخدام، false = إعادة تشغيل)
const isFirstUse = await checkAndMarkPreimage(preimage);

الأنواع

import type { L402Options, LightningProvider, Invoice, L402Token } from 'l402-kit';

interface L402Options {
  priceSats: number;
  lightning: LightningProvider;
  supabaseUrl?: string;
  supabaseKey?: string;
  onPayment?: (token: L402Token, amountSats: number) => void | Promise<void>;
  webhookUrl?: string;
  webhookSecret?: string;
  replayAdapter?: ReplayAdapter;
}

interface Invoice {
  paymentRequest: string;
  paymentHash: string;
  macaroon: string;
  amountSats: number;
  expiresAt: number;        // Unix ms
}

interface LightningProvider {
  createInvoice(amountSats: number): Promise<Invoice>;
  checkPayment(paymentHash: string): Promise<boolean>;
}

توقيت التحقق

يُشغّل التحقق من الرمز العملية SHA256(preimage) == paymentHash في الذاكرة — أقل من ميلي ثانية، بدون استدعاء شبكة على المسار الساخن. يعمل ReplayAdapter في الذاكرة (الافتراضي) أيضاً بشكل متزامن. إذا استخدمت RedisReplayAdapter، أضف 5–50 مللي ثانية لزمن رحلة Redis ذهاباً وإياباً لكل طلب. ضع في اعتبارك هذا الأمر عند تخطيط السعة لنقاط النهاية عالية التردد.

توافق x402 (ترويسة X-Payment)

يقبل الوسيط بصمت ترويسة X-Payment (المستخدمة في بروتوكول x402 من Coinbase) إضافةً إلى الترويسة القياسية Authorization: L402 …. يُعامَل كلاهما بشكل متطابق — مفيد إذا أردت خدمة العملاء الذين يتحدثون أياً من البروتوكولين.
// عميل يستخدم ترويسة x402 — يُعالَج بشفافية
// X-Payment: <macaroon>:<preimage>
لا يلزم أي إعداد؛ فهو مُفعَّل دائماً.

دليل الترقية

v1.1 ← v1.2

أعد تسمية العمود في جدول payments الخاص بك:
ALTER TABLE payments RENAME COLUMN preimage TO payment_hash;