customers->ensureCustomerId($tenant); $customData = $this->buildMetadata( $tenant, $package, array_merge( $options['metadata'] ?? [], $options['custom_data'] ?? [], array_filter([ 'success_url' => $options['success_url'] ?? null, 'cancel_url' => $options['return_url'] ?? null, ], static fn ($value) => $value !== null && $value !== '') ) ); $payload = [ 'customer_id' => $customerId, 'items' => [ [ 'price_id' => $package->paddle_price_id, 'quantity' => 1, ], ], 'custom_data' => $customData, ]; if (! empty($options['discount_id'])) { $payload['discount_id'] = $options['discount_id']; } $response = $this->client->post('/transactions', $payload); $checkoutUrl = Arr::get($response, 'data.checkout.url') ?? Arr::get($response, 'checkout.url') ?? Arr::get($response, 'data.url') ?? Arr::get($response, 'url'); if (! $checkoutUrl) { Log::warning('Paddle checkout response missing url', ['response' => $response]); } 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' => Arr::get($response, 'data.id') ?? Arr::get($response, 'id'), ]; } /** * @param array $extra * @return array */ protected function buildMetadata(Tenant $tenant, Package $package, array $extra = []): array { $metadata = [ 'tenant_id' => (string) $tenant->id, 'package_id' => (string) $package->id, ]; foreach ($extra as $key => $value) { if (! is_string($key)) { continue; } if (is_scalar($value) || (is_object($value) && method_exists($value, '__toString'))) { $metadata[$key] = (string) $value; } } return $metadata; } }