diff --git a/app/Http/Controllers/StripeController.php b/app/Http/Controllers/StripeController.php new file mode 100644 index 0000000..09abb24 --- /dev/null +++ b/app/Http/Controllers/StripeController.php @@ -0,0 +1,125 @@ + ['card'], + 'line_items' => [ + [ + 'price_data' => [ + 'currency' => 'usd', + 'product_data' => [ + 'name' => "Invoice {$invoice->invoice_number}", + ], + 'unit_amount' => (int) ($invoice->balance_due * 100), + ], + 'quantity' => 1, + ], + ], + 'mode' => 'payment', + 'success_url' => route('stripe.success', $invoice), + 'cancel_url' => route('invoices.show', $invoice), + 'metadata' => [ + 'invoice_id' => $invoice->id, + ], + ]); + + return redirect($session->url); + } + + public function webhook(Request $request) + { + $payload = $request->getContent(); + $signature = $request->header('Stripe-Signature'); + + try { + $event = \Stripe\Webhook::constructEvent( + $payload, + $signature, + config('services.stripe.webhook_secret') + ); + } catch (\Exception $e) { + return response('Invalid signature', 400); + } + + if ($event->type === 'checkout.session.completed') { + $session = $event->data->object; + + $invoice = Invoice::find($session->metadata->invoice_id); + + if ($invoice) { + Stripe::setApiKey(config('services.stripe.secret')); + + // Retrieve PaymentIntent with expanded charge and balance_transaction + $paymentIntent = \Stripe\PaymentIntent::retrieve([ + 'id' => $session->payment_intent, + 'expand' => ['latest_charge.balance_transaction'], + ]); + + $feeAmount = $paymentIntent->latest_charge->balance_transaction->fee; + + Payment::create([ + 'invoice_id' => $invoice->id, + 'payment_date' => now(), + 'status' => PaymentStatus::COMPLETED, + 'payment_method' => PaymentMethod::CARD, + 'reference' => $session->payment_intent, + 'stripe_payment_intent_id' => $session->payment_intent, + 'amount' => $session->amount_total / 100, + 'fee_amount' => $feeAmount / 100, + ]); + } + } + + return response('OK', 200); + } + + public function checkoutTutorial() + { + Stripe::setApiKey(config('stripe.sk')); + + $session = \Stripe\Checkout\Session::create([ + 'line_items' => [ + [ + 'price_data' => [ + 'currency' => 'usd', + 'product_data' => [ + 'name' => 'send me money', + ], + 'unit_amount' => 3250, // in cents + ], + 'quantity' => 1, + ], + ], + 'mode' => 'payment', + 'success_url' => route('stripe.success'), + 'cancel_url' => route('stripe.index'), + ]); + + return redirect()->away($session->url); + } + + public function success(Invoice $invoice) + { + return view('stripe.success', compact('invoice')); + } +} diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..184e281 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,8 +11,12 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - // + $middleware->validateCsrfTokens(except: [ + 'stripe/webhook', + ]); }) + + ->withExceptions(function (Exceptions $exceptions): void { // })->create(); diff --git a/composer.json b/composer.json index 956c553..8aa6241 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "laravel/framework": "^12.49", "laravel/tinker": "^2.11.0", "livewire/flux": "^2.11.1", - "livewire/livewire": "^4.1" + "livewire/livewire": "^4.1", + "stripe/stripe-php": "^19.3" }, "require-dev": { "fakerphp/faker": "^1.24.1", diff --git a/composer.lock b/composer.lock index 502d044..631ee18 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aa16a4acb2dc4a2a7bb2eb1dd04b1907", + "content-hash": "fae50eff8a59cae5ee7908635763dacf", "packages": [ { "name": "bacon/bacon-qr-code", @@ -3721,6 +3721,65 @@ }, "time": "2025-12-14T04:43:48+00:00" }, + { + "name": "stripe/stripe-php", + "version": "v19.3.0", + "source": { + "type": "git", + "url": "https://github.com/stripe/stripe-php.git", + "reference": "462272ae7560ee29bb891763fd0967d5a77784e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/462272ae7560ee29bb891763fd0967d5a77784e5", + "reference": "462272ae7560ee29bb891763fd0967d5a77784e5", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.72.0", + "phpstan/phpstan": "^1.2", + "phpunit/phpunit": "^5.7 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Stripe\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stripe and contributors", + "homepage": "https://github.com/stripe/stripe-php/contributors" + } + ], + "description": "Stripe PHP Library", + "homepage": "https://stripe.com/", + "keywords": [ + "api", + "payment processing", + "stripe" + ], + "support": { + "issues": "https://github.com/stripe/stripe-php/issues", + "source": "https://github.com/stripe/stripe-php/tree/v19.3.0" + }, + "time": "2026-01-28T21:15:45+00:00" + }, { "name": "symfony/clock", "version": "v8.0.0", diff --git a/config/services.php b/config/services.php index 6a90eb8..1c2ef58 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,9 @@ return [ ], ], + 'stripe' => [ + 'secret' => env('STRIPE_SK'), + 'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'), + ], + ]; diff --git a/config/stripe.php b/config/stripe.php new file mode 100644 index 0000000..d6ac570 --- /dev/null +++ b/config/stripe.php @@ -0,0 +1,6 @@ + env('STRIPE_SK'), + 'pk' => env('STRIPE_PK'), +]; diff --git a/resources/views/invoices/show.blade.php b/resources/views/invoices/show.blade.php index ccb6fbb..0ea62d9 100644 --- a/resources/views/invoices/show.blade.php +++ b/resources/views/invoices/show.blade.php @@ -137,12 +137,33 @@ @endif @if($invoice->balance_due != 0) -
Please make payment to:
-eBandroom
-540 W. Louse Ave.
-Vinita, OK 74301
+Pay securely with your credit or debit card.
+ +Make check payable to:
+eBandroom
+540 W. Louse Ave.
+Vinita, OK 74301
+Thank you for your payment.
+ +Invoice
+{{ $invoice->invoice_number }}
+{{ $invoice->client->name }}
+