Migrate billing from Paddle to Lemon Squeezy
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-03 10:59:54 +01:00
parent c96a73d884
commit 10c99de1e2
228 changed files with 4369 additions and 4067 deletions

View File

@@ -5,20 +5,16 @@ namespace App\Services\Addons;
use App\Models\Event;
use App\Models\EventPackageAddon;
use App\Models\Tenant;
use App\Services\Paddle\PaddleClient;
use App\Services\Paddle\PaddleCustomerService;
use Illuminate\Support\Arr;
use App\Services\LemonSqueezy\LemonSqueezyCheckoutService;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Throwable;
class EventAddonCheckoutService
{
public function __construct(
private readonly EventAddonCatalog $catalog,
private readonly PaddleClient $paddle,
private readonly PaddleCustomerService $customers,
private readonly LemonSqueezyCheckoutService $checkout,
) {}
/**
@@ -32,25 +28,17 @@ class EventAddonCheckoutService
$acceptedWaiver = (bool) ($payload['accepted_waiver'] ?? false);
$acceptedTerms = (bool) ($payload['accepted_terms'] ?? false);
try {
$customerId = $this->customers->ensureCustomerId($tenant);
} catch (Throwable $exception) {
throw ValidationException::withMessages([
'customer' => __('Konnte Paddle-Kundenkonto nicht anlegen: :message', ['message' => $exception->getMessage()]),
]);
}
if (! $addonKey || ! $this->catalog->find($addonKey)) {
throw ValidationException::withMessages([
'addon_key' => __('Unbekanntes Add-on.'),
]);
}
$priceId = $this->catalog->resolvePriceId($addonKey);
$variantId = $this->catalog->resolveVariantId($addonKey);
if (! $priceId) {
if (! $variantId) {
throw ValidationException::withMessages([
'addon_key' => __('Für dieses Add-on ist kein Paddle-Preis hinterlegt.'),
'addon_key' => __('Für dieses Add-on ist kein Lemon Squeezy Variant hinterlegt.'),
]);
}
@@ -73,6 +61,7 @@ class EventAddonCheckoutService
'addon_key' => $addonKey,
'addon_intent' => $addonIntent,
'quantity' => $quantity,
'lemonsqueezy_variant_id' => $variantId,
'legal_version' => $this->resolveLegalVersion(),
'accepted_terms' => $acceptedTerms ? '1' : '0',
'accepted_waiver' => $acceptedWaiver ? '1' : '0',
@@ -80,31 +69,18 @@ class EventAddonCheckoutService
'cancel_url' => $payload['cancel_url'] ?? null,
], static fn ($value) => $value !== null && $value !== '');
$requestPayload = array_filter([
'customer_id' => $customerId,
'items' => [
[
'price_id' => $priceId,
'quantity' => $quantity,
],
],
'custom_data' => $metadata,
], static fn ($value) => $value !== null && $value !== '');
$response = $this->checkout->createVariantCheckout($variantId, $metadata, [
'success_url' => $payload['success_url'] ?? null,
'return_url' => $payload['cancel_url'] ?? null,
'customer_email' => $tenant->contact_email ?? $tenant->user?->email,
]);
$response = $this->paddle->post('/transactions', $requestPayload);
$checkoutUrl = Arr::get($response, 'data.checkout.url')
?? Arr::get($response, 'checkout.url')
?? Arr::get($response, 'data.url')
?? Arr::get($response, 'url');
$checkoutId = Arr::get($response, 'data.checkout_id')
?? Arr::get($response, 'data.checkout.id')
?? Arr::get($response, 'checkout_id')
?? Arr::get($response, 'checkout.id');
$transactionId = Arr::get($response, 'data.id') ?? Arr::get($response, 'id');
$checkoutUrl = $response['checkout_url'] ?? null;
$checkoutId = $response['id'] ?? null;
$transactionId = null;
if (! $checkoutUrl) {
Log::warning('Paddle addon checkout response missing url', ['response' => $response]);
Log::warning('Lemon Squeezy addon checkout response missing url', ['response' => $response]);
}
EventPackageAddon::create([
@@ -113,7 +89,7 @@ class EventAddonCheckoutService
'tenant_id' => $tenant->id,
'addon_key' => $addonKey,
'quantity' => $quantity,
'price_id' => $priceId,
'variant_id' => $variantId,
'checkout_id' => $checkoutId,
'transaction_id' => $transactionId,
'status' => 'pending',
@@ -133,10 +109,8 @@ class EventAddonCheckoutService
return [
'checkout_url' => $checkoutUrl,
'expires_at' => Arr::get($response, 'data.checkout.expires_at')
?? Arr::get($response, 'data.expires_at')
?? Arr::get($response, 'expires_at'),
'id' => $transactionId ?? $checkoutId,
'expires_at' => $response['expires_at'] ?? null,
'id' => $checkoutId,
];
}