پرداخت ماشینی روی HTTP با MPP و لاراول
خلاصهٔ کاملتر
«تجارت عاملی» این روزها معنیهای خیلی متفاوتی داره — از دستیاری که برات کاپشن میخره تا ایجنتی که پول یه فراخوانی API رو میده — ولی به گفتهٔ نویسنده همهشون یه تغییر مشترک دارن: خریدار دیگه لزوماً آدمی پشت مرورگر نیست که دکمهٔ پرداخت رو بزنه. پروتکلهای سطح بالاتر مثل UCP سراغ کشف محصول، سبد خرید و checkout میرن، ولی Machine Payments Protocol پایینتر میشینه: نه ویترین فروشگاهه، نه کاتالوگ، بلکه یه پریمیتیو پرداخت برای منابع HTTP هست.
حلقهاش عمداً کوچیکه: کلاینت منبع رو میخواد، سرور با 402 Payment Required جواب میده و قیمت، روش پرداخت، دامنهٔ اعتبار و انقضا رو امضاشده اعلام میکنه، کلاینت اعتبارنامهٔ پرداخت رو میگیره و درخواست رو با هدر Authorization: Payment تکرار میکنه، و سرور فقط بعد از تأیید تسویه منبع رو میده. هیچ صفحهٔ checkout میزبانیشدهای وسط این حلقه نیست. پیادهسازی نمونه، پکیج square1/laravel-mpp روی PHP 8.4 و Laravel 12 یا 13 هست که قیمت رو مستقیم روی روت میذاره:
Route::get('/resource', fn () => response()->json([
'result' => 'some paid data',
]))->middleware('mpp:1.00,USD');مهمترین بخش پاسخ ۴۰۲، همون امضاست، چون شرایط اقتصادی معامله رو قفل میکنه: شناسهٔ چالش، مبلغ، ارز، روش، تعداد دسترسی و انقضا. یعنی کلاینت نمیتونه برای یه درخواست یکدلاری پرداخت دهسنتی بسازه و انتظار قبول شدن داشته باشه. شکل امضا به ریل پرداخت بستگی داره؛ Stripe اطلاعات کامل accepts رو میفرسته و Tempo فقط challengeId. سمت سرور هم قبل از اجرای کنترلر باید چالش رو پیدا کنه، انقضا و مصرفنشدنش رو چک کنه، امضا رو تأیید کنه، قفل تسویه بگیره، تسویه رو انجام بده و آخرش هدر Payment-Receipt رو بچسبونه.
به گفتهٔ نویسنده سختترین بخش، همون شکست همیشگی اینترنته: سرور کار رو انجام داده ولی کلاینت جواب رو ندیده. توی یه API پولی، اون retry میتونه به شارژ دوم تبدیل بشه. دفاع سهلایهست: چالش یکبارمصرفه و بعد از تسویهٔ موفق سوزونده میشه، مسیر تسویه با قفل کش محافظت میشه تا دو درخواست همزمان با هم به ریل نرسن، و در نهایت شناسهٔ چالش بهعنوان کلید idempotency به Stripe داده میشه:
$paymentIntent = $this->client()->paymentIntents->create($params, [
'idempotency_key' => $challenge->id,
]);برای روتهای کنتوردار، یه پرداخت چند دسترسی میده و سرور موجودی باقیمونده رو نگه میداره و اتمی ازش کم میکنه؛ برای همین نویسنده تأکید میکنه کش تکسروری فقط به درد دمو میخوره و توی محیط واقعی باید سراغ Redis یا درایور دیتابیس بری. یه مرز مهم هم هست: این middleware فقط پرداخت رو idempotent میکنه، نه تحویل محصول رو. اگه خروجی یه فایل یکبارمصرف یا گزارش سنگینه، خودت باید رکورد تحویل رو توی مدل دامنهٔ اپلیکیشن نگه داری.
نکات کلیدی:
- MPP یه پریمیتیو پرداخت برای منابع HTTP هست، نه ویترین یا کاتالوگ؛ لایههای بالاتر مثل UCP کار دیگهای میکنن
- حلقه: درخواست، پاسخ ۴۰۲ با شرایط امضاشده، تکرار درخواست با هدر پرداخت، و تحویل فقط بعد از تسویه
- پکیج laravel-mpp قیمت رو با middleware، اتریبیوت #[RequiresPayment] یا price book توی کانفیگ میگیره
- Shared Payment Token خودِ پرداخت نیست؛ یه اعتبارنامهٔ محدوده و سرور باید PaymentIntent رو بسازه و مبلغ و ارز رو با چالش امضاشده تطبیق بده
- چک وجود منبع باید قبل از گیت پرداخت اجرا بشه، وگرنه کلاینت پول میده و آخرش ۴۰۴ میگیره




