From c7739e64eccab8872608de2f73865b8b414e38f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Sat, 29 May 2021 13:16:22 +0200 Subject: [PATCH 1/4] Update logic for checking if webhook configuration is present --- .../Subscription/SubscriptionService.php | 137 +++++++++--------- 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index 17169b85b157..3d3c83689f63 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -52,8 +52,8 @@ class SubscriptionService $this->subscription = $subscription; } - /* - Performs the initial purchase of a + /* + Performs the initial purchase of a one time or recurring product */ public function completePurchase(PaymentHash $payment_hash) @@ -184,13 +184,13 @@ class SubscriptionService /** * Returns an upgrade price when moving between plans * - * However we only allow people to move between plans + * However we only allow people to move between plans * if their account is in good standing. - * - * @param RecurringInvoice $recurring_invoice - * @param Subscription $target - * - * @return float + * + * @param RecurringInvoice $recurring_invoice + * @param Subscription $target + * + * @return float */ public function calculateUpgradePrice(RecurringInvoice $recurring_invoice, Subscription $target) :?float { @@ -210,7 +210,7 @@ class SubscriptionService ->where('client_id', $recurring_invoice->client_id) ->where('is_deleted', 0) ->orderBy('id', 'desc') - ->first(); + ->first(); if ($outstanding->count() == 0){ //nothing outstanding @@ -232,15 +232,15 @@ class SubscriptionService /** * We refund unused days left. - * - * @param Invoice $invoice - * @return float + * + * @param Invoice $invoice + * @return float */ private function calculateProRataRefund($invoice) :float { if(!$this->invoice->date) return 0; - + $start_date = Carbon::parse($invoice->date); $current_date = now(); @@ -259,9 +259,9 @@ class SubscriptionService * Returns refundable set of line items * transformed for direct injection into * the invoice - * - * @param Invoice $invoice - * @return array + * + * @param Invoice $invoice + * @return array */ private function calculateProRataRefundItems($invoice, $is_credit = false) :array { @@ -285,14 +285,14 @@ class SubscriptionService if($item->product_key != ctrans('texts.refund')) { - + $item->cost = ($item->cost*$ratio*$multiplier); $item->product_key = ctrans('texts.refund'); $item->notes = ctrans('texts.refund') . ": ". $item->notes; - + $line_items[] = $item; - + } } @@ -302,13 +302,13 @@ class SubscriptionService /** * We only charge for the used days - * - * @param Invoice $invoice - * @return float + * + * @param Invoice $invoice + * @return float */ private function calculateProRataCharge($invoice) :float { - + $start_date = Carbon::parse($invoice->date); $current_date = now(); @@ -320,7 +320,7 @@ class SubscriptionService nlog("days to charge = {$days_to_charge} fays in frequency = {$days_in_frequency}"); $pro_rata_charge = round(($days_to_charge/$days_in_frequency) * $invoice->amount ,2); - + nlog("pro rata charge = {$pro_rata_charge}"); return $pro_rata_charge; @@ -329,15 +329,15 @@ class SubscriptionService /** * When downgrading, we may need to create * a credit - * - * @param array $data + * + * @param array $data */ public function createChangePlanCredit($data) { $recurring_invoice = $data['recurring_invoice']; $old_subscription = $data['subscription']; $target_subscription = $data['target']; - + $pro_rata_charge_amount = 0; $pro_rata_refund_amount = 0; @@ -346,9 +346,9 @@ class SubscriptionService ->where('is_deleted', 0) ->withTrashed() ->orderBy('id', 'desc') - ->first(); + ->first(); - if($last_invoice->balance > 0) + if($last_invoice->balance > 0) { $pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice, $old_subscription); nlog("pro rata charge = {$pro_rata_charge_amount}"); @@ -364,7 +364,7 @@ class SubscriptionService nlog("total payable = {$total_payable}"); $credit = $this->createCredit($last_invoice, $target_subscription); - + $new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice); $context = [ @@ -386,9 +386,9 @@ class SubscriptionService /** * When changing plans, we need to generate a pro rata invoice - * - * @param array $data - * @return Invoice + * + * @param array $data + * @return Invoice */ public function createChangePlanInvoice($data) { @@ -404,9 +404,9 @@ class SubscriptionService ->where('is_deleted', 0) ->withTrashed() ->orderBy('id', 'desc') - ->first(); + ->first(); - if($last_invoice->balance > 0) + if($last_invoice->balance > 0) { $pro_rata_charge_amount = $this->calculateProRataCharge($last_invoice, $old_subscription); nlog("pro rata charge = {$pro_rata_charge_amount}"); @@ -424,10 +424,10 @@ class SubscriptionService } /** - * Response from payment service on + * Response from payment service on * return from a plan change - * - * @param PaymentHash $payment_hash + * + * @param PaymentHash $payment_hash */ private function handlePlanChange($payment_hash) { @@ -457,9 +457,9 @@ class SubscriptionService /** * Creates a new recurring invoice when changing * plans - * - * @param RecurringInvoice $old_recurring_invoice - * @return RecurringInvoice + * + * @param RecurringInvoice $old_recurring_invoice + * @return RecurringInvoice */ private function createNewRecurringInvoice($old_recurring_invoice) :RecurringInvoice { @@ -485,8 +485,8 @@ class SubscriptionService /** * Handle a plan change where no payment is required - * - * @param array $data + * + * @param array $data */ public function handlePlanChangeNoPayment($data) { @@ -511,10 +511,10 @@ class SubscriptionService /** * Creates a credit note if the plan change requires - * - * @param Invoice $last_invoice - * @param Subscription $target - * @return Credit + * + * @param Invoice $last_invoice + * @param Subscription $target + * @return Credit */ private function createCredit($last_invoice, $target) { @@ -543,10 +543,10 @@ class SubscriptionService /** * When changing plans we need to generate a pro rata * invoice which takes into account any credits. - * - * @param Invoice $last_invoice - * @param Subscription $target - * @return Invoice + * + * @param Invoice $last_invoice + * @param Subscription $target + * @return Invoice */ private function proRataInvoice($last_invoice, $target) { @@ -575,9 +575,9 @@ class SubscriptionService /** * Generates the first invoice when a subscription is purchased - * - * @param array $data - * @return Invoice + * + * @param array $data + * @return Invoice */ public function createInvoice($data): ?\App\Models\Invoice { @@ -602,9 +602,9 @@ class SubscriptionService /** * Generates a recurring invoice based on * the specifications of the subscription - * + * * @param int $client_id The Client Id - * @return RecurringInvoice + * @return RecurringInvoice */ public function convertInvoiceToRecurring($client_id) :RecurringInvoice { @@ -625,12 +625,11 @@ class SubscriptionService /** * Hit a 3rd party API if defined in the subscription * - * @param array $context + * @param array $context */ public function triggerWebhook($context) { - /* If no webhooks have been set, then just return gracefully */ - if(!array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) || !array_key_exists('post_purchase_rest_method', $this->subscription->webhook_configuration)) { + if (empty($this->subscription->webhook_configuration['post_purchase_url']) || empty($this->subscription->webhook_configuration['post_purchase_rest_method'])) { return true; } @@ -666,7 +665,7 @@ class SubscriptionService SystemLog::CATEGORY_WEBHOOK, SystemLog::EVENT_WEBHOOK_RESPONSE, SystemLog::TYPE_WEBHOOK_RESPONSE, - $client, + $client, $client->company, ); @@ -717,9 +716,9 @@ class SubscriptionService /** * Handle the cancellation of a subscription - * - * @param RecurringInvoice $recurring_invoice - * + * + * @param RecurringInvoice $recurring_invoice + * */ public function handleCancellation(RecurringInvoice $recurring_invoice) { @@ -729,7 +728,7 @@ class SubscriptionService ->where('client_id', $recurring_invoice->client_id) ->where('is_deleted', 0) ->orderBy('id', 'desc') - ->first(); + ->first(); $invoice_start_date = Carbon::parse($outstanding_invoice->date); $refund_end_date = $invoice_start_date->addSeconds($this->subscription->refund_period); @@ -806,15 +805,15 @@ class SubscriptionService default: return 0; } - + } - + /** * 'email' => $this->email ?? $this->contact->email, * 'quantity' => $this->quantity, * 'contact_id' => $this->contact->id, - */ + */ public function handleNoPaymentRequired(array $data) { @@ -826,7 +825,7 @@ class SubscriptionService // Hit the redirect return $this->handleRedirect($context['redirect_url']); - + } /** @@ -839,5 +838,5 @@ class SubscriptionService return redirect($this->subscription->webhook_configuration['return_url']); return redirect($default_redirect); - } + } } From f64b0b3d19fec8392bb99e67cc745cd5303764ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Sat, 29 May 2021 13:16:45 +0200 Subject: [PATCH 2/4] Passing data about campaign into billing components --- app/Http/Livewire/BillingPortalPurchase.php | 33 +++++++++++-------- .../views/billing-portal/purchase.blade.php | 2 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/Http/Livewire/BillingPortalPurchase.php b/app/Http/Livewire/BillingPortalPurchase.php index 8a42098b2090..67638e5ba686 100644 --- a/app/Http/Livewire/BillingPortalPurchase.php +++ b/app/Http/Livewire/BillingPortalPurchase.php @@ -162,6 +162,13 @@ class BillingPortalPurchase extends Component */ public $passwordless_login_btn = false; + /** + * Campaign reference. + * + * @var string|null + */ + public $campaign; + public function mount() { $this->price = $this->subscription->price; @@ -182,8 +189,8 @@ class BillingPortalPurchase extends Component $this->validate(); $contact = ClientContact::where('email', $this->email) - ->where('company_id', $this->subscription->company_id) - ->first(); + ->where('company_id', $this->subscription->company_id) + ->first(); if ($contact && $this->steps['existing_user'] === false) { return $this->steps['existing_user'] = true; @@ -272,8 +279,8 @@ class BillingPortalPurchase extends Component return $this; } - - if((int)$this->subscription->price == 0) + + if ((int)$this->subscription->price == 0) $this->steps['payment_required'] = false; else $this->steps['fetched_payment_methods'] = true; @@ -332,7 +339,7 @@ class BillingPortalPurchase extends Component $is_eligible = $this->subscription->service()->isEligible($this->contact); - if ($is_eligible['exception']['message'] != 'Success') { + if (is_array($is_eligible) && $is_eligible['exception']['message'] != 'Success') { $this->steps['not_eligible'] = true; $this->steps['not_eligible_message'] = $is_eligible['exception']['message']; $this->steps['show_loading_bar'] = false; @@ -341,13 +348,13 @@ class BillingPortalPurchase extends Component } 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', - now()->addMinutes(60)] - ); + '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'); } @@ -370,7 +377,7 @@ class BillingPortalPurchase extends Component public function handlePaymentNotRequired() { - $is_eligible = $this->subscription->service()->isEligible($this->contact); + $is_eligible = $this->subscription->service()->isEligible($this->contact); if ($is_eligible['status_code'] != 200) { $this->steps['not_eligible'] = true; diff --git a/resources/views/billing-portal/purchase.blade.php b/resources/views/billing-portal/purchase.blade.php index 4a723896429f..308520c25d72 100644 --- a/resources/views/billing-portal/purchase.blade.php +++ b/resources/views/billing-portal/purchase.blade.php @@ -2,7 +2,7 @@ @section('meta_title', ctrans('texts.purchase')) @section('body') - @livewire('billing-portal-purchase', ['subscription' => $subscription, 'contact' => auth('contact')->user(), 'hash' => $hash, 'request_data' => $request_data]) + @livewire('billing-portal-purchase', ['subscription' => $subscription, 'contact' => auth('contact')->user(), 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null]) @stop @push('footer') From 7eb6925c8edf2f0e9ac603470e585e8bb7c3c679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Sat, 29 May 2021 13:17:02 +0200 Subject: [PATCH 3/4] Note about accessing the UTM data in the BaseDriver.php --- app/PaymentDrivers/BaseDriver.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 96352f0c3d50..a78ba36420e9 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -244,6 +244,9 @@ class BaseDriver extends AbstractPaymentDriver if (property_exists($this->payment_hash->data, 'billing_context')) { $billing_subscription = \App\Models\Subscription::find($this->payment_hash->data->billing_context->subscription_id); + // To access campaign hash => $this->payment_hash->data->billing_context->campaign; + // To access utm data => session()->get('utm-' . CAMPAIGN_HASH); + (new SubscriptionService($billing_subscription))->completePurchase($this->payment_hash); } From b20b27567528ed264b9448ad517aea00ac816c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Sat, 29 May 2021 13:19:36 +0200 Subject: [PATCH 4/4] Update notes about accessign campaign data in BaseDriver.php --- app/PaymentDrivers/BaseDriver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index a78ba36420e9..be015b015ca6 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -245,6 +245,7 @@ class BaseDriver extends AbstractPaymentDriver $billing_subscription = \App\Models\Subscription::find($this->payment_hash->data->billing_context->subscription_id); // To access campaign hash => $this->payment_hash->data->billing_context->campaign; + // To access campaign data => Cache::get(CAMPAIGN_HASH) // To access utm data => session()->get('utm-' . CAMPAIGN_HASH); (new SubscriptionService($billing_subscription))->completePurchase($this->payment_hash);