Skip to content

Commit

Permalink
Merge pull request #332 from liberu-billing/sweep/Enhance-Control-Pan…
Browse files Browse the repository at this point in the history
…el-Clients-and-Billing-Service-with-Robust-Server-Management-and-Payment-Handling

Enhance Control Panel Clients and Billing Service with Robust Server Management and Payment Handling
  • Loading branch information
curtisdelicata authored Dec 25, 2024
2 parents 57e892a + 66feec9 commit 2926e67
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 105 deletions.
49 changes: 49 additions & 0 deletions app/Services/BillingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ public function processAutomaticPayment(Invoice $invoice)
$paymentMethod = $customer->defaultPaymentMethod;

if (!$paymentMethod) {
$this->handleFailedPayment($invoice);
return ['success' => false, 'message' => 'No default payment method found'];
}

Expand All @@ -402,16 +403,64 @@ public function processAutomaticPayment(Invoice $invoice)
$result = $paymentGatewayService->processPayment($payment);
if ($result['success']) {
$payment->transaction_id = $result['transaction_id'];
$payment->status = 'completed';
$payment->save();

$invoice->status = 'paid';
$invoice->paid_at = now();
$invoice->save();

// Unsuspend any suspended hosting accounts
if ($invoice->subscription) {
$hostingAccount = $invoice->subscription->hostingAccount;
if ($hostingAccount && $hostingAccount->status === 'suspended') {
$this->serviceProvisioningService->manageService(
$invoice->subscription,
'unsuspend'
);

Log::info('Hosting account unsuspended after successful payment', [
'invoice_id' => $invoice->id,
'hosting_account_id' => $hostingAccount->id
]);
}
}

return ['success' => true, 'payment' => $payment];
} else {
$this->handleFailedPayment($invoice);
return ['success' => false, 'message' => $result['message']];
}
} catch (\Exception $e) {
$this->handleFailedPayment($invoice);
return ['success' => false, 'message' => 'Payment processing failed: ' . $e->getMessage()];
}
}

protected function handleFailedPayment(Invoice $invoice)
{
// If payment fails and grace period is over, suspend hosting
if ($invoice->due_date->addDays(config('billing.grace_period', 3))->isPast()) {
if ($invoice->subscription) {
$hostingAccount = $invoice->subscription->hostingAccount;
if ($hostingAccount && $hostingAccount->status === 'active') {
$this->serviceProvisioningService->manageService(
$invoice->subscription,
'suspend'
);

Log::info('Hosting account suspended due to failed payment', [
'invoice_id' => $invoice->id,
'hosting_account_id' => $hostingAccount->id
]);
}
}
}

$invoice->status = 'overdue';
$invoice->save();
}

public function sendUpcomingInvoiceReminders()
{
$reminderCount = 0;
Expand Down
83 changes: 64 additions & 19 deletions app/Services/ControlPanels/CpanelClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,95 +5,140 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use App\Models\HostingServer;

class CpanelClient
{
protected $client;
protected $apiUrl;
protected $server;
protected $apiToken;

public function __construct()
{
$this->client = new Client();
$this->apiUrl = config('services.cpanel.api_url');
$this->apiToken = config('services.cpanel.api_token');
}

public function setServer(HostingServer $server)
{
$this->server = $server;
$this->apiToken = $server->api_token;
}

public function createAccount($username, $domain, $package)
{
$endpoint = '/createacct';
$endpoint = '/json-api/createacct';
$params = [
'username' => $username,
'domain' => $domain,
'plan' => $package,
'featurelist' => $package,
'password' => $this->generatePassword(),
'contactemail' => $username . '@' . $domain,
'quota' => 0, // Unlimited
'maxftp' => 'unlimited',
'maxsql' => 'unlimited',
'maxpop' => 'unlimited',
'cpmod' => 'paper_lantern',
'maxsub' => 'unlimited',
'maxpark' => 'unlimited',
'maxaddon' => 'unlimited',
'bwlimit' => 0, // Unlimited
'customip' => $this->server->ip_address ?? '',
'shell' => 'n',
'owner' => $this->server->username
];

return $this->makeApiCall($endpoint, $params);
}

public function suspendAccount($username)
{
$endpoint = '/suspendacct';
$endpoint = '/json-api/suspendacct';
$params = [
'user' => $username,
'reason' => 'Non-payment',
'leave-ftp' => 0
];

return $this->makeApiCall($endpoint, $params);
}

public function unsuspendAccount($username)
{
$endpoint = '/unsuspendacct';
$endpoint = '/json-api/unsuspendacct';
$params = [
'user' => $username,
'user' => $username
];

return $this->makeApiCall($endpoint, $params);
}

public function changePackage($username, $newPackage)
{
$endpoint = '/changepackage';
$endpoint = '/json-api/changepackage';
$params = [
'user' => $username,
'pkg' => $newPackage,
'pkg' => $newPackage
];

return $this->makeApiCall($endpoint, $params);
}

public function terminateAccount($username)
{
$endpoint = '/removeacct';
$endpoint = '/json-api/removeacct';
$params = [
'user' => $username,
'keepdns' => 0
];

return $this->makeApiCall($endpoint, $params);
}

protected function makeApiCall($endpoint, $params)
{
if (!$this->server) {
throw new \Exception('Server not configured');
}

try {
$response = $this->client->request('POST', $this->apiUrl . $endpoint, [
$response = $this->client->request('GET', 'https://' . $this->server->hostname . ':2087' . $endpoint, [
'headers' => [
'Authorization' => 'WHM ' . $this->apiToken,
'Authorization' => 'WHM ' . $this->server->username . ':' . $this->apiToken,
],
'form_params' => $params,
'query' => $params,
'verify' => false // Only if using self-signed SSL
]);

$result = json_decode($response->getBody(), true);

if (isset($result['result']) && $result['result'] === 1) {
Log::info("cPanel API call successful", ['endpoint' => $endpoint, 'params' => $params]);
if (isset($result['metadata']['result']) && $result['metadata']['result'] === 1) {
Log::info("cPanel API call successful", [
'endpoint' => $endpoint,
'server' => $this->server->hostname
]);
return true;
} else {
Log::error("cPanel API call failed", ['endpoint' => $endpoint, 'params' => $params, 'response' => $result]);
return false;
}

Log::error("cPanel API call failed", [
'endpoint' => $endpoint,
'server' => $this->server->hostname,
'error' => $result['metadata']['reason'] ?? 'Unknown error'
]);
return false;

} catch (GuzzleException $e) {
Log::error("cPanel API call error", ['endpoint' => $endpoint, 'params' => $params, 'error' => $e->getMessage()]);
Log::error("cPanel API call error", [
'endpoint' => $endpoint,
'server' => $this->server->hostname,
'error' => $e->getMessage()
]);
return false;
}
}

protected function generatePassword()
{
return bin2hex(random_bytes(12));
}
}
Loading

0 comments on commit 2926e67

Please sign in to comment.