صُمّمت مفاتيح واجهات البرمجة للبشر. يسجّل المطوّر حساباً، ويقرأ صفحة الأسعار، ويُدخل بطاقته البنكية، وينسخ مفتاحاً سرياً إلى ملف .env، ومنذ تلك اللحظة يصبح المفتاح هو هوية المستدعي. هذه الطقوس كلها تفترض وجود إنسان في الطرف الآخر قادر على إكمال نموذج تسجيل.
وكلاء الذكاء الاصطناعي يكسرون هذا الافتراض. الوكيل الذي يكتشف واجهة الطقس الخاصة بك أثناء التشغيل لا يستطيع إنشاء حساب، ولا اجتياز إجراءات "اعرف عميلك"، ولا انتظار ردّ فريق المبيعات بعد ثلاثة أيام. لكنه يستطيع توقيع دفعة وإعادة إرسال الطلب خلال ثانية واحدة تقريباً.
x402 هو البروتوكول المفتوح الذي يجعل ذلك ممكناً. يُعيد إحياء رمز الحالة HTTP 402 Payment Required الذي ظلّ خاملاً عقوداً، ويحوّله إلى طبقة دفع حقيقية: الخادم يردّ على الطلب غير المدفوع بالرمز 402 مرفقاً بمتطلبات دفع قابلة للقراءة آلياً، فيوقّع العميل تحويلاً بعملة مستقرة، ثم يُعيد إرسال الطلب مع التوقيع. بلا حسابات، ولا جلسات، ولا مفاتيح واجهات برمجية.
يبني هذا الدرس الطرفين معاً — البائع (واجهة Next.js تتقاضى رسماً عن كل استدعاء) والمشتري (وكيل يدفع دون أن يستأذن أحداً) — ثم يربط نقطة النهاية المدفوعة بخادم MCP كي يستخدمها Claude كأداة.
ملاحظة عن الإصدار: كل ما يرد هنا يستهدف x402 v2، الذي أدخل ترويستَي PAYMENT-SIGNATURE وPAYMENT-RESPONSE، ومعرّفات الشبكات بصيغة CAIP-2، وعائلة الحزم المُنطّقة @x402/*. إن صادفت دروساً أقدم تستخدم x402-next بلا نطاق وترويسة X-PAYMENT، فتلك هي النسخة الأولى — الترحيل بينهما بسيط، لكن الواجهات ليست متبادلة.
المتطلبات المسبقة
قبل البدء، تأكّد من توفّر:
- Node.js 20 أو أحدث، مع pnpm أو npm
- Next.js 15 أو 16 مع App Router وTypeScript
- إلمام بـ async/await ومعالجات المسارات والوسيط (middleware)
- فهم أساسي لمعنى عنوان المحفظة — لست بحاجة إلى خبرة في كتابة العقود الذكية
- نحو 5 دولارات من USDC على شبكة Base Sepolia التجريبية (مجانية من أي صنبور Base) للطرف المشتري
- اختيارياً: Claude Desktop أو أي عميل MCP آخر للخطوة الأخيرة
لست بحاجة إلى حساب Coinbase، ولا مفتاح من منصة مطوّري Coinbase، ولا أي منتج تابع لها. فـ x402 مرخّص بموجب Apache-2.0، وميسّر الشبكة التجريبية على https://x402.org/facilitator مفتوح للجميع.
ما الذي ستبنيه
بنهاية هذا الدرس ستمتلك:
- إعداداً مشتركاً لـ خادم الموارد يعرف كيف يتحقّق من المدفوعات ويسوّيها
- معالج مسار في Next.js محمياً بـ
withX402بسعر 0.001 دولار لكل استدعاء - وسيطاً قائماً على البروكسي يسعّر عدة مسارات دفعة واحدة، بما فيها طبقة متميّزة
- عميلاً مشترياً — سكربتاً مستقلاً يستدعي نقطة النهاية، يتلقّى 402، يوقّع، ثم يُعيد المحاولة
- خادم MCP يعرض الواجهة المدفوعة كأداة لـ Claude، مع حدود إنفاق
- قائمة تحقّق للانتقال من Base Sepolia إلى شبكة Base الرئيسية
الخطوة 1: فهم مصافحة 402
قبل كتابة أي سطر، من المفيد معرفة ما يجري فعلياً على السلك. التدفق الكامل ستّ حركات:
- يطلب العميل
GET /api/weatherدون دفع. - يردّ الخادم بـ
402 Payment Requiredمع ترويسةPAYMENT-REQUIREDتحوي JSON مُرمّزاً بـ Base64. داخلها مصفوفةaccepts— عنصر لكل خيار دفع يقبله الخادم (الشبكة، الأصل، السعر، عنوان الوجهة، النظام المستخدم). - يختار العميل عنصراً يستطيع الوفاء به، ويبني حمولة دفع وفق نظام ذلك العنصر، ويوقّعها بمفتاح محفظته. في نظام
exactعلى سلسلة متوافقة مع EVM يكون هذا توقيعTransferWithAuthorizationوفق معيار EIP-3009 — تفويض خارج السلسلة بنقل مبلغ محدّد من USDC، دون حاجة إلى موافقة مسبقة على السلسلة من المشتري. - يُعيد العميل الطلب ذاته حرفياً، حاملاً هذه المرة ترويسة
PAYMENT-SIGNATUREتضمّ الحمولة الموقّعة مُرمّزة بـ Base64. - يُسلّم الخادم الحمولة إلى ميسّر — خدمة تتحقّق من مطابقة التوقيع للمتطلبات عبر
POST /verify، ثم تبثّ التحويل على السلسلة عبرPOST /settle. الميسّر لا يحتفظ بالأموال إطلاقاً؛ فهو يمرّر توقيعاً أنتجه المشتري سلفاً. وأي ميسّر يعبث بالمبلغ يُنتج توقيعاً باطلاً فيفشل التحويل. - يُعيد الخادم
200 OKمع المورد، إضافة إلى ترويسةPAYMENT-RESPONSEتحمل إيصال التسوية مُرمّزاً بـ Base64.
ثلاث خصائص تنبع من هذا التصميم وتستحقّ الاستيعاب:
- البروتوكول عديم الحالة. لا يخزّن الخادم شيئاً عن المشتري بين الاستدعاءات. لا جلسة، ولا سلّة تحديد معدّل مرتبطة بحساب، ولا جدول مستخدمين.
- عنوان المحفظة هو الهوية. إن أردت تحليلات لكل مستدعٍ أو قوائم سماح، فعنوان الدافع هو المفتاح الذي تبني عليه.
- التسوية دفعٌ نهائي لا رجعة فيه. نظام
exactلا يعرف ردّ المبالغ. أما الاسترداد، إن قدّمته، فهو تحويل ثانٍ تبادر إليه من منطق العمل لديك.
الخطوة 2: تهيئة المشروع
ابدأ من تطبيق Next.js يستخدم App Router. ثبّت حزم طرف البائع:
pnpm add @x402/next @x402/core @x402/evmالتقسيم مقصود: @x402/core يحوي آليات البروتوكول، و@x402/evm ينفّذ نظام exact لسلاسل EVM، و@x402/next يوفّر الربط مع Next.js. وإن أردت قبول مدفوعات Solana أيضاً، أضف @x402/svm.
تحتاج الآن إلى عنوان محفظة لاستقبال المدفوعات. البائع لا يحتاج سوى العنوان — وهو عام وآمن لوضعه في .env.local بل وحتى لكشفه في حزمة المتصفّح. فالبائع لا يوقّع شيئاً، ولا حاجة لمفتاح خاص على خادمك.
إن لم يكن لديك عنوان بعد، ولّد زوج مفاتيح مؤقتاً للشبكة التجريبية:
node -e "const {generatePrivateKey,privateKeyToAccount}=require('viem/accounts');const k=generatePrivateKey();console.log('PRIVATE_KEY=',k);console.log('ADDRESS=',privateKeyToAccount(k).address)"أضف العنوان إلى .env.local:
# .env.local
NEXT_PUBLIC_EVM_ADDRESS=0xYourReceivingAddressHereتحذير: المفتاح الخاص المطبوع أعلاه يخصّ طرف المشتري لاحقاً في هذا الدرس، ويجب ألّا يصل أبداً إلى حزمة متصفّح أو إلى مستودع Git. العنوان وحده هو العام.
الخطوة 3: إعداد خادم الموارد
كل المسارات المحمية تتشارك نسخة واحدة من x402ResourceServer، تجمع عميل الميسّر وأنظمة الدفع التي تقبلها. أنشئ الملف x402.ts في جذر المشروع:
// x402.ts
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
// ميسّر الشبكة التجريبية — مجاني ومفتوح ولا يتطلب أي اعتمادات.
// استبدل الرابط بميسّر للشبكة الرئيسية عند الإطلاق (الخطوة 9).
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
export const server = new x402ResourceServer(facilitatorClient);
// سجّل نظام "exact" لكل سلاسل EIP-155.
// رمز البدل يعني Base وBase Sepolia وأي شبكة EVM أخرى
// تُدرجها لاحقاً في مصفوفة accepts الخاصة بمسار ما.
server.register("eip155:*", new ExactEvmScheme());
export const evmAddress = process.env.NEXT_PUBLIC_EVM_ADDRESS as `0x${string}`;
if (!evmAddress) {
throw new Error("NEXT_PUBLIC_EVM_ADDRESS is not set — payments cannot be received");
}تفصيلان مهمّان هنا.
الشبكات تُعرّف بمعرّفات CAIP-2 لا بأسماء ودّية. فـ Base Sepolia هي eip155:84532، وBase الرئيسية هي eip155:8453. الخطأ هنا هو السبب الأول لشكوى "توقيعي مرفوض" — لأن معرّف السلسلة جزء ممّا يوقّعه المشتري، فأي عدم تطابق يُبطل الحمولة بدل أن يُنتج رسالة خطأ مفيدة.
تسجيل النظام على الخادم لا يأخذ موقّعاً. قارن ذلك بالمشتري في الخطوة 6، حيث يأخذ ExactEvmScheme موقّعاً. الاسم نفسه، ومسار الاستيراد معاكس (/server مقابل /client)، والمسؤولية معاكسة: الخادم يتحقّق، والمشتري يوقّع.
الخطوة 4: فرض رسم على مسار واحد
أنظف طريقة لتسعير نقطة نهاية واحدة هي withX402، التي تغلّف معالج المسار مباشرة:
// app/api/weather/route.ts
import { NextRequest, NextResponse } from "next/server";
import { withX402 } from "@x402/next";
import { server, evmAddress } from "@/x402";
const handler = async (req: NextRequest) => {
const city = req.nextUrl.searchParams.get("city") ?? "Tunis";
// عملك الحقيقي هنا — استعلام قاعدة بيانات، أو استدعاء نموذج،
// أو واجهة طرف ثالث تُعيد بيعها.
const report = await getForecast(city);
return NextResponse.json({ city, report }, { status: 200 });
};
export const GET = withX402(
handler,
{
accepts: [
{
scheme: "exact",
price: "$0.001",
network: "eip155:84532", // Base Sepolia
payTo: evmAddress,
},
],
description: "Current weather forecast for a city",
mimeType: "application/json",
},
server,
);هذا هو التكامل بأكمله. صار الطلب غير المدفوع GET /api/weather يُعيد 402 مع المتطلبات، بينما يُشغّل الطلب المدفوع handler ويُعيد النشرة.
لماذا withX402 بدل الوسيط؟ بسبب توقيت التسوية. فـ withX402 لا يسوّي الدفعة إلا بعد أن يُعيد معالجك استجابة ناجحة — بحالة أقل من 400. فإن أطلق getForecast استثناءً، أو أعاد 503 لأن المزوّد الخارجي متوقّف، لا يُخصم من المشتري شيء. أما الاعتراض عبر الوسيط فيسوّي قبل تشغيل معالجك، ولا يستطيع تقديم هذا الضمان. لذا غلّف المعالج في كل ما يمكن أن يفشل.
بخصوص صيغة السعر: استخدم دائماً صيغة النص المسبوق بعلامة الدولار، "$0.001". حذف $ يُطلق خطأ تحقّق بدل أن يُفسَّر كمقدار خام من الرمز. وخلف الكواليس يُترجم ذلك إلى USDC — بستّ خانات عشرية — فيكون $0.001 مساوياً لـ 1000 وحدة أساسية. أما الحدّ العملي الأدنى فحوالي $0.0001؛ وما دونه يبدأ التقريب في التأثير.
الخطوة 5: تسعير عدة مسارات دفعة واحدة
حين تملك عائلة من نقاط النهاية، يصبح تكرار accepts في كل معالج مُرهقاً. تتيح لك paymentProxy كتابة جدول الأسعار مرة واحدة وتطبيقه عبر وسيط Next.js.
وسّع ملف x402.ts:
// x402.ts (تكملة)
import { paymentProxy } from "@x402/next";
const priced = (price: string, description: string) => ({
accepts: [
{
scheme: "exact" as const,
price,
network: "eip155:84532" as const,
payTo: evmAddress,
},
],
description,
mimeType: "application/json",
});
export const proxy = paymentProxy(
{
"/api/weather": priced("$0.001", "Current weather forecast"),
"/api/forecast/extended": priced("$0.01", "14-day extended forecast"),
"/api/historical": priced("$0.05", "Historical weather archive, per query"),
},
server,
);ثم اربطه في middleware.ts:
// middleware.ts
export { proxy as middleware } from "@/x402";
export const config = {
matcher: [
"/api/weather",
"/api/forecast/:path*",
"/api/historical/:path*",
],
runtime: "nodejs",
};لاحظ runtime: "nodejs". فالتحقّق من الدفع يستخدم أساسيات تشفير غير متاحة في بيئة Edge، لذا يجب أن يختار المُطابق بيئة Node.
طبقات التسعير أعلاه تُجسّد نمطاً يستحقّ الاحتذاء: ميّز حسب كلفة الخدمة لا حسب سلّم خطط اعتباطي. فاستعلام الظروف الحالية المخزّن مؤقتاً يكاد لا يكلّف شيئاً، فاطلب عُشر السنت. أما استعلام الأرشيف التاريخي فيمسح تخزيناً حقيقياً، فاطلب خمسين ضعفاً. ولأن لا خطة تُتفاوض عليها، يتوجّه الوكلاء إلى الطبقة التي تتحمّلها ميزانيتهم — أنت لا تفرض التزاماً بـ 99 دولاراً شهرياً على مستدعٍ يريد تسعة طلبات.
الجمع بين الأسلوبين ممكن وصائب في الغالب: استخدم paymentProxy لنقاط النهاية الرخيصة الموثوقة للقراءة فقط، وwithX402 للمكلفة التي تريد تعليق تسويتها على النجاح. فقط احرص ألّا يقع مسار تحت الاثنين معاً، وإلا دفع المشتري مرتين.
الخطوة 6: بناء المشتري
الآن الطرف الآخر. أنشئ مشروعاً صغيراً منفصلاً — أو مجلد scripts/ داخل المستودع نفسه — للوكيل الذي يستهلك الواجهة:
pnpm add @x402/fetch @x402/core @x402/evm viem dotenvيحتاج المشتري إلى موقّع، ومن هنا يأتي المفتاح الخاص. ضعه في ملف .env الخاص بالمشتري وحده، لا في تطبيق Next.js:
# buyer/.env
EVM_PRIVATE_KEY=0xthe_key_you_generated_in_step_2
RESOURCE_SERVER_URL=http://localhost:3000إعداد العميل يحاكي الخادم، مع إضافة موقّع:
// buyer/client.ts
import { wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { config } from "dotenv";
config();
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
export const fetchWithPayment = wrapFetchWithPayment(fetch, client);
export const httpClient = new x402HTTPClient(client);
export const payerAddress = signer.address;والاستدعاء نفسه يبدو كـ fetch عادي تماماً:
// buyer/main.ts
import { fetchWithPayment, httpClient, payerAddress } from "./client";
const base = process.env.RESOURCE_SERVER_URL!;
async function main() {
console.log("Paying from", payerAddress);
const response = await fetchWithPayment(`${base}/api/weather?city=Tunis`, {
method: "GET",
});
const result = await httpClient.processResponse(response);
console.log("Data:", result.body);
if (result.paymentStatus === "settled") {
console.log("Settlement receipt:", result.header);
}
}
main().catch((error) => {
console.error("Request failed:", error);
process.exit(1);
});شغّله وسترى سطر سجلّ واحداً، بينما وقعت في الحقيقة أربعة أحداث على مستوى HTTP: الطلب الأول، ثم 402، ثم التوقيع، ثم إعادة المحاولة. فـ wrapFetchWithPayment يستوعبها جميعاً. وبعدها تفكّ processResponse ترميز ترويسة PAYMENT-RESPONSE إلى إيصال تسوية — بصمة المعاملة والمبلغ والشبكة — وهو ما ينبغي أن تحفظه للمحاسبة.
أين يتحرّك المال: وقّع المشتري تفويضاً وفق EIP-3009، وقدّمه الميسّر ودفع رسوم الغاز. وعلى شبكة Base من الطبقة الثانية تبلغ تلك الرسوم نحو 0.001 دولار يتحمّلها الميسّر، ولا يضيف ميسّر Coinbase حالياً أي رسم فوقها. تصل USDC الخاصة بالمشتري إلى محفظة البائع خلال ثانية تقريباً، دون وسيط يحتجزها بينهما.
الخطوة 7: فرض حدود الإنفاق
وكيل يحمل مفتاحاً خاصاً وحلقة while وصفةٌ سريعة لخسارة المال. لا تُطلق مشترياً بلا سقف أبداً.
الضابط الأول بنيوي: موّل محفظة الوكيل بما يُسمح له بإنفاقه فقط. فمحفظة ساخنة تحوي 20 دولاراً من USDC تملك سقفاً صلباً لا يمكن تجاوزه مهما بلغ الخلل في شفرتك. أعد تعبئتها دورياً من محفظة خزينة لا يصل إليها الوكيل. هذا الإجراء وحده يتفوّق على كل حارس برمجي، لأنه لا يتوقّف على صحّة برنامجك.
الضابط الثاني ميزانية لكل عملية تشغيل. غلّف عملية الجلب الدافعة:
// buyer/budget.ts
import { fetchWithPayment, httpClient } from "./client";
const BUDGET_USD = Number(process.env.SESSION_BUDGET_USD ?? "0.50");
const MAX_PER_CALL_USD = Number(process.env.MAX_PER_CALL_USD ?? "0.01");
let spent = 0;
export class BudgetExceededError extends Error {}
export async function paidFetch(url: string, init?: RequestInit) {
if (spent >= BUDGET_USD) {
throw new BudgetExceededError(
`Session budget of $${BUDGET_USD} exhausted after $${spent.toFixed(4)}`,
);
}
// اسبر أولاً: الطلب غير المدفوع يكشف السعر دون الالتزام به.
const probe = await fetch(url, init);
if (probe.status === 402) {
const requirements = httpClient.parsePaymentRequired(probe);
const quoted = Math.max(
...requirements.accepts.map((a) => Number(String(a.price).replace("$", ""))),
);
if (quoted > MAX_PER_CALL_USD) {
throw new BudgetExceededError(
`Endpoint quoted $${quoted}, above the per-call cap of $${MAX_PER_CALL_USD}`,
);
}
if (spent + quoted > BUDGET_USD) {
throw new BudgetExceededError(
`Call would cost $${quoted}, exceeding the remaining budget`,
);
}
spent += quoted;
}
return fetchWithPayment(url, init);
}
export const spentSoFar = () => spent;يكلّف السبر رحلة ذهاب وإياب إضافية، وهي مقايضة عادلة مقابل معرفة السعر قبل التفويض به. فبدونه سيوقّع الوكيل بكل رضا على أي مبلغ يعرضه خادم خبيث أو سيّئ الإعداد — والخادم يستطيع أن يعرض ما يشاء.
ثلاثة ضوابط إضافية تستحقّ الإضافة في الإنتاج:
- سجّل كل تسوية. احفظ بصمة المعاملة والمبلغ ونقطة النهاية والطابع الزمني. ولأن التسوية تقع على السلسلة ولا رجعة فيها، تبقى سجلّاتك الموضع الوحيد الذي يوثّق سبب الإنفاق.
- قيّد الوجهات بقائمة سماح. الوكيل الذي يتتبّع الروابط يمكن توجيهه نحو نقطة نهاية يسيطر عليها مهاجم. حدّد المضيفات التي يُسمح لـ
paidFetchبالدفع لها. - خزّن مؤقتاً بسخاء. أرخص دفعة هي التي لا تدفعها. تخزين مؤقّت لستّين ثانية على نقطة نهاية الطقس يمحو معظم الإنفاق في حلقة وكيل ثرثارة.
الخطوة 8: عرض الواجهة المدفوعة على Claude عبر MCP
الغاية من هذا كلّه هي الوكلاء، فلنجعل أحدهم يستخدمها. يستطيع خادم MCP أن يغلّف نقطة النهاية المدفوعة ويقدّمها إلى Claude كأداة عادية — دون أن يرى النموذج الدفع إطلاقاً.
pnpm add @modelcontextprotocol/sdk @x402/axios @x402/evm axios viem dotenv// mcp/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import axios from "axios";
import { config } from "dotenv";
config();
const baseURL = process.env.RESOURCE_SERVER_URL ?? "http://localhost:3000";
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const api = wrapAxiosWithPayment(axios.create({ baseURL, timeout: 15_000 }), client);
const server = new McpServer({ name: "paid-weather", version: "1.0.0" });
server.tool(
"get_weather",
"Get the current weather for a city. Each call costs $0.001 in USDC.",
{ city: { type: "string", description: "City name, e.g. Tunis" } },
async ({ city }) => {
try {
const res = await api.get("/api/weather", { params: { city } });
return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
} catch (error) {
const message = axios.isAxiosError(error)
? `Weather lookup failed (${error.response?.status ?? "network"}): ${error.message}`
: String(error);
return { content: [{ type: "text", text: message }], isError: true };
}
},
);
await server.connect(new StdioServerTransport());سجّله في Claude Desktop:
{
"mcpServers": {
"paid-weather": {
"command": "pnpm",
"args": ["--silent", "-C", "/absolute/path/to/mcp", "dev"],
"env": {
"EVM_PRIVATE_KEY": "0xyour_testnet_key",
"RESOURCE_SERVER_URL": "http://localhost:3000"
}
}
}
}أعد تشغيل Claude Desktop، واسأله عن طقس تونس، فسيستدعي الأداة. وخلف استدعاء الأداة الواحد ذاك: رمز 402، وتوقيع وفق EIP-3009، وتحويل USDC على السلسلة، وإيصال تسوية — ولم يفكّر النموذج في أيّ منها.
ذكر الكلفة في وصف الأداة مقصود. فهو يمنح النموذج ما يحتاجه لتفادي الاستدعاءات المكرّرة بلا داعٍ، ولا يكلّفك شيئاً.
الخطوة 9: الانتقال إلى الشبكة الرئيسية
الانتقال من Base Sepolia إلى Base الرئيسية فرقٌ صغير في الشفرة وتحوّل كبير في العواقب. إليك قائمة التحقّق:
غيّر معرّف الشبكة أينما ورد — يصبح eip155:84532 هو eip155:8453. ولأنه بيانات موقّعة، فإن معرّفاً تجريبياً متبقّياً يُنتج مدفوعات مرفوضة لا رسالة خطأ واضحة.
وجّه إلى ميسّر للشبكة الرئيسية. فنقطة النهاية https://x402.org/facilitator تجريبية فقط. تشغّل منصة مطوّري Coinbase ميسّراً إنتاجياً على https://api.cdp.coinbase.com/platform/v2/x402 بتسوية بلا رسوم على Base وSolana؛ كما تشغّل PayAI بديلاً يغطي Base وSolana وPolygon. والميسّر اعتمادية قابلة للاستبدال — فهو لا يحتفظ بالأموال — لذا التبديل لاحقاً زهيد الكلفة.
انقل عنوان الاستقبال بعيداً عن مفتاح ساخن. عنوان البائع يستقبل فقط، فيمكن أن يكون محفظة عتادية أو محفظة متعدّدة التواقيع أو عنوان إيداع في منصة. لا سبب يدعو لأن يكون مفتاحاً قابعاً في .env.local.
موّل المشتري بـ USDC على الشبكة الرئيسية وقليل من ETH. فـ USDC التجريبية بلا قيمة على الشبكة الرئيسية، ويحتاج المشترون رصيد ETH صغيراً للحالات الحدّية التي يقدّمون فيها معاملاتهم بأنفسهم.
تأكّد أن تسعيرك يصمد أمام الواقع. على الشبكة التجريبية، 0.05 دولار للاستعلام مجرّد رقم في ملف إعداد. أما على الرئيسية فهو مال يتحرّك بلا رجعة، وبأي حجم يقرّره وكيل. تخيّل مستدعياً واحداً يرسل عشرة آلاف طلب في ساعة: أهذا دخل تسرّ به، أم بنية تحتية لا تقوى على خدمتها؟
الإعداد المرتبط بالبيئة يجعل هذا قابلاً للإدارة:
// x402.ts
const IS_PRODUCTION = process.env.NODE_ENV === "production";
export const NETWORK = IS_PRODUCTION ? "eip155:8453" : "eip155:84532";
const facilitatorClient = new HTTPFacilitatorClient({
url: IS_PRODUCTION
? "https://api.cdp.coinbase.com/platform/v2/x402"
: "https://x402.org/facilitator",
});اختبار التنفيذ
افحص شكل استجابة 402 أولاً. قبل إشراك أي محفظة، تأكّد أن الخادم يتحدّث البروتوكول:
curl -i http://localhost:3000/api/weather?city=Tunisتريد أن ترى HTTP/1.1 402 Payment Required وترويسة PAYMENT-REQUIRED. فكّ ترميزها لترى المتطلبات التي سيتصرّف المشتري بناءً عليها:
curl -sI http://localhost:3000/api/weather | grep -i payment-required | cut -d' ' -f2 | base64 -d | jqتحقّق أن network وpayTo وprice هي ما قصدته. وظهور payTo بقيمة undefined يعني أن متغيّر البيئة لم يُحمّل.
ثم شغّل المشتري من طرف إلى طرف وافحص المعاملة على BaseScan الخاص بـ Sepolia. ابحث عن عنوان الدافع؛ يجب أن يظهر تحويل USDC خلال ثوانٍ. هذا هو الدليل الوحيد على أن التسوية حدثت فعلاً لا أنها أُبلغت فحسب.
اختبر مسار الفشل. اجعل معالجك يُطلق استثناءً وتأكّد أن المشتري لا يُخصم منه عند استخدام withX402. فهذا هو الضمان الذي اخترت withX402 من أجله، فتحقّق منه بدل افتراضه.
اختبر حارس الميزانية. اضبط SESSION_BUDGET_USD=0.002 وكرّر الاستدعاء؛ يجب أن يُطلق الاستدعاء الثالث BudgetExceededError بدل الإنفاق.
استكشاف الأخطاء
ما زلت أتلقّى 402 بعد إرفاق PAYMENT-SIGNATURE. السبب في الغالب أحد ثلاثة: معرّف السلسلة في التوقيع لا يطابق ما في المتطلبات؛ أو المبلغ الموقّع أقلّ من المطلوب؛ أو رصيد USDC في محفظة الدافع غير كافٍ. جسم استجابة الخادم يحمل حقل error يسمّي السبب — اقرأه قبل التخمين.
"يعمل على Sepolia ويفشل على الشبكة الرئيسية". غيّرت رابط الميسّر دون معرّف الشبكة، أو العكس. لا بدّ أن يتحرّكا معاً. وتأكّد أيضاً أن المحفظة تحمل USDC على الشبكة الرئيسية — فرصيد الشبكة التجريبية لا ينتقل.
أخطاء بيئة Edge في الوسيط. التحقّق من الدفع يحتاج تشفير Node. أضف runtime: "nodejs" إلى تصدير config في الوسيط.
المشتري يوقّع لكن لا شيء يُسوّى. تأكّد أن رابط الميسّر قابل للوصول من خادمك لا من جهازك فقط. ففي نشر داخل حاويات، يكون الخروج الشبكي نحو الميسّر اعتمادية يجب السماح بها صراحة.
رفض الأسعار كقيم غير صالحة. بادئة $ إلزامية. فـ "0.001" خطأ تحقّق لا مرادف لـ "$0.001".
خصم مزدوج. المسار المغطّى بوسيط paymentProxy وبـ withX402 معاً سيطالب بدفعتين. اختر واحداً لكل مسار.
الخطوات التالية
- أضف تحليلات استخدام مفتاحها عنوان الدافع — فمع غياب الحسابات، يبقى عنوان المحفظة بُعدك الوحيد لتمييز المستدعين، وهو بُعد جيّد.
- استكشف نظام
uptoقيد التطوير حالياً، الذي يسوّي المبلغ النهائي بناءً على استخدام مقيس (الرموز المولّدة، الميغابايتات المنقولة) بدل سعر ثابت يُتّفق عليه مسبقاً. - اجمع x402 مع Web Bot Auth ومعيار RFC 9421 لتتمكّن من التعرّف على حركة الوكلاء وفرض رسوم عليها معاً.
- اقرأ درس بناء خادم MCP إن مرّت الخطوة الثامنة أسرع ممّا تحبّ.
- راجع ضوابط أمان وكلاء الذكاء الاصطناعي — فالوكيل القادر على الإنفاق يجعل حقن التعليمات مشكلة مالية لا معلوماتية فحسب.
الخلاصة
يُزيل x402 نموذج التسجيل من منتصف تجارة الواجهات البرمجية. البائع يعلن سعراً في إعداد مسار؛ والمشتري يوقّع ويُعيد المحاولة؛ والتسوية تتمّ خلال ثانية تقريباً برسوم تكاد تكون معدومة. ولا يحتفظ أيّ من الطرفين بحساب للآخر.
ولمن يُطلق واجهات برمجية في إنترنت يكتظّ بالوكلاء، يغيّر هذا اقتصاديات الذيل الطويل. فنقاط النهاية التي لم تكن لتبرّر خطة بـ 29 دولاراً شهرياً — استعلام واحد، تحويل مستند واحد، بيانات منطقة واحدة — تصبح مجدية بعُشر السنت للاستدعاء، لأن كلفة إجراء المعاملة هبطت أخيراً دون قيمة الطلب الواحد.
الهندسة هنا صغيرة بحقّ: إعداد خادم مشترك واحد، وغلاف واحد لكل مسار، وfetch مغلّف واحد لدى المشتري. العمل الحقيقي في الانضباط. موّل محافظ الوكلاء بما تحتمل خسارته فقط، واسبر الأسعار قبل التفويض بها، وسجّل كل تسوية، وتذكّر أن المدفوعات على السلسلة لا تعود. أتقن ذلك، وستستطيع أن تسلّم وكيلاً محفظة دون أن يقضّ ذلك مضجعك.