refactor: 장바구니·KCP 통보·간편결제 코드의 분기와 출력 가독성 개선

KCP 거래 검증과 가상계좌 처리를 분리하고 압축된 조건문과 대입문을 풀어 쓴다. 장바구니 검증에는 명시적인 블록과 단계 설명을 추가하고, 간편결제 표시값을 지역변수로 명명하여 HTML 조합을 읽기 쉽게 정리한다.

기존 검증 순서, SQL 처리 순서, 통보 응답 및 HTML 출력은 유지한다. KVE-2026-2345 및 KVE-2026-2346 수정 코드의 가독성 정리이며 새로운 보안 정책 변경은 없다.

검증: 장바구니 공통 46개, MyISAM·InnoDB 장바구니 HTTP/DB 126개 및 KCP HTTP/DB 420개 통과. 간편결제 PHP·JavaScript 회귀 검사와 HTML 변경 전후 100개 조합 일치, PHP 문법 및 diff 공백 검사 통과. 실제 외부 PG 승인·입금은 수행하지 않았다.
This commit is contained in:
whitedot
2026-09-10 05:17:59 +00:00
parent 97b5d7e122
commit 062b33dde3
3 changed files with 273 additions and 105 deletions
+203 -75
View File
@@ -4,38 +4,57 @@ if (!defined('_GNUBOARD_')) exit;
// KVE-2026-2346: 테스트 통보도 발신지와 저장된 거래를 모두 검증한다.
function kcp_noti_request($post, $server, $default)
{
if (!isset($server['REQUEST_METHOD']) || $server['REQUEST_METHOD'] !== 'POST') return false;
if (!isset($server['REQUEST_METHOD']) || $server['REQUEST_METHOD'] !== 'POST') {
return false;
}
// https://developer.kcp.co.kr/guide/webhook (2026-09-10 확인)
$ips = !empty($default['de_card_test'])
? array('210.122.176.144')
: array('103.215.144.173', '103.215.144.174', '210.122.72.173');
if (!isset($server['REMOTE_ADDR']) || !in_array($server['REMOTE_ADDR'], $ips, true)) return false;
if (!isset($server['REMOTE_ADDR']) || !in_array($server['REMOTE_ADDR'], $ips, true)) {
return false;
}
// 전달 헤더나 로그인 세션은 PG 발신 인증에 사용하지 않는다.
$patterns = array(
'site_cd' => '/\A[A-Z0-9]{5}\z/', 'tno' => '/\A[0-9]{14}\z/',
'order_no' => '/\A[1-9][0-9]{0,19}\z/', 'tx_cd' => '/\ATX0[0-7]\z/',
'site_cd' => '/\A[A-Z0-9]{5}\z/',
'tno' => '/\A[0-9]{14}\z/',
'order_no' => '/\A[1-9][0-9]{0,19}\z/',
'tx_cd' => '/\ATX0[0-7]\z/',
'tx_tm' => '/\A[0-9]{14}\z/'
);
if (isset($post['tx_cd']) && $post['tx_cd'] === 'TX00') {
$patterns += array('ipgm_mnyx' => '/\A[0-9]{1,12}\z/',
'account' => '/\A[T]?[0-9]{1,19}\z/', 'noti_id' => '/\A[0-9]{20}\z/',
'op_cd' => '/\A(?:50|13)\z/');
$patterns += array(
'ipgm_mnyx' => '/\A[0-9]{1,12}\z/',
'account' => '/\A[T]?[0-9]{1,19}\z/',
'noti_id' => '/\A[0-9]{20}\z/',
'op_cd' => '/\A(?:50|13)\z/'
);
}
$data = array();
foreach ($patterns as $name => $pattern) {
if (!isset($post[$name]) || !is_string($post[$name]) || !preg_match($pattern, $post[$name])) return false;
if (!isset($post[$name]) || !is_string($post[$name]) || !preg_match($pattern, $post[$name])) {
return false;
}
$data[$name] = $post[$name];
}
$time = $data['tx_tm'];
if ((int)substr($time, 0, 4) < 1000 || !checkdate((int)substr($time, 4, 2), (int)substr($time, 6, 2), (int)substr($time, 0, 4))
|| substr($time, 8, 2) > 23 || substr($time, 10, 2) > 59 || substr($time, 12, 2) > 59) return false;
if ((int) substr($time, 0, 4) < 1000 || !checkdate((int) substr($time, 4, 2), (int) substr($time, 6, 2), (int) substr($time, 0, 4))
|| substr($time, 8, 2) > 23 || substr($time, 10, 2) > 59 || substr($time, 12, 2) > 59) {
return false;
}
$test = in_array($data['site_cd'], array('T0000', 'T0007'), true);
if ($test !== !empty($default['de_card_test'])) return false;
if (!$test && substr($data['site_cd'], 0, 2) !== 'SR') return false;
if ($test !== !empty($default['de_card_test'])) {
return false;
}
if (!$test && substr($data['site_cd'], 0, 2) !== 'SR') {
return false;
}
if ($data['tx_cd'] === 'TX00') {
// 주문 금액 컬럼은 signed INT이다. 변환 전에 범위를 검사한다.
if ((float)$data['ipgm_mnyx'] < 1 || (float)$data['ipgm_mnyx'] > 2147483647) return false;
$data['ipgm_mnyx'] = (string)(int)$data['ipgm_mnyx'];
if ((float) $data['ipgm_mnyx'] < 1 || (float) $data['ipgm_mnyx'] > 2147483647) {
return false;
}
$data['ipgm_mnyx'] = (string) (int) $data['ipgm_mnyx'];
}
return $data;
}
@@ -43,7 +62,9 @@ function kcp_noti_request($post, $server, $default)
function kcp_noti_query($sql)
{
$result = sql_query($sql, false);
if (!$result) throw new Exception('KCP notification database failure');
if (!$result) {
throw new Exception('KCP notification database failure');
}
return $result;
}
@@ -51,13 +72,15 @@ function kcp_noti_rows($sql)
{
$result = kcp_noti_query($sql);
$rows = array();
while ($row = sql_fetch_array($result)) $rows[] = $row;
while ($row = sql_fetch_array($result)) {
$rows[] = $row;
}
return $rows;
}
function kcp_noti_quote($value)
{
return "'".sql_escape_string((string)$value)."'";
return "'".sql_escape_string((string) $value)."'";
}
function kcp_noti_tables()
@@ -76,7 +99,9 @@ function kcp_noti_process($data)
// 기본 설치의 MyISAM에서도 다른 통보 및 관리자 UPDATE와 경쟁하지 않게 한다.
// DDL은 관리자 DB 업그레이드에서만 실행하며 스키마/권한이 없으면 실패 응답한다.
$locks = array();
foreach ($tables as $table) $locks[] = '`'.$table.'` WRITE';
foreach ($tables as $table) {
$locks[] = '`'.$table.'` WRITE';
}
kcp_noti_query('LOCK TABLES '.implode(', ', $locks));
$locked = true;
$success = kcp_noti_locked($data, $tables);
@@ -104,19 +129,25 @@ function kcp_noti_step($table, $id_field, $row, $changes, $guards)
{
$before = array();
foreach (array_unique(array_merge(array($id_field), array_keys($changes), $guards)) as $field) {
if (!array_key_exists($field, $row)) throw new Exception('Missing notification schema');
$before[$field] = (string)$row[$field];
if (!array_key_exists($field, $row)) {
throw new Exception('Missing notification schema');
}
$before[$field] = (string) $row[$field];
}
$after = $before;
foreach ($changes as $field => $value) $after[$field] = (string)$value;
return array('table' => $table, 'id_field' => $id_field, 'id' => (string)$row[$id_field],
foreach ($changes as $field => $value) {
$after[$field] = (string) $value;
}
return array('table' => $table, 'id_field' => $id_field, 'id' => (string) $row[$id_field],
'before' => $before, 'after' => $after);
}
function kcp_noti_matches($row, $expected)
{
foreach ($expected as $field => $value) {
if (!array_key_exists($field, $row) || (string)$row[$field] !== $value) return false;
if (!array_key_exists($field, $row) || (string) $row[$field] !== $value) {
return false;
}
}
return true;
}
@@ -126,19 +157,28 @@ function kcp_noti_apply($plan, $tables)
// 먼저 모든 행을 검사한다. 실패 후 외부에서 수정한 행을 과거 값으로 덮어쓰지 않는다.
foreach ($plan as $step) {
$rows = kcp_noti_rows('SELECT * FROM `'.$tables[$step['table']].'` WHERE `'.$step['id_field'].'` = '.kcp_noti_quote($step['id']));
if (count($rows) !== 1 || (!kcp_noti_matches($rows[0], $step['before']) && !kcp_noti_matches($rows[0], $step['after']))) return false;
if (count($rows) !== 1 || (!kcp_noti_matches($rows[0], $step['before']) && !kcp_noti_matches($rows[0], $step['after']))) {
return false;
}
}
foreach ($plan as $step) {
$sets = array();
foreach ($step['after'] as $field => $value) {
if ($value !== $step['before'][$field]) $sets[] = '`'.$field.'` = '.kcp_noti_quote($value);
if ($value !== $step['before'][$field]) {
$sets[] = '`'.$field.'` = '.kcp_noti_quote($value);
}
}
if ($sets) {
$sql = 'UPDATE `'.$tables[$step['table']].'` SET '.implode(', ', $sets)
.' WHERE `'.$step['id_field'].'` = '.kcp_noti_quote($step['id']);
kcp_noti_query($sql);
}
if ($sets) kcp_noti_query('UPDATE `'.$tables[$step['table']].'` SET '.implode(', ', $sets)
.' WHERE `'.$step['id_field'].'` = '.kcp_noti_quote($step['id']));
}
foreach ($plan as $step) {
$rows = kcp_noti_rows('SELECT * FROM `'.$tables[$step['table']].'` WHERE `'.$step['id_field'].'` = '.kcp_noti_quote($step['id']));
if (count($rows) !== 1 || !kcp_noti_matches($rows[0], $step['after'])) return false;
if (count($rows) !== 1 || !kcp_noti_matches($rows[0], $step['after'])) {
return false;
}
}
return true;
}
@@ -149,81 +189,153 @@ function kcp_noti_locked($data, $tables)
$personal = kcp_noti_rows("SELECT * FROM `{$tables['personal']}` WHERE pp_id = $order_no");
$orders = kcp_noti_rows("SELECT * FROM `{$tables['order']}` WHERE od_id = $order_no");
// 동일 번호가 두 종류에 존재하면 임의로 한쪽을 선택하지 않는다.
if ($personal && $orders) return false;
if ($personal && $orders) {
return false;
}
$pp = $personal ? $personal[0] : null;
if ($pp) {
$prefix = 'pp'; $payment = $pp;
$prefix = 'pp';
$payment = $pp;
$orders = $pp['od_id'] ? kcp_noti_rows("SELECT * FROM `{$tables['order']}` WHERE od_id = ".kcp_noti_quote($pp['od_id'])) : array();
if ($pp['od_id'] && !$orders) return false;
if ($pp['od_id'] && !$orders) {
return false;
}
} else {
if (!$orders) return false;
$prefix = 'od'; $payment = $orders[0];
if (!$orders) {
return false;
}
$prefix = 'od';
$payment = $orders[0];
}
$od = $orders ? $orders[0] : null;
if ($payment[$prefix.'_pg'] !== 'kcp' || $payment[$prefix.'_tno'] !== $data['tno']
|| empty($payment[$prefix.'_kcp_site_cd']) || $payment[$prefix.'_kcp_site_cd'] !== $data['site_cd']) return false;
|| empty($payment[$prefix.'_kcp_site_cd']) || $payment[$prefix.'_kcp_site_cd'] !== $data['site_cd']) {
return false;
}
// 처리하지 않는 기존 에스크로 이벤트는 거래를 확인한 뒤 수신만 확인한다.
if ($data['tx_cd'] !== 'TX00') return true;
if ($payment[$prefix.'_settle_case'] !== '가상계좌') return false;
if ($od && (bool)$od['od_test'] !== in_array($data['site_cd'], array('T0000', 'T0007'), true)) return false;
if ($data['tx_cd'] !== 'TX00') {
return true;
}
if ($payment[$prefix.'_settle_case'] !== '가상계좌') {
return false;
}
if ($od && (bool) $od['od_test'] !== in_array($data['site_cd'], array('T0000', 'T0007'), true)) {
return false;
}
$account_parts = preg_split('/\s+/', trim($payment[$prefix.'_bank_account']));
if (end($account_parts) !== $data['account']) return false;
if (end($account_parts) !== $data['account']) {
return false;
}
return kcp_noti_deposit($data, $tables, $payment, $prefix, $pp, $od);
}
// 거래 검증과 테이블 잠금이 끝난 가상계좌 통보의 이력·금액·상태를 처리한다.
function kcp_noti_deposit($data, $tables, $payment, $prefix, $pp, $od)
{
// 기존 통보 이력과 미완료 처리를 먼저 확인한다.
$trade = hash('sha256', $data['site_cd'].'|'.$data['tno'].'|'.$data['order_no']);
$key = hash('sha256', $trade.'|'.$data['noti_id'].'|'.$data['op_cd']);
$payload = hash('sha256', $data['ipgm_mnyx'].'|'.$data['account']);
$events = kcp_noti_rows("SELECT * FROM `{$tables['event']}` WHERE kn_trade = '$trade'");
$deposit = null; $cancel = null; $active = false;
$deposit = null;
$cancel = null;
$active = false;
$by_id = array();
foreach ($events as $event) {
if ($event['kn_key'] === $key) {
if ($event['kn_payload'] !== $payload) return false;
if ($event['kn_done']) return true;
if ($event['kn_payload'] !== $payload) {
return false;
}
if ($event['kn_done']) {
return true;
}
$plan = json_decode($event['kn_plan'], true);
if (!is_array($plan) || !kcp_noti_apply($plan, $tables)) return false;
if (!is_array($plan) || !kcp_noti_apply($plan, $tables)) {
return false;
}
kcp_noti_query("UPDATE `{$tables['event']}` SET kn_done = 1 WHERE kn_key = '$key'");
return true;
}
if (!$event['kn_done']) return false;
if (!$event['kn_done']) {
return false;
}
$by_id[$event['kn_noti_id']][$event['kn_op_cd']] = $event;
if ($event['kn_noti_id'] === $data['noti_id']) {
if ($event['kn_payload'] !== $payload) return false;
if ($event['kn_op_cd'] === '50') $deposit = $event;
else $cancel = $event;
if ($event['kn_payload'] !== $payload) {
return false;
}
if ($event['kn_op_cd'] === '50') {
$deposit = $event;
} else {
$cancel = $event;
}
}
}
foreach ($by_id as $pair) {
if (isset($pair['50']) && !isset($pair['13'])) $active = true;
if (isset($pair['50']) && !isset($pair['13'])) {
$active = true;
}
}
// 망취소가 먼저 도착했거나 취소 뒤 입금 통보가 재전송되어도 재입금하지 않는다.
if ($data['op_cd'] === '50' && $cancel) return true;
if ($data['op_cd'] === '50' && $cancel) {
return true;
}
$canceling = $data['op_cd'] === '13';
if (!$canceling && $active) return false;
if ($canceling && !$deposit && $active) return false;
if ($pp && !$pp['pp_use']) return false;
$amount = (int)$data['ipgm_mnyx'];
$receipt = (int)$payment[$prefix.'_receipt_price'];
if ($pp && ((int)$pp['pp_price'] !== $amount || (int)$pp['pp_price'] <= 0)) return false;
if (!$canceling && $receipt !== 0) return false;
if ($canceling && $deposit && $receipt < $amount) return false;
if ($canceling && !$deposit && $receipt !== 0) return false;
if (!$canceling && $active) {
return false;
}
if ($canceling && !$deposit && $active) {
return false;
}
if ($pp && !$pp['pp_use']) {
return false;
}
// 통보 금액과 현재 주문·개인결제의 잔액을 대조한다.
$amount = (int) $data['ipgm_mnyx'];
$receipt = (int) $payment[$prefix.'_receipt_price'];
if ($pp && ((int) $pp['pp_price'] !== $amount || (int) $pp['pp_price'] <= 0)) {
return false;
}
if (!$canceling && $receipt !== 0) {
return false;
}
if ($canceling && $deposit && $receipt < $amount) {
return false;
}
if ($canceling && !$deposit && $receipt !== 0) {
return false;
}
$od_id = $od ? $od['od_id'] : '0';
if ($od) {
$pending = kcp_noti_rows("SELECT kn_key FROM `{$tables['event']}` WHERE od_id = ".kcp_noti_quote($od_id)." AND kn_done = 0");
if ($pending) return false;
$due = (int)$od['od_cart_price'] + (int)$od['od_send_cost'] + (int)$od['od_send_cost2']
- (int)$od['od_cart_coupon'] - (int)$od['od_coupon'] - (int)$od['od_send_coupon']
- (int)$od['od_cancel_price'] - (int)$od['od_receipt_point'] + (int)$od['od_refund_price'];
$misu = $due - (int)$od['od_receipt_price'];
if ($misu !== (int)$od['od_misu'] || $due <= 0) return false;
if (!$pp && ($due !== $amount || (int)$od['od_cancel_price'] !== 0 || (int)$od['od_refund_price'] !== 0)) return false;
if (!$canceling && ($od['od_status'] !== '주문' || $misu < $amount)) return false;
if ($canceling && !in_array($od['od_status'], array('주문', '입금', '준비', '배송', '완료'), true)) return false;
if ($canceling && $deposit && (int)$od['od_receipt_price'] < $amount) return false;
if ($pending) {
return false;
}
$due = (int) $od['od_cart_price'] + (int) $od['od_send_cost'] + (int) $od['od_send_cost2']
- (int) $od['od_cart_coupon'] - (int) $od['od_coupon'] - (int) $od['od_send_coupon']
- (int) $od['od_cancel_price'] - (int) $od['od_receipt_point'] + (int) $od['od_refund_price'];
$misu = $due - (int) $od['od_receipt_price'];
if ($misu !== (int) $od['od_misu'] || $due <= 0) {
return false;
}
if (!$pp && ($due !== $amount || (int) $od['od_cancel_price'] !== 0 || (int) $od['od_refund_price'] !== 0)) {
return false;
}
if (!$canceling && ($od['od_status'] !== '주문' || $misu < $amount)) {
return false;
}
if ($canceling && !in_array($od['od_status'], array('주문', '입금', '준비', '배송', '완료'), true)) {
return false;
}
if ($canceling && $deposit && (int) $od['od_receipt_price'] < $amount) {
return false;
}
}
$time = substr($data['tx_tm'], 0, 4).'-'.substr($data['tx_tm'], 4, 2).'-'.substr($data['tx_tm'], 6, 2)
.' '.substr($data['tx_tm'], 8, 2).':'.substr($data['tx_tm'], 10, 2).':'.substr($data['tx_tm'], 12, 2);
// 개인결제, 장바구니, 주문 순서로 변경 전/후 목표값을 구성한다.
$delta = $canceling ? ($deposit ? -$amount : 0) : $amount;
$plan = array();
if ($pp && $delta) {
@@ -232,16 +344,26 @@ function kcp_noti_locked($data, $tables)
array('od_id', 'pp_pg', 'pp_tno', 'pp_kcp_site_cd', 'pp_settle_case', 'pp_price', 'pp_use', 'pp_bank_account'));
}
if ($od && $delta) {
$new_receipt = (int)$od['od_receipt_price'] + $delta;
$new_receipt = (int) $od['od_receipt_price'] + $delta;
$new_misu = $due - $new_receipt;
if ($new_receipt < 0 || $new_receipt > 2147483647 || $new_misu > 2147483647) return false;
if ($new_receipt < 0 || $new_receipt > 2147483647 || $new_misu > 2147483647) {
return false;
}
$status = $od['od_status'];
if (!$canceling && $new_misu === 0) $status = '입금';
if ($canceling && $status === '입금') $status = '주문';
if (!$canceling && $new_misu === 0) {
$status = '입금';
}
if ($canceling && $status === '입금') {
$status = '주문';
}
$cart = kcp_noti_rows("SELECT ct_id, od_id, ct_status FROM `{$tables['cart']}` WHERE od_id = ".kcp_noti_quote($od_id));
if (!$cart) return false;
if (!$cart) {
return false;
}
foreach ($cart as $item) {
if (!$canceling && $item['ct_status'] !== '주문') return false;
if (!$canceling && $item['ct_status'] !== '주문') {
return false;
}
if ($status !== $od['od_status'] && $item['ct_status'] === $od['od_status']) {
$plan[] = kcp_noti_step('cart', 'ct_id', $item, array('ct_status' => $status), array('od_id'));
}
@@ -252,12 +374,18 @@ function kcp_noti_locked($data, $tables)
array('od_pg', 'od_tno', 'od_kcp_site_cd', 'od_settle_case', 'od_test', 'od_cart_price', 'od_send_cost',
'od_send_cost2', 'od_cart_coupon', 'od_coupon', 'od_send_coupon', 'od_cancel_price', 'od_receipt_point', 'od_refund_price'));
}
// 복구에 사용할 계획을 저장한 뒤 적용하고 완료 이력을 기록한다.
$json = json_encode($plan);
if ($json === false) return false;
if ($json === false) {
return false;
}
kcp_noti_query("INSERT INTO `{$tables['event']}` SET kn_key = '$key', kn_trade = '$trade', kn_noti_id = ".kcp_noti_quote($data['noti_id'])
.", kn_op_cd = ".kcp_noti_quote($data['op_cd']).", kn_payload = '$payload', od_id = ".kcp_noti_quote($od_id)
.", kn_plan = ".kcp_noti_quote($json).", kn_created_at = ".kcp_noti_quote($time));
if (!kcp_noti_apply($plan, $tables)) return false;
if (!kcp_noti_apply($plan, $tables)) {
return false;
}
kcp_noti_query("UPDATE `{$tables['event']}` SET kn_done = 1 WHERE kn_key = '$key'");
return true;
}
+57 -27
View File
@@ -6,19 +6,23 @@ if (!defined('_GNUBOARD_')) exit;
function shop_validate_cart_rows($item, $options, $rows, $check_price = false)
{
$error = '상품 또는 옵션 정보가 올바르지 않습니다. 장바구니에서 삭제한 뒤 다시 담아 주십시오.';
if (empty($item['it_id']) || empty($item['it_use']) || !empty($item['it_tel_inq']) || !$rows)
if (empty($item['it_id']) || empty($item['it_use']) || !empty($item['it_tel_inq']) || !$rows) {
return array('error' => $error);
}
$option_map = array();
$has_selection = false;
foreach ($options as $option) {
if ((string) $option['it_id'] !== (string) $item['it_id'])
if ((string) $option['it_id'] !== (string) $item['it_id']) {
return array('error' => $error);
if ((string) $option['io_type'] === '0')
}
if ((string) $option['io_type'] === '0') {
$has_selection = true;
}
$key = $option['io_type'].':'.$option['io_id'];
if (isset($option_map[$key]))
if (isset($option_map[$key])) {
return array('error' => $error);
}
$option_map[$key] = $option;
}
@@ -27,47 +31,58 @@ function shop_validate_cart_rows($item, $options, $rows, $check_price = false)
$base_qty = 0;
$total = 0;
foreach ($rows as $row) {
// 요청값의 형식을 먼저 확인한다.
if (!isset($row['io_id'], $row['io_type'], $row['ct_qty']) ||
!is_string($row['io_id']) ||
!(is_string($row['io_type']) || is_int($row['io_type'])) ||
!in_array((string) $row['io_type'], array('0', '1'), true) ||
!(is_string($row['ct_qty']) || is_int($row['ct_qty'])) ||
!preg_match('/^[1-9][0-9]*$/D', (string) $row['ct_qty']) ||
(float) $row['ct_qty'] > 2147483647)
(float) $row['ct_qty'] > 2147483647) {
return array('error' => $error);
}
// 서버 상품·옵션 정보로 종류와 가격을 결정한다.
$type = (int) $row['io_type'];
if (!$validated && $type !== 0)
if (!$validated && $type !== 0) {
return array('error' => $error);
}
$id = $row['io_id'];
$key = $type.':'.$id;
if ($id === '') {
if ($type !== 0 || $has_selection)
if ($type !== 0 || $has_selection) {
return array('error' => $error);
}
$price = 0;
$stock = (int) $item['it_stock_qty'];
} else {
if (!isset($option_map[$key]) || empty($option_map[$key]['io_use']))
if (!isset($option_map[$key]) || empty($option_map[$key]['io_use'])) {
return array('error' => $error);
}
$option = $option_map[$key];
$type = (int) $option['io_type'];
$price = (int) $option['io_price'];
$stock = (int) $option['io_stock_qty'];
}
// 누적 재고와 저장된 가격을 검사한다.
$qty = (int) $row['ct_qty'];
$quantities[$key] = isset($quantities[$key]) ? $quantities[$key] + $qty : $qty;
if ($quantities[$key] > $stock)
if ($quantities[$key] > $stock) {
return array('error' => '상품 또는 옵션의 재고수량이 부족합니다.');
}
$unit_price = $type === 1 ? $price : (int) $item['it_price'] + $price;
if ($unit_price < 0)
if ($unit_price < 0) {
return array('error' => $error);
}
if ($check_price && (!isset($row['ct_price'], $row['io_price']) ||
(int) $row['ct_price'] !== (int) $item['it_price'] || (int) $row['io_price'] !== $price))
(int) $row['ct_price'] !== (int) $item['it_price'] || (int) $row['io_price'] !== $price)) {
return array('error' => '상품 또는 옵션 금액이 변경되었습니다. 장바구니를 다시 확인해 주십시오.');
}
if ($type === 0)
if ($type === 0) {
$base_qty += $qty;
}
$row['io_type'] = $type;
$row['io_price'] = $price;
$row['ct_price'] = (int) $item['it_price'];
@@ -77,8 +92,9 @@ function shop_validate_cart_rows($item, $options, $rows, $check_price = false)
}
if (!$base_qty || (!empty($item['it_buy_min_qty']) && $base_qty < $item['it_buy_min_qty']) ||
(!empty($item['it_buy_max_qty']) && $base_qty > $item['it_buy_max_qty']))
(!empty($item['it_buy_max_qty']) && $base_qty > $item['it_buy_max_qty'])) {
return array('error' => '상품의 선택옵션과 최소/최대 구매수량을 확인해 주십시오.');
}
return array('error' => '', 'rows' => $validated, 'total' => $total);
}
@@ -90,8 +106,9 @@ function shop_cart_option_data($it_id)
$item = sql_fetch(" select * from {$g5['g5_shop_item_table']} where it_id = '$id' ");
$result = sql_query(" select * from {$g5['g5_shop_item_option_table']} where it_id = '$id' ");
$options = array();
while ($row = sql_fetch_array($result))
while ($row = sql_fetch_array($result)) {
$options[] = $row;
}
return array($item, $options);
}
@@ -101,34 +118,41 @@ function shop_validate_cart_request($post, $multi = false)
$error = '장바구니 요청 정보가 올바르지 않습니다.';
$products = array();
if (empty($post['it_id']) || !is_array($post['it_id']) ||
array_keys($post['it_id']) !== range(0, count($post['it_id']) - 1))
array_keys($post['it_id']) !== range(0, count($post['it_id']) - 1)) {
return array('error' => $error);
}
foreach ($post['it_id'] as $i => $id) {
if ($multi && empty($post['chk_it_id'][$i]))
if ($multi && empty($post['chk_it_id'][$i])) {
continue;
if (!is_string($id) || $id === '' || safe_replace_regex($id, 'it_id') !== $id || isset($products[$id]))
}
if (!is_string($id) || $id === '' || safe_replace_regex($id, 'it_id') !== $id || isset($products[$id])) {
return array('error' => $error);
}
foreach (array('io_id', 'io_type', 'ct_qty') as $field) {
if (!isset($post[$field][$id]) || !is_array($post[$field][$id]) || !$post[$field][$id])
if (!isset($post[$field][$id]) || !is_array($post[$field][$id]) || !$post[$field][$id]) {
return array('error' => $error);
}
}
$keys = range(0, count($post['io_id'][$id]) - 1);
foreach (array('io_id', 'io_type', 'ct_qty') as $field) {
if (array_keys($post[$field][$id]) !== $keys)
if (array_keys($post[$field][$id]) !== $keys) {
return array('error' => $error);
}
}
$rows = array();
foreach ($keys as $k) {
$io_id = $post['io_id'][$id][$k];
if (!is_string($io_id) || preg_replace(G5_OPTION_ID_FILTER, '', $io_id) !== $io_id ||
(isset($post['io_value'][$id][$k]) && !is_string($post['io_value'][$id][$k])))
(isset($post['io_value'][$id][$k]) && !is_string($post['io_value'][$id][$k]))) {
return array('error' => $error);
}
$rows[] = array('io_id' => $io_id, 'io_type' => $post['io_type'][$id][$k], 'ct_qty' => $post['ct_qty'][$id][$k]);
}
list($item, $options) = shop_cart_option_data($id);
$validated = shop_validate_cart_rows($item, $options, $rows);
if ($validated['error'] !== '')
if ($validated['error'] !== '') {
return $validated;
}
$validated['item'] = $item;
$products[$id] = $validated;
}
@@ -147,14 +171,16 @@ function shop_validate_cart_merge($cart_id, $products, $direct = false, $replace
if (!$replace) {
$exclude = $direct ? ' and ct_direct <> 1 ' : '';
$result = sql_query(" select * from {$g5['g5_shop_cart_table']} where od_id = '$cart_id' and it_id = '$id' and ct_status = '쇼핑' $exclude order by ct_id asc ");
while ($row = sql_fetch_array($result))
while ($row = sql_fetch_array($result)) {
$rows[] = $row;
}
}
$rows = array_merge($rows, $product['rows']);
list($item, $options) = shop_cart_option_data($it_id);
$validated = shop_validate_cart_rows($item, $options, $rows, true);
if ($validated['error'] !== '')
if ($validated['error'] !== '') {
return $validated['error'];
}
// 바로구매에서는 다른 장바구니가 선택해 둔 수량도 선삭제 전에 검사한다.
if ($direct) {
@@ -168,8 +194,9 @@ function shop_validate_cart_merge($cart_id, $products, $direct = false, $replace
$type = (int) $row['io_type'];
$reserved = sql_fetch(" select SUM(ct_qty) as cnt from {$g5['g5_shop_cart_table']} where od_id <> '$cart_id' and it_id = '$id' and io_id = '$io_id' and io_type = '$type' and ct_stock_use = 0 and ct_status = '쇼핑' and ct_select = '1' ");
$stock = $row['io_id'] === '' ? get_it_stock_qty($it_id) : get_option_stock_qty($it_id, $row['io_id'], $type);
if ($quantities[$type.':'.$row['io_id']] + (int) $reserved['cnt'] > $stock)
if ($quantities[$type.':'.$row['io_id']] + (int) $reserved['cnt'] > $stock) {
return '상품 또는 옵션의 재고수량이 부족합니다.';
}
}
}
}
@@ -184,17 +211,20 @@ function shop_validate_order_cart($cart_id)
$result = sql_query(" select * from {$g5['g5_shop_cart_table']} where od_id = '$id' and ct_select = '1' order by ct_id asc ");
$products = array();
while ($row = sql_fetch_array($result)) {
if ($row['ct_status'] !== '쇼핑')
if ($row['ct_status'] !== '쇼핑') {
return '이미 처리되었거나 올바르지 않은 장바구니입니다.';
}
$products[$row['it_id']][] = $row;
}
if (!$products)
if (!$products) {
return '주문하실 상품을 선택해 주십시오.';
}
foreach ($products as $it_id => $rows) {
list($item, $options) = shop_cart_option_data($it_id);
$validated = shop_validate_cart_rows($item, $options, $rows, true);
if ($validated['error'] !== '')
if ($validated['error'] !== '') {
return $validated['error'];
}
}
return '';
}
+13 -3
View File
@@ -88,12 +88,22 @@ function shop_easypay_available($key, $mobile)
function shop_easypay_button($key, $provider, $mobile, $money = false)
{
$label = $provider[0];
$pay_code = $provider[1];
$css_class = $provider[2];
$value = isset($provider[3]) ? $provider[3] : '간편결제';
$legacy_ids = array('inicis_samsungpay' => 'samsungpay', 'inicis_lpay' => 'inicislpay');
$id = 'od_settle_'.(isset($legacy_ids[$key]) ? $legacy_ids[$key] : $key);
$attrs = isset($provider[3]) ? ' data-case="'.$provider[1].'"' : '';
if ($money) $attrs .= ' data-money="1"';
$html = '<input type="radio" id="'.$id.'" name="od_settle_case" data-pay="'.$provider[1].'" value="'.$value.'"'.$attrs.'> <label for="'.$id.'" class="'.$provider[2].' '.$key.' lb_icon" title="'.$provider[0].'">'.$provider[0].'</label>';
$attrs = isset($provider[3]) ? ' data-case="'.$pay_code.'"' : '';
if ($money) {
$attrs .= ' data-money="1"';
}
$html = '<input type="radio" id="'.$id.'" name="od_settle_case"'
.' data-pay="'.$pay_code.'" value="'.$value.'"'.$attrs.'> '
.'<label for="'.$id.'" class="'.$css_class.' '.$key.' lb_icon"'
.' title="'.$label.'">'.$label.'</label>';
return $mobile ? '<li>'.$html.'</li>' : $html;
}