Retire Events subscriptions and owner payment gateways.
Deploy Ladill Events / deploy (push) Successful in 55s
Deploy Ladill Events / deploy (push) Successful in 55s
Make Ladill Events free for all accounts, remove plan upgrade UI, force Ladill Pay only for tickets and contributions, and reinstate standard platform fees.
This commit is contained in:
@@ -3,134 +3,52 @@
|
||||
namespace App\Http\Controllers\Events;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Events\ProSubscription;
|
||||
use App\Models\User;
|
||||
use App\Services\Billing\BillingClient;
|
||||
use App\Services\Events\SubscriptionService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* Paid Events plans are retired. Routes remain so old bookmarks and emails
|
||||
* do not 404; every action redirects to the dashboard.
|
||||
*/
|
||||
class ProController extends Controller
|
||||
{
|
||||
public function __construct(private readonly SubscriptionService $subscriptions) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
public function index(Request $request): RedirectResponse
|
||||
{
|
||||
$user = ladill_account() ?? $request->user();
|
||||
$planKey = $this->subscriptions->planKey($user);
|
||||
|
||||
return view('events.pro.index', [
|
||||
'subscription' => $this->subscriptions->subscriptionFor($user),
|
||||
'planKey' => $planKey,
|
||||
'isPro' => $planKey === ProSubscription::PLAN_PRO,
|
||||
'isEnterprise' => $planKey === ProSubscription::PLAN_ENTERPRISE,
|
||||
'hasPaidPlan' => $this->subscriptions->hasPaidPlan($user),
|
||||
'gatingActive' => $this->subscriptions->gatingActive(),
|
||||
'proPriceMinor' => $this->subscriptions->priceMinor(),
|
||||
'enterprisePriceMinor' => $this->subscriptions->enterprisePriceMinor(),
|
||||
'prepaidMonths' => (array) config('events.prepaid_months', [6, 12, 24]),
|
||||
'currency' => (string) config('events.pro.currency', 'GHS'),
|
||||
]);
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
public function subscribe(Request $request): RedirectResponse
|
||||
{
|
||||
[$ok, $message] = $this->subscriptions->subscribe(ladill_account() ?? $request->user());
|
||||
|
||||
return redirect()->route('events.pro.index')->with($ok ? 'success' : 'error', $message);
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
public function subscribeEnterprise(Request $request): RedirectResponse
|
||||
{
|
||||
[$ok, $message] = $this->subscriptions->subscribeEnterprise(ladill_account() ?? $request->user());
|
||||
|
||||
return redirect()->route('events.pro.index')->with($ok ? 'success' : 'error', $message);
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
public function subscribePrepaid(Request $request, BillingClient $billing): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'plan' => ['required', 'in:pro,enterprise'],
|
||||
'months' => ['required', 'integer', 'in:6,12,24'],
|
||||
]);
|
||||
|
||||
$user = ladill_account() ?? $request->user();
|
||||
if ($this->subscriptions->hasPaidPlan($user)) {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('success', 'You already have an active subscription.');
|
||||
}
|
||||
|
||||
$plan = $validated['plan'];
|
||||
$months = (int) $validated['months'];
|
||||
$monthlyMinor = $plan === 'enterprise'
|
||||
? $this->subscriptions->enterprisePriceMinor()
|
||||
: $this->subscriptions->priceMinor();
|
||||
|
||||
try {
|
||||
$checkout = $billing->initiatePlanCheckout(
|
||||
$user,
|
||||
$plan,
|
||||
$months,
|
||||
$monthlyMinor * $months,
|
||||
route('events.pro.paystack.callback'),
|
||||
['user_id' => $user->id],
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('error', 'Could not start Paystack checkout. Please try again.');
|
||||
}
|
||||
|
||||
$url = trim((string) ($checkout['checkout_url'] ?? ''));
|
||||
if ($url === '') {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('error', 'Paystack did not return a checkout URL.');
|
||||
}
|
||||
|
||||
return redirect()->away($url);
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
public function paystackCallback(Request $request, BillingClient $billing): RedirectResponse
|
||||
{
|
||||
$reference = (string) $request->query('reference', '');
|
||||
if ($reference === '') {
|
||||
return redirect()->route('events.pro.index')->with('error', 'Missing payment reference.');
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $billing->verifyPlanCheckout($reference);
|
||||
} catch (\Throwable) {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('error', 'Could not verify payment. Contact support with reference: '.$reference);
|
||||
}
|
||||
|
||||
if (! ($result['paid'] ?? false)) {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('error', 'Payment was not completed.');
|
||||
}
|
||||
|
||||
$metadata = (array) ($result['metadata'] ?? []);
|
||||
$user = User::query()->find((int) ($metadata['user_id'] ?? 0));
|
||||
$account = ladill_account() ?? $request->user();
|
||||
if (! $user || ! $account || $user->id !== $account->id) {
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('error', 'Account not found for this payment.');
|
||||
}
|
||||
|
||||
$plan = (string) ($result['plan'] ?? 'pro');
|
||||
$months = (int) ($result['months'] ?? 0);
|
||||
$this->subscriptions->activatePrepaid($user, $plan, $months);
|
||||
|
||||
$label = $plan === 'enterprise' ? 'Events Business' : 'Events Pro';
|
||||
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('success', "{$label} is active for {$months} months.");
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
public function cancel(Request $request): RedirectResponse
|
||||
{
|
||||
[$ok, $message] = $this->subscriptions->cancel(ladill_account() ?? $request->user());
|
||||
return $this->retired();
|
||||
}
|
||||
|
||||
return redirect()->route('events.pro.index')->with($ok ? 'success' : 'error', $message);
|
||||
private function retired(): RedirectResponse
|
||||
{
|
||||
return redirect()->route('events.dashboard')
|
||||
->with('success', 'Ladill Events is free — paid subscriptions are no longer offered. Ticket payments use Ladill Pay with the standard platform fee.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,7 @@ class EnsurePro
|
||||
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$user = ladill_account() ?? $request->user();
|
||||
if ($user && $this->subscriptions->isPro($user)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect()->route('events.pro.index')
|
||||
->with('upsell', 'This feature is part of Ladill Events Pro or Business.');
|
||||
// Paid Events plans are retired — all authenticated users have full access.
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ class SubscriptionService
|
||||
|
||||
public function gatingActive(): bool
|
||||
{
|
||||
return (bool) config('events.pro.enabled', true) && $this->billing->configured();
|
||||
// Paid Events subscriptions are retired — product is free for all accounts.
|
||||
return false;
|
||||
}
|
||||
|
||||
public function priceMinor(): int
|
||||
@@ -74,10 +75,10 @@ class SubscriptionService
|
||||
return $this->hasPaidPlan($user);
|
||||
}
|
||||
|
||||
/** Own payment gateway (Paystack / Flutterwave / Hubtel) requires Pro or Business. */
|
||||
/** Custom / owner payment gateways are retired — Ladill Pay only. */
|
||||
public function canUsePaymentGateway(User $user): bool
|
||||
{
|
||||
return $this->hasPaidPlan($user);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function liveEventCount(User $user): int
|
||||
|
||||
@@ -24,8 +24,9 @@ class MerchantGatewayService
|
||||
|
||||
public function shouldUseOwnerGateway(User|string $owner): bool
|
||||
{
|
||||
return $this->ownerMayUseGateway($owner)
|
||||
&& (bool) $this->settingFor($owner)?->isConfigured();
|
||||
// Custom / owner payment gateways are retired. All checkouts use Ladill Pay
|
||||
// with platform fees.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,15 +89,7 @@ class MerchantGatewayService
|
||||
|
||||
protected function ownerMayUseGateway(User|string $owner): bool
|
||||
{
|
||||
$user = $owner instanceof User
|
||||
? $owner
|
||||
: User::query()->where('public_id', (string) $owner)->first();
|
||||
|
||||
if (! $user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return app(SubscriptionService::class)->canUsePaymentGateway($user);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user