feature: implement stripe webhook
This commit is contained in:
parent
a51a2cd436
commit
14cb5a36ae
27
backend/app/Actions/MarkPaymentAsPaidAction.php
Normal file
27
backend/app/Actions/MarkPaymentAsPaidAction.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Enums\PaymentStatusEnum;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentStatus;
|
||||||
|
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||||
|
|
||||||
|
final readonly class MarkPaymentAsPaidAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
*
|
||||||
|
* @throws NotFoundResourceException
|
||||||
|
*/
|
||||||
|
public function execute(Payment $payment): bool
|
||||||
|
{
|
||||||
|
$status = PaymentStatus::whereName(PaymentStatusEnum::Paid->value)->value('id');
|
||||||
|
if (! $status) {
|
||||||
|
throw new NotFoundResourceException('Paid Status not found');
|
||||||
|
}
|
||||||
|
$payment->payment_status_id = $status;
|
||||||
|
|
||||||
|
return $payment->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
backend/app/Enums/StripeEventType.php
Normal file
8
backend/app/Enums/StripeEventType.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum StripeEventType: string
|
||||||
|
{
|
||||||
|
case CheckoutSessionCompleted = 'checkout.session.completed';
|
||||||
|
}
|
||||||
55
backend/app/Http/Controllers/StripeWebhookController.php
Normal file
55
backend/app/Http/Controllers/StripeWebhookController.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\MarkPaymentAsPaidAction;
|
||||||
|
use App\Enums\StripeEventType;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Log;
|
||||||
|
use Stripe\Exception\SignatureVerificationException;
|
||||||
|
use Stripe\Webhook;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
class StripeWebhookController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(private readonly MarkPaymentAsPaidAction $paidAction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
$payload = $request->getContent();
|
||||||
|
$sigHeader = $request->header('Stripe-Signature');
|
||||||
|
try {
|
||||||
|
$event = Webhook::constructEvent($payload, $sigHeader, config('services.stripe.webhook'));
|
||||||
|
} catch (SignatureVerificationException|UnexpectedValueException $e) {
|
||||||
|
Log::error('Stripe webhook signature verification error.', [$e->getMessage()]);
|
||||||
|
throw new BadRequestHttpException('Invalid Signature');
|
||||||
|
}
|
||||||
|
if ($event->type === StripeEventType::CheckoutSessionCompleted->value) {
|
||||||
|
$sessionId = $event->data->object->id ?? null;
|
||||||
|
if ($sessionId) {
|
||||||
|
$this->handleCheckoutSessionCompleted($sessionId);
|
||||||
|
} else {
|
||||||
|
throw new NotFoundHttpException('Session id not found in event');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleCheckoutSessionCompleted(string $sessionId): void
|
||||||
|
{
|
||||||
|
$payment = Payment::where('transaction_id', $sessionId)->first();
|
||||||
|
|
||||||
|
if (! $payment) {
|
||||||
|
Log::error('Stripe Webhook: Payment record not found.', ['session_id' => $sessionId]);
|
||||||
|
throw new NotFoundHttpException('Payment record not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->paidAction->execute($payment);
|
||||||
|
|
||||||
|
Log::info('Stripe Webhook: Payment successfully marked as paid', ['order_id' => $payment->order_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@
|
|||||||
use App\Http\Controllers\ProductController;
|
use App\Http\Controllers\ProductController;
|
||||||
use App\Http\Controllers\ProductImagesController;
|
use App\Http\Controllers\ProductImagesController;
|
||||||
use App\Http\Controllers\RegisteredUserController;
|
use App\Http\Controllers\RegisteredUserController;
|
||||||
|
use App\Http\Controllers\StripeWebhookController;
|
||||||
use App\Http\Controllers\UserAddressController;
|
use App\Http\Controllers\UserAddressController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@ -34,3 +35,5 @@
|
|||||||
});
|
});
|
||||||
Route::get('/categories', [ProductCategoryController::class, 'index']);
|
Route::get('/categories', [ProductCategoryController::class, 'index']);
|
||||||
Route::apiResource('products', ProductController::class);
|
Route::apiResource('products', ProductController::class);
|
||||||
|
|
||||||
|
Route::post('/webhook/stripe', StripeWebhookController::class);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user