/home/kueuepay/public_html/app/Http/Controllers/User/CheckoutController.php
<?php

namespace App\Http\Controllers\User;

use Exception;
use Carbon\Carbon;
use App\Models\Card;
use App\Models\User;
use GuzzleHttp\Psr7\Uri;
use App\Models\Transaction;
use Jenssegers\Agent\Agent;
use App\Constants\CardConst;
use Illuminate\Http\Request;
use App\Models\TemporaryData;
use App\Constants\GlobalConst;
use App\Http\Helpers\Response;
use App\Models\MerchantDetails;
use App\Models\UserNotification;
use Illuminate\Support\Facades\DB;
use App\Models\Admin\BasicSettings;
use App\Http\Controllers\Controller;
use App\Constants\PaymentGatewayConst;
use App\Models\UserWallet;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Notification;
use App\Notifications\User\CardPaymentEmailNotification;

class CheckoutController extends Controller
{
    public $charge_payable = true;
    /**
     * Method for checkout index page
     * @param $order_id
     * @param Illuminate\Http\Request $request 
     */
    public function checkout(Request $request,$order_id){
        $page_title         = "Order Checkout";
        $order              = TemporaryData::where('identifier',$order_id)->first();
        $user               = User::with(['merchant_api_keys','merchant_details'])
                                ->where('id',auth()->user()->id)
                                ->first();
        if($order->data->merchant_api_key->env == GlobalConst::ENV_SANDBOX){
            $card_payments   = Card::where('type',CardConst::TEST)->orderBy('id','desc')->get();
        }else{
            $card_payments   = Card::auth()->orderBy('id','desc')->get();
        }
        
        
        $user_wallet     = UserWallet::auth()->with(['currency'])->first();                
            
        
        return view('order.pages.checkout',compact(
            'page_title',
            'order',
            'user',
            'card_payments',
            'user_wallet'
        ));        
    }
    /**
     * Method for submit checkout page information.Specifically: CardPayment
     * @param $order_id
     * @param Illuminate\Http\Request $request
     */
    public function submit(Request $request,$order_id){
        $basic_settings     = BasicSettings::first();
        
        $user   = User::with(['merchant_api_keys','merchant_details'])
                    ->where('id',auth()->user()->id)->first();
        
        if($user->kyc_verified != GlobalConst::VERIFIED) return back()->with(['error' => ['Sorry! You are not KYC verified user. Please verify your KYC.']]);
        $validator  = Validator::make($request->all(),[
            'card_payment'    => 'required',
            'type'            => 'required',
        ]);
        if($validator->fails()) return back()->withErrors($validator)->withInput($request->all());

        $validated      = $validator->validate();
        $order          = TemporaryData::where('identifier',$order_id)->first();
        if(!$order) return back()->with(['error' => ['Data not found!']]);
        if (Carbon::parse($order->data->expiration) < Carbon::now()) {
            $order->delete();
            return redirect()->away($order->data->cancel_url)->with(['error'=> ['Sorry! Link is expired.']]);
        }
        if(!$order) return back()->with(['error' => ['Order not found!']]);
        
        if($validated['type'] == GlobalConst::WALLET){
            $user_wallet     = UserWallet::auth()->where('id',$validated['card_payment'])->first();
            if(!$user_wallet) return back()->with(['error' => ['Wallet not found!']]);
            if($user_wallet->balance < $order->data->total_payable) return back()->with(['error' => ['Insufficient wallet balance!']]);
            $status         = PaymentGatewayConst::STATUSSUCCESS;
            $type           = GlobalConst::WALLET;
            $card_payment   = null;
            $charges        = null;
            $wallet_id      = $user_wallet->id;

            $receiver_info  = MerchantDetails::where('merchant_id',$order->data->merchant_account->merchant_id)->first();
            $merchant_wallet = UserWallet::where('user_id',$receiver_info->user_id)->first();
            $merchant       = User::where('id',$receiver_info->user_id)->first();
            $merchant_wallet_id = $merchant_wallet->id;
            
            try{
                $attribute  = PaymentGatewayConst::SEND;
                $this->transactionInformation($user,$card_payment,$order,$status,$basic_settings,$charges,$wallet_id,$type,$attribute,$merchant);

                $this->transactionReceiveInformation($merchant,$card_payment,$order,$status,$basic_settings,$charges,$merchant_wallet_id,$type,$attribute  = PaymentGatewayConst::RECEIVED,$sender_info = $user);
    
                $user_wallet->update([
                    'balance'   => $user_wallet->balance - $order->data->total_payable
                ]);

                $merchant_wallet->update([
                    'balance'   => $merchant_wallet->balance + $order->data->amount
                ]);

            }catch(Exception $e){
                return back()->with(['error' => ['Something went wrong!']]);
            }
            $success_url    = $order->data->success_url;
            
            return redirect()->away($success_url)->with(['success' => ['Payment Successfully Done.']]);
            
        }else{
            $card_payment   = Card::where('id',$validated['card_payment'])->first();
            if(!$card_payment) return back()->with(['error'=> ['Card Payment not found!']]);
            
            $merchant_stripe_secret = $order->data->merchant_api_key->stripe_secret;
            $receiver_info  = MerchantDetails::where('merchant_id',$order->data->merchant_account->merchant_id)->first();
            $merchant       = User::where('id',$receiver_info->user_id)->first();
            $card_number    = decrypt($card_payment->card_number);
            $expiry_date    = decrypt($card_payment->expiry_date);
            $expiry_month   = trim(explode("/",$expiry_date)[0]);
            $expiry_year    = trim(explode("/",$expiry_date)[1]);
            $card_cvc       = decrypt($card_payment->card_cvc);
            if($card_payment->type == card_payment_const()::TEST){
                $status     = PaymentGatewayConst::STATUSTEST;
            }else{
                $status     = PaymentGatewayConst::STATUSSUCCESS;
            }
            $trx_id         = generateTrxString("transactions","trx_id","CP",8);
          
            try{
                $stripe             = new \Stripe\StripeClient($merchant_stripe_secret);
                $response           = $stripe->tokens->create([
                    'card'          => [
                        'number'    => $card_number,
                        'exp_month' => $expiry_month,
                        'exp_year'  => $expiry_year,
                        'cvc'       => $card_cvc,
                    ],
                ]);
                
                if($this->charge_payable == true){
                    
                    $charges           =  $stripe->charges->create([
                        'amount'       => $order->data->total_payable * 100,
                        'currency'     => 'usd',
                        'source'       => $response->id,
                    ]);
                    
                    if($charges->status == 'succeeded'){
                        $wallet = null;
                        $type   = "card";
                        $attribute  = PaymentGatewayConst::SEND;
                        
                        $this->transactionInformation($user,$card_payment->id,$order,$status,$basic_settings,$charges,$wallet,$type,$attribute,$merchant);
                    }else{
                        return back()->with(['error' => ['Something went wrong! Please try again.']]);
                    }
                }else{
                    $wallet     = null;
                    $type       = "card";
                    $attribute  = PaymentGatewayConst::SEND;
                    $this->transactionInformation($user,$card_payment->id,$order,$status,$basic_settings,$charges=null,$wallet,$type,$attribute,$merchant);
                }
            }catch (Exception $e) {
               return back()->with(['error' => [$e->getMessage()]]);
            }
            $transaction_data   = Transaction::where('trx_id',$trx_id)->first();
            if($transaction_data == null){
                $wallet = null;
                $type   = "card";
                $attribute  = PaymentGatewayConst::SEND;
                $this->transactionInformation($user,$card_payment->id,$order,$status,$basic_settings,$charges,$wallet,$type,$attribute,$merchant);
            }
            $success_url    = $order->data->success_url;
            if(filter_var($success_url,FILTER_VALIDATE_URL)){
                $url        = new Uri($success_url);
                if($this->charge_payable == true){
                    $token  = $charges->id;
                }else{
                    $token  = $response->id;
                }
                $new_url                = $url->withQuery(http_build_query([
                    'token'             => $token,
                    'transaction_id'    => $trx_id
                ]));
                return redirect()->away($new_url)->with(['success' => ['Payment Successfully Done.']]);
            }else {
                return redirect()->away($order->data->cancel_url)->with(['error'    => ['Success Url Not found.']]);
            }
        }
    }
    // save the transaction information
    function transactionInformation($user,$card_payment,$order,$status,$basic_settings,$charges,$wallet,$type,$attribute,$merchant){
        $trx_id         = generateTrxString("transactions","trx_id","CP",8);
        $data                       = [
            'trx_id'                => $trx_id,
            'type'                  => PaymentGatewayConst::CARD_PAYMENT,
            'user_id'               => $user->id,
            'user_wallet_id'        => $wallet,
            'card_id'               => $card_payment,
            'payment_type'          => $type,
            'payable_charge'        => $this->charge_payable,
            'request_amount'        => $order->data->amount,
            'fixed_charge'          => $order->data->fixed_charge,
            'percent_charge'        => $order->data->percent_charge,
            'total_charge'          => $order->data->total_charge,
            'total_payable'         => $order->data->total_payable,
            'request_currency'      => $order->data->currency,
            'available_balance'     => null,
            'payment_currency'      => $order->data->currency,
            'remark'                => ucwords(remove_special_char("Order Placed"," ")) . " Using" . " " . $type,
            'details'               => [
                'token'             => $order->data->token,
                'order'             => $order->data,
                'payment_info'      => $charges,
                'receiver_info'     => [
                    'name'          => $merchant->fullname,
                    'email'          => $merchant->email,
                ]
                
            ],
            'attribute'             => $attribute,
            'status'                => $status,
        ];
        
        $transaction = Transaction::create($data);
        $this->transactionDevice($transaction->id);
        $this->userNotification($user->id,$transaction->id,$order->data->amount,$order->data->currency,$type);
        if($basic_settings->email_notification == true){
            try{
                Notification::route('mail',$user->email)->notify(new CardPaymentEmailNotification($transaction->id,$user));
            }catch(Exception $e){}
        }
        $order->delete();
    }
    // transaction received data
    function transactionReceiveInformation($user,$card_payment,$order,$status,$basic_settings,$charges,$wallet,$type,$attribute,$sender_info){
        $trx_id         = generateTrxString("transactions","trx_id","CP",8);
        $data                       = [
            'trx_id'                => $trx_id,
            'type'                  => PaymentGatewayConst::CARD_PAYMENT,
            'user_id'               => $user->id,
            'user_wallet_id'        => $wallet,
            'card_id'               => $card_payment,
            'payment_type'          => $type,
            'payable_charge'        => $this->charge_payable,
            'request_amount'        => $order->data->amount,
            'fixed_charge'          => $order->data->fixed_charge,
            'percent_charge'        => $order->data->percent_charge,
            'total_charge'          => $order->data->total_charge,
            'total_payable'         => $order->data->total_payable,
            'request_currency'      => $order->data->currency,
            'available_balance'     => null,
            'payment_currency'      => $order->data->currency,
            'remark'                => ucwords(remove_special_char("Order Received"," ")) . " Using" . " " . $type,
            'details'               => [
                'token'             => $order->data->token,
                'order'             => $order->data,
                'payment_info'      => $charges,
                'sender_info'       => [
                    'name'          => $sender_info->fullname,
                    'email'         => $sender_info->email
                ],
                
            ],
            'attribute'             => $attribute,
            'status'                => $status,
        ];
        
        $transaction = Transaction::create($data);
        $this->transactionDevice($transaction->id);
        $this->userNotification($user->id,$transaction->id,$order->data->amount,$order->data->currency,$type);
        if($basic_settings->email_notification == true){
            try{
                Notification::route('mail',$user->email)->notify(new CardPaymentEmailNotification($transaction->id,$user));
            }catch(Exception $e){}
        }
        $order->delete();
    }

    // save the transaction device information
    function transactionDevice($id){
        $client_ip = request()->ip() ?? false;
        $location = geoip()->getLocation($client_ip);
        $agent = new Agent();
        $mac = "";
        DB::beginTransaction();
        try{
            DB::table("transaction_devices")->insert([
                'transaction_id'=> $id,
                'ip'            => $client_ip,
                'mac'           => $mac,
                'city'          => $location['city'] ?? "",
                'country'       => $location['country'] ?? "",
                'longitude'     => $location['lon'] ?? "",
                'latitude'      => $location['lat'] ?? "",
                'timezone'      => $location['timezone'] ?? "",
                'browser'       => $agent->browser() ?? "",
                'os'            => $agent->platform() ?? "",
            ]);
            DB::commit();
        }catch(Exception $e) {
            DB::rollBack();
            throw new Exception($e->getMessage());
        }
    }
    //user notification
    public function userNotification($user_id,$id,$amount,$currency,$type){
        UserNotification::create([
            'user_id'           => $user_id,
            'transaction_id'    => $id,
            'details'           => [
                'title'         => 'Your Order Using ' . $type,
                'amount'        => floatval($amount),
                'currency'      => $currency,
                'message'       => "Successfully placed."
            ],
        ]);
    }
    /**
     * Method for retrieve the payment information
     * @param $id
     * @param Illuminate\Http\Request $request
     */
    public function details(Request $request){
        $transaction        = Transaction::auth()->with(['user'])->where('trx_id',$request->transaction_id)->first();
        if(!$transaction) return Response::error(['Transaction data not found'],[],400);
        $response           = [
            'token'         => $transaction->details->token ?? '',
            'trx_id'        => $transaction->trx_id ?? '',
            'payer'         => [
                'username'  => $transaction->user->username,
                'email'     => $transaction->user->email,
            ],
            'status'        => $this->getStatusBadge($transaction->status)
        ];
        return Response::success(['Success'],[
            'data'  => $response
        ],200);
    }
    // get the status data
    function getStatusBadge($status) {
        if ($status == PaymentGatewayConst::STATUSPENDING) {
            $statusText     = "Pending";
            return $statusText;
        } else if ($status == PaymentGatewayConst::STATUSSUCCESS) {
            $statusText     = "Success";
            return $statusText;
        } else if ($status == PaymentGatewayConst::STATUSREJECTED) {
            $statusText     = "Rejected";
            return $statusText;
        } else if ($status == PaymentGatewayConst::STATUSWAITING) {
            $statusText     = "Waiting";
            return $statusText;
        } else if ($status == PaymentGatewayConst::STATUSHOLD) {
            $statusText     = "Hold";
            return $statusText;
        } else {
            $statusText     = "Test";
            return $statusText;
        }
    }
    
}
Forgot Password