giftcon_dev/app/Services/Payments/PaymentArchiveService.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,
];
});
}
}