update to provisioner code nwew paddle code

This commit is contained in:
Mark Gregory
2026-09-30 13:15:21 +01:00
parent 0f051bb956
commit 527bb1b017
+112 -24
View File
@@ -2,6 +2,7 @@
namespace App\Services\Payments;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use RuntimeException;
@@ -19,60 +20,147 @@ final class PaddleClient
if (! $response->successful()) {
return null;
}
$data = $response->json('data');
return is_array($data) ? $data : null;
}
public function updateSubscriptionPrice(string $subscriptionID, string $priceID): array
public function subscription(string $subscriptionID): array
{
$this->requireWritableConfiguration();
$response = $this->request()->patch('/subscriptions/'.$subscriptionID, [
'proration_billing_mode' => 'prorated_immediately',
'items' => [[
'price_id' => $priceID,
'quantity' => 1,
]],
]);
$response = $this->request()
->get('/subscriptions/'.$subscriptionID);
return $this->subscriptionData($response, 'Paddle could not update the subscription.');
return $this->subscriptionData(
$response,
'Paddle could not retrieve the subscription.'
);
}
public function cancelSubscriptionImmediately(string $subscriptionID): array
{
public function updateSubscriptionPrice(
string $subscriptionID,
string $priceID
): array {
$this->requireWritableConfiguration();
$response = $this->request()->post('/subscriptions/'.$subscriptionID.'/cancel', [
'effective_from' => 'immediately',
]);
/*
* Paddle restricts subscription item changes while a subscription
* is trialing. Existing item quantities may only be updated using
* do_not_bill during the trial.
*
* Once the subscription is outside its trial we can use normal
* immediate proration.
*/
$subscription = $this->subscription($subscriptionID);
return $this->subscriptionData($response, 'Paddle could not cancel the subscription.');
$status = (string) ($subscription['status'] ?? '');
$prorationBillingMode = $status === 'trialing'
? 'do_not_bill'
: 'prorated_immediately';
/*
* Preserve the existing subscription item and replace its price.
*
* Paddle treats the submitted items array as the desired
* subscription items, so quantity is retained where possible.
*/
$existingItems = is_array($subscription['items'] ?? null)
? $subscription['items']
: [];
$existingItem = $existingItems[0] ?? [];
$quantity = max(
1,
(int) ($existingItem['quantity'] ?? 1)
);
$response = $this->request()->patch(
'/subscriptions/'.$subscriptionID,
[
'proration_billing_mode' => $prorationBillingMode,
'items' => [
[
'price_id' => $priceID,
'quantity' => $quantity,
],
],
]
);
return $this->subscriptionData(
$response,
'Paddle could not update the subscription.'
);
}
private function request()
public function cancelSubscriptionImmediately(
string $subscriptionID
): array {
$this->requireWritableConfiguration();
$response = $this->request()->post(
'/subscriptions/'.$subscriptionID.'/cancel',
[
'effective_from' => 'immediately',
]
);
return $this->subscriptionData(
$response,
'Paddle could not cancel the subscription.'
);
}
private function request(): PendingRequest
{
return Http::baseUrl((string) config('sonardesk.paddle.api_url'))
->withToken((string) config('sonardesk.paddle.api_key'))
return Http::baseUrl(
(string) config('sonardesk.paddle.api_url')
)
->withToken(
(string) config('sonardesk.paddle.api_key')
)
->acceptJson()
->timeout(15);
}
private function requireWritableConfiguration(): void
{
if (trim((string) config('sonardesk.paddle.api_key')) === '') {
throw new RuntimeException('Paddle subscription management is not configured.');
if (
trim(
(string) config('sonardesk.paddle.api_key')
) === ''
) {
throw new RuntimeException(
'Paddle subscription management is not configured.'
);
}
}
private function subscriptionData(Response $response, string $fallback): array
{
private function subscriptionData(
Response $response,
string $fallback
): array {
if (! $response->successful()) {
$detail = trim((string) ($response->json('error.detail') ?? ''));
throw new RuntimeException($detail !== '' ? $detail : $fallback);
$detail = trim(
(string) (
$response->json('error.detail')
?? ''
)
);
throw new RuntimeException(
$detail !== ''
? $detail
: $fallback
);
}
$data = $response->json('data');
if (! is_array($data)) {
throw new RuntimeException($fallback);
}