<?php namespace App\Http\Controllers; use App\Models\PaymentItem; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\ProductionEnvironment; use PayPalCheckoutSdk\Core\SandboxEnvironment; use PayPalCheckoutSdk\Orders\OrdersCreateRequest; use PayPalCheckoutSdk\Orders\OrdersCaptureRequest; use PayPalHttp\HttpException; use App\Models\Checkout; use App\Models\Invoice; use App\Models\Payment; class PaypalController extends Controller { private $client; private $o = NULL; // Create a new instance with our paypal credentials public function __construct() { if (config('paypal.settings.mode') == 'sandbox') $environment = new SandboxEnvironment(config('paypal.sandbox_client_id'),config('paypal.sandbox_secret')); else $environment = new ProductionEnvironment(config('paypal.live_client_id'),config('paypal.live_secret')); $this->client = new PayPalHttpClient($environment); $this->o = Checkout::where('name','paypal')->firstOrFail(); } public function cancel(Request $request) { return redirect()->to('u/invoice/cart'); } /** * Authorize a paypal payment, and redirect the user to pay. * * @param Request $request * @return \Illuminate\Http\RedirectResponse */ public function authorise(Request $request) { $currency = 'AUD'; // @todo TO determine from DB.; $cart = $request->session()->get('invoice.cart'); if (! $cart) return redirect()->to('u/home'); $invoices = Invoice::find($cart); $paypal = new OrdersCreateRequest(); $paypal->prefer('return=minimal'); // Paypal Purchase Units $items = collect(); foreach ($invoices as $io) { $fee = $this->o->fee($io->due,count($cart)); $total = round($io->due+$fee,2); $items->push([ 'reference_id'=>$io->id, 'invoice_id'=>$io->id, 'description'=>'Invoice Payment', 'custom_id'=>sprintf('%s:%s',$io->account_id,$fee*100), 'amount'=>[ 'value'=>$total, 'currency_code'=>$currency, 'breakdown'=>[ 'item_total'=>[ 'value'=>$total, 'currency_code'=>$currency, ] ], ], 'items'=>[ [ 'name'=>'Invoice: '.$io->id, 'unit_amount'=>[ 'value'=>$total, 'currency_code'=>$currency, ], 'quantity'=>1, 'description'=>'Invoice Payment', 'category'=>'DIGITAL_GOODS', ], ] ]); } $data = collect(); $data->put('intent','CAPTURE'); $data->put('purchase_units',$items->toArray()); $data->put('application_context',[ 'return_url' => url('pay/paypal/capture'), 'cancel_url' => url('u/invoice/cart'), ]); $paypal->body = $data->toArray(); try { $response = $this->client->execute($paypal); } catch (HttpException $e) { Log::error('Paypal Exception',['request'=>$paypal,'response'=>$e->getMessage()]); return redirect()->to('u/invoice/cart')->withErrors('Paypal Exception: '.$e->getCode()); } catch (\HttpException $e) { Log::error('HTTP Exception',['request'=>$request,'response'=>$e->getMessage()]); return redirect()->to('u/invoice/cart')->withErrors('HTTP Exception: '.$e->getCode()); } // Get the approval link $redirect_url = ''; foreach ($response->result->links as $link) { if ($link->rel == 'approve') { $redirect_url = $link->href; break; } } if ($redirect_url) { return redirect()->away($redirect_url); } return redirect()->to('u/invoice/cart')->withErrors('An error occurred with Paypal?'); } /** * Capture a paypal payment * * @param Request $request * @return \Illuminate\Http\RedirectResponse */ public function capture(Request $request) { $paypal = new OrdersCaptureRequest($request->query('token')); $paypal->prefer('return=representation'); $redirect_url = ''; try { $response = $this->client->execute($paypal); } catch (HttpException $e) { $result = json_decode($e->getMessage()); Log::error(sprintf('Paypal Declined: Code: %s, DebugID: %s, Name: %s,Message: %s',$e->getCode(),$result->debug_id,$result->name,$result->message)); switch ($result->name) { case 'UNPROCESSABLE_ENTITY': foreach ($result->details as $detail) Log::error(sprintf('Paypal Declined: Issue: %s Message: %s',$detail->issue,$detail->description)); // If we got a redirect link, lets redirect foreach ($result->links as $link) { if ($link->rel == 'redirect') { $redirect_url = $link->href; break; } } break; default: Log::error(sprintf('Paypal Unhandled: %s',$result)); } // If we got a redirect. if ($redirect_url) { Log::error('Paypal Capture: Redirect back to Paypal.'); return redirect()->away($redirect_url); } return redirect()->to('u/invoice/cart')->withErrors('An error occurred with Paypal?'); } catch (\HttpException $e) { Log::error('HTTP Exception',['request'=>$paypal,'response'=>$e->getMessage()]); return redirect()->to('u/invoice/cart')->withErrors('HTTP Exception: '.$e->getCode()); } if (! $response OR ! $response->result->purchase_units) { Log::error('Paypal Capture: No Purchase Units?'); return redirect()->to('u/invoice/cart')->withErrors('Paypal Exception: NPU'); } // If we got here, we got a payment foreach ($response->result->purchase_units as $pu) { foreach ($pu->payments->captures as $cap) { $po = new Payment; $po->active = TRUE; switch ($cap->status) { case 'PENDING': $po->pending_status = TRUE; $po->pending = $cap->status_details->reason; break; case 'FAILED': Log::error(sprintf('Paypal Payment Failed: Invoice: %s (%s).',$pu->invoice_id,$cap->error->details[0]->description)); continue 2; default: $po->pending_status = TRUE; $po->pending = $cap->status_details->reason ?? 'Unknown Status'; break; } $po->paid_at = Carbon::parse($cap->create_time); $po->checkout_id = $this->o->id; $po->checkout_data = $cap->id; list($account_id,$fee) = explode(':',$cap->custom_id); $po->fees_amt = $fee/100; $po->total_amt = $cap->amount->value-$po->fees_amt; $po->account_id = $account_id; $pio = new PaymentItem; $pio->site_id = 1; // @todo To implement $pio->invoice_id = $cap->invoice_id; $pio->amount = $cap->amount->value-$po->fees_amt; $po->items->push($pio); // @todo To store payment fees on invoice try { $po->pushNew(); } catch (\Exception $e) { Log::error('Error recording payment',['po'=>$po,'e'=>$e->getMessage(),'token'=>$request->query('token')]); } } } $request->session()->forget('invoice.cart'); Log::info('Paypal Payment Recorded',['po'=>$po->id]); return redirect()->to('u/home')->with('success','Payment recorded thank you.'); } }