Deploy Ladill Hosting / deploy (push) Failing after 17s
Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
276 lines
10 KiB
PHP
276 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Services\ResellerClub;
|
|
|
|
use App\Models\RcServiceOrder;
|
|
use App\Services\Domain\DomainRegistrarService;
|
|
use App\Support\ResellerClubLegacy;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
|
|
class RcOrderFulfillmentService
|
|
{
|
|
public function __construct(
|
|
private readonly ResellerClubCustomerService $customers,
|
|
private readonly ResellerClubProductService $products,
|
|
private readonly DomainRegistrarService $registrar,
|
|
) {
|
|
}
|
|
|
|
public function processPaidOrder(int|RcServiceOrder $order): void
|
|
{
|
|
$order = $order instanceof RcServiceOrder
|
|
? $order->fresh(['user'])
|
|
: RcServiceOrder::query()->with('user')->find($order);
|
|
|
|
if (! $order || $order->payment_status !== RcServiceOrder::PAYMENT_STATUS_PAID) {
|
|
return;
|
|
}
|
|
|
|
if (in_array($order->fulfillment_status, [RcServiceOrder::FULFILLMENT_SUBMITTING, RcServiceOrder::FULFILLMENT_FULFILLED], true)) {
|
|
return;
|
|
}
|
|
|
|
if (! ResellerClubLegacy::canAutomateFulfillment($order)) {
|
|
$message = ResellerClubLegacy::isRenewalRcServiceOrder($order)
|
|
? ResellerClubLegacy::renewalsDisabledMessage()
|
|
: ResellerClubLegacy::fulfillmentDisabledMessage();
|
|
$this->markManualReview($order, $message);
|
|
|
|
return;
|
|
}
|
|
|
|
$order->forceFill([
|
|
'status' => RcServiceOrder::STATUS_PROCESSING,
|
|
'fulfillment_status' => RcServiceOrder::FULFILLMENT_SUBMITTING,
|
|
])->save();
|
|
|
|
$resolved = $this->customers->resolveCustomerForUser($order->user);
|
|
if (! ($resolved['success'] ?? false)) {
|
|
$this->markManualReview($order, $resolved['message'] ?? 'Could not prepare the ResellerClub customer.');
|
|
|
|
return;
|
|
}
|
|
|
|
$customerId = (string) ($resolved['customer_id'] ?? '');
|
|
|
|
match ($order->order_type) {
|
|
RcServiceOrder::TYPE_DOMAIN_REGISTRATION => $this->submitDomainRegistration($order, $customerId),
|
|
RcServiceOrder::TYPE_DOMAIN_TRANSFER => $this->submitDomainTransfer($order, $customerId),
|
|
RcServiceOrder::TYPE_RENEWAL => $this->submitRenewal($order, $customerId),
|
|
default => $this->submitServiceOrder($order, $customerId),
|
|
};
|
|
}
|
|
|
|
private function submitDomainRegistration(RcServiceOrder $order, string $customerId): void
|
|
{
|
|
$contact = $this->registrar->resolveContactForCustomer($order->user, $customerId);
|
|
if (! ($contact['success'] ?? false)) {
|
|
$this->markManualReview($order, $contact['message'] ?? 'Could not prepare the domain contact.');
|
|
|
|
return;
|
|
}
|
|
|
|
$registration = $this->registrar->registerDomain(
|
|
$order->domain_name,
|
|
max(1, (int) ($order->term_years ?: 1)),
|
|
$customerId,
|
|
(string) ($contact['contact_id'] ?? '')
|
|
);
|
|
|
|
if (! ($registration['success'] ?? false)) {
|
|
$this->markManualReview($order, $registration['message'] ?? 'The registrar did not accept the domain order.');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->markAwaitingVendor($order, $customerId, (string) ($registration['order_id'] ?? ''));
|
|
}
|
|
|
|
private function submitDomainTransfer(RcServiceOrder $order, string $customerId): void
|
|
{
|
|
$contact = $this->registrar->resolveContactForCustomer($order->user, $customerId);
|
|
if (! ($contact['success'] ?? false)) {
|
|
$this->markManualReview($order, $contact['message'] ?? 'Could not prepare the domain contact.');
|
|
|
|
return;
|
|
}
|
|
|
|
$encryptedAuthCode = (string) Arr::get($order->meta ?? [], 'transfer_auth_code_encrypted', '');
|
|
$authCode = $encryptedAuthCode !== '' ? Crypt::decryptString($encryptedAuthCode) : null;
|
|
|
|
$transfer = $this->registrar->transferDomain(
|
|
$order->domain_name,
|
|
$authCode,
|
|
$customerId,
|
|
(string) ($contact['contact_id'] ?? '')
|
|
);
|
|
|
|
if (! ($transfer['success'] ?? false)) {
|
|
$this->markManualReview($order, $transfer['message'] ?? 'The registrar did not accept the transfer request.');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->markAwaitingVendor($order, $customerId, (string) ($transfer['order_id'] ?? ''));
|
|
}
|
|
|
|
private function submitRenewal(RcServiceOrder $order, string $customerId): void
|
|
{
|
|
$rcOrderId = (string) data_get($order->meta, 'renew_rc_order_id', $order->rc_order_id ?? '');
|
|
|
|
if ($rcOrderId === '') {
|
|
$this->markManualReview($order, 'Renewal is missing the upstream ResellerClub order id.');
|
|
|
|
return;
|
|
}
|
|
|
|
if (in_array($order->category, ['domain-renewal', 'domain-registration', 'domain-transfer'], true)
|
|
&& $order->order_type === RcServiceOrder::TYPE_RENEWAL) {
|
|
$years = max(1, (int) ($order->term_years ?: 1));
|
|
$renewal = $this->registrar->renewDomain(
|
|
$order->domain_name,
|
|
$years,
|
|
$customerId
|
|
);
|
|
|
|
if (! ($renewal['success'] ?? false)) {
|
|
$this->markManualReview($order, $renewal['message'] ?? 'The registrar did not accept the domain renewal.');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->markRenewalComplete($order, $customerId, (string) ($renewal['order_id'] ?? $rcOrderId));
|
|
|
|
return;
|
|
}
|
|
|
|
$months = max(1, (int) ($order->term_months ?: 1));
|
|
$renewal = $this->products->renewOrder($order->category, $rcOrderId, $months);
|
|
|
|
if (! ($renewal['success'] ?? false)) {
|
|
$this->markManualReview($order, $renewal['message'] ?? 'The registrar did not accept the renewal.');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->markRenewalComplete($order, $customerId, $rcOrderId);
|
|
}
|
|
|
|
private function markRenewalComplete(RcServiceOrder $order, string $customerId, string $rcOrderId): void
|
|
{
|
|
$details = $this->products->getOrderDetails($order->category === 'domain-renewal' ? 'domain-registration' : $order->category, $rcOrderId);
|
|
|
|
if ($details['success'] ?? false) {
|
|
$synced = $this->products->syncOrderToLocal(
|
|
$order->category === 'domain-renewal' ? 'domain-registration' : $order->category,
|
|
(array) ($details['order'] ?? []),
|
|
$order->user_id,
|
|
$customerId,
|
|
$order
|
|
);
|
|
|
|
$synced->forceFill([
|
|
'status' => $synced->status === RcServiceOrder::STATUS_ACTIVE
|
|
? RcServiceOrder::STATUS_ACTIVE
|
|
: RcServiceOrder::STATUS_PROCESSING,
|
|
'payment_status' => RcServiceOrder::PAYMENT_STATUS_PAID,
|
|
'fulfillment_status' => RcServiceOrder::FULFILLMENT_FULFILLED,
|
|
'rc_order_id' => $rcOrderId,
|
|
'submitted_at' => $synced->submitted_at ?: now(),
|
|
'last_synced_at' => now(),
|
|
])->save();
|
|
|
|
return;
|
|
}
|
|
|
|
$order->forceFill([
|
|
'rc_customer_id' => $customerId,
|
|
'rc_order_id' => $rcOrderId,
|
|
'status' => RcServiceOrder::STATUS_ACTIVE,
|
|
'fulfillment_status' => RcServiceOrder::FULFILLMENT_FULFILLED,
|
|
'submitted_at' => $order->submitted_at ?: now(),
|
|
'last_synced_at' => now(),
|
|
])->save();
|
|
}
|
|
|
|
private function submitServiceOrder(RcServiceOrder $order, string $customerId): void
|
|
{
|
|
$input = (array) Arr::get($order->meta ?? [], 'order_input', []);
|
|
$input['domain_name'] = $input['domain_name'] ?? $order->domain_name;
|
|
$input['plan_id'] = $input['plan_id'] ?? $order->plan_id;
|
|
$input['months'] = $input['months'] ?? $order->term_months ?? 1;
|
|
|
|
$result = $this->products->order($order->category, $customerId, $input);
|
|
if (! ($result['success'] ?? false)) {
|
|
$this->markManualReview($order, $result['message'] ?? 'The provisioning request could not be submitted.');
|
|
|
|
return;
|
|
}
|
|
|
|
$orderId = (string) ($result['order_id'] ?? '');
|
|
if ($orderId === '') {
|
|
$this->markManualReview($order, 'The provisioning request did not return an upstream order id.');
|
|
|
|
return;
|
|
}
|
|
|
|
$details = $this->products->getOrderDetails($order->category, $orderId);
|
|
if ($details['success'] ?? false) {
|
|
$synced = $this->products->syncOrderToLocal(
|
|
$order->category,
|
|
(array) ($details['order'] ?? []),
|
|
$order->user_id,
|
|
$customerId,
|
|
$order
|
|
);
|
|
|
|
$synced->forceFill([
|
|
'status' => $synced->status === RcServiceOrder::STATUS_ACTIVE
|
|
? RcServiceOrder::STATUS_ACTIVE
|
|
: RcServiceOrder::STATUS_PROCESSING,
|
|
'payment_status' => RcServiceOrder::PAYMENT_STATUS_PAID,
|
|
'fulfillment_status' => $synced->status === RcServiceOrder::STATUS_ACTIVE
|
|
? RcServiceOrder::FULFILLMENT_FULFILLED
|
|
: RcServiceOrder::FULFILLMENT_AWAITING_VENDOR,
|
|
'submitted_at' => $synced->submitted_at ?: now(),
|
|
'last_synced_at' => now(),
|
|
])->save();
|
|
|
|
return;
|
|
}
|
|
|
|
$this->markAwaitingVendor($order, $customerId, $orderId, $details['message'] ?? null);
|
|
}
|
|
|
|
private function markAwaitingVendor(RcServiceOrder $order, string $customerId, string $orderId, ?string $message = null): void
|
|
{
|
|
$meta = (array) ($order->meta ?? []);
|
|
if ($message !== null && $message !== '') {
|
|
$meta['last_message'] = $message;
|
|
}
|
|
|
|
$order->forceFill([
|
|
'rc_customer_id' => $customerId,
|
|
'rc_order_id' => $orderId !== '' ? $orderId : $order->rc_order_id,
|
|
'status' => RcServiceOrder::STATUS_PROCESSING,
|
|
'fulfillment_status' => RcServiceOrder::FULFILLMENT_AWAITING_VENDOR,
|
|
'submitted_at' => $order->submitted_at ?: now(),
|
|
'last_synced_at' => now(),
|
|
'meta' => $meta,
|
|
])->save();
|
|
}
|
|
|
|
private function markManualReview(RcServiceOrder $order, string $message): void
|
|
{
|
|
$meta = (array) ($order->meta ?? []);
|
|
$meta['last_message'] = $message;
|
|
|
|
$order->forceFill([
|
|
'status' => RcServiceOrder::STATUS_PROCESSING,
|
|
'fulfillment_status' => RcServiceOrder::FULFILLMENT_MANUAL_REVIEW,
|
|
'meta' => $meta,
|
|
])->save();
|
|
}
|
|
}
|