Files
fotospiel-app/app/Http/Controllers/LemonSqueezyCheckoutController.php
Codex Agent 049c4f82b9
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Allowing local lemonsqueezy payment skip and emulate success response
2026-02-04 10:39:53 +01:00

139 lines
5.0 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Requests\LemonSqueezy\LemonSqueezyCheckoutRequest;
use App\Models\CheckoutSession;
use App\Models\Package;
use App\Services\Checkout\CheckoutSessionService;
use App\Services\Coupons\CouponService;
use App\Services\LemonSqueezy\LemonSqueezyCheckoutService;
use App\Support\CheckoutRequestContext;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LemonSqueezyCheckoutController extends Controller
{
public function __construct(
private readonly LemonSqueezyCheckoutService $checkout,
private readonly CheckoutSessionService $sessions,
private readonly CouponService $coupons,
) {}
public function create(LemonSqueezyCheckoutRequest $request): JsonResponse
{
$data = $request->validated();
$user = Auth::user();
$tenant = $user?->tenant;
if (! $tenant) {
throw ValidationException::withMessages(['tenant' => 'Tenant context missing.']);
}
$package = Package::findOrFail((int) $data['package_id']);
if (! $package->lemonsqueezy_variant_id) {
throw ValidationException::withMessages(['package_id' => 'Package is not linked to a Lemon Squeezy variant.']);
}
$session = $this->sessions->createOrResume($user, $package, array_merge(
CheckoutRequestContext::fromRequest($request),
[
'tenant' => $tenant,
]
));
$this->sessions->selectProvider($session, CheckoutSession::PROVIDER_LEMONSQUEEZY);
$now = now();
$session->forceFill([
'accepted_terms_at' => $now,
'accepted_privacy_at' => $now,
'accepted_withdrawal_notice_at' => $now,
'digital_content_waiver_at' => null,
'legal_version' => $this->resolveLegalVersion(),
])->save();
$couponCode = Str::upper(trim((string) ($data['coupon_code'] ?? '')));
if ($couponCode !== '') {
$preview = $this->coupons->preview($couponCode, $package, $tenant);
$this->sessions->applyCoupon($session, $preview['coupon'], $preview['pricing']);
}
if (app()->environment('local')) {
$checkout = $this->simulateLocalCheckout($session, $package, $couponCode);
return response()->json(array_merge($checkout, [
'checkout_session_id' => $session->id,
]));
}
$checkout = $this->checkout->createCheckout($tenant, $package, [
'success_url' => $data['success_url'] ?? null,
'return_url' => $data['return_url'] ?? null,
'metadata' => [
'checkout_session_id' => $session->id,
'coupon_code' => $couponCode ?: null,
'legal_version' => $session->legal_version,
'accepted_terms' => true,
],
'discount_code' => $couponCode ?: null,
'customer_email' => $user?->email,
'customer_name' => trim(($user?->first_name ?? '').' '.($user?->last_name ?? '')) ?: ($user?->name ?? null),
]);
$session->forceFill([
'lemonsqueezy_checkout_id' => $checkout['id'] ?? $session->lemonsqueezy_checkout_id,
'provider_metadata' => array_merge($session->provider_metadata ?? [], array_filter([
'lemonsqueezy_checkout_id' => $checkout['id'] ?? null,
'lemonsqueezy_checkout_url' => $checkout['checkout_url'] ?? null,
'lemonsqueezy_expires_at' => $checkout['expires_at'] ?? null,
])),
])->save();
return response()->json(array_merge($checkout, [
'checkout_session_id' => $session->id,
]));
}
/**
* @return array{checkout_url: string|null, id: string, order_id: string, simulated: bool}
*/
protected function simulateLocalCheckout(CheckoutSession $session, Package $package, string $couponCode): array
{
$checkoutId = 'chk_'.Str::uuid();
$orderId = 'order_'.Str::uuid();
$session->forceFill([
'lemonsqueezy_checkout_id' => $checkoutId,
'provider_metadata' => array_merge($session->provider_metadata ?? [], array_filter([
'lemonsqueezy_checkout_id' => $checkoutId,
'lemonsqueezy_order_id' => $orderId,
'lemonsqueezy_status' => 'paid',
'lemonsqueezy_local_simulated_at' => now()->toIso8601String(),
'coupon_code' => $couponCode ?: null,
])),
])->save();
return [
'checkout_url' => route('marketing.success', [
'locale' => app()->getLocale(),
'packageId' => $package->id,
], absolute: true),
'id' => $checkoutId,
'order_id' => $orderId,
'simulated' => true,
];
}
protected function resolveLegalVersion(): string
{
return config('app.legal_version', now()->toDateString());
}
}