diff --git a/lib/kcp_notification.lib.php b/lib/kcp_notification.lib.php index d0a2602cc..c77a4b31a 100644 --- a/lib/kcp_notification.lib.php +++ b/lib/kcp_notification.lib.php @@ -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; } diff --git a/lib/shop.cartvalidate.lib.php b/lib/shop.cartvalidate.lib.php index cf45133e1..9481db52f 100644 --- a/lib/shop.cartvalidate.lib.php +++ b/lib/shop.cartvalidate.lib.php @@ -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 ''; } diff --git a/lib/shop.easypay.lib.php b/lib/shop.easypay.lib.php index ec97ed7dd..100cf1f3f 100644 --- a/lib/shop.easypay.lib.php +++ b/lib/shop.easypay.lib.php @@ -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 = ' '; + $attrs = isset($provider[3]) ? ' data-case="'.$pay_code.'"' : ''; + if ($money) { + $attrs .= ' data-money="1"'; + } + + $html = ' ' + .''; + return $mobile ? '