diff --git a/adm/dbupgrade.php b/adm/dbupgrade.php index a6f1e3e76..467bd414a 100644 --- a/adm/dbupgrade.php +++ b/adm/dbupgrade.php @@ -317,6 +317,40 @@ if (!isset($member['mb_marketing_agree'])) { $is_check = true; } +// 쿠폰 로그 테이블에 UNIQUE 인덱스 추가 (쿠폰 이중사용 방지) +if (defined('G5_USE_SHOP') && G5_USE_SHOP) { + $result = sql_query("SHOW INDEX FROM `{$g5['g5_shop_coupon_log_table']}` WHERE Key_name = 'idx_coupon_use'", false); + if (!$result || !sql_num_rows($result)) { + // 기존에 동일 쿠폰이 중복 사용된 데이터가 있으면 UNIQUE 인덱스 생성 실패하므로 중복 데이터 정리 + $dup_sql = " SELECT cp_id, mb_id, MIN(cl_id) as keep_id + FROM `{$g5['g5_shop_coupon_log_table']}` + GROUP BY cp_id, mb_id + HAVING COUNT(*) > 1 "; + + $dup_result = sql_query($dup_sql, false); + if ($dup_result && sql_num_rows($dup_result)) { + while ($dup_row = sql_fetch_array($dup_result)) { + + echo $dup_row['cp_id']." 의 동일 쿠폰이 중복 사용된 데이터가 있으므로 인덱스 생성이 불가합니다.
"; + + $sql = " DELETE FROM `{$g5['g5_shop_coupon_log_table']}` + WHERE cp_id = '{$dup_row['cp_id']}' + AND mb_id = '{$dup_row['mb_id']}' + AND cl_id != '{$dup_row['keep_id']}' "; + if ($is_admin === 'super') { + echo "데이터베이스에서 검토후에 이 쿼리문을 실행해 주세요.
$sql
"; + } + // sql_query($sql); + } + } + + // MyISAM + utf8mb4 환경에서 키 길이 초과 방지: cp_id varchar(100), mb_id varchar(100)으로 조정 + sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` MODIFY `cp_id` varchar(100) NOT NULL DEFAULT '', MODIFY `mb_id` varchar(100) NOT NULL DEFAULT ''", false); + sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` ADD UNIQUE KEY `idx_coupon_use` (`cp_id`, `mb_id`)", false); + $is_check = true; + } +} + $is_check = run_replace('admin_dbupgrade', $is_check); $db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.
현재 DB 업그레이드가 완료된 상태입니다.'; diff --git a/install/gnuboard5shop.sql b/install/gnuboard5shop.sql index e431e7a95..de6a5647f 100644 --- a/install/gnuboard5shop.sql +++ b/install/gnuboard5shop.sql @@ -169,6 +169,7 @@ CREATE TABLE IF NOT EXISTS `g5_shop_coupon_log` ( `cp_price` int(11) NOT NULL DEFAULT '0', `cl_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`cl_id`), + UNIQUE KEY `idx_coupon_use` (`cp_id`, `mb_id`), KEY `mb_id` (`mb_id`), KEY `od_id` (`od_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/mobile/shop/orderformupdate.php b/mobile/shop/orderformupdate.php index 6289d06ae..d28e1f2c6 100644 --- a/mobile/shop/orderformupdate.php +++ b/mobile/shop/orderformupdate.php @@ -557,32 +557,8 @@ if($od_settle_case == 'KAKAOPAY') if($tno) { if((int)$order_price !== (int)$pg_price) { $cancel_msg = '결제금액 불일치'; - switch($od_pg) { - case 'lg': - include G5_SHOP_PATH.'/lg/xpay_cancel.php'; - break; - case 'toss': - include G5_SHOP_PATH.'/toss/toss_cancel.php'; - break; - case 'inicis': - include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; - break; - case 'nicepay': - $cancelAmt = (int)$pg_price; - include G5_SHOP_PATH.'/nicepay/cancel_process.php'; - break; - case 'KAKAOPAY': - $_REQUEST['TID'] = $tno; - $_REQUEST['Amt'] = $amount; - $_REQUEST['CancelMsg'] = $cancel_msg; - $_REQUEST['PartialCancelCode'] = 0; - include G5_SHOP_PATH.'/kakaopay/kakaopay_cancel.php'; - break; - default: - include G5_SHOP_PATH.'/kcp/pp_ax_hub_cancel.php'; - break; - } - + include G5_SHOP_PATH.'/cancel_pg.inc.php'; + if(function_exists('add_order_post_log')) add_order_post_log($cancel_msg); die("Receipt Amount Error"); } @@ -713,31 +689,7 @@ if(! $result && (isset($exists_order['od_id']) && $od_id && $exists_order['od_id if(! $result || ! (isset($exists_order['od_id']) && $od_id && $exists_order['od_id'] === $od_id)) { if($tno) { $cancel_msg = '주문정보 입력 오류 : '.$sql; - switch($od_pg) { - case 'lg': - include G5_SHOP_PATH.'/lg/xpay_cancel.php'; - break; - case 'toss': - include G5_SHOP_PATH.'/toss/toss_cancel.php'; - break; - case 'inicis': - include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; - break; - case 'nicepay': - $cancelAmt = (int)$pg_price; - include G5_SHOP_PATH.'/nicepay/cancel_process.php'; - break; - case 'KAKAOPAY': - $_REQUEST['TID'] = $tno; - $_REQUEST['Amt'] = $amount; - $_REQUEST['CancelMsg'] = $cancel_msg; - $_REQUEST['PartialCancelCode'] = 0; - include G5_SHOP_PATH.'/kakaopay/kakaopay_cancel.php'; - break; - default: - include G5_SHOP_PATH.'/kcp/pp_ax_hub_cancel.php'; - break; - } + include G5_SHOP_PATH.'/cancel_pg.inc.php'; } // 관리자에게 오류 알림 메일발송 @@ -778,31 +730,7 @@ $result = sql_query($sql, false); if(!$result) { if($tno) { $cancel_msg = '주문상태 변경 오류'; - switch($od_pg) { - case 'lg': - include G5_SHOP_PATH.'/lg/xpay_cancel.php'; - break; - case 'toss': - include G5_SHOP_PATH.'/toss/toss_cancel.php'; - break; - case 'inicis': - include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; - break; - case 'nicepay': - $cancelAmt = (int)$pg_price; - include G5_SHOP_PATH.'/nicepay/cancel_process.php'; - break; - case 'KAKAOPAY': - $_REQUEST['TID'] = $tno; - $_REQUEST['Amt'] = $amount; - $_REQUEST['CancelMsg'] = $cancel_msg; - $_REQUEST['PartialCancelCode'] = 0; - include G5_SHOP_PATH.'/kakaopay/kakaopay_cancel.php'; - break; - default: - include G5_SHOP_PATH.'/kcp/pp_ax_hub_cancel.php'; - break; - } + include G5_SHOP_PATH.'/cancel_pg.inc.php'; } // 관리자에게 오류 알림 메일발송 @@ -823,6 +751,7 @@ if ($is_member && $od_receipt_point) $od_memo = nl2br(htmlspecialchars2(stripslashes($od_memo))) . " "; // 쿠폰사용내역기록 +$coupon_duplicate = false; if($is_member) { $it_cp_cnt = (isset($_POST['cp_id']) && is_array($_POST['cp_id'])) ? count($_POST['cp_id']) : 0; for($i=0; $i<$it_cp_cnt; $i++) { @@ -831,13 +760,26 @@ if($is_member) { $cp_prc = isset($arr_it_cp_prc[$cp_it_id]) ? (int) $arr_it_cp_prc[$cp_it_id] : 0; if(trim($cid)) { + // 쿠폰 이중사용 방지: INSERT 직전 재확인 + if(is_used_coupon($member['mb_id'], $cid)) { + $coupon_duplicate = true; + break; + } + $sql = " insert into {$g5['g5_shop_coupon_log_table']} set cp_id = '$cid', mb_id = '{$member['mb_id']}', od_id = '$od_id', cp_price = '$cp_prc', cl_datetime = '".G5_TIME_YMDHIS."' "; - sql_query($sql); + sql_query($sql, false); + + // affected_rows 체크: 0이면 UNIQUE 제약 등으로 INSERT 실패 + $cp_affected = function_exists('get_sql_affected_rows') ? get_sql_affected_rows() : 0; + if ($cp_affected == 0) { + $coupon_duplicate = true; + break; + } } // 쿠폰사용금액 cart에 기록 @@ -851,27 +793,70 @@ if($is_member) { sql_query($sql); } - if(isset($_POST['od_cp_id']) && $_POST['od_cp_id']) { - $sql = " insert into {$g5['g5_shop_coupon_log_table']} - set cp_id = '{$_POST['od_cp_id']}', - mb_id = '{$member['mb_id']}', - od_id = '$od_id', - cp_price = '$tot_od_cp_price', - cl_datetime = '".G5_TIME_YMDHIS."' "; - sql_query($sql); + if(!$coupon_duplicate && isset($_POST['od_cp_id']) && $_POST['od_cp_id']) { + // 쿠폰 이중사용 방지: INSERT 직전 재확인 + if(is_used_coupon($member['mb_id'], $_POST['od_cp_id'])) { + $coupon_duplicate = true; + } else { + $sql = " insert into {$g5['g5_shop_coupon_log_table']} + set cp_id = '{$_POST['od_cp_id']}', + mb_id = '{$member['mb_id']}', + od_id = '$od_id', + cp_price = '$tot_od_cp_price', + cl_datetime = '".G5_TIME_YMDHIS."' "; + sql_query($sql, false); + + $cp_affected = function_exists('get_sql_affected_rows') ? get_sql_affected_rows() : 0; + if ($cp_affected == 0) + $coupon_duplicate = true; + } } - if(isset($_POST['sc_cp_id']) && $_POST['sc_cp_id']) { - $sql = " insert into {$g5['g5_shop_coupon_log_table']} - set cp_id = '{$_POST['sc_cp_id']}', - mb_id = '{$member['mb_id']}', - od_id = '$od_id', - cp_price = '$tot_sc_cp_price', - cl_datetime = '".G5_TIME_YMDHIS."' "; - sql_query($sql); + if(!$coupon_duplicate && isset($_POST['sc_cp_id']) && $_POST['sc_cp_id']) { + // 쿠폰 이중사용 방지: INSERT 직전 재확인 + if(is_used_coupon($member['mb_id'], $_POST['sc_cp_id'])) { + $coupon_duplicate = true; + } else { + $sql = " insert into {$g5['g5_shop_coupon_log_table']} + set cp_id = '{$_POST['sc_cp_id']}', + mb_id = '{$member['mb_id']}', + od_id = '$od_id', + cp_price = '$tot_sc_cp_price', + cl_datetime = '".G5_TIME_YMDHIS."' "; + sql_query($sql, false); + + $cp_affected = function_exists('get_sql_affected_rows') ? get_sql_affected_rows() : 0; + if ($cp_affected == 0) + $coupon_duplicate = true; + } } } +// 쿠폰 이중사용 감지 시 결제 취소 및 주문 삭제 +if ($coupon_duplicate) { + // 이미 기록된 쿠폰 로그 삭제 + sql_query(" delete from {$g5['g5_shop_coupon_log_table']} where od_id = '$od_id' ", false); + + // PG 결제 취소 + if ($tno) { + $cancel_msg = '쿠폰 이중사용 감지'; + include G5_SHOP_PATH.'/cancel_pg.inc.php'; + } + + // 포인트 원복 + if ($is_member && $od_receipt_point) + insert_point($member['mb_id'], $od_receipt_point, "주문번호 $od_id 쿠폰 이중사용으로 결제취소 포인트 원복", '@shop_order', $od_id, 'cancel'); + + // 주문 삭제 + sql_query(" delete from {$g5['g5_shop_order_table']} where od_id = '$od_id' ", false); + + // 장바구니 원복 + sql_query(" update {$g5['g5_shop_cart_table']} set od_id = '$tmp_cart_id', ct_status = '쇼핑' where od_id = '$od_id' ", false); + + if(function_exists('add_order_post_log')) add_order_post_log('쿠폰 이중사용 감지로 주문 취소'); + alert('쿠폰이 이미 사용되었습니다. 다시 주문해 주십시오.', G5_SHOP_URL.'/orderform.php'); +} + include_once(G5_SHOP_PATH.'/ordermail1.inc.php'); include_once(G5_SHOP_PATH.'/ordermail2.inc.php'); diff --git a/shop/cancel_pg.inc.php b/shop/cancel_pg.inc.php new file mode 100644 index 000000000..17e832657 --- /dev/null +++ b/shop/cancel_pg.inc.php @@ -0,0 +1,34 @@ +