[security]KVE-2026-0882 race condition 잔액 부족 시 결제 취소 처리로 변경

이전 커밋(e53689ac3)의 미수금 처리 방식은 사용자에게 결제 금액과
다른 청구가 발생해 혼란/분쟁을 유발할 수 있어, 동시 주문 race 로
포인트 잔액이 부족하면 결제 자체를 취소하는 방식으로 변경.

- PG 결제가 진행된 경우 (\$tno 존재): cancel_pg.inc.php 로 환불 요청
- 장바구니 복구: 기존 line 839 동일 패턴 (od_id = tmp_cart_id, ct_status = '쇼핑')
- 주문 삭제: g5_shop_order_table 에서 od_id 제거
- 사용자에게 die 로 명확한 오류 메시지 표시

lock timeout 케이스도 동일하게 결제 취소 처리 (보수적).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thisgun
2026-05-26 07:10:53 +00:00
co-authored by Claude Opus 4.7
parent 56b1958d37
commit 4c00c60006
2 changed files with 54 additions and 24 deletions
+27 -12
View File
@@ -708,27 +708,42 @@ if(!$result) {
if ($is_member && $od_receipt_point) {
$point_lock_key = 'g5pt_order_'.md5($member['mb_id']);
$lock_row = sql_fetch(" select get_lock('$point_lock_key', 5) as lk ");
$lock_acquired = !empty($lock_row['lk']);
$point_shortage = false;
if (!empty($lock_row['lk'])) {
if ($lock_acquired) {
$current_point = (int) get_point_sum($member['mb_id']);
if ($current_point >= $od_receipt_point) {
insert_point($member['mb_id'], (-1) * $od_receipt_point, "주문번호 $od_id 결제");
} else {
// race condition 으로 잔액 부족 — 사용 가능한 만큼만 차감하고 부족분은 미수금 처리
$actual_point = $current_point > 0 ? $current_point : 0;
$shortage = $od_receipt_point - $actual_point;
if ($actual_point > 0) {
insert_point($member['mb_id'], (-1) * $actual_point, "주문번호 $od_id 결제");
}
sql_query(" update {$g5['g5_shop_order_table']}
set od_receipt_point = '$actual_point',
od_misu = od_misu + '$shortage'
where od_id = '$od_id' ");
$point_shortage = true;
}
sql_query(" do release_lock('$point_lock_key') ");
} else {
// lock timeout — 안전을 위해 결제 취소 처리
$point_shortage = true;
}
if ($point_shortage) {
// race condition 으로 잔액 부족 — PG 환불 + 장바구니 복구 + 주문 삭제
if ($tno) {
$cancel_msg = '포인트 잔액 부족으로 결제 취소 (동시 주문 race)';
include G5_SHOP_PATH.'/cancel_pg.inc.php';
}
// 장바구니 복구 (기존 line 839 동일 패턴)
sql_query(" update {$g5['g5_shop_cart_table']} set od_id = '$tmp_cart_id', ct_status = '쇼핑' where od_id = '$od_id' ", false);
// 주문 삭제
sql_query(" delete from {$g5['g5_shop_order_table']} where od_id = '$od_id' ");
if (function_exists('add_order_post_log')) {
add_order_post_log("동시 주문 race 로 인한 포인트 잔액 부족. 주문 $od_id 취소.");
}
die('<p>회원님의 포인트 잔액이 부족하여 주문이 완료되지 않았습니다.</p><p>'.strtoupper($od_pg).'를 이용한 전자결제(신용카드, 계좌이체, 가상계좌 등)은 자동 취소되었습니다.</p>');
}
}