Files
Gnuboard5/lib/shop_order_maintenance.lib.php
T

76 lines
5.1 KiB
PHP

<?php
if (!defined('_GNUBOARD_')) exit;
function shop_order_cleanup($apply = false, $grace = 86400)
{
global $g5;
$cutoff = G5_SERVER_TIME - max(3600, (int)$grace);
$table = shop_order_state_table();
$rows = sql_query("select od_id from $table where (status in ('completed','failed','cancelled') and updated_at < $cutoff) or (status='pending' and pg in ('toss','kcp') and expires < $cutoff) order by od_id limit 500");
$result = array();
while ($candidate = sql_fetch_array($rows)) {
$id = $candidate['od_id']; shop_order_state_lock('order', $id);
$row = shop_order_state_row($id);
if (!$row) { shop_order_state_unlock('order', $id); continue; }
$safe = (in_array($row['status'], array('completed','failed','cancelled'), true) && (int)$row['updated_at'] < $cutoff) ||
($row['status'] === 'pending' && in_array($row['pg'], array('toss','kcp'), true) && (int)$row['expires'] < $cutoff);
if (!$safe) { shop_order_state_unlock('order', $id); continue; }
$result[] = array('order'=>$id, 'status'=>$row['status'], 'action'=>$apply ? 'purged' : 'would_purge');
if ($apply) {
$pg = sql_escape_string($row['pg']);
if (!sql_query("delete from {$g5['g5_shop_order_data_table']} where od_id='$id' and dt_pg='$pg'", false)) shop_order_access_fail();
shop_order_state_write($id, array('status'=>'purged','state_json'=>'','response_json'=>'','token_hash'=>'','payment_key'=>''));
}
shop_order_state_unlock('order', $id);
}
return $result;
}
function shop_order_quarantine_legacy($id)
{
global $g5;
shop_order_state_lock('order', $id);
if (shop_order_state_row($id)) throw new RuntimeException('이미 상태 기록이 있는 주문입니다.');
$res = sql_query("select * from {$g5['g5_shop_order_data_table']} where od_id='$id'");
if (sql_num_rows($res) !== 1) throw new RuntimeException('임시 주문을 하나로 확정할 수 없습니다.');
$row = sql_fetch_array($res);
$data = shop_order_decode_data($row['dt_data']);
if (!is_array($data)) throw new RuntimeException('임시 데이터 형식을 확인해 주십시오.');
$password_hash = isset($data['od_pwd']) && is_string($data['od_pwd']) ? get_encrypt_string($data['od_pwd']) : '';
unset($data['od_pwd']);
$data = shop_order_filter_data($data, $row['dt_pg']);
$encoded = base64_encode(serialize($data));
// 먼저 접근 불가능한 상태를 기록한다. 세션이 남아 있어도 자동 전환하지 않는다.
$state = sql_escape_string(json_encode(array('password_hash'=>$password_hash, 'legacy'=>true)));
$pg = sql_escape_string($row['dt_pg']);
if (!sql_query("insert into ".shop_order_state_table()." set od_id='$id',pg='$pg',status='legacy',state_json='$state',response_json='',updated_at=".G5_SERVER_TIME, false)) shop_order_access_fail();
if (!sql_query("update {$g5['g5_shop_order_data_table']} set dt_data='$encoded' where od_id='$id'", false)) shop_order_access_fail();
return array('order'=>$id, 'status'=>'legacy', 'plaintext_removed'=>true, 'token_issued'=>false);
}
function shop_order_reconcile_toss($id, $toss)
{
global $g5;
shop_order_state_lock('order', $id);
$row = shop_order_state_row($id);
$state = $row ? json_decode($row['state_json'], true) : null;
if (!$row || $row['pg'] !== 'toss' || empty($state['expected_amount']) || $row['payment_key'] === '' ||
!in_array($row['status'], array('approving','approved','finalizing','unknown','cancel_pending'), true)) throw new RuntimeException('승인 대조가 필요한 Toss 주문이 아닙니다.');
if (!$toss->getPaymentByOrderId($id)) throw new RuntimeException('PG 조회 실패: 상태를 변경하지 않았습니다.');
$p = $toss->responseData;
if (!isset($p['orderId'],$p['paymentKey'],$p['totalAmount'],$p['status']) || $p['orderId'] !== $id ||
$p['paymentKey'] !== $row['payment_key'] || (int)$p['totalAmount'] !== (int)$state['expected_amount']) throw new RuntimeException('PG 주문번호·키·금액이 일치하지 않습니다.');
if ($p['status'] === 'CANCELED') {
shop_order_state_write($id, array('status'=>'cancelled'));
return array('order'=>$id, 'status'=>'cancelled');
}
if (!($p['status'] === 'DONE' || ($p['status'] === 'WAITING_FOR_DEPOSIT' && isset($p['method']) && $p['method'] === '가상계좌'))) throw new RuntimeException('PG 거래를 재승인하지 말고 별도 확인해 주십시오.');
$saved = $state['personal'] ? sql_fetch("select pp_tno as tno from {$g5['g5_shop_personalpay_table']} where pp_id='$id'") : sql_fetch("select od_tno as tno from {$g5['g5_shop_order_table']} where od_id='$id'");
if (($state['personal'] && !empty($saved['tno'])) || (!$state['personal'] && $saved)) {
shop_order_state_write($id, array('status'=>'finalizing'));
return array('order'=>$id, 'status'=>'finalizing', 'action'=>'주문·장바구니·포인트·쿠폰의 부분 저장 대조 필요');
}
shop_order_state_approved($id, $p);
return array('order'=>$id, 'status'=>'approved', 'action'=>'원래 복귀 토큰으로 재시도 가능, 신규 승인 호출 없음');
}