diff --git a/.env.example b/.env.example index 8222755..07e08d4 100644 --- a/.env.example +++ b/.env.example @@ -39,3 +39,7 @@ OIDC_CLIENT_ID= OIDC_CLIENT_SECRET= OIDC_REDIRECT_URI="${APP_URL}/auth/social/oidc/callback" +NORTH_CHECKOUT_ID= +NORTH_PROFILE_ID= +NORTH_PRIVATE_API_KEY= + diff --git a/app/Http/Controllers/NorthCheckoutController.php b/app/Http/Controllers/NorthCheckoutController.php new file mode 100644 index 0000000..941d72d --- /dev/null +++ b/app/Http/Controllers/NorthCheckoutController.php @@ -0,0 +1,153 @@ +firstOrFail(); + + // Check if already checked out + if (Checkout::where('bidder_num', $bidder->idbidders)->exists()) { + return redirect('/mywinnings?bidder_number=' . $bidder->bidder_assigned_number)->with('error', 'Bidder has already checked out.'); + } + + $winnings = WinningBids::where('winning_bidder_num', $bidder->idbidders)->get(); + $total_cost = $winnings->sum('winning_cost'); + + if ($total_cost <= 0) { + return redirect('/mywinnings?bidder_number=' . $bidder->bidder_assigned_number)->with('error', 'No winnings found for this bidder.'); + } + + return view('north_checkout', [ + 'bidder' => $bidder, + 'total_cost' => $total_cost, + ]); + } + + public function createSession(Request $request, $bidder_id) + { + $bidder = Bidders::findOrFail($bidder_id); + $winnings = WinningBids::where('winning_bidder_num', $bidder->idbidders)->get(); + $total_cost = $winnings->sum('winning_cost'); + + $apiKey = config('services.north.private_api_key'); + $checkoutId = config('services.north.checkout_id'); + $profileId = config('services.north.profile_id'); + + if (!$apiKey || !$checkoutId || !$profileId) { + return response()->json(['error' => 'North configuration missing.'], 500); + } + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apiKey, + ])->post('https://checkout.north.com/api/sessions', [ + 'checkoutId' => $checkoutId, + 'profileId' => $profileId, + 'amount' => $total_cost, + ]); + + if ($response->failed()) { + Log::error('North Session Creation Failed: ' . $response->body()); + return response()->json(['error' => 'Failed to create checkout session.'], 500); + } + + return response()->json($response->json()); + } + + public function verify(Request $request, $bidder_id) + { + $bidder = Bidders::findOrFail($bidder_id); + $sessionToken = $request->query('sessionToken'); + if (!$sessionToken) { + return redirect('/mywinnings?bidder_number=' . $bidder->bidder_assigned_number)->with('error', 'Missing session token.'); + } + + $apiKey = config('services.north.private_api_key'); + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apiKey, + ])->get('https://checkout.north.com/api/sessions/status', [ + 'sessionToken' => $sessionToken, + ]); + + if ($response->failed()) { + Log::error('North Session Verification Failed: ' . $response->body()); + return redirect('/mywinnings?bidder_number=' . $bidder->bidder_assigned_number)->with('error', 'Failed to verify payment status.'); + } + + $status = $response->json(); + + // The North API status check might return success/completed. + // Based on docs, we should check status. + if (isset($status['status']) && ($status['status'] === 'completed' || $status['status'] === 'success')) { + // Check if already checked out to avoid duplicates + $existingCheckout = Checkout::where('bidder_num', $bidder->idbidders)->first(); + + if (!$existingCheckout) { + $winnertotal = $status['amount'] ?? WinningBids::where('winning_bidder_num', $bidder->idbidders)->sum('winning_cost'); + $payment_method = 3; // Credit Card + $cc_transaction = $status['transactionId'] ?? 'NORTH_EC'; + $cc_amount = $status['amount'] ?? $winnertotal; + $check_number = null; + + $checkout_id = DB::table('checkout')->insertGetID( + [ + 'bidder_num' => $bidder->idbidders, + 'winnertotal' => $winnertotal, + 'payment_method' => $payment_method, + 'check_number' => $check_number, + 'cc_transaction' => $cc_transaction, + 'cc_amount' => $cc_amount, + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } else { + $checkout_id = $existingCheckout->checkout_id; + $payment_method = $existingCheckout->payment_method; + $cc_transaction = $existingCheckout->cc_transaction; + $check_number = $existingCheckout->check_number; + } + + // Replicate the data for checkout_complete view + $checkout_list_results = DB::select("SELECT + *, items.item_assigned_num, items.item_desc + FROM winning_bids + INNER JOIN items AS items + ON winning_bids.winning_item_num=items.iditems + WHERE winning_bidder_num = $bidder->idbidders + "); + + $checkout_info_results = DB::select("SELECT + winning_bids.*, + bidders.*, + sum(winning_cost) AS total_cost + FROM winning_bids + INNER JOIN bidders AS bidders + ON winning_bids.winning_bidder_num=bidders.idbidders + WHERE winning_bidder_num = $bidder->idbidders + GROUP BY winning_bids.winning_bidder_num + "); + + return view('checkout_complete', [ + 'checkout_result' => $checkout_id, + 'checkout_list_results' => $checkout_list_results, + 'checkout_info_results' => $checkout_info_results, + 'payment_method' => $payment_method, + 'check_number' => $check_number, + 'cc_transaction' => $cc_transaction + ]); + } + + return redirect('/mywinnings?bidder_number=' . $bidder->bidder_assigned_number)->with('error', 'Payment not completed. Status: ' . ($status['status'] ?? 'unknown')); + } +} diff --git a/app/Http/Controllers/PagesController.php b/app/Http/Controllers/PagesController.php index 5078e37..2838c2d 100644 --- a/app/Http/Controllers/PagesController.php +++ b/app/Http/Controllers/PagesController.php @@ -535,10 +535,13 @@ class PagesController extends Controller $total_cost = $winnings->sum('winning_cost'); + $is_checked_out = \App\Models\Checkout::where('bidder_num', $bidder->idbidders)->exists(); + return view('mywinnings_results', [ 'bidder' => $bidder, 'winnings' => $winnings, - 'total_cost' => $total_cost + 'total_cost' => $total_cost, + 'is_checked_out' => $is_checked_out ]); } } diff --git a/config/services.php b/config/services.php index 0b3b30c..230389e 100644 --- a/config/services.php +++ b/config/services.php @@ -42,4 +42,10 @@ return [ 'redirect' => env('OIDC_REDIRECT_URI'), ], + 'north' => [ + 'checkout_id' => env('NORTH_CHECKOUT_ID'), + 'profile_id' => env('NORTH_PROFILE_ID'), + 'private_api_key' => env('NORTH_PRIVATE_API_KEY'), + ], + ]; diff --git a/resources/views/mywinnings_results.blade.php b/resources/views/mywinnings_results.blade.php index 5ad93e3..1bea837 100644 --- a/resources/views/mywinnings_results.blade.php +++ b/resources/views/mywinnings_results.blade.php @@ -8,6 +8,24 @@
Winnings for Bidder #{{ $bidder->bidder_assigned_number }} - {{ $bidder->bidder_fname }} {{ $bidder->bidder_lname }}
+ @if (isset($error)) +
+ {{ $error }} +
+ @endif + + @if (session('error')) +
+ {{ session('error') }} +
+ @endif + + @if (session('success')) +
+ {{ session('success') }} +
+ @endif + @if($winnings->count() > 0) @@ -33,6 +51,16 @@
+ + @if(!$is_checked_out) +
+ Pay Now with Credit Card +
+ @else +
+ Checked Out! Your payment has been processed. +
+ @endif @else

No winning bids found for this bidder number.

@endif diff --git a/resources/views/north_checkout.blade.php b/resources/views/north_checkout.blade.php new file mode 100644 index 0000000..a0478e0 --- /dev/null +++ b/resources/views/north_checkout.blade.php @@ -0,0 +1,74 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
Checkout for Bidder #{{ $bidder->bidder_assigned_number }}
+ +
+

Total Amount Due: ${{ number_format($total_cost, 2) }}

+
+ +
+
+

Loading secure checkout...

+
+ Loading... +
+
+
+ +
+ +
+
+
+
+
+ + + +@endsection diff --git a/routes/web.php b/routes/web.php index ee3faf9..2355d21 100644 --- a/routes/web.php +++ b/routes/web.php @@ -27,6 +27,11 @@ Route::get('showscoresbycar', [ 'uses' => 'PagesController@showscoresbycar']); Route::get('mywinnings', [ 'uses' => 'PagesController@myWinnings']); Route::post('mywinnings', [ 'uses' => 'PagesController@myWinnings']); +// North Embedded Checkout +Route::get('north/checkout/{bidder_id}', [ 'uses' => 'NorthCheckoutController@checkout' ])->name('north.checkout'); +Route::post('north/session/{bidder_id}', [ 'uses' => 'NorthCheckoutController@createSession' ])->name('north.session'); +Route::get('north/verify/{bidder_id}', [ 'uses' => 'NorthCheckoutController@verify' ])->name('north.verify'); + Route::group(['middleware' => 'auth'], function() { Route::get('bidders', [ 'uses' => 'PagesController@bidders']); Route::post('bidders', [ 'uses' => 'PagesController@bidders']);