142 lines
5.3 KiB
PHP
142 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Payments;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
final class PaymentArchiveService
|
|
{
|
|
public function run(int $days, int $timeoutMin, int $timeoutArchiveMin, int $batch, string $batchId): array
|
|
{
|
|
return DB::transaction(function () use ($days, $timeoutMin, $timeoutArchiveMin, $batch, $batchId) {
|
|
|
|
// 1) redirected 타임아웃 → cancelled + 주문 stat_pay=c 로 정리
|
|
$cutoffRedirect = now()->subMinutes(30)->toDateTimeString();
|
|
|
|
$affectedTimeout = DB::update("
|
|
UPDATE gc_payment_attempts a
|
|
JOIN gc_pin_order o ON o.oid = a.oid
|
|
SET
|
|
a.status='cancelled',
|
|
a.return_code='TIMEOUT',
|
|
a.return_msg='결제시간초과',
|
|
a.returned_at=NOW(),
|
|
a.updated_at=NOW(),
|
|
o.stat_pay='c',
|
|
o.ret_code='TIMEOUT',
|
|
o.ret_msg='결제시간초과',
|
|
o.cancelled_at=NOW(),
|
|
o.updated_at=NOW()
|
|
WHERE
|
|
a.status='redirected'
|
|
AND a.redirected_at IS NOT NULL
|
|
AND a.redirected_at < ?
|
|
AND o.stat_pay='ready'
|
|
", [$cutoffRedirect]);
|
|
|
|
// 2) 아카이브 대상 attempt 뽑기
|
|
$rows = DB::select("
|
|
SELECT a.id, a.oid
|
|
FROM gc_payment_attempts a
|
|
WHERE
|
|
a.status IN ('cancelled','failed')
|
|
OR (
|
|
a.status = 'redirected'
|
|
AND a.redirected_at IS NOT NULL
|
|
AND a.redirected_at < ?
|
|
)
|
|
ORDER BY a.id
|
|
LIMIT {$batch}
|
|
", [$cutoffRedirect]);
|
|
|
|
if (!$rows) {
|
|
return [
|
|
'timeout_updated' => $affectedTimeout,
|
|
'archived_attempts' => 0,
|
|
'archived_orders' => 0,
|
|
'deleted_orders' => 0,
|
|
];
|
|
}
|
|
|
|
$ids = array_map(fn($r) => (int)$r->id, $rows);
|
|
$oids = array_values(array_unique(array_map(fn($r) => (string)$r->oid, $rows)));
|
|
|
|
$placeIds = implode(',', array_fill(0, count($ids), '?'));
|
|
$placeOids = implode(',', array_fill(0, count($oids), '?'));
|
|
|
|
// 3) attempts → attempts_bak
|
|
DB::insert("
|
|
INSERT IGNORE INTO gc_payment_attempts_bak
|
|
SELECT
|
|
a.*,
|
|
NOW(),
|
|
?,
|
|
CASE WHEN a.return_code='TIMEOUT' THEN 'timeout' ELSE 'old' END
|
|
FROM gc_payment_attempts a
|
|
WHERE a.id IN ($placeIds)
|
|
", array_merge([$batchId], $ids));
|
|
|
|
// 4) orders → orders_bak (oid 기준)
|
|
DB::insert("
|
|
INSERT IGNORE INTO gc_pin_order_bak
|
|
SELECT
|
|
o.*,
|
|
NOW(),
|
|
?,
|
|
'linked'
|
|
FROM gc_pin_order o
|
|
WHERE o.oid IN ($placeOids)
|
|
", array_merge([$batchId], $oids));
|
|
|
|
// 5) items → items_bak (order_id 기준, oid로 join)
|
|
DB::insert("
|
|
INSERT IGNORE INTO gc_pin_order_items_bak
|
|
SELECT
|
|
i.*,
|
|
NOW(),
|
|
?,
|
|
'linked'
|
|
FROM gc_pin_order_items i
|
|
JOIN gc_pin_order o ON o.id = i.order_id
|
|
WHERE o.oid IN ($placeOids)
|
|
", array_merge([$batchId], $oids));
|
|
|
|
// 6) 원본 attempts 삭제
|
|
DB::delete("DELETE FROM gc_payment_attempts WHERE id IN ($placeIds)", $ids);
|
|
|
|
// 7) 원본 order/items 삭제는 “해당 oid에 남은 attempt가 없을 때만” 삭제(중요)
|
|
$orderRows = DB::select("
|
|
SELECT o.id
|
|
FROM gc_pin_order o
|
|
WHERE o.oid IN ($placeOids)
|
|
AND NOT EXISTS (SELECT 1 FROM gc_payment_attempts a WHERE a.oid=o.oid)
|
|
", $oids);
|
|
|
|
$orderIds = array_map(fn($r) => (int)$r->id, $orderRows);
|
|
|
|
$deletedOrders = 0;
|
|
if ($orderIds) {
|
|
$placeOrderIds = implode(',', array_fill(0, count($orderIds), '?'));
|
|
DB::delete("DELETE FROM gc_pin_order_items WHERE order_id IN ($placeOrderIds)", $orderIds);
|
|
$deletedOrders = DB::delete("DELETE FROM gc_pin_order WHERE id IN ($placeOrderIds)", $orderIds);
|
|
}
|
|
|
|
Log::info('[payments:archive] done', [
|
|
'batch' => $batchId,
|
|
'timeout_updated' => $affectedTimeout,
|
|
'attempts' => count($ids),
|
|
'oids' => count($oids),
|
|
'deleted_orders' => $deletedOrders,
|
|
]);
|
|
|
|
return [
|
|
'timeout_updated' => $affectedTimeout,
|
|
'archived_attempts' => count($ids),
|
|
'archived_orders' => count($oids),
|
|
'deleted_orders' => $deletedOrders,
|
|
];
|
|
});
|
|
}
|
|
}
|