144 lines
5.3 KiB
PHP
144 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\CheckoutSession;
|
|
use App\Models\Package;
|
|
use App\Services\Checkout\CheckoutSessionService;
|
|
use App\Services\Coupons\CouponService;
|
|
use App\Services\Paddle\PaddleCheckoutService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class PaddleCheckoutController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly PaddleCheckoutService $checkout,
|
|
private readonly CheckoutSessionService $sessions,
|
|
private readonly CouponService $coupons,
|
|
) {}
|
|
|
|
public function create(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'package_id' => ['required', 'exists:packages,id'],
|
|
'success_url' => ['nullable', 'url'],
|
|
'return_url' => ['nullable', 'url'],
|
|
'inline' => ['sometimes', 'boolean'],
|
|
'coupon_code' => ['nullable', 'string', 'max:64'],
|
|
'accepted_terms' => ['required', 'boolean', 'accepted'],
|
|
'accepted_waiver' => ['sometimes', 'boolean'],
|
|
]);
|
|
|
|
$user = Auth::user();
|
|
$tenant = $user?->tenant;
|
|
|
|
if (! $tenant) {
|
|
throw ValidationException::withMessages(['tenant' => 'Tenant context missing.']);
|
|
}
|
|
|
|
$package = Package::findOrFail((int) $data['package_id']);
|
|
|
|
if (! $package->paddle_price_id) {
|
|
throw ValidationException::withMessages(['package_id' => 'Package is not linked to a Paddle price.']);
|
|
}
|
|
|
|
$requiresWaiver = (bool) ($package->activates_immediately ?? true);
|
|
|
|
if ($requiresWaiver && ! $request->boolean('accepted_waiver')) {
|
|
throw ValidationException::withMessages([
|
|
'accepted_waiver' => 'Ein sofortiger Beginn der digitalen Dienstleistung erfordert Ihre ausdrückliche Zustimmung.',
|
|
]);
|
|
}
|
|
|
|
$session = $this->sessions->createOrResume($user, $package, [
|
|
'tenant' => $tenant,
|
|
]);
|
|
|
|
$this->sessions->selectProvider($session, CheckoutSession::PROVIDER_PADDLE);
|
|
|
|
$now = now();
|
|
|
|
$session->forceFill([
|
|
'accepted_terms_at' => $now,
|
|
'accepted_privacy_at' => $now,
|
|
'accepted_withdrawal_notice_at' => $now,
|
|
'digital_content_waiver_at' => $requiresWaiver ? $now : null,
|
|
'legal_version' => $this->resolveLegalVersion(),
|
|
])->save();
|
|
|
|
$couponCode = Str::upper(trim((string) ($data['coupon_code'] ?? '')));
|
|
$discountId = null;
|
|
|
|
if ($couponCode !== '') {
|
|
$preview = $this->coupons->preview($couponCode, $package, $tenant);
|
|
$this->sessions->applyCoupon($session, $preview['coupon'], $preview['pricing']);
|
|
$discountId = $preview['coupon']->paddle_discount_id;
|
|
}
|
|
|
|
if ($request->boolean('inline') && $discountId === null) {
|
|
$metadata = array_merge($session->provider_metadata ?? [], [
|
|
'mode' => 'inline',
|
|
]);
|
|
|
|
$session->forceFill([
|
|
'provider_metadata' => $metadata,
|
|
])->save();
|
|
|
|
return response()->json([
|
|
'mode' => 'inline',
|
|
'items' => [
|
|
[
|
|
'priceId' => $package->paddle_price_id,
|
|
'quantity' => 1,
|
|
],
|
|
],
|
|
'custom_data' => [
|
|
'tenant_id' => (string) $tenant->id,
|
|
'package_id' => (string) $package->id,
|
|
'checkout_session_id' => (string) $session->id,
|
|
'legal_version' => $session->legal_version,
|
|
'accepted_terms' => '1',
|
|
'accepted_waiver' => $requiresWaiver && $request->boolean('accepted_waiver') ? '1' : '0',
|
|
],
|
|
'customer' => array_filter([
|
|
'email' => $user->email,
|
|
'name' => trim(($user->first_name ?? '').' '.($user->last_name ?? '')) ?: ($user->name ?? null),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
$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,
|
|
'accepted_waiver' => $requiresWaiver && $request->boolean('accepted_waiver'),
|
|
],
|
|
'discount_id' => $discountId,
|
|
]);
|
|
|
|
$session->forceFill([
|
|
'paddle_checkout_id' => $checkout['id'] ?? $session->paddle_checkout_id,
|
|
'provider_metadata' => array_merge($session->provider_metadata ?? [], array_filter([
|
|
'paddle_checkout_id' => $checkout['id'] ?? null,
|
|
'paddle_checkout_url' => $checkout['checkout_url'] ?? null,
|
|
'paddle_expires_at' => $checkout['expires_at'] ?? null,
|
|
])),
|
|
])->save();
|
|
|
|
return response()->json($checkout);
|
|
}
|
|
|
|
protected function resolveLegalVersion(): string
|
|
{
|
|
return config('app.legal_version', now()->toDateString());
|
|
}
|
|
}
|