update email template

This commit is contained in:
baodev0
2026-09-09 16:56:22 +07:00
parent d3d9f76ba0
commit 2365ef72e9
210 changed files with 6335 additions and 4688 deletions
+36 -32
View File
@@ -5,6 +5,7 @@ namespace App\Services;
use App\Enums\Invoice\InvoiceStatus;
use App\Enums\Order\OrderStatus;
use App\Enums\TransactionStatus;
use App\Helpers\OrderHelper;
use App\Jobs\Hosting\ProvisionHostingAccountJob;
use App\Jobs\VirtualMachine\Action\VMJobs;
use App\Jobs\VirtualMachine\Create\CreateVirtualMachineJob;
@@ -28,23 +29,34 @@ class InvoicePaymentService
throw new \InvalidArgumentException('Hóa đơn không thuộc tài khoản này.');
}
// Balance/status are re-checked below under row locks — this is just a fast
// fail-fast path so we don't open a transaction for an already-paid invoice.
if ((int) $invoice->status === InvoiceStatus::PAID->value) {
return $invoice->fresh(['order.orderItems.product', 'services', 'customer']);
}
if (! in_array((int) $invoice->status, [InvoiceStatus::UNPAID->value, InvoiceStatus::OVERDUE->value], true)) {
throw new \InvalidArgumentException('Hóa đơn không ở trạng thái có thể thanh toán.');
}
if ((int) $customer->total < (int) $invoice->total) {
throw new \InvalidArgumentException(sprintf(
'Số dư không đủ. Hiện có: %s VNĐ, cần: %s VNĐ.',
number_format((int) $customer->total),
number_format((int) $invoice->total)
));
}
return DB::transaction(function () use ($invoice, $customer, $method) {
// Lock both rows so two concurrent payment requests for the same invoice/customer
// (double-click, retry) cannot both pass the balance check before either commits.
$invoice = Invoice::where('id', $invoice->id)->lockForUpdate()->firstOrFail();
$customer = Customer::where('id', $customer->id)->lockForUpdate()->firstOrFail();
if ((int) $invoice->status === InvoiceStatus::PAID->value) {
return $invoice->fresh(['order.orderItems.product', 'services', 'customer']);
}
if (! in_array((int) $invoice->status, [InvoiceStatus::UNPAID->value, InvoiceStatus::OVERDUE->value], true)) {
throw new \InvalidArgumentException('Hóa đơn không ở trạng thái có thể thanh toán.');
}
if ((int) $customer->total < (int) $invoice->total) {
throw new \InvalidArgumentException(sprintf(
'Số dư không đủ. Hiện có: %s VNĐ, cần: %s VNĐ.',
number_format((int) $customer->total),
number_format((int) $invoice->total)
));
}
$customer->decrement('total', (int) $invoice->total);
$invoice->update([
@@ -128,12 +140,13 @@ class InvoicePaymentService
return;
}
if ((int) $order->status !== OrderStatus::ACTIVE->value) {
$order->update([
'status' => OrderStatus::ACTIVE->value,
'payment_method' => $invoice->payment_method,
]);
}
// payment_method/paid_at always mirror the invoice that was just paid, even if the
// order's status doesn't change (e.g. it's already ACTIVE from an earlier payment).
$order->update([
'status' => OrderStatus::ACTIVE->value,
'payment_method' => $invoice->payment_method,
'paid_at' => $invoice->paid_at,
]);
foreach ($order->orderItems as $item) {
$isHostingProvision = ! $item->os_id
@@ -150,7 +163,7 @@ class InvoicePaymentService
'status' => ($item->os_id || $isHostingProvision) ? 'pending' : 'active',
'auto_renew' => false,
'started_at' => now(),
'expires_at' => now()->addMonths($item->months ?: $this->cycleToMonths($item->billing_cycle)),
'expires_at' => now()->addMonths($item->months ?: OrderHelper::cycleToMonths($item->billing_cycle)),
]
);
@@ -177,10 +190,13 @@ class InvoicePaymentService
return;
}
if ($invoice->order && (int) $invoice->order->status !== OrderStatus::ACTIVE->value) {
if ($invoice->order) {
// Same rule as activateOrder(): payment_method/paid_at always mirror the invoice
// that was just paid, regardless of whether the order's status actually changes.
$invoice->order->update([
'status' => OrderStatus::ACTIVE->value,
'payment_method' => $invoice->payment_method,
'paid_at' => $invoice->paid_at,
]);
}
@@ -239,16 +255,4 @@ class InvoicePaymentService
}
}
private function cycleToMonths(?string $cycle): int
{
return match ($cycle) {
'monthly' => 1,
'quarterly' => 3,
'semi_annually' => 6,
'annually' => 12,
'biennially' => 24,
'triennially' => 36,
default => 1,
};
}
}