diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index c150d7dcfe17..2a4dab445893 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -32,6 +32,7 @@ use App\Models\Expense; use App\Models\Product; use App\Models\Project; use App\Models\Quote; +use App\Models\RecurringInvoice; use App\Models\Task; use App\Models\User; use App\Models\Vendor; @@ -255,6 +256,7 @@ class CreateSingleAccount extends Command $sub->recurring_product_ids = "{$p1->hashed_id}"; $sub->webhook_configuration = $webhook_config; $sub->allow_plan_changes = true; + $sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY; $sub->save(); $sub = SubscriptionFactory::create($company->id, $user->id); @@ -263,6 +265,7 @@ class CreateSingleAccount extends Command $sub->recurring_product_ids = "{$p2->hashed_id}"; $sub->webhook_configuration = $webhook_config; $sub->allow_plan_changes = true; + $sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY; $sub->save(); $sub = SubscriptionFactory::create($company->id, $user->id); @@ -271,6 +274,7 @@ class CreateSingleAccount extends Command $sub->recurring_product_ids = "{$p3->hashed_id}"; $sub->webhook_configuration = $webhook_config; $sub->allow_plan_changes = true; + $sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY; $sub->save(); } diff --git a/app/Http/Livewire/BillingPortalPurchase.php b/app/Http/Livewire/BillingPortalPurchase.php index 8610ea99f438..c864cc666f3d 100644 --- a/app/Http/Livewire/BillingPortalPurchase.php +++ b/app/Http/Livewire/BillingPortalPurchase.php @@ -112,6 +112,7 @@ class BillingPortalPurchase extends Component 'show_loading_bar' => false, 'not_eligible' => null, 'not_eligible_message' => null, + 'payment_required' => true, ]; /** @@ -269,8 +270,11 @@ class BillingPortalPurchase extends Component return $this; } - - $this->steps['fetched_payment_methods'] = true; + + if((int)$this->subscription->price == 0) + $this->steps['payment_required'] = false; + else + $this->steps['fetched_payment_methods'] = true; $this->methods = $contact->client->service()->getPaymentMethods($this->price); @@ -357,6 +361,30 @@ class BillingPortalPurchase extends Component '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['exception']['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' => '', ]); } diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 23aafa6c51ea..c82f16605f91 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -105,10 +105,10 @@ class NinjaMailerJob implements ShouldQueue switch ($class) { case Invoice::class: - event(new InvoiceWasEmailedAndFailed($this->nmo->invitation, $this->nmo->company, $message, $this->nmo->reminder_template, Ninja::eventVars(auth()->user()->id))); + event(new InvoiceWasEmailedAndFailed($this->nmo->invitation, $this->nmo->company, $message, $this->nmo->reminder_template, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); break; case Payment::class: - event(new PaymentWasEmailedAndFailed($this->nmo->entity, $this->nmo->company, $message, Ninja::eventVars(auth()->user()->id))); + event(new PaymentWasEmailedAndFailed($this->nmo->entity, $this->nmo->company, $message, Ninja::eventVars(auth()->user ? auth()->user()->id : null))); break; default: # code... diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index 513cb97ccedc..7c02d9aa6347 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -29,6 +29,7 @@ use App\Models\SystemLog; use App\Repositories\InvoiceRepository; use App\Repositories\RecurringInvoiceRepository; use App\Repositories\SubscriptionRepository; +use App\Services\Subscription\ZeroCostProduct; use App\Utils\Ninja; use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; @@ -93,12 +94,10 @@ class SubscriptionService $response = $this->triggerWebhook($context); - nlog($response); + // nlog($response); - if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1) - return redirect($this->subscription->webhook_configuration['post_purchase_url']); + $this->handleRedirect('/client/recurring_invoices/'.$recurring_invoice->hashed_id); - return redirect('/client/recurring_invoices/'.$recurring_invoice->hashed_id); } else { @@ -114,10 +113,7 @@ class SubscriptionService //execute any webhooks $this->triggerWebhook($context); - if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1) - return redirect($this->subscription->webhook_configuration['post_purchase_url']); - - return redirect('/client/invoices/'.$this->encodePrimaryKey($payment_hash->fee_invoice_id)); + $this->handleRedirect('/client/invoices/'.$this->encodePrimaryKey($payment_hash->fee_invoice_id)); } } @@ -417,6 +413,7 @@ class SubscriptionService } + public function createInvoice($data): ?\App\Models\Invoice { @@ -438,7 +435,7 @@ class SubscriptionService } - private function convertInvoiceToRecurring($client_id) :RecurringInvoice + public function convertInvoiceToRecurring($client_id) :RecurringInvoice { $subscription_repo = new SubscriptionRepository(); @@ -480,7 +477,8 @@ class SubscriptionService else { $status = $response->getStatusCode(); - $response_body = $response->getBody(); + + //$response_body = $response->getReasonPhrase(); $body = array_merge($body, ['status' => $status, 'response_body' => $response_body]); } @@ -580,6 +578,36 @@ class SubscriptionService } } + + /** + * 'email' => $this->email ?? $this->contact->email, + * 'quantity' => $this->quantity, + * 'contact_id' => $this->contact->id, + */ + public function handleNoPaymentRequired(array $data) + { + $context = (new ZeroCostProduct($this->subscription, $data))->run(); + + // Forward payload to webhook + if(array_key_exists('context', $context)) + $response = $this->triggerWebhook($context); + + // Hit the redirect + return $this->handleRedirect($context['redirect_url']); + + } + + /** + * Handles redirecting the user + */ + private function handleRedirect($default_redirect) + { + + if(array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url']) >=1) + return redirect($this->subscription->webhook_configuration['return_url']); + + return redirect($default_redirect); + } } diff --git a/app/Services/Subscription/ZeroCostProduct.php b/app/Services/Subscription/ZeroCostProduct.php new file mode 100644 index 000000000000..c132ee4b5f0e --- /dev/null +++ b/app/Services/Subscription/ZeroCostProduct.php @@ -0,0 +1,84 @@ + $this->email ?? $this->contact->email, + 'quantity' => $this->quantity, + 'contact_id' => $this->contact->id, + 'client_id' => $this->contact->client->id, + ]; + */ + public function __construct(Subscription $subscription, array $data) + { + $this->subscription = $subscription; + + $this->data = $data; + + } + + public function run() + { + //create a zero dollar invoice. + + $invoice = $this->subscription->service()->createInvoice($this->data); + + $invoice->service() + ->markPaid() + ->save(); + + $redirect_url = "/client/invoices/{$invoice->hashed_id}"; + + //create a recurring zero dollar invoice attached to this subscription. + + if(strlen($this->subscription->recurring_product_ids) >=1){ + + $recurring_invoice = $this->subscription->service()->convertInvoiceToRecurring($this->data['client_id']); + $recurring_invoice_repo = new RecurringInvoiceRepository(); + + $recurring_invoice->next_send_date = now(); + $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice); + $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate(); + + /* Start the recurring service */ + $recurring_invoice->service() + ->start() + ->save(); + + $context = [ + 'context' => 'recurring_purchase', + 'recurring_invoice' => $recurring_invoice->hashed_id, + 'invoice' => $invoice->hashed_id, + 'client' => $recurring_invoice->client->hashed_id, + 'subscription' => $this->subscription->hashed_id, + 'contact' => auth('contact')->user()->hashed_id, + 'redirect_url' => "/client/recurring_invoices/{$recurring_invoice->hashed_id}", + ]; + + return $context; + } + + return ['redirect_url' => $redirect_url]; + } + +} diff --git a/app/Utils/Traits/SubscriptionHooker.php b/app/Utils/Traits/SubscriptionHooker.php index ced93bbb2f21..29f187bf4658 100644 --- a/app/Utils/Traits/SubscriptionHooker.php +++ b/app/Utils/Traits/SubscriptionHooker.php @@ -40,11 +40,13 @@ trait SubscriptionHooker RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false ]); - return $response; + return array_merge($body, ['exception' => json_decode($response->getBody(),true), 'status_code' => $response->getStatusCode()]); } catch(\Exception $e) { - $body = array_merge($body, ['exception' => $e->getMessage()]); + //; + // dd($e); + $body = array_merge($body, ['exception' => ['message' => $e->getMessage(), 'status_code' => 500]]); return $body; } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 09945112a32d..6429e4e8ce12 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4210,7 +4210,7 @@ $LANG = array( 'activity_83' => ':user deleted subscription :subscription', 'activity_84' => ':user restored subscription :subscription', 'amount_greater_than_balance_v5' => 'The amount is greater than the invoice balance. You cannot overpay an invoice.', - + 'click_to_continue' => 'Click to continue', ); return $LANG; diff --git a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php index 8836fd65aa41..9b9ea5632f5b 100644 --- a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php @@ -135,6 +135,13 @@ @endif + @elseif(!$steps['payment_required']) +
+ @csrf + +
@elseif($steps['show_start_trial'])
@csrf