mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 03:01:34 +00:00
136 lines
5.7 KiB
PHP
136 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Payment;
|
|
use App\Models\User;
|
|
use App\Services\AdminDashboardSnapshotService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class BtcPaymentController extends BasePageController
|
|
{
|
|
/**
|
|
* BtcPay callback.
|
|
*/
|
|
public function btcPayCallback(Request $request): Response
|
|
{
|
|
$hashCheck = 'sha256='.hash_hmac('sha256', $request->getContent(), config('nntmux.btcpay_webhook_secret'));
|
|
if ($hashCheck !== $request->header('btcpay-sig')) {
|
|
Log::channel('btc_payment')->error('BTCPay webhook hash check failed: '.$request->header('btcpay-sig'));
|
|
|
|
return response('Not Found', 404);
|
|
}
|
|
$payload = json_decode($request->getContent(), true);
|
|
// We have received a payment for an invoice and user should be upgraded to a paid plan based on order
|
|
if ($payload['type'] === 'InvoicePaymentSettled') {
|
|
$user = User::query()->where('email', '=', $payload['metadata']['buyerEmail'])->first();
|
|
if ($user) {
|
|
$checkOrder = Payment::query()->where('invoice_id', '=', $payload['invoiceId'])->where('payment_status', '=', Payment::PAYMENT_STATUS_SETTLED)->first();
|
|
if ($checkOrder !== null) {
|
|
Log::channel('btc_payment')->error('Duplicate BTCPay webhook: '.$payload['webhookId']);
|
|
|
|
return response('OK', 200);
|
|
}
|
|
|
|
Payment::create([
|
|
'email' => $payload['metadata']['buyerEmail'],
|
|
'username' => $user->username,
|
|
'item_description' => $payload['metadata']['itemDesc'],
|
|
'order_id' => $payload['metadata']['orderId'],
|
|
'payment_id' => $payload['payment']['id'],
|
|
'payment_status' => $payload['payment']['status'],
|
|
'invoice_amount' => $payload['metadata']['invoice_amount'],
|
|
'payment_method' => $payload['paymentMethodId'],
|
|
'payment_value' => $payload['payment']['value'],
|
|
'webhook_id' => $payload['webhookId'],
|
|
'invoice_id' => $payload['invoiceId'],
|
|
]);
|
|
|
|
Cache::forget(AdminDashboardSnapshotService::CACHE_KEY);
|
|
|
|
return response('OK', 200);
|
|
}
|
|
|
|
Log::channel('btc_payment')->error('User not found for BTCPay webhook: '.$payload['metadata']['buyerEmail']);
|
|
|
|
return response('Not Found', 404);
|
|
}
|
|
|
|
if ($payload['type'] === 'InvoiceSettled') {
|
|
// Check if we have the invoice_id in payments table and if we do, update the user account
|
|
$checkOrder = Payment::query()->where('invoice_id', '=', $payload['invoiceId'])->where('payment_status', '=', Payment::PAYMENT_STATUS_SETTLED)->where(function ($query) {
|
|
return $query->where('invoice_status', Payment::INVOICE_STATUS_PENDING)->orWhereNull('invoice_status');
|
|
})->first();
|
|
if ($checkOrder !== null) {
|
|
$user = User::query()->where('email', '=', $checkOrder->email)->first();
|
|
|
|
// If user not found and email ends with pm.me, try proton.me and protonmail.com
|
|
if (! $user && str_ends_with($checkOrder->email, '@pm.me')) {
|
|
$emailLocalPart = substr($checkOrder->email, 0, -6); // Remove @pm.me
|
|
$user = User::query()->where('email', '=', $emailLocalPart.'@proton.me')->first();
|
|
if (! $user) {
|
|
$user = User::query()->where('email', '=', $emailLocalPart.'@protonmail.com')->first();
|
|
}
|
|
}
|
|
if ($user) {
|
|
// Extract role name and addYears from item_description using regex
|
|
// Matches patterns like "User 1", "Admin ++ 2", "Friend 3"
|
|
if (preg_match('/(?P<role>\w+(\s\+\+)?)[\s]+(?P<addYears>\d+)/i', $checkOrder->item_description, $matches)) {
|
|
$roleName = $matches['role'];
|
|
$addYears = (int) $matches['addYears'];
|
|
} else {
|
|
$roleName = $checkOrder->item_description;
|
|
$addYears = null;
|
|
}
|
|
|
|
User::updateUserRole($user->id, $roleName, addYears: $addYears);
|
|
$checkOrder->update(['invoice_status' => Payment::INVOICE_STATUS_SETTLED]);
|
|
Log::channel('btc_payment')->info('User: '.$user->username.' upgraded to '.$roleName.' (+'.$addYears.' years) for BTCPay webhook: '.$checkOrder->webhook_id);
|
|
|
|
return response('OK', 200);
|
|
}
|
|
|
|
Log::channel('btc_payment')->error('User not found for BTCPay webhook: '.$checkOrder->webhook_id);
|
|
|
|
return response('Not Found', 404);
|
|
}
|
|
}
|
|
|
|
return response('OK', 200);
|
|
}
|
|
|
|
/**
|
|
* Payment.status values from BTCPay Greenfield API (payload.payment.status). Used for admin filters.
|
|
*
|
|
* @return list<string>
|
|
*/
|
|
public static function paymentStatusesForAdminFilter(): array
|
|
{
|
|
return [
|
|
'Pending',
|
|
'Processing',
|
|
Payment::PAYMENT_STATUS_SETTLED,
|
|
'Invalid',
|
|
'Manual',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* invoice_status values used in our DB (migration default Pending; webhook sets Settled after role upgrade).
|
|
*
|
|
* @return list<string>
|
|
*/
|
|
public static function invoiceStatusesForAdminFilter(): array
|
|
{
|
|
return [
|
|
Payment::INVOICE_STATUS_PENDING,
|
|
Payment::INVOICE_STATUS_SETTLED,
|
|
];
|
|
}
|
|
}
|