coupon code system eingeführt. coupons werden vom super admin gemanaged. coupons werden mit paddle synchronisiert und dort validiert. plus: einige mobil-optimierungen im tenant admin pwa.

This commit is contained in:
Codex Agent
2025-11-09 20:26:50 +01:00
parent f3c44be76d
commit 082b78cd43
80 changed files with 4855 additions and 435 deletions

View File

@@ -9,12 +9,14 @@ use App\Models\Package;
use App\Models\PackagePurchase;
use App\Models\TenantPackage;
use App\Services\Checkout\CheckoutSessionService;
use App\Services\Coupons\CouponService;
use App\Services\Paddle\PaddleCheckoutService;
use App\Support\Concerns\PresentsPackages;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use League\CommonMark\Environment\Environment;
@@ -32,6 +34,7 @@ class MarketingController extends Controller
public function __construct(
private readonly CheckoutSessionService $checkoutSessions,
private readonly PaddleCheckoutService $paddleCheckout,
private readonly CouponService $coupons,
) {}
public function index()
@@ -107,8 +110,10 @@ class MarketingController extends Controller
Log::info('Buy packages called', ['auth' => Auth::check(), 'locale' => $locale, 'package_id' => $packageId]);
$package = Package::findOrFail($packageId);
$couponCode = $this->rememberCouponFromRequest($request, $package);
if (! Auth::check()) {
return redirect()->route('register', ['package_id' => $package->id])
return redirect()->route('register', ['package_id' => $package->id, 'coupon' => $couponCode])
->with('message', __('marketing.packages.register_required'));
}
@@ -167,6 +172,19 @@ class MarketingController extends Controller
$this->checkoutSessions->selectProvider($session, CheckoutSession::PROVIDER_PADDLE);
$appliedDiscountId = null;
if ($couponCode) {
try {
$preview = $this->coupons->preview($couponCode, $package, $tenant);
$this->checkoutSessions->applyCoupon($session, $preview['coupon'], $preview['pricing']);
$appliedDiscountId = $preview['coupon']->paddle_discount_id;
$request->session()->forget('marketing.checkout.coupon');
} catch (ValidationException $exception) {
$request->session()->flash('coupon_error', $exception->errors()['code'][0] ?? __('marketing.coupon.errors.generic'));
}
}
$checkout = $this->paddleCheckout->createCheckout($tenant, $package, [
'success_url' => route('marketing.success', [
'locale' => app()->getLocale(),
@@ -178,7 +196,9 @@ class MarketingController extends Controller
]),
'metadata' => [
'checkout_session_id' => $session->id,
'coupon_code' => $couponCode,
],
'discount_id' => $appliedDiscountId,
]);
$session->forceFill([
@@ -210,6 +230,34 @@ class MarketingController extends Controller
return Inertia::render('marketing/Success', compact('packageId'));
}
protected function rememberCouponFromRequest(Request $request, Package $package): ?string
{
$input = Str::upper(trim((string) $request->input('coupon')));
if ($input !== '') {
$request->session()->put('marketing.checkout.coupon', [
'package_id' => $package->id,
'code' => $input,
]);
return $input;
}
if ($request->has('coupon')) {
$request->session()->forget('marketing.checkout.coupon');
return null;
}
$stored = $request->session()->get('marketing.checkout.coupon');
if ($stored && (int) ($stored['package_id'] ?? 0) === (int) $package->id) {
return $stored['code'] ?? null;
}
return null;
}
public function blogIndex(Request $request)
{
$locale = $request->get('locale', app()->getLocale());