['required', 'email'], // 'data' => ['required', 'array'], // 'data.*.recurring_qty' => ['required', 'between:100,1000'], // 'data.*.optional_recurring_qty' => ['required', 'between:100,1000'], // 'data.*.optional_qty' => ['required', 'between:100,1000'], // ]; /** * Id for CompanyGateway record. * * @var string|integer */ public $company_gateway_id; /** * Id for GatewayType. * * @var string|integer */ public $payment_method_id; /** * List of steps that frontend form follows. * * @var array */ public $steps = [ 'passed_email' => false, 'existing_user' => false, 'fetched_payment_methods' => false, 'fetched_client' => false, 'show_start_trial' => false, 'passwordless_login_sent' => false, 'started_payment' => false, 'discount_applied' => false, 'show_loading_bar' => false, 'not_eligible' => null, 'not_eligible_message' => null, 'payment_required' => true, ]; public $data = []; /** * List of payment methods fetched from client. * * @var array */ public $methods = []; /** * Instance of \App\Models\Invoice * * @var Invoice */ public $invoice; /** * Coupon model for user input * * @var string */ public $coupon; /** * Quantity for seats * * @var int */ public $quantity; /** * First-hit request data (queries, locales...). * * @var array */ public $request_data; /** * @var string */ public $price; /** * Disabled state of passwordless login button. * * @var bool */ public $passwordless_login_btn = false; /** * Instance of company. * * @var Company */ public $company; /** * Campaign reference. * * @var string|null */ public $campaign; public $bundle; public $recurring_products; public $products; public $optional_recurring_products; public $optional_products; public $total; public $discount; public $sub_total; public function mount() { MultiDB::setDb($this->company->db); $this->discount = 0; $this->sub_total = 0; $this->data = []; $this->price = $this->subscription->price; if (request()->query('coupon')) { $this->coupon = request()->query('coupon'); $this->handleCoupon(); } elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){ $this->price = $this->subscription->promo_price; } $this->recurring_products = $this->subscription->service()->recurring_products(); $this->products = $this->subscription->service()->products(); $this->optional_recurring_products = $this->subscription->service()->optional_recurring_products(); $this->optional_products = $this->subscription->service()->optional_products(); $this->bundle = collect(); } /** * Handle a coupon being entered into the checkout */ public function handleCoupon() { if($this->coupon == $this->subscription->promo_code) $this->buildBundle(); else $this->discount = 0; } /** * Build the bundle in the checkout */ public function buildBundle() { $this->bundle = collect(); $data = $this->data; /* Recurring products can have a variable quantity */ foreach($this->recurring_products as $key => $p) { $qty = isset($data[$key]['recurring_qty']) ? $data[$key]['recurring_qty'] : 1; $total = $p->price * $qty; $this->bundle->push([ 'product' => nl2br(substr($p->notes, 0, 50)), 'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id), 'total' => $total, 'qty' => $qty, 'is_recurring' => true, ]); } /* One time products can only have a single quantity */ foreach($this->products as $key => $p) { $qty = 1; $total = $p->price * $qty; $this->bundle->push([ 'product' => nl2br(substr($p->notes, 0, 50)), 'price' => Number::formatMoney($total, $this->subscription->company), 'total' => $total, 'qty' => $qty, 'is_recurring' => false ]); } foreach($this->data as $key => $value) { /* Optional recurring products can have a variable quantity */ if(isset($this->data[$key]['optional_recurring_qty'])) { $p = $this->optional_recurring_products->first(function ($v,$k) use($key){ return $k == $key; }); $qty = isset($this->data[$key]['optional_recurring_qty']) ? $this->data[$key]['optional_recurring_qty'] : 0; $total = $p->price * $qty; if($qty == 0) return; $this->bundle->push([ 'product' => nl2br(substr($p->notes, 0, 50)), 'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id), 'total' => $total, 'qty' => $qty, 'is_recurring' => true ]); } /* Optional products can have a variable quantity */ if(isset($this->data[$key]['optional_qty'])) { $p = $this->optional_products->first(function ($v,$k) use($key){ return $k == $key; }); $qty = isset($this->data[$key]['optional_qty']) ? $this->data[$key]['optional_qty'] : 0; $total = $p->price * $qty; if($qty == 0) return; $this->bundle->push([ 'product' => nl2br(substr($p->notes, 0, 50)), 'price' => Number::formatMoney($total, $this->subscription->company), 'total' => $total, 'qty' => $qty, 'is_recurring' => false ]); } } $this->sub_total = Number::formatMoney($this->bundle->sum('total'), $this->subscription->company); $this->total = $this->sub_total; if($this->coupon == $this->subscription->promo_code) { if($this->subscription->is_amount_discount) $discount = $this->subscription->promo_discount; else $discount = round($this->bundle->sum('total') * ($this->subscription->promo_discount / 100), 2); $this->discount = Number::formatMoney($discount, $this->subscription->company); $this->total = Number::formatMoney(($this->bundle->sum('total') - $discount), $this->subscription->company); } return $this; } public function updatedData() { } public function updating($prop) { } public function updated($propertyName) { $this->buildBundle(); } public function rules() { $rules = [ ]; return $rules; } public function attributes() { $attributes = [ ]; return $attributes; } public function store() { } /** * Handle user authentication * * @return $this|bool|void */ public function authenticate() { $this->validate(); $contact = ClientContact::where('email', $this->email) ->where('company_id', $this->subscription->company_id) ->first(); if ($contact && $this->steps['existing_user'] === false) { return $this->steps['existing_user'] = true; } if ($contact && $this->steps['existing_user']) { $attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]); return $attempt ? $this->getPaymentMethods($contact) : session()->flash('message', 'These credentials do not match our records.'); } $this->steps['existing_user'] = false; $contact = $this->createBlankClient(); if ($contact && $contact instanceof ClientContact) { $this->getPaymentMethods($contact); } } /** * Create a blank client. Used for new customers purchasing. * * @return mixed * @throws \Laracasts\Presenter\Exceptions\PresenterException */ protected function createBlankClient() { $company = $this->subscription->company; $user = $this->subscription->user; $user->setCompany($company); $client_repo = new ClientRepository(new ClientContactRepository()); $data = [ 'name' => '', 'contacts' => [ ['email' => $this->email], ], 'client_hash' => Str::random(40), 'settings' => ClientSettings::defaults(), ]; foreach ($this->request_data as $field => $value) { if (in_array($field, Client::$subscriptions_fillable)) { $data[$field] = $value; } if (in_array($field, ClientContact::$subscription_fillable)) { $data['contacts'][0][$field] = $value; } } if(array_key_exists('currency_id', $this->request_data)) { $currency = Cache::get('currencies')->filter(function ($item){ return $item->id == $this->request_data['currency_id']; })->first(); if($currency) $data['settings']->currency_id = $currency->id; } elseif($this->subscription->group_settings && property_exists($this->subscription->group_settings->settings, 'currency_id')) { $currency = Cache::get('currencies')->filter(function ($item){ return $item->id == $this->subscription->group_settings->settings->currency_id; })->first(); if($currency) $data['settings']->currency_id = $currency->id; } if (array_key_exists('locale', $this->request_data)) { $request = $this->request_data; $record = Cache::get('languages')->filter(function ($item) use ($request) { return $item->locale == $request['locale']; })->first(); if ($record) { $data['settings']['language_id'] = (string)$record->id; } } $client = $client_repo->save($data, ClientFactory::create($company->id, $user->id)); return $client->fresh()->contacts->first(); } /** * Fetching payment methods from the client. * * @param ClientContact $contact * @return $this */ protected function getPaymentMethods(ClientContact $contact): self { Auth::guard('contact')->loginUsingId($contact->id, true); $this->contact = $contact; if ($this->subscription->trial_enabled) { $this->heading_text = ctrans('texts.plan_trial'); $this->steps['show_start_trial'] = true; return $this; } if ((int)$this->price == 0) $this->steps['payment_required'] = false; else $this->steps['fetched_payment_methods'] = true; $this->methods = $contact->client->service()->getPaymentMethods($this->price); $this->heading_text = ctrans('texts.payment_methods'); return $this; } /** * Middle method between selecting payment method & * submitting the from to the backend. * * @param $company_gateway_id * @param $gateway_type_id */ public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id) { $this->company_gateway_id = $company_gateway_id; $this->payment_method_id = $gateway_type_id; $this->handleBeforePaymentEvents(); } /** * Method to handle events before payments. * * @return void */ public function handleBeforePaymentEvents() { $this->steps['started_payment'] = true; $this->steps['show_loading_bar'] = true; $data = [ 'client_id' => $this->contact->client->id, 'date' => now()->format('Y-m-d'), 'invitations' => [[ 'key' => '', 'client_contact_id' => $this->contact->hashed_id, ]], 'user_input_promo_code' => $this->coupon, 'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon, // 'quantity' => $this->quantity, ]; $is_eligible = $this->subscription->service()->isEligible($this->contact); if (is_array($is_eligible) && $is_eligible['message'] != 'Success') { $this->steps['not_eligible'] = true; $this->steps['not_eligible_message'] = $is_eligible['message']; $this->steps['show_loading_bar'] = false; return; } $this->invoice = $this->subscription ->service() ->createInvoice($data, $this->quantity) ->service() ->markSent() ->fillDefaults() ->adjustInventory() ->save(); Cache::put($this->hash, [ 'subscription_id' => $this->subscription->id, 'email' => $this->email ?? $this->contact->email, 'client_id' => $this->contact->client->id, 'invoice_id' => $this->invoice->id, 'context' => 'purchase', 'campaign' => $this->campaign, ], now()->addMinutes(60)); $this->emit('beforePaymentEventsCompleted'); } /** * Proxy method for starting the trial. * * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function handleTrial() { return $this->subscription->service()->startTrial([ 'email' => $this->email ?? $this->contact->email, 'quantity' => $this->quantity, 'contact_id' => $this->contact->id, 'client_id' => $this->contact->client->id, ]); } public function handlePaymentNotRequired() { $is_eligible = $this->subscription->service()->isEligible($this->contact); if ($is_eligible['status_code'] != 200) { $this->steps['not_eligible'] = true; $this->steps['not_eligible_message'] = $is_eligible['message']; $this->steps['show_loading_bar'] = false; return; } return $this->subscription->service()->handleNoPaymentRequired([ 'email' => $this->email ?? $this->contact->email, 'quantity' => $this->quantity, 'contact_id' => $this->contact->id, 'client_id' => $this->contact->client->id, 'coupon' => $this->coupon, ]); } public function passwordlessLogin() { $this->passwordless_login_btn = true; $contact = ClientContact::query() ->where('email', $this->email) ->where('company_id', $this->subscription->company_id) ->first(); $mailer = new NinjaMailerObject(); $mailer->mailable = new ContactPasswordlessLogin($this->email, $this->subscription->company, (string)route('client.subscription.purchase', $this->subscription->hashed_id) . '?coupon=' . $this->coupon); $mailer->company = $this->subscription->company; $mailer->settings = $this->subscription->company->settings; $mailer->to_user = $contact; NinjaMailerJob::dispatch($mailer); $this->steps['passwordless_login_sent'] = true; $this->passwordless_login_btn = false; } public function render() { if (array_key_exists('email', $this->request_data)) { $this->email = $this->request_data['email']; } if ($this->contact instanceof ClientContact) { $this->getPaymentMethods($this->contact); } return render('components.livewire.billing-portal-purchasev2'); } // public function changeData() // { // nlog($this->data); // } }