From bd82074dea93b6d7910b03d4bfdb6d698456b472 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 11 Jun 2024 17:20:25 +0100 Subject: [PATCH] Improve plan downgrade behaviour. Use the new Subscription Schedules so that downgrade will only apply from the next billing period. --- classes/Subscription.php | 93 ++++++++++++++----- .../html/api/subscription_detail.php | 4 + 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/classes/Subscription.php b/classes/Subscription.php index dbbbaa6962..c347d51912 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -12,6 +12,7 @@ class Subscription { public $has_payment_data = false; private static $plans = ['twfy-1k', 'twfy-5k', 'twfy-10k', 'twfy-0k']; + private static $prices = [2000, 5000, 10000, 30000]; public function __construct($arg) { # User ID @@ -62,6 +63,7 @@ public function __construct($arg) { 'customer.default_source', 'customer.invoice_settings.default_payment_method', 'latest_invoice.payment_intent', + 'schedule.phases.items.plan', ], ]); } catch (\Stripe\Exception\InvalidRequestException $e) { @@ -98,33 +100,74 @@ private function update_subscription($form_data) { $this->update_payment_method($form_data['payment_method']); } - # Update Stripe subscription - $args = [ - 'payment_behavior' => 'allow_incomplete', - 'plan' => $form_data['plan'], - 'metadata' => $form_data['metadata'], - 'cancel_at_period_end' => false, # Needed in Stripe 2018-02-28 - ]; - if ($form_data['coupon']) { - $args['coupon'] = $form_data['coupon']; - } elseif ($this->stripe->discount) { - $args['coupon'] = ''; + foreach ($this::$plans as $i => $plan) { + if ($plan == $form_data['plan']) { + $new_price = $this::$prices[$i]; + if ($form_data['coupon'] == 'charitable100') { + $new_price = 0; + } elseif ($form_data['coupon'] == 'charitable50') { + $new_price /= 2; + } + } + if ($plan == $this->stripe->plan->id) { + $old_price = $this::$prices[$i]; + if ($this->stripe->discount && ($coupon = $this->stripe->discount->coupon)) { + if ($coupon->percent_off == 100) { + $old_price = 0; + } elseif ($coupon->percent_off == 50) { + $old_price /= 2; + } + } + } } - \Stripe\Subscription::update($this->stripe->id, $args); - # Attempt immediate payment on the upgrade - try { - $invoice = \Stripe\Invoice::create([ - 'customer' => $this->stripe->customer, - 'subscription' => $this->stripe, - 'tax_rates' => [STRIPE_TAX_RATE], - ]); - $invoice->finalizeInvoice(); - $invoice->pay(); - } catch (\Stripe\Exception\InvalidRequestException $e) { - # No invoice created if nothing to pay - } catch (\Stripe\Exception\CardException $e) { - # A source may still require 3DS... Stripe will have sent an email :-/ + if ($old_price >= $new_price) { + if ($this->stripe->schedule) { + \Stripe\SubscriptionSchedule::release($this->stripe->schedule); + } + $schedule = \Stripe\SubscriptionSchedule::create(['from_subscription' => $this->stripe->id]); + $phases = [ + [ + 'items' => [['price' => $schedule->phases[0]->items[0]->price]], + 'start_date' => $schedule->phases[0]->start_date, + 'end_date' => $schedule->phases[0]->end_date, + 'proration_behavior' => 'none', + 'default_tax_rates' => [STRIPE_TAX_RATE], + ], + [ + 'items' => [['price' => $form_data['plan']]], + 'iterations' => 1, + 'metadata' => $form_data['metadata'], + 'proration_behavior' => 'none', + 'default_tax_rates' => [STRIPE_TAX_RATE], + ], + ]; + if ($schedule->phases[0]->discounts && $schedule->phases[0]->discounts[0]->coupon) { + $phases[0]['discounts'] = [['coupon' => $schedule->phases[0]->discounts[0]->coupon]]; + } + if ($form_data['coupon']) { + $phases[1]['coupon'] = $form_data['coupon']; + } + \Stripe\SubscriptionSchedule::update($schedule->id, ['phases' => $phases]); + } + + if ($old_price < $new_price) { + $args = [ + 'payment_behavior' => 'allow_incomplete', + 'plan' => $form_data['plan'], + 'metadata' => $form_data['metadata'], + 'cancel_at_period_end' => false, # Needed in Stripe 2018-02-28 + 'proration_behavior' => 'always_invoice', + ]; + if ($form_data['coupon']) { + $args['coupon'] = $form_data['coupon']; + } elseif ($this->stripe->discount) { + $args['coupon'] = ''; + } + if ($this->stripe->schedule) { + \Stripe\SubscriptionSchedule::release($this->stripe->schedule); + } + \Stripe\Subscription::update($this->stripe->id, $args); } } diff --git a/www/includes/easyparliament/templates/html/api/subscription_detail.php b/www/includes/easyparliament/templates/html/api/subscription_detail.php index bdbe9e22be..bacb24210a 100644 --- a/www/includes/easyparliament/templates/html/api/subscription_detail.php +++ b/www/includes/easyparliament/templates/html/api/subscription_detail.php @@ -40,6 +40,10 @@

+ stripe->schedule->phases[1] && $subscription->stripe->schedule->phases[1]->items[0]->plan->nickname != $subscription->stripe->plan->nickname) ?> +

You are switching to stripe->schedule->phases[1]->items[0]->plan->nickname ?> at the end of your current period.

+ + stripe->discount && $subscription->stripe->discount->end) { ?>

Your discount will expire on stripe->discount->end ?>.