From ef11a2258d5609c72678a25fcafd0d04561d5fbf Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 6 Oct 2021 14:47:17 +1100 Subject: [PATCH] Add Pay Link Functionality --- .../ClientPortal/InvitationController.php | 34 ++- .../ClientPortal/PaymentController.php | 231 +----------------- app/Http/Middleware/SetInviteDb.php | 3 + app/PaymentDrivers/BaseDriver.php | 36 +-- 4 files changed, 56 insertions(+), 248 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 5deddc23ffa4..7a7296cae120 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -21,6 +21,8 @@ use App\Models\Client; use App\Models\ClientContact; use App\Models\InvoiceInvitation; use App\Models\Payment; +use App\Services\ClientPortal\InstantPayment; +use App\Utils\CurlUtils; use App\Utils\Ninja; use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesHash; @@ -189,11 +191,41 @@ class InvitationController extends Controller } - public function payInvoice(string $invitation_key) + public function payInvoice(Request $request, string $invitation_key) { $invitation = InvoiceInvitation::where('key', $invitation_key) ->with('contact.client') ->firstOrFail(); + auth()->guard('contact')->login($invitation->contact, true); + + $invoice = $invitation->invoice; + + if($invoice->partial > 0) + $amount = round($invoice->partial, (int)$invoice->client->currency()->precision); + else + $amount = round($invoice->balance, (int)$invoice->client->currency()->precision); + + + $gateways = $invitation->contact->client->service()->getPaymentMethods($amount); + + if(is_array($gateways)) + { + + $data = [ + 'company_gateway_id' => $gateways[0]['company_gateway_id'], + 'payment_method_id' => $gateways[0]['gateway_type_id'], + 'payable_invoices' => [ + ['invoice_id' => $invitation->invoice->hashed_id, 'amount' => $amount], + ], + 'signature' => false + ]; + + $request->replace($data); + + return (new InstantPayment($request))->run(); + } + + } } diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index e4bed3334897..fcdbeacff4cd 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -23,6 +23,7 @@ use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentHash; use App\Models\SystemLog; +use App\Services\ClientPortal\InstantPayment; use App\Services\Subscription\SubscriptionService; use App\Utils\Number; use App\Utils\Traits\MakesDates; @@ -79,235 +80,7 @@ class PaymentController extends Controller */ public function process(Request $request) { - $is_credit_payment = false; - $tokens = []; - - if ($request->input('company_gateway_id') == CompanyGateway::GATEWAY_CREDIT) { - $is_credit_payment = true; - } - - $gateway = CompanyGateway::find($request->input('company_gateway_id')); - - /** - * find invoices - * - * ['invoice_id' => xxx, 'amount' => 22.00] - */ - - $payable_invoices = collect($request->payable_invoices); - $invoices = Invoice::whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->withTrashed()->get(); - - $invoices->each(function($invoice){ - $invoice->service()->removeUnpaidGatewayFees()->save(); - }); - - /* pop non payable invoice from the $payable_invoices array */ - - $payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices) { - return $invoices->where('hashed_id', $payable_invoice['invoice_id'])->first()->isPayable(); - }); - - /*return early if no invoices*/ - - if ($payable_invoices->count() == 0) { - return redirect() - ->route('client.invoices.index') - ->with(['message' => 'No payable invoices selected.']); - } - - $settings = auth()->user()->client->getMergedSettings(); - - // nlog($settings); - - /* This loop checks for under / over payments and returns the user if a check fails */ - - foreach ($payable_invoices as $payable_invoice) { - - /*Match the payable invoice to the Model Invoice*/ - - $invoice = $invoices->first(function ($inv) use ($payable_invoice) { - return $payable_invoice['invoice_id'] == $inv->hashed_id; - }); - - /* - * Check if company supports over & under payments. - * Determine the payable amount and the max payable. ie either partial or invoice balance - */ - - $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), auth()->user()->client->currency()->precision); - $invoice_balance = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), auth()->user()->client->currency()->precision); - - /*If we don't allow under/over payments force the payable amount - prevents inspect element adjustments in JS*/ - - if ($settings->client_portal_allow_under_payment == false && $settings->client_portal_allow_over_payment == false) { - $payable_invoice['amount'] = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), auth()->user()->client->currency()->precision); - } - - if (!$settings->client_portal_allow_under_payment && $payable_amount < $invoice_balance) { - return redirect() - ->route('client.invoices.index') - ->with('message', ctrans('texts.minimum_required_payment', ['amount' => $invoice_balance])); - } - - if ($settings->client_portal_allow_under_payment) { - if ($invoice_balance < $settings->client_portal_under_payment_minimum && $payable_amount < $invoice_balance) { - return redirect() - ->route('client.invoices.index') - ->with('message', ctrans('texts.minimum_required_payment', ['amount' => $invoice_balance])); - } - - if ($invoice_balance < $settings->client_portal_under_payment_minimum) { - // Skip the under payment rule. - } - - if ($invoice_balance >= $settings->client_portal_under_payment_minimum && $payable_amount < $settings->client_portal_under_payment_minimum) { - return redirect() - ->route('client.invoices.index') - ->with('message', ctrans('texts.minimum_required_payment', ['amount' => $settings->client_portal_under_payment_minimum])); - } - } - - /* If we don't allow over payments and the amount exceeds the balance */ - - if (!$settings->client_portal_allow_over_payment && $payable_amount > $invoice_balance) { - return redirect() - ->route('client.invoices.index') - ->with('message', ctrans('texts.over_payments_disabled')); - } - - } - - /*Iterate through invoices and add gateway fees and other payment metadata*/ - - //$payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) { - $payable_invoice_collection = collect(); - - foreach ($payable_invoices as $payable_invoice) { - // nlog($payable_invoice); - - $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); - - $invoice = $invoices->first(function ($inv) use ($payable_invoice) { - return $payable_invoice['invoice_id'] == $inv->hashed_id; - }); - - $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), auth()->user()->client->currency()->precision); - $invoice_balance = Number::roundValue($invoice->balance, auth()->user()->client->currency()->precision); - - $payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format()); - $payable_invoice['invoice_number'] = $invoice->number; - - if (isset($invoice->po_number)) { - $additional_info = $invoice->po_number; - } elseif (isset($invoice->public_notes)) { - $additional_info = $invoice->public_notes; - } else { - $additional_info = $invoice->date; - } - - $payable_invoice['additional_info'] = $additional_info; - - $payable_invoice_collection->push($payable_invoice); - } - //}); - - if (request()->has('signature') && !is_null(request()->signature) && !empty(request()->signature)) { - $invoices->each(function ($invoice) use ($request) { - InjectSignature::dispatch($invoice, $request->signature); - }); - } - - $payable_invoices = $payable_invoice_collection; - - $payment_method_id = $request->input('payment_method_id'); - $invoice_totals = $payable_invoices->sum('amount'); - $first_invoice = $invoices->first(); - $credit_totals = $first_invoice->client->getSetting('use_credits_payment') == 'always' ? $first_invoice->client->service()->getCreditBalance() : 0; - $starting_invoice_amount = $first_invoice->balance; - - if ($gateway) { - $first_invoice->service()->addGatewayFee($gateway, $payment_method_id, $invoice_totals)->save(); - } - - /** - * Gateway fee is calculated - * by adding it as a line item, and then subtract - * the starting and finishing amounts of the invoice. - */ - $fee_totals = $first_invoice->balance - $starting_invoice_amount; - - if ($gateway) { - $tokens = auth()->user()->client->gateway_tokens() - ->whereCompanyGatewayId($gateway->id) - ->whereGatewayTypeId($payment_method_id) - ->get(); - } - - if(!$is_credit_payment){ - $credit_totals = 0; - } - - $hash_data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals, 'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals))]; - - if ($request->query('hash')) { - $hash_data['billing_context'] = Cache::get($request->query('hash')); - } - - $payment_hash = new PaymentHash; - $payment_hash->hash = Str::random(32); - $payment_hash->data = $hash_data; - $payment_hash->fee_total = $fee_totals; - $payment_hash->fee_invoice_id = $first_invoice->id; - - $payment_hash->save(); - - if($is_credit_payment){ - $amount_with_fee = max(0, (($invoice_totals + $fee_totals) - $credit_totals)); - } - else{ - $credit_totals = 0; - $amount_with_fee = max(0, $invoice_totals + $fee_totals); - } - - $totals = [ - 'credit_totals' => $credit_totals, - 'invoice_totals' => $invoice_totals, - 'fee_total' => $fee_totals, - 'amount_with_fee' => $amount_with_fee, - ]; - - $data = [ - 'payment_hash' => $payment_hash->hash, - 'total' => $totals, - 'invoices' => $payable_invoices, - 'tokens' => $tokens, - 'payment_method_id' => $payment_method_id, - 'amount_with_fee' => $invoice_totals + $fee_totals, - ]; - - if ($is_credit_payment || $totals <= 0) { - return $this->processCreditPayment($request, $data); - } - - try { - return $gateway - ->driver(auth()->user()->client) - ->setPaymentMethod($payment_method_id) - ->setPaymentHash($payment_hash) - ->checkRequirements() - ->processPaymentView($data); - } catch (\Exception $e) { - SystemLogger::dispatch( - $e->getMessage(), - SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_ERROR, - SystemLog::TYPE_FAILURE, - auth('contact')->user()->client, - auth('contact')->user()->client->company - ); - - throw new PaymentFailed($e->getMessage()); - } + return (new InstantPayment($request))->run(); } public function response(PaymentResponseRequest $request) diff --git a/app/Http/Middleware/SetInviteDb.php b/app/Http/Middleware/SetInviteDb.php index 589f483302a6..11bea4528342 100644 --- a/app/Http/Middleware/SetInviteDb.php +++ b/app/Http/Middleware/SetInviteDb.php @@ -42,6 +42,9 @@ class SetInviteDb $entity = $request->route('entity'); } + if($entity == "pay") + $entity = "invoice"; + if ($request->getSchemeAndHttpHost() && config('ninja.db.multi_db_enabled') && ! MultiDB::findAndSetDbByInvitation($entity, $request->route('invitation_key'))) { if (request()->json) { return response()->json($error, 403); diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 2c7141c71c46..d77852749918 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -492,81 +492,81 @@ class BaseDriver extends AbstractPaymentDriver public function checkRequirements() { if ($this->company_gateway->require_billing_address) { - if ($this->checkRequiredResource(auth()->user('contact')->client->address1)) { + if ($this->checkRequiredResource($this->client->address1)) { $this->required_fields[] = 'billing_address1'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->address2)) { + if ($this->checkRequiredResource($this->client->address2)) { $this->required_fields[] = 'billing_address2'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->city)) { + if ($this->checkRequiredResource($this->client->city)) { $this->required_fields[] = 'billing_city'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->state)) { + if ($this->checkRequiredResource($this->client->state)) { $this->required_fields[] = 'billing_state'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) { + if ($this->checkRequiredResource($this->client->postal_code)) { $this->required_fields[] = 'billing_postal_code'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->country_id)) { + if ($this->checkRequiredResource($this->client->country_id)) { $this->required_fields[] = 'billing_country'; } } if ($this->company_gateway->require_shipping_address) { - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address1)) { + if ($this->checkRequiredResource($this->client->shipping_address1)) { $this->required_fields[] = 'shipping_address1'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address2)) { + if ($this->checkRequiredResource($this->client->shipping_address2)) { $this->required_fields[] = 'shipping_address2'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_city)) { + if ($this->checkRequiredResource($this->client->shipping_city)) { $this->required_fields[] = 'shipping_city'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_state)) { + if ($this->checkRequiredResource($this->client->shipping_state)) { $this->required_fields[] = 'shipping_state'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_postal_code)) { + if ($this->checkRequiredResource($this->client->shipping_postal_code)) { $this->required_fields[] = 'shipping_postal_code'; } - if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_country_id)) { + if ($this->checkRequiredResource($this->client->shipping_country_id)) { $this->required_fields[] = 'shipping_country'; } } if ($this->company_gateway->require_client_name) { - if ($this->checkRequiredResource(auth()->user('contact')->client->name)) { + if ($this->checkRequiredResource($this->client->name)) { $this->required_fields[] = 'name'; } } if ($this->company_gateway->require_client_phone) { - if ($this->checkRequiredResource(auth()->user('contact')->client->phone)) { + if ($this->checkRequiredResource($this->client->phone)) { $this->required_fields[] = 'phone'; } } if ($this->company_gateway->require_contact_email) { - if ($this->checkRequiredResource(auth()->user('contact')->email)) { + if ($this->checkRequiredResource($this->email)) { $this->required_fields[] = 'contact_email'; } } if ($this->company_gateway->require_contact_name) { - if ($this->checkRequiredResource(auth()->user('contact')->first_name)) { + if ($this->checkRequiredResource($this->first_name)) { $this->required_fields[] = 'contact_first_name'; } - if ($this->checkRequiredResource(auth()->user('contact')->last_name)) { + if ($this->checkRequiredResource($this->last_name)) { $this->required_fields[] = 'contact_last_name'; } } @@ -580,7 +580,7 @@ class BaseDriver extends AbstractPaymentDriver } } - if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) { + if ($this->checkRequiredResource($this->client->postal_code)) { $this->required_fields[] = 'postal_code'; } }