From 57983a6dbc6d1282473f6972ec2a40f90681004f Mon Sep 17 00:00:00 2001 From: thisgun Date: Tue, 8 Jul 2025 10:45:10 +0900 Subject: [PATCH 01/31] =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EB=A8=BC=EC=B8=A0=20=EB=A8=B8=ED=8A=B8=ED=82=A4=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=EB=B0=A9=EB=B2=95=20=EC=84=A4=EB=AA=85=EB=AC=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/shop_admin/configform.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adm/shop_admin/configform.php b/adm/shop_admin/configform.php index ac00e0aa7..041a53d36 100644 --- a/adm/shop_admin/configform.php +++ b/adm/shop_admin/configform.php @@ -835,7 +835,7 @@ if(!$default['de_kakaopay_cancelpwd']){ - 계약정보 -> 상점정보관리에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3\n기본환경설정 > 본인확인 설정의 토스페이먼츠 MERT KEY와 동일합니다."); ?> + 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3"); ?> From 255675353052818d484ccaf55f4d63b40367f4a8 Mon Sep 17 00:00:00 2001 From: thisgun Date: Tue, 8 Jul 2025 11:02:20 +0900 Subject: [PATCH 02/31] =?UTF-8?q?url=5Fauto=5Flink=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=20url=5Fauto=5Flink=5Fbefore=20hook=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#361?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.lib.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/common.lib.php b/lib/common.lib.php index a37d0b69d..f0a8901bf 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -257,7 +257,13 @@ function url_auto_link($str) { global $g5; global $config; - + + if ($replace_str = run_replace('url_auto_link_before', '', $str)) { + return $replace_str; + } + + $ori_str = $str; + // 140326 유창화님 제안코드로 수정 // http://sir.kr/pg_lecture/461 // http://sir.kr/pg_lecture/463 @@ -290,7 +296,7 @@ function url_auto_link($str) $str = preg_replace("/\t_gt_\t/", ">", $str); */ - return run_replace('url_auto_link', $str); + return run_replace('url_auto_link', $str, $ori_str); } From 9191199ef4ad0703e1b74a9863dfbceceb230d2a Mon Sep 17 00:00:00 2001 From: thisgun Date: Tue, 8 Jul 2025 11:16:16 +0900 Subject: [PATCH 03/31] =?UTF-8?q?db=5Ftable.optimize.php=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20hook=20=EC=B6=94=EA=B0=80=20#362?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bbs/db_table.optimize.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bbs/db_table.optimize.php b/bbs/db_table.optimize.php index dc92eb385..a751c50ba 100644 --- a/bbs/db_table.optimize.php +++ b/bbs/db_table.optimize.php @@ -69,4 +69,8 @@ if($captcha_mp3 && is_array($captcha_mp3)) { // 실행일 기록 if(isset($config['cf_optimize_date'])) { sql_query(" update {$g5['config_table']} set cf_optimize_date = '".G5_TIME_YMD."' "); -} \ No newline at end of file + + run_event('cf_optimize_date_update', $config); +} + +run_event('db_table_optimize_end', $config); \ No newline at end of file From a9eab8d86ae0ce0805cfdb786ad2f1e33f3a80ac Mon Sep 17 00:00:00 2001 From: thisgun Date: Tue, 8 Jul 2025 11:40:47 +0900 Subject: [PATCH 04/31] =?UTF-8?q?insert=5Fuse=5Fpoint=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=20hook=20=EC=B6=94=EA=B0=80=20#363?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.lib.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/common.lib.php b/lib/common.lib.php index f0a8901bf..dfd161a1d 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -1141,7 +1141,11 @@ function insert_point($mb_id, $point, $content='', $rel_table='', $rel_id='', $r function insert_use_point($mb_id, $point, $po_id='') { global $g5, $config; - + + if ($replace_insert = run_replace('insert_use_point_before', '', $mb_id, $point, $po_id)) { + return $replace_insert; + } + if($config['cf_point_term']) $sql_order = " order by po_expire_date asc, po_id asc "; else From 9602f3c7a7118948d6f31cb39885ca8df63d9ac3 Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 21 Jul 2025 12:48:53 +0900 Subject: [PATCH 05/31] =?UTF-8?q?php=20warning=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bbs/qawrite_update.php | 1 + 1 file changed, 1 insertion(+) diff --git a/bbs/qawrite_update.php b/bbs/qawrite_update.php index 6f6409172..4c6902936 100644 --- a/bbs/qawrite_update.php +++ b/bbs/qawrite_update.php @@ -86,6 +86,7 @@ $qa_related = 0; $qa_email_recv = (isset($_POST['qa_email_recv']) && $_POST['qa_email_recv']) ? 1 : 0; $qa_sms_recv = (isset($_POST['qa_sms_recv']) && $_POST['qa_sms_recv']) ? 1 : 0; $qa_status = 0; +$qa_html = (isset($_POST['qa_html']) && $_POST['qa_html']) ? (int) $_POST['qa_html'] : 0; $answer_id = null; for ($i=1; $i<=5; $i++) { From f1da95f055d6ce22b839eb5272aa02778faee371 Mon Sep 17 00:00:00 2001 From: thisgun Date: Wed, 30 Jul 2025 15:03:01 +0900 Subject: [PATCH 06/31] =?UTF-8?q?Open=20redirect=20=EC=B7=A8=EC=95=BD?= =?UTF-8?q?=EC=A0=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.lib.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/common.lib.php b/lib/common.lib.php index dfd161a1d..5fad9574d 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -3685,6 +3685,10 @@ function check_url_host($url, $msg='', $return_url=G5_URL, $is_redirect=false) alert('url 에 올바르지 않은 값이 포함되어 있습니다.'); } + if (preg_match('#//[^/@]+@#', $url)) { + alert('url에 사용자 정보가 포함되어 있어 접근할 수 없습니다.'); + } + while ( ( $replace_url = preg_replace(array('/\/{2,}/', '/\\@/'), array('//', ''), urldecode($url)) ) != $url ) { $url = $replace_url; } From 9d5f8e137fb7a83b89f9db15f0dbf05633f7459c Mon Sep 17 00:00:00 2001 From: thisgun Date: Thu, 31 Jul 2025 17:12:43 +0900 Subject: [PATCH 07/31] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=EC=8B=9C=20=EC=83=88=EA=B8=80=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20OPIMIZE=20=EC=8B=A4=ED=96=89=EC=8B=9C=20?= =?UTF-8?q?=EB=94=9C=EB=A0=88=EC=9D=B4=20=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EA=B0=9C=EC=84=A02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bbs/db_table.optimize.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bbs/db_table.optimize.php b/bbs/db_table.optimize.php index a751c50ba..6ec1875dd 100644 --- a/bbs/db_table.optimize.php +++ b/bbs/db_table.optimize.php @@ -14,7 +14,9 @@ if($config['cf_visit_del'] > 0) { $tmp_before_date = date("Y-m-d", G5_SERVER_TIME - ($config['cf_visit_del'] * 86400)); $sql = " delete from {$g5['visit_table']} where vi_date < '$tmp_before_date' "; sql_query($sql); - sql_query(" OPTIMIZE TABLE `{$g5['visit_table']}`, `{$g5['visit_sum_table']}` "); + if (defined('G5_USE_OPTIMIZE_DBTABLE') && G5_USE_OPTIMIZE_DBTABLE) { + sql_query(" OPTIMIZE TABLE `{$g5['visit_table']}`, `{$g5['visit_sum_table']}` "); + } } // 설정일이 지난 인기검색어 삭제 @@ -22,7 +24,9 @@ if($config['cf_popular_del'] > 0) { $tmp_before_date = date("Y-m-d", G5_SERVER_TIME - ($config['cf_popular_del'] * 86400)); $sql = " delete from {$g5['popular_table']} where pp_date < '$tmp_before_date' "; sql_query($sql); - sql_query(" OPTIMIZE TABLE `{$g5['popular_table']}` "); + if (defined('G5_USE_OPTIMIZE_DBTABLE') && G5_USE_OPTIMIZE_DBTABLE) { + sql_query(" OPTIMIZE TABLE `{$g5['popular_table']}` "); + } } // 설정일이 지난 최근게시물 삭제 @@ -40,7 +44,9 @@ if($config['cf_new_del'] > 0) { if($config['cf_memo_del'] > 0) { $sql = " delete from {$g5['memo_table']} where (TO_DAYS('".G5_TIME_YMDHIS."') - TO_DAYS(me_send_datetime)) > '{$config['cf_memo_del']}' "; sql_query($sql); - sql_query(" OPTIMIZE TABLE `{$g5['memo_table']}` "); + if (defined('G5_USE_OPTIMIZE_DBTABLE') && G5_USE_OPTIMIZE_DBTABLE) { + sql_query(" OPTIMIZE TABLE `{$g5['memo_table']}` "); + } } // 탈퇴회원 자동 삭제 From 9758007f910fd4a38be73b996c27c4cde2e95785 Mon Sep 17 00:00:00 2001 From: thisgun Date: Thu, 31 Jul 2025 20:21:22 +0900 Subject: [PATCH 08/31] =?UTF-8?q?NHN=5FKCP=20=EB=84=A4=EC=9D=B4=EB=B2=84?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=20=EA=B0=84=ED=8E=B8=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EB=98=90=EB=8A=94=20=EB=A8=B8=EB=8B=88?= =?UTF-8?q?=EA=B2=B0=EC=A0=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/default_shop.css | 3 +++ css/mobile_shop.css | 2 ++ lib/shop.lib.php | 2 +- mobile/shop/kcp/m_order.script.php | 8 ++++++ mobile/shop/kcp/order_approval_form.php | 2 +- mobile/shop/kcp/orderform.1.php | 3 +++ mobile/shop/kcp/pp_ax_hub.php | 5 +++- mobile/shop/orderform.sub.php | 30 +++++++++++++++++++--- shop/kcp/global_nhn_kcp_order.script.php | 8 ++++++ shop/kcp/pp_ax_hub.php | 5 +++- shop/orderform.sub.php | 32 +++++++++++++++++++++--- theme/basic/css/default_shop.css | 2 ++ theme/basic/css/mobile_shop.css | 2 ++ 13 files changed, 92 insertions(+), 12 deletions(-) diff --git a/css/default_shop.css b/css/default_shop.css index ec5188f20..8f6191f2e 100644 --- a/css/default_shop.css +++ b/css/default_shop.css @@ -812,6 +812,7 @@ box-shadow: 1px 2px 2px #eee;} #sod_frm_paysel .inicis_kakaopay em{position:absolute;top:15px;left:45px;width:70px;height:30px;background:url('../img/kakao.png') no-repeat 50% 50% #ffeb00;overflow:hidden;text-indent:-999px;border-radius:30px} #sod_frm_paysel .kakaopay_icon{background:url('../img/kakao.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} #sod_frm_paysel .naverpay_icon{background:url('../img/ico-default-naverpay.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} +#sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;background-position: 50% 30%;padding-top:35px;padding-left:0;text-align:center} #sod_frm_paysel .samsungpay_icon{background:url('../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .ssgpay_icon{background:url('../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .skpay_icon{background:url('../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px} @@ -1151,6 +1152,8 @@ box-shadow: 1px 2px 2px #eee;} .sod_frm_mobile #m_sod_frm_paysel .inicis_lpay{background:url(../img/lpay_logo.png) no-repeat;width:35px;height:12px;overflow:hidden;text-indent:-999px;display:inline-block;background-size:100%} .sod_frm_mobile #m_sod_frm_paysel .inicis_kakaopay{background:url(../img/kakao.png) no-repeat 50% 50% #f4dc34;border-radius:30px;height:22px;width:74px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} .sod_frm_mobile #m_sod_frm_paysel .kakaopay_icon{background:url(../img/kakao.png) no-repeat 50% 50% #f4dc34;border-radius:30px;height:22px;width:74px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} +.sod_frm_mobile #m_sod_frm_paysel .naverpay_icon{background:url(../img/ico-default-naverpay.png) no-repeat 50% 50% #fff;border-radius:30px;height:22px;width:50px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} +.sod_frm_mobile #m_sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;padding-left:50px;padding-top:2px;width:83px} .sod_frm_mobile #m_sod_frm_paysel .applepay_icon{background:url(../img/ico-mobile-applepay.png) no-repeat 50% 50% #fff;border-radius:30px;height:23px;width:50px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} .sod_frm_mobile #m_sod_frm_paysel .samsung_pay{margin-left:-23px;background:url(../img/samsungpay.png) no-repeat 24px 3px;height:25px;width:106px;display:inline-block;overflow:hidden;text-indent:-999px} .sod_frm_mobile #sod_frm_pay{border-top:1px solid #f3f3f3} diff --git a/css/mobile_shop.css b/css/mobile_shop.css index 07849cb09..748730fc3 100644 --- a/css/mobile_shop.css +++ b/css/mobile_shop.css @@ -228,6 +228,7 @@ box-shadow: 0 0 6px rgba(0,0,0,0.2);} #m_sod_frm_paysel .inicis_kakaopay{background:url('../img/kakao.png') no-repeat 50% 50% #ffeb00;border-radius:30px;height:26px;width:74px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:36px auto} #m_sod_frm_paysel .kakaopay_icon{background:url('../img/ico-mobile-kakaopay.png') no-repeat #fff;height:23px;width:63px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:45px auto;background-position: 10% 40%} #m_sod_frm_paysel .naverpay_icon{background:url('../img/ico-mobile-naverpay.png') no-repeat #fff;height:23px;width:60px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:45px auto;background-position: 0% 30%} +#m_sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;padding-left:50px;padding-top:2px;width:83px} #m_sod_frm_paysel .applepay_icon{background:url('../img/ico-mobile-applepay.png') no-repeat #fff;height:30px;width:60px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:43px auto} #m_sod_frm_paysel .ssgpay_icon{background:url('../img/ssgpay_icon.png') no-repeat 0px 3px #fff;width:55px;height:20px;background-size:100%;display:inline-block;overflow:hidden;text-indent:-999px} #m_sod_frm_paysel .skpay_icon{background:url('../img/skpay11_icon.png') no-repeat 0px 3px #fff;width:55px;height:20px;background-size:100%;display:inline-block;overflow:hidden;text-indent:-999px} @@ -914,6 +915,7 @@ box-shadow:0 0 8px rgba(65,98,255,0.8)} #sod_frm_paysel .inicis_kakaopay{background:url('../img/kakao.png') no-repeat 50% 50% #f4dc34;overflow:hidden;text-indent:-999px} #sod_frm_paysel .kakaopay_icon{background:url('../img/kakao.png') no-repeat 50% 50% #f4dc34;overflow:hidden;text-indent:-999px} #sod_frm_paysel .naverpay_icon{background:url('../img/ico-default-naverpay.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} +#sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;background-position: 50% 30%;padding-top:35px;padding-left:0;text-align:center} #sod_frm_paysel .samsungpay_icon{background:url('../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .ssgpay_icon{background:url('../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .skpay_icon{background:url('../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px} diff --git a/lib/shop.lib.php b/lib/shop.lib.php index 1b2f4c885..4825dcd69 100644 --- a/lib/shop.lib.php +++ b/lib/shop.lib.php @@ -2823,7 +2823,7 @@ function check_pay_name_replace($payname, $od=array(), $is_client=0){ } else if( isset($od['od_pg']) && $od['od_pg'] === 'inicis' ){ return 'KPAY'; } else if( isset($od['od_pg']) && $od['od_pg'] === 'kcp' ){ - if( isset($od['od_other_pay_type']) && $od['od_other_pay_type'] === 'OT16' ){ + if( isset($od['od_other_pay_type']) && ($od['od_other_pay_type'] === 'OT16' || $od['od_other_pay_type'] === 'NHNKCP_NAVERMONEY')){ return '네이버페이_NHNKCP'.$add_str; } else if( isset($od['od_other_pay_type']) && ($od['od_other_pay_type'] === 'OT13' || $od['od_other_pay_type'] === 'NHNKCP_KAKAOMONEY') ){ return '카카오페이_NHNKCP'.$add_str; diff --git a/mobile/shop/kcp/m_order.script.php b/mobile/shop/kcp/m_order.script.php index bfa82f97e..fda560078 100644 --- a/mobile/shop/kcp/m_order.script.php +++ b/mobile/shop/kcp/m_order.script.php @@ -23,6 +23,14 @@ jQuery(function($){ if( nhnkcp_settle_case == "naverpay" ){ if(typeof nhnkcp_pay_form.naverpay_direct !== "undefined") nhnkcp_pay_form.naverpay_direct.value = "Y"; + + var is_money = jQuery("input[name='od_settle_case']:checked" ).attr("data-money"); + + if (is_money) { // 머니/포인트 결제 + jQuery(nhnkcp_pay_form).find("input[name='naverpay_point_direct']").val("Y"); + } else { // 카드 결제 + jQuery(nhnkcp_pay_form).find("input[name='naverpay_point_direct']").val(""); + } } if( ! jQuery("form[name='sm_form']").length ){ diff --git a/mobile/shop/kcp/order_approval_form.php b/mobile/shop/kcp/order_approval_form.php index d42b865a9..1c325d7da 100644 --- a/mobile/shop/kcp/order_approval_form.php +++ b/mobile/shop/kcp/order_approval_form.php @@ -279,7 +279,7 @@ if($enc_data != '' && $enc_info != '' && $tran_cd != '') { - + diff --git a/mobile/shop/kcp/orderform.1.php b/mobile/shop/kcp/orderform.1.php index 101548a20..06bf875f6 100644 --- a/mobile/shop/kcp/orderform.1.php +++ b/mobile/shop/kcp/orderform.1.php @@ -23,6 +23,9 @@ $param_opt_3 = isset($_REQUEST['param_opt_3']) ? clean_xss_tags($_REQUEST['param + + + diff --git a/mobile/shop/kcp/pp_ax_hub.php b/mobile/shop/kcp/pp_ax_hub.php index 8b52d4ef8..0a547573a 100644 --- a/mobile/shop/kcp/pp_ax_hub.php +++ b/mobile/shop/kcp/pp_ax_hub.php @@ -223,11 +223,14 @@ $kcp_pay_method = $c_PayPlus->mf_get_res_data( "pay_method" ); // 카카오페이 결제수단 // 카드 코드는 PACA, 카카오머니 코드는 PAKM - + // https://developer.kcp.co.kr/page/document/directpay + if( $kcp_pay_method == "PAKM" ){ // 카카오머니 $card_mny = $kakaomny_mny = $c_PayPlus->mf_get_res_data( "kakaomny_mny" ); $app_time = $app_kakaomny_time = $c_PayPlus->mf_get_res_data( "app_kakaomny_time" ); $od_other_pay_type = 'NHNKCP_KAKAOMONEY'; + } else if( $kcp_pay_method == "PANP" ){ // 네이버페이머니 + $od_other_pay_type = 'NHNKCP_NAVERMONEY'; } } diff --git a/mobile/shop/orderform.sub.php b/mobile/shop/orderform.sub.php index 4a42b6610..d4befcae4 100644 --- a/mobile/shop/orderform.sub.php +++ b/mobile/shop/orderform.sub.php @@ -621,7 +621,13 @@ if($is_kakaopay_use) { $easypay_prints['nhnkcp_payco'] = '
  • '; } if( in_array('nhnkcp_naverpay', $de_easy_pay_service_array) ){ - $easypay_prints['nhnkcp_naverpay'] = '
  • '; + if(isset($default['de_easy_pay_services']) && in_array('used_nhnkcp_naverpay_point', explode(',', $default['de_easy_pay_services'])) ){ + $easypay_prints['nhnkcp_naverpay_card'] = '
  • '; + + $easypay_prints['nhnkcp_naverpay_money'] = '
  • '; + } else { + $easypay_prints['nhnkcp_naverpay_card'] = '
  • '; + } } if( in_array('nhnkcp_kakaopay', $de_easy_pay_service_array) ){ $easypay_prints['nhnkcp_kakaopay'] = '
  • '; @@ -663,7 +669,14 @@ if($is_kakaopay_use) { } if( ! isset($easypay_prints['nhnkcp_naverpay']) && function_exists('is_use_easypay') && is_use_easypay('global_nhnkcp') ){ - $easypay_prints['nhnkcp_naverpay'] = '
  • '; + + if(isset($default['de_easy_pay_services']) && in_array('used_nhnkcp_naverpay_point', explode(',', $default['de_easy_pay_services'])) ){ + $easypay_prints['nhnkcp_naverpay_card'] = '
  • '; + + $easypay_prints['nhnkcp_naverpay_money'] = '
  • '; + } else { + $easypay_prints['nhnkcp_naverpay'] = '
  • '; + } } if($easypay_prints) { @@ -1058,7 +1071,7 @@ $(function() { $("#show_pay_btn").css("display", "inline"); }); - $("#od_settle_iche,#od_settle_card,#od_settle_vbank,#od_settle_hp,#od_settle_easy_pay,#od_settle_kakaopay,#od_settle_samsungpay,#od_settle_nhnkcp_payco,#od_settle_nhnkcp_naverpay,#od_settle_nhnkcp_kakaopay,#od_settle_inicislpay,#od_settle_inicis_kakaopay").bind("click", function() { + $("#od_settle_iche,#od_settle_card,#od_settle_vbank,#od_settle_hp,#od_settle_easy_pay,#od_settle_kakaopay,#od_settle_samsungpay,#od_settle_nhnkcp_payco,#od_settle_nhnkcp_naverpay,#od_settle_nhnkcp_naverpay_money,#od_settle_nhnkcp_kakaopay,#od_settle_inicislpay,#od_settle_inicis_kakaopay").bind("click", function() { $("#settle_bank").hide(); $("#show_req_btn").css("display", "inline"); $("#show_pay_btn").css("display", "none"); @@ -1338,12 +1351,21 @@ function pay_approval() if(typeof f.pay_method !== "undefined") f.pay_method.value = ""; if(settle_method == "간편결제"){ - var nhnkcp_easy_pay = jQuery("input[name='od_settle_case']:checked" ).attr("data-pay"); + var nhnkcp_easy_pay = jQuery("input[name='od_settle_case']:checked").attr("data-pay"); if(nhnkcp_easy_pay === "naverpay"){ if(typeof f.naverpay_direct !== "undefined"){ f.naverpay_direct.value = "Y"; } + + var is_money = jQuery("input[name='od_settle_case']:checked").attr("data-money"); + + if (is_money) { // 머니/포인트 결제 + jQuery(f).find("input[name='naverpay_point_direct']").val("Y"); + } else { // 카드 결제 + jQuery(f).find("input[name='naverpay_point_direct']").val(""); + } + } else if(nhnkcp_easy_pay === "kakaopay"){ if(typeof f.kakaopay_direct !== "undefined") f.kakaopay_direct.value = "Y"; } else if(nhnkcp_easy_pay === "applepay"){ diff --git a/shop/kcp/global_nhn_kcp_order.script.php b/shop/kcp/global_nhn_kcp_order.script.php index 9d72cb5b8..633d5b6bb 100644 --- a/shop/kcp/global_nhn_kcp_order.script.php +++ b/shop/kcp/global_nhn_kcp_order.script.php @@ -28,6 +28,14 @@ jQuery(function($){ if( nhnkcp_settle_case == "naverpay" ){ if(typeof nhnkcp_pay_form.naverpay_direct !== "undefined") nhnkcp_pay_form.naverpay_direct.value = "Y"; + + var is_money = jQuery("input[name='od_settle_case']:checked" ).attr("data-money"); + + if (is_money) { // 머니/포인트 결제 + jQuery(nhnkcp_pay_form).find("input[name='naverpay_point_direct']").val("Y"); + } else { // 카드 결제 + jQuery(nhnkcp_pay_form).find("input[name='naverpay_point_direct']").val(""); + } } nhnkcp_pay_form.pay_method.value = "100000000000"; diff --git a/shop/kcp/pp_ax_hub.php b/shop/kcp/pp_ax_hub.php index 4e7606ef4..61fcd41ad 100644 --- a/shop/kcp/pp_ax_hub.php +++ b/shop/kcp/pp_ax_hub.php @@ -293,11 +293,14 @@ if ( $req_tx == "pay" ) $kcp_pay_method = $c_PayPlus->mf_get_res_data( "pay_method" ); // 카카오페이 결제수단 // 카드 코드는 PACA, 카카오머니 코드는 PAKM - + // https://developer.kcp.co.kr/page/document/directpay + if( $kcp_pay_method == "PAKM" ){ // 카카오머니 $card_mny = $kakaomny_mny = $c_PayPlus->mf_get_res_data( "kakaomny_mny" ); $app_time = $app_kakaomny_time = $c_PayPlus->mf_get_res_data( "app_kakaomny_time" ); $od_other_pay_type = 'NHNKCP_KAKAOMONEY'; + } else if( $kcp_pay_method == "PANP" ){ // 네이버페이머니 + $od_other_pay_type = 'NHNKCP_NAVERMONEY'; } /* = -------------------------------------------------------------- = */ diff --git a/shop/orderform.sub.php b/shop/orderform.sub.php index 1353b1a8a..7ecbc9720 100644 --- a/shop/orderform.sub.php +++ b/shop/orderform.sub.php @@ -640,7 +640,15 @@ if($is_kakaopay_use) { $easypay_prints['nhnkcp_payco'] = ' '; } if( in_array('nhnkcp_naverpay', $de_easy_pay_service_array) ){ - $easypay_prints['nhnkcp_naverpay'] = ' '; + + if(isset($default['de_easy_pay_services']) && in_array('used_nhnkcp_naverpay_point', explode(',', $default['de_easy_pay_services'])) ){ + $easypay_prints['nhnkcp_naverpay_card'] = ' '; + + $easypay_prints['nhnkcp_naverpay_money'] = ' '; + } else { + $easypay_prints['nhnkcp_naverpay_card'] = ' '; + } + } if( in_array('nhnkcp_kakaopay', $de_easy_pay_service_array) ){ $easypay_prints['nhnkcp_kakaopay'] = ' '; @@ -675,7 +683,14 @@ if($is_kakaopay_use) { } if( ! isset($easypay_prints['nhnkcp_naverpay']) && function_exists('is_use_easypay') && is_use_easypay('global_nhnkcp') ){ - $easypay_prints['nhnkcp_naverpay'] = ' '; + + if(isset($default['de_easy_pay_services']) && in_array('used_nhnkcp_naverpay_point', explode(',', $default['de_easy_pay_services'])) ){ + $easypay_prints['nhnkcp_naverpay_card'] = ' '; + + $easypay_prints['nhnkcp_naverpay_money'] = ' '; + } else { + $easypay_prints['nhnkcp_naverpay'] = ' '; + } } if($easypay_prints) { @@ -1049,7 +1064,7 @@ $(function() { $("#settle_bank").show(); }); - $("#od_settle_iche,#od_settle_card,#od_settle_vbank,#od_settle_hp,#od_settle_easy_pay,#od_settle_kakaopay,#od_settle_nhnkcp_payco,#od_settle_nhnkcp_naverpay,#od_settle_nhnkcp_kakaopay,#od_settle_inicislpay,#od_settle_inicis_kakaopay").bind("click", function() { + $("#od_settle_iche,#od_settle_card,#od_settle_vbank,#od_settle_hp,#od_settle_easy_pay,#od_settle_kakaopay,#od_settle_nhnkcp_payco,#od_settle_nhnkcp_naverpay,#od_settle_nhnkcp_naverpay_money,#od_settle_nhnkcp_kakaopay,#od_settle_inicislpay,#od_settle_inicis_kakaopay").bind("click", function() { $("#settle_bank").hide(); }); @@ -1461,10 +1476,19 @@ function forderform_check(f) case "간편결제": f.pay_method.value = "100000000000"; - var nhnkcp_easy_pay = jQuery("input[name='od_settle_case']:checked" ).attr("data-pay"); + var nhnkcp_easy_pay = jQuery("input[name='od_settle_case']:checked").attr("data-pay"); if(nhnkcp_easy_pay === "naverpay"){ if(typeof f.naverpay_direct !== "undefined") f.naverpay_direct.value = "Y"; + + var is_money = jQuery("input[name='od_settle_case']:checked").attr("data-money"); + + if (is_money) { // 머니/포인트 결제 + jQuery(f).find("input[name='naverpay_point_direct']").val("Y"); + } else { // 카드 결제 + jQuery(f).find("input[name='naverpay_point_direct']").val(""); + } + } else if(nhnkcp_easy_pay === "kakaopay"){ if(typeof f.kakaopay_direct !== "undefined") f.kakaopay_direct.value = "Y"; } else { diff --git a/theme/basic/css/default_shop.css b/theme/basic/css/default_shop.css index a2d440793..76ddaf037 100644 --- a/theme/basic/css/default_shop.css +++ b/theme/basic/css/default_shop.css @@ -802,6 +802,7 @@ box-shadow: 1px 2px 2px #eee;} #sod_frm_paysel .inicis_kakaopay em{position:absolute;top:15px;left:45px;width:70px;height:30px;background:url('../../../img/kakao.png') no-repeat 50% 50% #ffeb00;overflow:hidden;text-indent:-999px;border-radius:30px} #sod_frm_paysel .kakaopay_icon{background:url('../../../img/kakao.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} #sod_frm_paysel .naverpay_icon{background:url('../../../img/ico-default-naverpay.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} +#sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;background-position: 50% 30%;padding-top:35px;padding-left:0;text-align:center} #sod_frm_paysel .samsungpay_icon{background:url('../../../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .ssgpay_icon{background:url('../../../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .skpay_icon{background:url('../../../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px} @@ -1170,6 +1171,7 @@ a.btn_frmline.is-long-text{height:auto;width:160px} .sod_frm_mobile #m_sod_frm_paysel .kakaopay_icon{background:url(../../../img/kakao.png) no-repeat 50% 50% #f4dc34;border-radius:30px;height:22px;width:74px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} .sod_frm_mobile #m_sod_frm_paysel .applepay_icon{background:url(../../../img/ico-mobile-applepay.png) no-repeat 50% 50% #fff;border-radius:30px;height:23px;width:50px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} .sod_frm_mobile #m_sod_frm_paysel .naverpay_icon{background:url(../../../img/ico-default-naverpay.png) no-repeat 50% 50% #fff;border-radius:30px;height:22px;width:50px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:35px auto} +.sod_frm_mobile #m_sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;padding-left:50px;padding-top:2px;width:83px} .sod_frm_mobile #m_sod_frm_paysel .samsung_pay{margin-left:-23px;background:url(../../../img/samsungpay.png) no-repeat 24px 3px;height:25px;width:106px;display:inline-block;overflow:hidden;text-indent:-999px} .sod_frm_mobile #sod_frm_pay{border-top:1px solid #f3f3f3} .sod_frm_mobile #sod_frm_pay h2{margin:10px 0;font-size:1.25em} diff --git a/theme/basic/css/mobile_shop.css b/theme/basic/css/mobile_shop.css index 72a1dd1c3..471d8853b 100644 --- a/theme/basic/css/mobile_shop.css +++ b/theme/basic/css/mobile_shop.css @@ -226,6 +226,7 @@ box-shadow: 0 0 6px rgba(0,0,0,0.2);} #m_sod_frm_paysel .inicis_kakaopay{background:url('../../../img/kakao.png') no-repeat 50% 50% #ffeb00;border-radius:30px;height:26px;width:74px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:36px auto} #m_sod_frm_paysel .kakaopay_icon{background:url('../../../img/ico-mobile-kakaopay.png') no-repeat #fff;height:23px;width:63px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:45px auto;background-position: 10% 40%} #m_sod_frm_paysel .naverpay_icon{background:url('../../../img/ico-mobile-naverpay.png') no-repeat #fff;height:23px;width:60px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:45px auto;background-position: 0% 30%} +#m_sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;padding-left:50px;padding-top:2px;width:83px} #m_sod_frm_paysel .applepay_icon{background:url('../../../img/ico-mobile-applepay.png') no-repeat #fff;height:30px;width:60px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:43px auto} #m_sod_frm_paysel .applepay_icon{background:url('../../../img/ico-mobile-applepay.png') no-repeat #fff;height:30px;width:60px;display:inline-block;overflow:hidden;text-indent:-999px;background-size:43px auto} #m_sod_frm_paysel .ssgpay_icon{background:url('../../../img/ssgpay_icon.png') no-repeat 0px 3px #fff;width:55px;height:20px;background-size:100%;display:inline-block;overflow:hidden;text-indent:-999px} @@ -913,6 +914,7 @@ box-shadow:0 0 8px rgba(65,98,255,0.8)} #sod_frm_paysel .inicis_kakaopay{background:url('../../../img/kakao.png') no-repeat 50% 50% #f4dc34;overflow:hidden;text-indent:-999px} #sod_frm_paysel .kakaopay_icon{background:url('../../../img/kakao.png') no-repeat 50% 50% #f4dc34;overflow:hidden;text-indent:-999px} #sod_frm_paysel .naverpay_icon{background:url('../../../img/ico-default-naverpay.png') no-repeat 50% 50% #fff;overflow:hidden;text-indent:-999px} +#sod_frm_paysel .naverpay_icon.nhnkcp_icon{text-indent:0;background-position: 50% 30%;padding-top:35px;padding-left:0;text-align:center} #sod_frm_paysel .samsungpay_icon{background:url('../../../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .ssgpay_icon{background:url('../../../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px} #sod_frm_paysel .skpay_icon{background:url('../../../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px} From d5b541724fa687f5a6ef118d4580b76be1329de3 Mon Sep 17 00:00:00 2001 From: thisgun Date: Thu, 31 Jul 2025 20:23:59 +0900 Subject: [PATCH 09/31] =?UTF-8?q?create=5Fhash=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=20fclose=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pbkdf2.compat.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pbkdf2.compat.php b/lib/pbkdf2.compat.php index b1e18001b..995f16d37 100644 --- a/lib/pbkdf2.compat.php +++ b/lib/pbkdf2.compat.php @@ -50,6 +50,7 @@ function create_hash($password, $force_compat = false) $salt = base64_encode(mcrypt_create_iv(PBKDF2_COMPAT_SALT_BYTES, MCRYPT_DEV_URANDOM)); } elseif (@file_exists('/dev/urandom') && $fp = @fopen('/dev/urandom', 'r')) { $salt = base64_encode(fread($fp, PBKDF2_COMPAT_SALT_BYTES)); + fclose($fp); // 파일 닫기 } else { $salt = ''; for ($i = 0; $i < PBKDF2_COMPAT_SALT_BYTES; $i += 2) { From 5da91ab73e5928acbd79969c4055bfda90448f43 Mon Sep 17 00:00:00 2001 From: thisgun Date: Thu, 31 Jul 2025 20:25:39 +0900 Subject: [PATCH 10/31] =?UTF-8?q?=EB=B2=84=EC=A0=84=205.6.15=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 3f429493e..cb4630bdf 100644 --- a/version.php +++ b/version.php @@ -2,7 +2,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 define('G5_VERSION', '그누보드5'); -define('G5_GNUBOARD_VER', '5.6.14'); +define('G5_GNUBOARD_VER', '5.6.15'); // 그누보드5.4.5.5 버전과 영카트5.4.5.5.1 버전을 합쳐서 그누보드5.4.6 버전에서 시작함 (kagla-210617) // G5_YOUNGCART_VER 이 상수를 사용하는 곳이 있으므로 주석 처리 해제함 // 그누보드5.4.6 이상 버전 부터는 영카트를 그누보드에 포함하여 배포하므로 영카트5의 버전은 의미가 없습니다. From cb1fadba4c7b5e89ac7938c8f870b71256f4fbcb Mon Sep 17 00:00:00 2001 From: chym1217 Date: Tue, 19 Aug 2025 15:31:58 +0900 Subject: [PATCH 11/31] =?UTF-8?q?fix:=20#42=20SMS5=20DB=EC=97=90=20G5=5FTA?= =?UTF-8?q?BLE=5FPREFIX=20=EC=A0=81=EC=9A=A9=20-=20=EA=B8=B0=EC=A1=B4=20sm?= =?UTF-8?q?s5=5F*=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=A1=B4=EC=9E=AC=20?= =?UTF-8?q?=EC=8B=9C=20dbupgrade=EC=97=90=EC=84=9C=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/dbupgrade.php | 20 ++++++++++++++++++++ adm/sms_admin/_common.php | 10 ++++++++++ extend/sms5.extend.php | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/adm/dbupgrade.php b/adm/dbupgrade.php index 48394b6ba..aa69f2add 100644 --- a/adm/dbupgrade.php +++ b/adm/dbupgrade.php @@ -269,6 +269,26 @@ while ($row = sql_fetch_array($result)){ } } +// SMS5 테이블 G5_TABLE_PREFIX 적용 +if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sms5_config'"))) +{ + $tables = array('config','write','history','book','book_group','form','form_group'); + + foreach($tables as $name){ + $old_table = 'sms5_' . $name; + $new_table = $g5['sms5_prefix'] . $name; + + // 기존 테이블이 있고, G5_TABLE_PREFIX 적용 테이블이 없을 경우 → 테이블명 변경 + if(sql_num_rows(sql_query("SHOW TABLES LIKE '{$old_table}' "))){ + if(!sql_num_rows(sql_query("SHOW TABLES LIKE '{$new_table}' "))){ + sql_query("RENAME TABLE {$old_table} TO {$new_table}", false); + } + } + } + + $is_check = true; +} + $is_check = run_replace('admin_dbupgrade', $is_check); $db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.
    현재 DB 업그레이드가 완료된 상태입니다.'; diff --git a/adm/sms_admin/_common.php b/adm/sms_admin/_common.php index e295daf97..8c13c71a9 100644 --- a/adm/sms_admin/_common.php +++ b/adm/sms_admin/_common.php @@ -5,6 +5,16 @@ include_once(G5_ADMIN_PATH.'/admin.lib.php'); include_once(G5_SMS5_PATH.'/sms5.lib.php'); if (!strstr($_SERVER['SCRIPT_NAME'], 'install.php')) { + // SMS5 테이블 G5_TABLE_PREFIX 적용 + if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sms5_config'"))) + { + echo ''; + exit; + } + if(!sql_num_rows(sql_query(" show tables like '{$g5['sms5_config_table']}' "))) goto_url('install.php'); diff --git a/extend/sms5.extend.php b/extend/sms5.extend.php index 3c0212d42..a31d43f28 100644 --- a/extend/sms5.extend.php +++ b/extend/sms5.extend.php @@ -20,7 +20,7 @@ define('G5_ICODE_LMS_MAX_LENGTH', 1500); // 구버전 LMS 최대길이 define('G5_ICODE_JSON_MAX_LENGTH', 2000); // JSON 버전 LMS 최대길이 // SMS 테이블명 -$g5['sms5_prefix'] = 'sms5_'; +$g5['sms5_prefix'] = G5_TABLE_PREFIX.'sms5_'; $g5['sms5_config_table'] = $g5['sms5_prefix'] . 'config'; $g5['sms5_write_table'] = $g5['sms5_prefix'] . 'write'; $g5['sms5_history_table'] = $g5['sms5_prefix'] . 'history'; From f69b66dcedf79cea759c0c75a6cf382917af9aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B4=9D?= Date: Wed, 27 Aug 2025 11:48:36 +0900 Subject: [PATCH 12/31] =?UTF-8?q?[KVE-2025-0510]=20Stored=20XSS=20(bypass?= =?UTF-8?q?=20html=5Fpurify=20patch)=20to=20RCE=20=EC=B7=A8=EC=95=BD?= =?UTF-8?q?=EC=A0=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/board_form.php | 14 ++++-- adm/board_form_update.php | 2 +- lib/common.lib.php | 2 +- mobile/shop/kcp/order_approval_form.php | 62 ++++++++++++------------- plugin/htmlpurifier/extend.video.php | 5 +- 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/adm/board_form.php b/adm/board_form.php index 82de4248b..a28cd6aa6 100644 --- a/adm/board_form.php +++ b/adm/board_form.php @@ -1447,12 +1447,12 @@ function frm_check_file(){ jQuery(function($){ if( window.self !== window.top ){ // frame 또는 iframe을 사용할 경우 체크 - $("#bo_include_head, #bo_include_tail").on("change paste keyup", function(e) { - frm_check_file(); - }); - use_captcha_check(); } + + $("#bo_include_head, #bo_include_tail").on("change paste keyup", function(e) { + frm_check_file(); + }); }); function fboardform_submit(f) @@ -1487,10 +1487,14 @@ function fboardform_submit(f) return false; } + if (frm_check_file() == false) { + jQuery(window).scrollTop($('#bo_include_tail').offset().top - 30); + } + if( captcha_chk ) { } - + return true; } diff --git a/adm/board_form_update.php b/adm/board_form_update.php index b1c9af12d..dbd48dc39 100644 --- a/adm/board_form_update.php +++ b/adm/board_form_update.php @@ -37,7 +37,7 @@ $bo_include_head = isset($_POST['bo_include_head']) ? preg_replace(array("#[\\\] $bo_include_tail = isset($_POST['bo_include_tail']) ? preg_replace(array("#[\\\]+$#", "#(<\?php|<\?)#i"), "", substr($_POST['bo_include_tail'], 0, 255)) : ''; // 관리자가 자동등록방지를 사용해야 할 경우 -if ($board && (isset($board['bo_include_head']) && $board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail) && function_exists('get_admin_captcha_by') && get_admin_captcha_by()) { +if ($board && (isset($board['bo_include_head']) && $board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail)) { include_once(G5_CAPTCHA_PATH . '/captcha.lib.php'); if (!chk_captcha()) { diff --git a/lib/common.lib.php b/lib/common.lib.php index 5fad9574d..a7ab4197c 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -4248,7 +4248,7 @@ function is_include_path_check($path='', $is_input='') return false; } - if (preg_match('/\/data\/(file|editor|qa|cache|member|member_image|session|tmp)\/[A-Za-z0-9_]{1,20}\//i', $replace_path) || preg_match('/pear(cmd)?\.php/i', $replace_path)){ + if (preg_match('/\/data\/(file|editor|qa|cache|member|member_image|session|tmp)\/[A-Za-z0-9_]{1,20}\//i', $replace_path) || preg_match('/pe(?:ar|cl)(?:cmd)?\.php/i', $replace_path)){ return false; } if( preg_match('/'.G5_PLUGIN_DIR.'\//i', $replace_path) && (preg_match('/'.G5_OKNAME_DIR.'\//i', $replace_path) || preg_match('/'.G5_KCPCERT_DIR.'\//i', $replace_path) || preg_match('/'.G5_LGXPAY_DIR.'\//i', $replace_path)) || (preg_match('/search\.skin\.php/i', $replace_path) ) ){ diff --git a/mobile/shop/kcp/order_approval_form.php b/mobile/shop/kcp/order_approval_form.php index 1c325d7da..9da35e7ce 100644 --- a/mobile/shop/kcp/order_approval_form.php +++ b/mobile/shop/kcp/order_approval_form.php @@ -37,7 +37,7 @@ include_once('./_common.php'); $tran_cd = isset($_POST["tran_cd"]) ? $_POST["tran_cd"] : ''; // 트랜잭션 코드 $ordr_idxx = isset($_POST["ordr_idxx"]) ? $_POST["ordr_idxx"] : ''; // 쇼핑몰 주문번호 $good_name = isset($_POST["good_name"]) ? $_POST["good_name"] : ''; // 상품명 - $good_mny = isset($_POST["good_mny"]) ? $_POST["good_mny"] : ''; // 결제 총금액 + $good_mny = isset($_POST["good_mny"]) ? (int) $_POST["good_mny"] : ''; // 결제 총금액 $buyr_name = isset($_POST["buyr_name"]) ? $_POST["buyr_name"] : ''; // 주문자명 $buyr_tel1 = isset($_POST["buyr_tel1"]) ? $_POST["buyr_tel1"] : ''; // 주문자 전화번호 $buyr_tel2 = isset($_POST["buyr_tel2"]) ? $_POST["buyr_tel2"] : ''; // 주문자 핸드폰 번호 @@ -60,9 +60,9 @@ include_once('./_common.php'); $bask_cntx = isset($_POST["bask_cntx"]) ? $_POST["bask_cntx"] : ''; // 장바구니 상품수 $tablet_size = isset($_POST["tablet_size"]) ? $_POST["tablet_size"] : ''; // 모바일기기 화면비율 - $comm_tax_mny = isset($_POST["comm_tax_mny"]) ? $_POST["comm_tax_mny"] : ''; // 과세금액 - $comm_vat_mny = isset($_POST["comm_vat_mny"]) ? $_POST["comm_vat_mny"] : ''; // 부가세 - $comm_free_mny = isset($_POST["comm_free_mny"]) ? $_POST["comm_free_mny"] : ''; // 비과세금액 + $comm_tax_mny = isset($_POST["comm_tax_mny"]) ? (int) $_POST["comm_tax_mny"] : ''; // 과세금액 + $comm_vat_mny = isset($_POST["comm_vat_mny"]) ? (int) $_POST["comm_vat_mny"] : ''; // 부가세 + $comm_free_mny = isset($_POST["comm_free_mny"]) ? (int) $_POST["comm_free_mny"] : ''; // 비과세금액 $payco_direct = isset($_POST["payco_direct"]) ? $_POST["payco_direct"] : ''; // PAYCO 결제창 호출 $naverpay_direct = isset($_POST["naverpay_direct"]) ? $_POST["naverpay_direct"] : ''; // NAVERPAY 결제창 호출 @@ -253,7 +253,7 @@ if($enc_data != '' && $enc_info != '' && $tran_cd != '') { echo make_order_field($data, $exclude); foreach($_POST as $key=>$value) { - echo ''.PHP_EOL; + echo ''.PHP_EOL; } echo ''.PHP_EOL; @@ -262,12 +262,12 @@ if($enc_data != '' && $enc_info != '' && $tran_cd != '') {
    - + - - - - + + + + - + - + - + - + @@ -311,29 +311,29 @@ if($enc_data != '' && $enc_info != '' && $tran_cd != '') { - + - + - + - + - + - + - + - + - + - + - + @@ -343,7 +343,7 @@ if($enc_data != '' && $enc_info != '' && $tran_cd != '') { - + - - - + + + +

    결제가 실패한 경우 아래 돌아가기 버튼을 클릭해주세요.

    - 돌아가기 + 돌아가기
    @@ -1105,12 +1156,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) { ?>
    -

    기본 메일 환경 설정

    +

    기본 알림 환경 설정

    - + @@ -1138,18 +1189,85 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) { > 회원만 사용 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    기본 메일 환경 설정기본 알림 환경 설정
    + + +
    사업자등록번호 + + +
    회신번호 + + +
    + + +
    + + +
    + + +
    + + 설정 확인 버튼을 클릭하면 팝빌 API와의 연결 상태 및 포인트 정보를 확인할 수 있습니다."); ?> + +
    +
    팝빌 연동신청 + 회원가입 시 연동회원으로 선택후 링크아이디 [SIRSOFT]를 넣어주세요.'); ?> + 팝빌 연동신청 +
    알림톡 프리셋 설정 + 환경설정 > 알림톡프리셋관리 에서 설정하실 수 있습니다. +
    -

    게시판 글 작성 시 메일 설정

    +

    게시판 글 작성 시 알림 설정

    - + @@ -1197,12 +1315,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
    -

    회원가입 시 메일 설정

    +

    회원가입 시 알림 설정

    게시판 글 작성 시 메일 설정게시판 글 작성 시 알림 설정
    - + @@ -1229,12 +1347,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
    -

    투표 기타의견 작성 시 메일 설정

    +

    투표 기타의견 작성 시 알림 설정

    회원가입 시 메일 설정회원가입 시 알림 설정
    - + @@ -1526,7 +1644,6 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) { -

    여분필드 기본 설정

    @@ -1732,6 +1849,50 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) { }); + +', 1); // 카카오톡5 솔루션 js 추가 ?> + + 'char', 'cf_stipulation' => 'text', 'cf_privacy' => 'text', + 'cf_use_promotion' => 'int', 'cf_open_modify' => 'int', 'cf_memo_send_point' => 'int', 'cf_mobile_new_skin' => 'char', @@ -157,7 +158,13 @@ $check_keys = array( 'cf_icode_server_ip' => 'char', 'cf_captcha' => 'char', 'cf_syndi_token' => '', - 'cf_syndi_except' => '' + 'cf_syndi_except' => '', + 'cf_kakaotalk_use' => 'char', + 'cf_kakaotalk_corpnum' => 'char', + 'cf_kakaotalk_sender_hp' => 'char', + 'cf_popbill_userid' => 'char', + 'cf_popbill_link_id' => 'char', + 'cf_popbill_secretkey' => 'char' ); for ($i = 1; $i <= 10; $i++) { @@ -300,6 +307,7 @@ $sql = " update {$g5['config_table']} cf_mobile_page_rows = '{$_POST['cf_mobile_page_rows']}', cf_stipulation = '{$_POST['cf_stipulation']}', cf_privacy = '{$_POST['cf_privacy']}', + cf_use_promotion = '{$_POST['cf_use_promotion']}', cf_open_modify = '{$_POST['cf_open_modify']}', cf_memo_send_point = '{$_POST['cf_memo_send_point']}', cf_mobile_new_skin = '{$_POST['cf_mobile_new_skin']}', @@ -348,6 +356,12 @@ $sql = " update {$g5['config_table']} cf_recaptcha_secret_key = '{$_POST['cf_recaptcha_secret_key']}', cf_payco_clientid = '{$_POST['cf_payco_clientid']}', cf_payco_secret = '{$_POST['cf_payco_secret']}', + cf_kakaotalk_use = '{$_POST['cf_kakaotalk_use']}', + cf_kakaotalk_corpnum = '{$_POST['cf_kakaotalk_corpnum']}', + cf_kakaotalk_sender_hp = '{$_POST['cf_kakaotalk_sender_hp']}', + cf_popbill_userid = '{$_POST['cf_popbill_userid']}', + cf_popbill_link_id = '{$_POST['cf_popbill_link_id']}', + cf_popbill_secretkey = '{$_POST['cf_popbill_secretkey']}', cf_1_subj = '{$_POST['cf_1_subj']}', cf_2_subj = '{$_POST['cf_2_subj']}', cf_3_subj = '{$_POST['cf_3_subj']}', diff --git a/adm/css/admin.css b/adm/css/admin.css index 75763206a..20802f756 100644 --- a/adm/css/admin.css +++ b/adm/css/admin.css @@ -28,6 +28,39 @@ box-sizing: border-box; h2{font-size: 1.083em;font-weight: bold;margin:10px 0} #wrapper {min-height:480px} + +/* admin 공통 */ +/* 공통 - display none/block */ +.is-hidden { display: none !important; } +.is-visible { display: block !important; } + +/* 공통 - 뷰포트 (pc / mobile) 별 display none/block */ +.pc-only { display: none; } +@media (min-width: 769px) { .pc-only { display: block !important; }} +.mobile-only { display: block; } +@media (min-width: 769px) { .mobile-only { display: none !important; }} + +/* 공통 - 레이어 팝업 */ +.popup-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.3); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); z-index: 9999; display: flex; justify-content: center; align-items: center; } +.popup-content { background: #fff; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); width: 800px; overflow: hidden; } +.popup-header, .popup-footer { padding: 18px 20px; display: flex; align-items: center; } +.popup-header { justify-content: space-between; border-bottom: 1px solid #e0e0e0; } +.popup-footer { gap: 20px; border-top: 1px solid #e0e0e0;} +.popup-close-btn { background: none; border: none; color: #888; font-size: 20px; cursor: pointer; padding: 4px; display: flex; align-items: center; justify-content: center; transition: color 0.2s ease; } +.popup-close-btn:hover { color: #333; } +.popup-title { font-size: 18px; font-weight: 600; } +.popup-body { padding: 20px; max-height: 400px; overflow-y: auto; color: #333; } +.popup-footer button { background: #3d70ff; color: white; border: 1px solid #3d70ff; padding: 8px 16px; border-radius: 6px; font-weight: 600; cursor: pointer; transition: background 0.2s ease, border-color 0.2s ease; } +.popup-footer button:hover { background: #2b3d9f; border-color: #2b3d9f; } + +/* 공통 - tab */ +.tab-container { display: flex; flex-direction: column; width: 100%; } +.tab-header { position: relative; bottom: -1px; display: flex; } +.tab-btn { padding: 10px 14px; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; color: inherit; font: inherit; } +.tab-btn.active { border-bottom-color: #000; font-weight: bold; } +.tab-body { width: 100%; border-top: 1px solid #ccc; } +.tab-content { padding: 16px 0; } + /* 레이아웃 */ #hd h1 {position:absolute;font-size:0;line-height:0;overflow:hidden} #hd_top{position:fixed;top:0;left:0;width:100%;height:50px;background:#3f51b5;z-index:1000} @@ -71,6 +104,8 @@ h2{font-size: 1.083em;font-weight: bold;margin:10px 0} #gnb .on .btn_op.menu-400{background:url(../img/menu-7.png) 50% 50% no-repeat #fff} #gnb .gnb_li .btn_op.menu-500{background:url(../img/menu-6-1.png) 50% 50% no-repeat #ebebeb } #gnb .on .btn_op.menu-500{background:url(../img/menu-6.png) 50% 50% no-repeat #fff} +#gnb .gnb_li .btn_op.menu-800{background:url(../img/menu-8-1.png) 50% 50% no-repeat #ebebeb } +#gnb .on .btn_op.menu-800{background:url(../img/menu-8.png) 50% 50% no-repeat #fff} #gnb .gnb_li .btn_op.menu-900{background:url(../img/menu-4-1.png) 50% 50% no-repeat #ebebeb } #gnb .on .btn_op.menu-900{background:url(../img/menu-4.png) 50% 50% no-repeat #fff} #gnb .gnb_li button:hover{background-color:#f3f3f3} @@ -95,9 +130,11 @@ box-shadow: 2px 0 2px rgba(150,150,150,0.1);} #container.container-small #container_title{padding-left:70px} .container_wr{padding:20px} -/* 화면낭독기 사용자용 */ +/* 화면낭독기 사용자용 (스크린 리더 대응) */ +/* 일반적인 .blind/.sr-only 사용시에 .sound_only 사용 권장 */ #hd_login_msg {position:absolute;top:0;left:0;width:1px;height:1px;overflow:hidden} -.msg_sound_only, .sound_only {display:inline-block !important;position:absolute;top:0;left:0;margin:0 !important;padding:0 !important;width:1px !important;height:1px !important;font-size:0;line-height:0;border:0 !important;overflow:hidden !important} +.sound_only, .msg_sound_only {overflow:hidden;position:absolute;width:1px;height:1px;margin:-1px;padding:0;clip:rect(0,0,0,0)} + /* 본문 바로가기 */ #to_content a {z-index:100000;position:absolute;top:0;left:0;font-size:0;line-height:0;overflow:hidden} #to_content a:focus, #to_content a:active {width:100%;height:70px;background:#fff;font-size:2em;font-weight:bold;text-align:center;text-decoration:none;line-height:3.1em} @@ -196,6 +233,8 @@ a.btn_submit{background:#ff4081;color:#fff} .btn_02,a.btn_02{background:#9eacc6;color:#fff;} .btn_03,a.btn_03{background:#3f51b5;color:#fff;} +.btn_04,a.btn_04{background:#555;color:#fff} +.btn_04:hover,a.btn_04:hover{background:#666} .btn_frmline{display:inline-block;background:#9eacc6;color:#fff;height:35px;border:0;border-radius:5px;padding:0 10px} a.btn_frmline{display:inline-block;background:#9eacc6;color:#fff;height:35px;line-height:33px;border-radius:5px;padding:0 10px;text-decoration:none !important} @@ -247,18 +286,14 @@ legend {position:absolute;width:0;height:0;font-size:0;line-height:0;text-indent .anchor a {display:inline-block;padding:5px 10px;border:1px solid #c8ced1;background:#d6dde1;text-decoration:none} .anchor .selected{background:#3f51b5} - - - #sort_mb {width:800px} #sort_sodr {width:600px} - /* 하단 레이아웃 */ #ft{background:#f3f3f3;padding:0 25px;color:#777;text-align:center} #ft p{line-height:50px;} -.scroll_top{position:fixed;bottom:10px;right:10px;width:50px;height:50px;border:0;text-align:center;background:#ddd;background:rgba(0,0,0,0.1)} +.scroll_top{position:fixed;bottom:10px;right:10px;width:50px;height:50px;border:0;text-align:center;background:#ddd;background:rgba(0,0,0,0.1);z-index:50;} .scroll_top span.top_img{display:inline-block;width: 0; height: 0; border-left: 5px solid transparent;border-right: 5px solid transparent; border-bottom: 5px solid black;} .scroll_top span.top_txt{display:block} @@ -281,9 +316,59 @@ border-bottom: 5px solid black;} .local_sch03 button{height:30px;padding:0 5px;border:0;background:#9eacc6;color:#fff;} .local_sch03 .btn_submit{height:30px;padding:0 5px;border:0;color:#fff;} .local_sch03 .frm_input{height:30px;border:1px solid #dcdcdc;padding:0 5px;} + +/* 회원 관리 데이터 필터링 */ +.member_list_data { display: flex; flex-direction: column; padding: 20px; margin: 20px 0 40px; background: #f9f9f9; border: 1px solid #f2f2f2; color: #333; } +.sch_table { display: flex; flex-direction: column; gap: 10px; font-size: 11.5px; color: #333; } +.member_list_data .sch_row { display: flex; align-items: center; gap: 12px; min-height: 30px; } +.label { min-width: 120px; font-weight: 500; white-space: nowrap; display: flex; align-items: center; } +.label label {display: flex; gap: 10px;} +.field { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } +.field input[type="text"], .field input[type="number"], .field input[type="date"], .field select { height: 30px; min-width: 100px; padding: 0 10px; font-size: 11.5px; border: 1px solid #ddd; border-radius: 8px; background: #fff; transition: border-color 0.2s ease, box-shadow 0.2s ease; } +.field input[type="text"]:focus, .field input[type="number"]:focus, .field input[type="date"]:focus, .field select:focus { border-color: #6f809a; box-shadow: 0 0 0 2px rgba(63,81,181,0.1); outline: none; } +.field input::placeholder { color: #aaa; } +.field input[type="checkbox"], .field input[type="radio"] { width: 14px; height: 14px; accent-color: #536177; } +.radio_group { display: flex; gap: 15px; align-items: center; padding: 0 10px;} +.radio_group label {display: flex; align-items: center; gap: 5px;} +.ad_range_wrap {flex: 1; padding-left: 20px;} +.ad_range_box {display: flex;} +.ad_range_box .label {width: 109px;} +.sch_notice { font-size: 11px; color: #999; } +.sch_btn { display: flex; gap: 20px; justify-content: center; margin-top: 40px; } +.sch_btn { display: flex; gap: 10px; } + +.btn_reset { display: flex; align-items: center; gap: 6px; padding: 0 20px; height: 40px; background: #9eacc6; color: #fff; font-weight: 600; border: none; border-radius: 8px; cursor: pointer; transition: background 0.2s ease, transform 0.15s ease; } +.btn_reset:hover { background: #5f6e89; } +.sch_btn button:not(.btn_reset) { padding: 0 20px; height: 40px; border: 1px solid #ccd1d8; background-color: #fff; color: #444; font-weight: 600; border-radius: 8px; cursor: pointer; user-select: none; transition: border-color 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.03); } +.sch_btn button:not(.btn_reset):hover { border-color: #6f809a; box-shadow: 0 2px 4px rgba(111, 128, 154, 0.15); } +.sch_btn button:not(.btn_reset):active { box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); } + +/* 회원 관리 다운로드 진행 팝업 */ +.excel-download-progress p { color: #374151; } +.excel-download-progress .progress-desc { padding: 40px 0 32px; text-align: center; } +.excel-download-progress .progress-summary { margin-bottom: 6px; font-size: 16px; font-weight: 500; color: #111827; } +.excel-download-progress .progress-message { font-size: 20px; font-weight: 600; color: #3b82f6; } +.excel-download-progress .progress-error { color:red; } +.progress-spinner { display: flex; flex-direction: column; align-items: center; gap: 45px; padding: 24px 0; transition: all 0.2s ease; } +.spinner { width: 48px; height: 48px; border: 5px solid #3b82f6; border-top: 5px solid #fff; border-radius: 50%; animation: spin 0.8s linear infinite; } +@keyframes spin { to { transform: rotate(360deg); } } +.loading-message { text-align: center; font-size: 14px; color: #374151; } +.excel-download-progress .progress-download-box { margin-top: 24px; background: #f9fafb; padding: 20px; border-radius: 8px; box-shadow: 0 1px 2px rgba(0,0,0,0.03); } +.excel-download-progress .progress-download-box a { display: block; width: 100%; height: auto; text-align: center; margin-top: 8px; font-weight: 600; font-size: 14px; padding: 10px 20px; background: #fff; border: 1px solid #ccd1d8; border-radius: 8px; color: #444; cursor: pointer; transition: border-color 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.03); } +.excel-download-progress .progress-download-box a:hover { border-color: #6f809a; box-shadow: 0 2px 4px rgba(111, 128, 154, 0.15); } +.excel-download-progress .progress-download-box a:active { box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); } + +.field-select-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 15px; gap: 0px 10px; padding: 10px; background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; color: #374151; } +.field-select-form label { display: flex; align-items: center; cursor: pointer; padding: 6px 10px; border-radius: 4px; } +.field-select-form label:hover { background-color: #f3f4f6; } +.field-select-form input[type="checkbox"] { margin-right: 8px; transform: scale(1.2); } +.field-separator { grid-column: 1 / -1; border-top: 1px solid #d1d5db; margin: 8px 0; } +.selected-fields-preview { padding: 8px; background-color: #eef2f7; border: 1px solid #d1d5db; border-radius: 6px; margin: 10px 0px; color: #1f2937; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } +.selected-fields-preview strong { padding: 4px 8px; } +.selected-fields-preview .field-tag { background-color: #dbeafe; color: #1e40af; padding: 4px 8px; border-radius: 4px; } + /* 페이지 내 실행 */ .local_cmd {min-width:960px} - .local_cmd01 {margin:0 0 10px;padding:0 } .local_cmd01 .cmd_tit {font-weight:bold} .local_cmd01 .btn_submit {padding:3px 5px;border:1px solid #ff3061;color:#fff;font-size:0.95em;vertical-align:middle} @@ -298,7 +383,7 @@ border-bottom: 5px solid black;} .local_desc01 {margin:10px 0 10px ;padding:10px 20px;border:1px solid #f2f2f2;background:#f9f9f9} .local_desc01 strong {color:#ff3061} -.local_desc01 a {text-decoration:underline} +.local_desc01 a {text-decoration:underline;text-underline-offset:2px;} .local_desc02 {margin:10px 0 ;min-width:960px} /* 주로 온라인 서식 관련 안내 내용에 사용 */ .local_desc02 p {padding:0;line-height:1.8em} @@ -401,6 +486,7 @@ tfoot th {} .mb_leave_msg {color:#b6b6b6} .mb_intercept_msg {color:#ff0000} #point_mng {margin-top:50px} +.ad_agree_log {max-height: 150px !important;} /* 게시판추가/수정 */ #anc_bo_extra .td_grpset label {width:auto} @@ -504,6 +590,7 @@ td.td_grpset {width:160px;border-left:1px solid #e9ecee;text-align:center} .td_time{text-align:center;width:130px} .td_center{text-align:center;} .td_type{width:120px} +.td_consent{width:200px} .td_mng_s{width:60px} .td_mng_m{width:100px} @@ -1134,3 +1221,24 @@ input[type="text"]{max-width:200px} /* Styles */ input[type="text"]{max-width:200px} } + +/* 쇼핑몰관리 > 알림톡프리셋관리 */ +.template_preview {display:none;z-index:10;position:absolute;padding:20px;width:400px;min-height:300px;max-height:300px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-align: left;} +.template_preview_txt {padding:10px;width:100%;height:300px;border:1px solid #696c71;background:#f7f7f7;overflow-y:scroll;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;} +.btn_template_preview_open {padding:5px 10px;width:77px;height:30px;line-height:10px;border:0;border-radius:5px;background:#484848;color:#fff} +.btn_template_preview_close {margin:0;padding:0;width:100%;height:40px;line-height:40px;border:0;background:#484848;color:#fff} +.variable_table {overflow:hidden;max-height:0;transition:max-height 0.7s} +.variable_table.is-visible {max-height:1000px !important;} +.variable_table .tbl_head01 tbody td {background-color: #fff;} + +.preset-toggle{position:relative;display:inline-block;width:50px;height:24px} +.preset-toggle input{opacity:0;width:0;height:0} +.preset-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#ccc;transition:.4s;border-radius:24px} +.preset-slider:before{position:absolute;content:"";height:18px;width:18px;left:3px;bottom:3px;background:#fff;transition:.4s;border-radius:50%} +input:checked+.preset-slider{background:#4CAF50} +input:checked+.preset-slider:before{transform:translateX(26px)} + +/* 친구톡 전송 관련 */ +.kakao-send-svg { width: 16px; height: 16px; fill: currentColor; margin-bottom: -3px; } +.kakao-setting-btn { height: auto; padding: 7px 12px; background: #3d70ff; color: white; border: none; border-radius: 6px; font-weight: 600; min-width: 110px;} +.kakao-setting-btn:hover { background: #2c5bd3; } \ No newline at end of file diff --git a/adm/dbupgrade.php b/adm/dbupgrade.php index aa69f2add..7b99b4b1a 100644 --- a/adm/dbupgrade.php +++ b/adm/dbupgrade.php @@ -289,6 +289,166 @@ if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sm $is_check = true; } +// 카카오톡 프리셋 테이블 +if( isset($g5['kakao5_preset_table']) && !sql_query(" DESC {$g5['kakao5_preset_table']} ", false)) { + sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['kakao5_preset_table']}` ( + `kp_id` int(11) NOT NULL AUTO_INCREMENT, + `kp_type` varchar(20) NOT NULL DEFAULT '', + `kp_category` varchar(20) NOT NULL DEFAULT '', + `kp_preset_code` varchar(100) NOT NULL DEFAULT '', + `kp_preset_name` varchar(100) NOT NULL DEFAULT '', + `kp_template_name` varchar(100) NOT NULL DEFAULT '', + `kp_alt_send` varchar(100) NOT NULL DEFAULT '1', + `kp_active` tinyint(1) NOT NULL DEFAULT '1', + `kp_created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `kp_updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`kp_id`) + ) ", true); + + // 기본 프리셋 데이터 추가 + sql_query("INSERT INTO `{$g5['kakao5_preset_table']}` + (`kp_type`, `kp_category`, `kp_preset_code`, `kp_preset_name`, `kp_template_name`, `kp_alt_send`, `kp_active`, `kp_created_at`, `kp_updated_at`) + VALUES + ('회원', '회원', 'CU-MB01', '회원가입완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '회원', 'AD-MB01', '회원가입완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + + ('작성자', '게시판', 'CU-BO01', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '게시판', 'AD-BO01', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('그룹관리자', '게시판', 'AD-BO02', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('게시판관리자', '게시판', 'AD-BO03', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('작성자', '게시판', 'CU-BO02', '새 댓글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('댓글 작성자', '게시판', 'CU-BO03', '새 댓글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('답변글 작성자', '게시판', 'CU-BO04', '답변글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '투표', 'AD-VO01', '기타의견 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + + ('주문자', '쇼핑몰', 'CU-OR01', '주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '쇼핑몰', 'AD-OR01', '주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR02', '무통장입금 주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '쇼핑몰', 'AD-OR02', '무통장입금 주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR03', '무통장입금 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '쇼핑몰', 'AD-OR03', '무통장입금 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR04', '(주문자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '쇼핑몰', 'AD-OR04', '(주문자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR05', '(관리자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '쇼핑몰', 'AD-OR05', '(관리자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR06', '반품', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-OR07', '품절', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-DE01', '배송 준비', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-DE02', '배송중', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('주문자', '쇼핑몰', 'CU-DE03', '배송 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('요청자', '쇼핑몰', 'CU-ST01', '재입고알림', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + + ('문의자', '1:1문의', 'CU-IQ01', '문의 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('최고관리자', '1:1문의', 'AD-IQ01', '문의 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + ('문의자', '1:1문의', 'CU-IQ02', '답변 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + ", true); + $is_check = true; +} + +// 카카오톡 프리셋 전송내역 테이블 +if( isset($g5['kakao5_preset_history_table']) && !sql_query(" DESC {$g5['kakao5_preset_history_table']} ", false)) { + sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['kakao5_preset_history_table']}` ( + `ph_id` int(11) NOT NULL AUTO_INCREMENT, + `kp_id` int(11) NOT NULL DEFAULT '0', + `mb_id` varchar(20) NOT NULL DEFAULT '', + `ph_rcvnm` varchar(100) NOT NULL DEFAULT '', + `ph_rcv` varchar(100) NOT NULL DEFAULT '', + `ph_template_code` varchar(100) NOT NULL DEFAULT '', + `ph_alt_send` varchar(100) NOT NULL DEFAULT '', + `ph_request_num` varchar(100) NOT NULL DEFAULT '', + `ph_receipt_num` varchar(100) NOT NULL DEFAULT '', + `ph_send_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `ph_state` tinyint(1) NOT NULL DEFAULT '0', + `ph_log` text NOT NULL, + PRIMARY KEY (`ph_id`), + KEY `kp_id` (`kp_id`), + KEY `mb_id` (`mb_id`) + ) ", true); + + $is_check = true; +} + +// 카카오톡 설정 필드 추가 +if (!isset($config['cf_kakaotalk_use'])) { + sql_query( + " ALTER TABLE `{$g5['config_table']}` + ADD `cf_kakaotalk_use` varchar(50) NOT NULL DEFAULT '' AFTER `cf_recaptcha_secret_key`, + ADD `cf_kakaotalk_corpnum` varchar(50) NOT NULL DEFAULT '' AFTER `cf_kakaotalk_use`, + ADD `cf_kakaotalk_sender_hp` varchar(50) NOT NULL DEFAULT '' AFTER `cf_kakaotalk_corpnum`, + ADD `cf_popbill_userid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakaotalk_sender_hp`, + ADD `cf_popbill_link_id` varchar(100) NOT NULL DEFAULT '' AFTER `cf_popbill_userid`, + ADD `cf_popbill_secretkey` varchar(255) NOT NULL DEFAULT '' AFTER `cf_popbill_link_id` ", + true + ); + + $is_check = true; +} + +// 광고성 정보 수신 동의 사용 필드 추가 +if (!isset($config['cf_use_promotion'])) { + sql_query( + " ALTER TABLE `{$g5['config_table']}` + ADD `cf_use_promotion` tinyint(1) NOT NULL DEFAULT '0' AFTER `cf_privacy` ", + true + ); + + $is_check = true; +} + +// 광고성 정보 수신 동의 여부 필드 추가 + 메일 / SMS 수신 일자 추가 +if (!isset($member['mb_marketing_agree'])) { + sql_query( + " ALTER TABLE `{$g5['member_table']}` + ADD `mb_marketing_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_scrap_cnt`, + ADD `mb_marketing_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_marketing_agree`, + ADD `mb_thirdparty_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_marketing_date`, + ADD `mb_thirdparty_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_thirdparty_agree`, + ADD `mb_agree_log` TEXT NOT NULL AFTER `mb_thirdparty_date`, + ADD `mb_mailling_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_mailling`, + ADD `mb_sms_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_sms` ", + true + ); + + $is_check = true; +} + +// 게시판 설정 - SMS/알림톡 사용 여부 추가 +if(!sql_query(" select bo_use_kakaotalk from {$g5['board_table']} limit 1", false)) { + sql_query( + " ALTER TABLE `{$g5['board_table']}` + ADD `bo_use_kakaotalk` VARCHAR(50) NOT NULL DEFAULT '' AFTER `bo_use_email` ", + true + ); + + $is_check = true; +} + +// 게시판 알림 설정 필드 추가 +if (!isset($member['mb_board_post'])) { + sql_query( + " ALTER TABLE `{$g5['member_table']}` + ADD `mb_board_post` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_agree_log`, + ADD `mb_board_reply` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_board_post`, + ADD `mb_board_comment` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_board_reply`, + ADD `mb_board_recomment` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_board_comment` ", + true + ); + + $is_check = true; +} + +// 재입고 알림 - 채널 구분 (1=SMS, 2=알림톡) +if(sql_query(" DESC {$g5['g5_shop_item_stocksms_table']} ", false) && !sql_query(" select ss_channel from {$g5['g5_shop_item_stocksms_table']} limit 1", false)) { + sql_query( + " ALTER TABLE `{$g5['g5_shop_item_stocksms_table']}` + ADD `ss_channel` tinyint(4) NOT NULL DEFAULT '1' AFTER `ss_ip` ", + true + ); + + $is_check = true; +} + + $is_check = run_replace('admin_dbupgrade', $is_check); $db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.
    현재 DB 업그레이드가 완료된 상태입니다.'; diff --git a/adm/img/svc_btn_07.jpg b/adm/img/svc_btn_07.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f76bd70d51b9c883678a26393ec89ac7a53516e7 GIT binary patch literal 6137 zcmbuCRag{I)5n)i=~O~k8c{;JVF7mqBwV_rB&9nIqQ z5JWmYd@tVH@8X?vGw1wge&;zC^PHKRnVTg54MY>H3Bbd{13bB1fSWmh`oG=(Wd9)l z6X0L+%@+VIDL@hsM1V&Nz^BC{pvAlC1#kiYcmzbZGT^^PN=A-HNJLD6Pe5@Spg;ov z5aZ(!;1l8F6W#e2gojV?PbQ@WkkOHI(K9fLsWNdhr?LQf#9!Ry<(E{`huZqVkl!b7 zvpH_T|M>rf|4Sz%CLkiYz2#PyroEL3Z;yy>`6U0L-?9Pt1hiZvVs!Kjs-%oe-1#-{jBE23^K6w{=R%O3!o&xyL|)!EkGHNC#!|sVhcZDH1z9VhYU_6yO6yX8 zN(nQwH7&F38YW84JZc{8{Jw4;)SwkIQ9advO`&w3H^qOiP+ZgOxkkRpXe`}m{dK>7#zi?I!;GYKRzXVXB;2(fo@wr{K{1i#2S(Z@8!p=!QV3Z^`Gp>(%wL5TT0 zBm7DOIbHoc#9sIwuhp_~PI5t&N#$YUlVdq=I)AaA>>-(~QMAraF!3uRmo^fiav@t% zYKS2-uCzQC$yaah%w{TIYsjjL!Q*RJEING{t(YdvSWa9)u4~?gYI@)&=dL*n+eGoo z`H#Y-$tuto8sD@SMu_Jf_B}s%e*Y5Rh9tG3vA64R9gSjJ_PShnZmLkZj3`h1PzC!B zA>y$-xsl}Irn-it5VJsBg`|V%n(vreIT-GXIy}bg-T?Bjf5#mU`-b5ew!-K+^-U)W zaOy)!!{|l{iBpFWN%Pk!@%mbKV(iKmr1?x+uKVKDLwt)Zwynd*CQ*pA&z@7{vn|H5 zR>Pk@n-JI5^=VHS?keZTuwfgQmD#t*3*NpVtiCaE{J@oCW#}@Ua>U zY-FG$`Zb7d_MgcGj1~~XRgnyJj zIrX^4YHo3+rdMKT!W?(EAQv79&7m#fm`nBd;0o_=ck}DlnV!C8XD}oZ9HB9l7T{jy zqUUJ?SEarqEEKFzktKvEkdnf_aN`5Gjyc0&Ma&;j*)CG`yGEVP@2ybd-5a|f6~h!1 z&643}8@8p-UN$Kf^}LHJ;1io_m zXrBmgT|YIi=65!(TcfkY41h<>4e}rl>JioyF9oDZ=A@(;H@!C6mttlYP=BC#%cGjC-@2`xgKVFRs5VVNZ5PJv<<$>*nzAFa&L+d z`LF~e%nNFFSk2}a{(J-$fyn%5s(4p|c6ZnzS}e z@n^-}L}7{fYvM>LM>;nfXGWxhL3OO?wh*m8w4NJpY2H_*_T6e62=H;pnK_skd(c<0HReGDc#%7wb zXFN0Imh!&rt__cj7lA&F)t(L7R63yomRU0zWrMbA0wtwuy*sMT#=vakO8!&RW%{>d zc6iAz9_s_o(j-(E1^Ld?s=_KB(ziuf?HY~XK|IJ=TPuVHohfyoFeep(Z#>(n_4{?| zny{^e$Q_wQ(_`Or%w*l?vM9gri@!2HrJ6-Z`WHE7Qi_3rqvkkgkWy(HV;6j7&qz?Zw!d zl#WHaMfpbx`!sea9^s^3F+Cf(5DP$I6ad6>O@FJe#cx!bAo{FnbK7DNKaB)X0SIc@ zTsj1M!F>to^w}{wmTQ%RjPMsbIpPz zYFW~mK(8E2>2-HEN)73MfMakcc=n-29`>n?bc&Oln8S%v+_Ch>gqwqqWE*r_X_y`H zd%;{&zS-Goyc@gVS5?;^czpvHr1z8JZ(J`Nd|@w)%?#3M{QY{>7A`yajzGr!y>$4?OP?82VK!A)NA7qdDVb38W7 zSRQVm0qU6p1-mzL>Pl?TW;|?esdK4-3~WSBf3^_NG(bd6a6hmJ)!c(>Xb2ewCt{2n zuM-xs?H61pKPW2m_T8@+lcgGw4;>C*QE%l+^*wBs&TtRVVl*WYnS@sCSKI(Jef^)d z*d5^IMy1GQFedAFIw5wLBdX`kAEkITRtnf@OCV;3e|eWfKe0S}VgdyEl=NSY%1br4 zAR|3@^?YNy+bgHOT@q4#?nw(0G<$UtFf+}u99)+pDQzAiyI%QT>l-79Bsp)IHT{oy z#X{3HC|JgPp751?)dGK>Nu>vAUJzl+|c@Yh7MH4 z#*@|*q8D|__DpVt_6sOymFfC67xddwDsxsv`8Q9NmI>43%3>Muw3j|Zc=Z%W-rjaI zGDHDW{FVC@_h?2y(%#@>#2;IItbt0^K)K3S*g4MbSicX%*#HyZxyQ0GhYy{WSvyB8Bv+Py(H4P>U?Rr zjQn#w$JxbzvTH0^uA$-H0knjxwp|3?0K)Vq-NLm_80DLe3KH<0DHzP1-W~cAQ$>;CeP`Nyrkh@5pC4z?w zCvNN_-SAM+Kzo3EN?;{_>cJ1^l(Yw|U0{hEP|V-Lhv{5{_W_^D+guo;Ilo;0OzEQ& z{5=Waf9BJ-NTXnEz1)s*nmT^f^h4e)-y<@D5iFHVDs8&jvOW5eHxt`()~SnV+ClJ~ zp56c?0|^EIL^OaU#rD8QYvV*Dk~^23+m^56MU=y)Zvb=dMbn>7PA;iq{&c^S5j_|TeEn__ zi1VP~!s6h8-wfBSb}1K%Nsy6!Tt?Enj4VK ze6iw1qpZYBS3)S3DekbO0V!2}u{sk(d*Q7|0AK;NpHauKlq^Mwy(_`H&l2_;QWkP> zeCadzI*@jCMrEFqnqV*_e4n17Kua)=T#xewXER%j&xvS6!E>&w6usi~df{K}aJRkY8wHBve)E(^ z%4k8D3w!Q}X9F`#!pU`tw&P-_xplW=U+eqtKzX)`2DlI3nft^BCn#}zlVq>(x?KjY zsSmPAZfF&jk(Q2%FwjKzV`~NP-cQ;wJx{$Cm{?_!j@@A_>Nh@)U^11kSfotMi!}O1 zMTao(|0S?Yrjl3tRZ0V$RLrXNcDNpsNI~TGRK|3DO52*UFKbJfC1(c4ts|@x_Z$1% z%k8vDicAARy)wa%rf6N-q*KM*Mi%3=m`jI{q7^N?Fy4Ice9V$Y{ z>n$Bfr<;43KTrJThZ+B?ZS)mrR74YOM!UK@=Mb~?Tw`D2@SslU&N!d`C+8{Ge!hXs z_|Q(^FkN5;ZoQ&fZ}8}Y&*o@lYHg^B5%D~Ilo@yxun?~8c*3LmejclG(-Lj8Xog%w5zc<8oC^cjtI2wJp` z{&>g)S)rqa4u_^2TbHck+Ml++XR?;3JaP=K?#r$xT-5C{?%F{_s8)xByzLBe+h1TU z&_Iu;b~cG5NAzrtPfdsZjWcHzmzgbJW!9xt&|)pBqTH9}wm9I?M6d)pIbGeA_O9ab zhnTr?Jsoe^K55&hjuy-Tt+?B>~rTJ(J9*HPWsR zvDMh%Vx?ciQPEi7+P#L(zW!lVQ5YCC?WUh)y(9Mh3%V-3)1k$2=;xSvzME|OsB?Y( z@nryemsbOVvwQJuy$<81xWX=$smc`0TccMxFvVY!nlVB_1pJOL z-$oZ{s&Q?j&C<;fClxy^|0%5hR@gtMT0gINML0WzceYAA>q*>#T8_LAvL>0MDWOfF z(r^`Bhv9NfOEPk7H^1QFu}}2a_Ns@Xc(l;N$ITw4n7bfT?#D3fVOsg1;)U0#0$m8c zl;8f)rB{J3qAp}y|1+h#$oEp?R>FKj%Op&_aF%9j`w-K6|A6Qsqw_DV_vF7$dT_+r z@@M*W#=ukiixN&AI+~^6{(Q*t@G?Ox--(`%AqxtPM_6yp)?^;QgY%JIR4zToGBgRL zDwW(V_Dl?+!5jAsmBG@{_t>7cMaeDy4UBswL(cIl93;K@LN6h8Gi*S)<`;>*(?udR zv%+S4WgYGD#~7AH{^_KWBcH0V-LmBlXFks}y!?QUlXP@*&`$)=G?U_S4M*_8~S0eWmlNqqeLB>YczwaR_>9IXHG8Wf;&Dh z5stHTHB#@(L^Qw0o^hHv(OzzgK1AIy{DhW$W*WpJ`}z+yE?4-_OV7 z+3$Y`-vDBPXJ7F!i!bq%*89wJyn)) zE<43(6a$7f3>EN+P!v+g88xo1@6wLtX)cACmFoFcH`z%A`hWC&nG_M1!-4kiH>tj0 z)v7h!GJ5*T26i6ai7jhY;OnN;Wi<-Vi0GZlp5qg~vO`SZ-d=cYOFtLpYs|#YkDrcTG! zilvmA*YnC{hB7y$%dFQZi}^$kNj}x-Z%ycLRqY%H6P@hPyW!GxUNJcbxId~9?i`(a z)${SIiYeltUXgrnKxGr>H8A)27Zen%L8iTRz;@ZgmN>jx-o{KrF*D7TqP6@(mbb&? zG)Pr66)7>mW>e4SG}pVoes~%pM^AI6ZqVeI3-_9oTA+DS=rkb}Z>8>)Q{xvaB%a_L zXE*PWynz)?%M6Ao^zO*wGVB@#4ftP{4|fgzXlxJ1R1vs}Y~{adPJB_7Q04hYmiyva zwWVqDL+#F2Rz2oR)HBqiGxtNN0_CovsfY7TfWIfN#N!p=nZd2f==4}a;gCZ2X#e1V2wgY|%2Ze`! zy*1(M*8rs^8Lrn|=}SVxE6OXBpV`F0xi{9#+XL^g#vttveho`Vl8&YD$=?9dwZ1GJ zN)8ZG|4Ld&bO^IDNo`(84$M$%q>~a%%PlWzMUb z)p09U6iZmf2i&>C&T?&u-Dwg@e}!yVS`LWg9sdiz@*as3h`wVU`m?!yWmKVfZLJ^_ zi$dipN1x|MBp=)WW|vO
    - + - + + + + + + + + + + + + + @@ -565,6 +637,42 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js run_event('admin_member_form_add', $mb, $w, 'table'); ?> + + + + + + + + + + + + + + + + diff --git a/adm/member_form_update.php b/adm/member_form_update.php index 29e96a38c..8616200be 100644 --- a/adm/member_form_update.php +++ b/adm/member_form_update.php @@ -18,6 +18,16 @@ $mb_certify_case = isset($_POST['mb_certify_case']) ? preg_replace('/[^0-9a-z_]/ $mb_certify = isset($_POST['mb_certify']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_certify']) : ''; $mb_zip = isset($_POST['mb_zip']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_zip']) : ''; +// 광고성 정보 수신 +$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? clean_xss_tags($_POST['mb_marketing_agree'], 1, 1) : '0'; +$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? clean_xss_tags($_POST['mb_thirdparty_agree'], 1, 1) : '0'; + +// 게시판알림 설정 +$mb_board_post = isset($_POST['mb_board_post']) ? (int)$_POST['mb_board_post'] : 0; +$mb_board_reply = isset($_POST['mb_board_reply']) ? (int)$_POST['mb_board_reply'] : 0; +$mb_board_comment = isset($_POST['mb_board_comment']) ? (int)$_POST['mb_board_comment'] : 0; +$mb_board_recomment = isset($_POST['mb_board_recomment']) ? (int)$_POST['mb_board_recomment'] : 0; + // 관리자가 자동등록방지를 사용해야 할 경우 ( 회원의 비밀번호 변경시 캡챠를 체크한다 ) if ($mb_password) { include_once(G5_CAPTCHA_PATH . '/captcha.lib.php'); @@ -86,8 +96,6 @@ foreach ($check_keys as $key) { } } -$mb_memo = isset($_POST['mb_memo']) ? $_POST['mb_memo'] : ''; - $sql_common = " mb_name = '{$posts['mb_name']}', mb_nick = '{$mb_nick}', mb_email = '{$mb_email}', @@ -109,8 +117,15 @@ $sql_common = " mb_name = '{$posts['mb_name']}', mb_mailling = '{$posts['mb_mailling']}', mb_sms = '{$posts['mb_sms']}', mb_open = '{$posts['mb_open']}', + mb_open_date = '".G5_TIME_YMDHIS."', mb_profile = '{$posts['mb_profile']}', mb_level = '{$posts['mb_level']}', + mb_marketing_agree = '{$mb_marketing_agree}', + mb_thirdparty_agree = '{$mb_thirdparty_agree}', + mb_board_post = '{$mb_board_post}', + mb_board_reply = '{$mb_board_reply}', + mb_board_comment = '{$mb_board_comment}', + mb_board_recomment = '{$mb_board_recomment}', mb_1 = '{$posts['mb_1']}', mb_2 = '{$posts['mb_2']}', mb_3 = '{$posts['mb_3']}', @@ -142,6 +157,36 @@ if ($w == '') { alert('이미 존재하는 이메일입니다.\\nID : ' . $row['mb_id'] . '\\n이름 : ' . $row['mb_name'] . '\\n닉네임 : ' . $row['mb_nick'] . '\\n메일 : ' . $row['mb_email']); } + $agree_items = []; + // 마케팅 목적의 개인정보 수집 및 이용 + if ($mb_marketing_agree == 1) { + $sql_common .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(동의)"; + } + + // 광고성 이메일 수신 + if ($mb_mailling == 1) { + $sql_common .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(동의)"; + } + + // 광고성 SMS/카카오톡 수신 + if ($mb_sms == 1) { + $sql_common .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(동의)"; + } + + // 개인정보 제3자 제공 + if ($mb_thirdparty_agree == 1) { + $sql_common .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(동의)"; + } + + // 동의 로그 추가 + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원추가] " . implode(' | ', $agree_items) . "\n"; + $sql_common .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } sql_query(" insert into {$g5['member_table']} set mb_id = '{$mb_id}', mb_password = '" . get_encrypt_string($mb_password) . "', mb_datetime = '" . G5_TIME_YMDHIS . "', mb_ip = '{$_SERVER['REMOTE_ADDR']}', mb_email_certify = '" . G5_TIME_YMDHIS . "', {$sql_common} "); } elseif ($w == 'u') { $mb = get_member($mb_id); @@ -193,10 +238,54 @@ if ($w == '') { $sql_certify = ""; } + // 현재 데이터 조회 + $row = sql_fetch("select * from {$g5['member_table']} where mb_id = '{$mb_id}' "); + $agree_items = []; + + // 마케팅 목적의 개인정보 수집 및 이용 + $sql_marketing_date = ""; + if ($row['mb_marketing_agree'] !== $mb_marketing_agree) { + $sql_marketing_date .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(" . ($mb_marketing_agree == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 이메일 수신 + $sql_mailling_date = ""; + if ($row['mb_mailling'] !== $mb_mailling) { + $sql_mailling_date .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(" . ($mb_mailling == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 SMS/카카오톡 수신 + $sql_sms_date = ""; + if ($row['mb_sms'] !== $mb_sms) { + $sql_sms_date .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(" . ($mb_sms == 1 ? "동의" : "철회") . ")"; + } + + // 개인정보 제3자 제공 + $sql_thirdparty_date = ""; + if ($row['mb_thirdparty_agree'] !== $mb_thirdparty_agree) { + $sql_thirdparty_date .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(" . ($mb_thirdparty_agree == 1 ? "동의" : "철회") . ")"; + } + + // 동의 로그 추가 + $sql_agree_log = ""; + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원수정] " . implode(' | ', $agree_items) . "\n"; + $sql_agree_log .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } + $sql = " update {$g5['member_table']} set {$sql_common} {$sql_password} {$sql_certify} + {$sql_mailling_date} + {$sql_sms_date} + {$sql_marketing_date} + {$sql_thirdparty_date} + {$sql_agree_log} where mb_id = '{$mb_id}' "; sql_query($sql); } else { diff --git a/adm/member_list.php b/adm/member_list.php index dc6348cdd..96abd12cf 100644 --- a/adm/member_list.php +++ b/adm/member_list.php @@ -127,7 +127,7 @@ $colspan = 16; - + @@ -137,7 +137,7 @@ $colspan = 16; - + @@ -252,14 +252,15 @@ $colspan = 16; > - - + - - + diff --git a/adm/shop_admin/itemstocksms.php b/adm/shop_admin/itemstocksms.php index 185bcad7b..f05e0b370 100644 --- a/adm/shop_admin/itemstocksms.php +++ b/adm/shop_admin/itemstocksms.php @@ -4,7 +4,7 @@ include_once('./_common.php'); auth_check_menu($auth, $sub_menu, "r"); -$g5['title'] = '재입고SMS 알림'; +$g5['title'] = '재입고 알림'; include_once (G5_ADMIN_PATH.'/admin.head.php'); // 테이블 생성 @@ -24,6 +24,12 @@ if(!sql_query(" select ss_id from {$g5['g5_shop_item_stocksms_table']} limit 1", ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true); } +// 채널 구분 (1=SMS, 2=알림톡) +if(!sql_query(" select ss_channel from {$g5['g5_shop_item_stocksms_table']} limit 1", false)) { + sql_query(" ALTER TABLE `{$g5['g5_shop_item_stocksms_table']}` + ADD `ss_channel` tinyint(4) NOT NULL DEFAULT '1' AFTER `ss_ip` ", true); +} + $doc = isset($_GET['doc']) ? clean_xss_tags($_GET['doc'], 1, 1) : ''; $sort1 = (isset($_GET['sort1']) && in_array($_GET['sort1'], array('it_id', 'ss_hp', 'ss_send', 'ss_send_time', 'ss_datetime'))) ? $_GET['sort1'] : 'ss_send'; $sort2 = (isset($_GET['sort2']) && in_array($_GET['sort2'], array('desc', 'asc'))) ? $_GET['sort2'] : 'asc'; @@ -109,8 +115,9 @@ $listall = '전체목 - - + + + @@ -139,13 +146,14 @@ $listall = '전체목 + '; + echo ''; ?>
    투표 기타의견 작성 시 메일 설정투표 기타의견 작성 시 알림 설정
    메일 수신광고성 이메일 수신 > > + + (동의 일자: ".$mb['mb_mailling_date'].")" : ''; + } ?> > > + (동의 일자: ".$mb['mb_sms_date'].")" : ''; + } ?>
    마케팅 목적의
    개인정보 수집 및 이용
    + > + + > + + + (동의 일자: ".$mb['mb_marketing_date'].")" : ''; + } ?> + + > + + > + + + (동의 일자: ".$mb['mb_thirdparty_date'].")" : ''; + } ?> +
    약관동의 변경내역 +
    +
    + +
    +
    +
    정보 공개 @@ -376,6 +445,9 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js > + (동의 일자: ".$mb['mb_open_date'].")" : ''; + } ?>
    게시글 작성 완료 알림 + > + + > + + 게시글 답변 알림 + > + + > + +
    댓글 알림 + > + + > + + 대댓글 알림 + > + + > + +
    본인확인 메일인증 정보공개메일수신광고성이메일수신 상태 휴대폰 최종접속
    이름 닉네임SMS수신광고성SMS/카카오톡수신 성인인증 접근차단 권한 Yes' : 'No'; ?> + + value="1" id="mb_sms_"> + diff --git a/adm/member_list_exel.lib.php b/adm/member_list_exel.lib.php new file mode 100644 index 000000000..32e845670 --- /dev/null +++ b/adm/member_list_exel.lib.php @@ -0,0 +1,289 @@ + [ + 'mb_id'=>'아이디', + 'mb_name'=>'이름', + 'mb_nick'=>'닉네임', + 'mb_email'=>'이메일', + 'mb_tel'=>'전화번호', + 'mb_hp'=>'휴대폰번호', + 'mb_addr1'=>'주소' + ], + 'point_cond_map' => [ + 'gte'=>'≥', + 'lte'=>'≤', + 'eq'=>'=' + ], + 'intercept_list' => [ + 'exclude'=>'차단회원 제외', + 'only'=>'차단회원만' + ], + 'ad_range_list' => [ + 'all' => '수신동의 회원 전체', + 'mailling_only' => '이메일 수신동의 회원만', + 'sms_only' => 'SMS/카카오톡 수신동의 회원만', + 'month_confirm' => date('m월').' 수신동의 확인 대상만', + 'custom_period' => '수신동의 기간 직접 입력' + ], + ]; + + return $type ? ($config[$type] ?? []) : $config; +} + +/** + * 파라미터 수집 및 유효성 검사 + */ +function get_member_export_params() +{ + // 친구톡 양식 - 엑셀 양식에 포함할 항목 + $fieldArray = array_map('trim', explode(',', $_GET['fields'] ?? '')); + $vars = []; + foreach ($fieldArray as $index => $field) { + if(!empty($field)){ + $vars['var' . ($index + 1)] = $field; + } + } + + $params = [ + 'page' => 1, + 'formatType' => (int)($_GET['formatType'] ?? 1), + 'use_stx' => $_GET['use_stx'] ?? 0, + 'stx_cond' => clean_xss_tags($_GET['stx_cond'] ?? 'like'), + 'sfl' => clean_xss_tags($_GET['sfl'] ?? ''), + 'stx' => clean_xss_tags($_GET['stx'] ?? ''), + 'use_level' => $_GET['use_level'] ?? 0, + 'level_start' => (int)($_GET['level_start'] ?? 1), + 'level_end' => (int)($_GET['level_end'] ?? 10), + 'use_date' => $_GET['use_date'] ?? 0, + 'date_start' => clean_xss_tags($_GET['date_start'] ?? ''), + 'date_end' => clean_xss_tags($_GET['date_end'] ?? ''), + 'use_point' => $_GET['use_point'] ?? 0, + 'point' => $_GET['point'] ?? '', + 'point_cond' => $_GET['point_cond'] ?? 'gte', + 'use_hp_exist' => $_GET['use_hp_exist'] ?? 0, + 'ad_range_only' => $_GET['ad_range_only'] ?? 0, + 'ad_range_type' => clean_xss_tags($_GET['ad_range_type'] ?? 'all'), + 'ad_mailling' => $_GET['ad_mailling'] ?? 0, + 'ad_sms' => $_GET['ad_sms'] ?? 0, + 'agree_date_start' => clean_xss_tags($_GET['agree_date_start'] ?? ''), + 'agree_date_end' => clean_xss_tags($_GET['agree_date_end'] ?? ''), + 'use_intercept' => $_GET['use_intercept'] ?? 0, + 'intercept' => clean_xss_tags($_GET['intercept'] ?? 'exclude'), + 'vars' => $vars, + ]; + + // 레벨 범위 검증 + if ($params['level_start'] > $params['level_end']) { + [$params['level_start'] , $params['level_end']] = [$params['level_end'], $params['level_start']]; + } + + // 가입기간 - 날짜 범위 검증 + if ($params['use_date'] && $params['date_start'] && $params['date_end']) { + if ($params['date_start'] > $params['date_end']) { + [$params['date_start'] , $params['date_end']] = [$params['date_end'], $params['date_start']]; + } + } + + // 수신동의기간 - 날짜 범위 검증 + if ($params['ad_range_type'] == 'custom_period' && $params['agree_date_start'] && $params['agree_date_end']) { + if ($params['agree_date_start'] > $params['agree_date_end']) { + [$params['agree_date_start'] , $params['agree_date_end']] = [$params['agree_date_end'], $params['agree_date_start']]; + } + } + + return $params; +} + +/** + * 전체 데이터 개수 조회 + */ +function member_export_get_total_count($params) +{ + global $g5; + + $where = member_export_build_where($params); + $sql = "SELECT COUNT(*) as cnt FROM {$g5['member_table']} {$where}"; + + $result = sql_query($sql); + if (!$result) { + throw new Exception("데이터 조회에 실패하였습니다. 다시 시도해주세요."); + } + + $row = sql_fetch_array($result); + return (int)$row['cnt']; +} + +/** + * WHERE 조건절 생성 + */ +function member_export_build_where($params) +{ + global $config; + $conditions = []; + + // 기본 조건 - 탈퇴하지 않은 사용자 + $conditions[] = "mb_leave_date = ''"; + + // 검색어 조건 (sql_escape_string 사용으로 보안 강화) + if (!empty($params['use_stx']) && $params['use_stx'] === '1') { + $sfl_list = get_export_config('sfl_list'); + $sfl = in_array($params['sfl'], array_keys($sfl_list)) ? $params['sfl'] : ''; + $stx = sql_escape_string($params['stx']); + + if(!empty($sfl) && !empty($stx)){ + if ($params['stx_cond'] === 'like') { + $conditions[] = "{$sfl} LIKE '%{$stx}%'"; + } else { + $conditions[] = "{$sfl} = '{$stx}'"; + } + } + } + + // 권한 조건 + if (!empty($params['use_level']) && $params['use_level'] === '1') { + $level_start = max(1, (int)$params['level_start']); + $level_end = min(10, (int)$params['level_end']); + + $conditions[] = "(mb_level BETWEEN {$level_start} AND {$level_end})"; + } + + // 가입기간 조건 + if (!empty($params['use_date']) && $params['use_date'] === '1') { + $date_start = isset($params['date_start']) ? sql_escape_string(trim($params['date_start'])) : ''; + $date_end = isset($params['date_end']) ? sql_escape_string(trim($params['date_end'])) : ''; + + if ($date_start && $date_end) { + $conditions[] = "mb_datetime BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'"; + } elseif ($date_start) { + $conditions[] = "mb_datetime >= '{$date_start} 00:00:00'"; + } elseif ($date_end) { + $conditions[] = "mb_datetime <= '{$date_end} 23:59:59'"; + } + } + + // 포인트 조건 + if (!empty($params['use_point']) && $params['use_point'] === '1') { + $point = $params['point']; + $point_cond = $params['point_cond']; + + if ($point != '') { + $point = (int)$point; // 정수로 캐스팅 + + switch ($point_cond) { + case 'lte': + $conditions[] = "mb_point <= {$point}"; + break; + case 'eq': + $conditions[] = "mb_point = {$point}"; + break; + default: + $conditions[] = "mb_point >= {$point}"; + break; + } + } + } + + // 휴대폰 번호 존재 조건 + if (!empty($params['use_hp_exist']) && $params['use_hp_exist'] === '1') { + $conditions[] = "(mb_hp is not null and mb_hp != '')"; + } + + // 정보수신동의 조건 + if (!empty($params['ad_range_only']) && $params['ad_range_only'] === '1') { + $range = $params['ad_range_type'] ?? ''; + + // 공통: 마케팅 목적 수집·이용 동의 + (필요 시) 제3자 동의 + $needs_thirdparty = ($config['cf_sms_use'] !== '' || $config['cf_kakaotalk_use'] !== ''); + $thirdparty_clause = $needs_thirdparty ? " AND mb_thirdparty_agree = 1" : ""; + $base_marketing = "mb_marketing_agree = 1{$thirdparty_clause}"; + + if ($range === 'all') { + // 마케팅 동의 + (이메일 OR SMS 동의) + $conditions[] = "({$base_marketing} AND (mb_mailling = 1 OR mb_sms = 1))"; + } elseif ($range === 'mailling_only') { + // 마케팅 동의 + 이메일 동의 + $conditions[] = "({$base_marketing} AND mb_mailling = 1)"; + } elseif ($range === 'sms_only') { + // 마케팅 동의 + SMS/카카오톡 동의 + $conditions[] = "({$base_marketing} AND mb_sms = 1)"; + } elseif ($range === 'month_confirm' || $range === 'custom_period') { + // 채널 필터 체크 + $useEmail = !empty($params['ad_mailling']); + $useSms = !empty($params['ad_sms']); + + if ($range === 'month_confirm') { + // 23개월 전 그 달 + $start = date('Y-m-01 00:00:00', strtotime('-23 months')); + $end = date('Y-m-t 23:59:59', strtotime('-23 months')); + $emailDateCond = "mb_mailling_date BETWEEN '{$start}' AND '{$end}'"; + $smsDateCond = "mb_sms_date BETWEEN '{$start}' AND '{$end}'"; + + } else { + // 수신동의기간 직접 입력 - custom_period + $date_start = $params['agree_date_start'] ?? ''; + $date_end = $params['agree_date_end'] ?? ''; + + if ($date_start && $date_end) { + $emailDateCond = "mb_mailling_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'"; + $smsDateCond = "mb_sms_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'"; + } elseif ($date_start) { + $emailDateCond = "mb_mailling_date >= '{$date_start} 00:00:00'"; + $smsDateCond = "mb_sms_date >= '{$date_start} 00:00:00'"; + } elseif ($date_end) { + $emailDateCond = "mb_mailling_date <= '{$date_end} 23:59:59'"; + $smsDateCond = "mb_sms_date <= '{$date_end} 23:59:59'"; + } else { + $emailDateCond = "mb_mailling_date <> '0000-00-00 00:00:00'"; + $smsDateCond = "mb_sms_date <> '0000-00-00 00:00:00'"; + } + } + + if (!$useEmail && !$useSms) { + $conditions[] = "0=1"; // 둘 다 해제 ⇒ 결과 0건 + } else { + // 조건 조립 + $parts = []; + if ($useEmail) $parts[] = "(mb_mailling = 1 AND {$emailDateCond})"; + if ($useSms) $parts[] = "(mb_sms = 1 AND {$smsDateCond})"; + + $conditions[] = !empty($parts) ? '(' . implode(' OR ', $parts) . ')' : ''; + } + } + } + + // 차단 회원 조건 + if (!empty($params['use_intercept']) && $params['use_intercept'] === '1') { + switch ($params['intercept']) { + case 'exclude': + $conditions[] = "mb_intercept_date = ''"; + break; + case 'only': + $conditions[] = "mb_intercept_date != ''"; + break; + } + } + + return empty($conditions) ? '' : 'WHERE ' . implode(' AND ', $conditions); +} diff --git a/adm/member_list_exel.php b/adm/member_list_exel.php new file mode 100644 index 000000000..912b97647 --- /dev/null +++ b/adm/member_list_exel.php @@ -0,0 +1,578 @@ +getMessage(); // 메서드 호출 괄호 필수 +} + +$g5['title'] = '회원관리파일'; +require_once './admin.head.php'; +$colspan = 14; +?> + +

    회원 엑셀 생성

    + +
    +

    회원수 건 초과 시 건 단위로 분리 저장되며, 엑셀 생성 최대 건수는 입니다. 초과 시 조건 추가 설정 후 재시도하시기 바랍니다.

    +

    수신동의 확인 대상은 만료일까지 1달 미만인 회원을 기준으로 필터링됩니다.

    + +
    +

    파일 생성 시 서버에 임시 생성된 파일 중 오늘 날짜를 제외 한 파일은 자동 삭제되며, 수동 삭제 필요 시 회원관리파일 일괄삭제에서 진행하시기 바랍니다.

    +

    회원 정보 수정은 회원 관리에서 진행하실 수 있습니다.

    + +
    +

    친구톡 양식카카오톡 사용 시에만 이용 가능합니다.

    + +

    친구톡 (광고성 카카오톡 포함)의 경우 기존 회원 데이터 엑셀 파일 다운로드 후 상단 [친구톡 보내기] 버튼을 누르면 팝빌 홈페이지로 이동하여 업로드 진행하실 수 있습니다.

    + +
    + + + + + +
    + + 총건수 + + + + + + +
    + + +
    + +
    + 회원 검색 필터링 +
    + + +
    +
    + +
    +
    + + + + + + +
    +
    + + +
    +
    + +
    +
    + ~ + +
    +
    + + +
    +
    + +
    +
    + ~ + +
    +
    + + +
    +
    + +
    +
    + + + + + + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    + +
    +

    「정보통신망이용촉진및정보보호등에관한법률」에 따라 광고성 정보 수신동의 여부매2년마다 확인해야 합니다.

    +
    +
    + +
    +
    +
    +
    + +
    +
    + + +
    + +
    +
    + ~ + +

    * 광고성 정보 수신(이메일 또는 SMS/카카오톡) 동의일자 기준

    +
    +
    + + + 개인정보 제3자 제공' : ''; + + $ad_range_text = [ + 'all' => "* 광고성 정보 수신(이메일 또는 SMS/카카오톡) / 마케팅 목적의 개인정보 수집 및 이용{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.", + 'mailling_only' => "* 광고성 이메일 수신 / 마케팅 목적의 개인정보 수집 및 이용{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.", + 'sms_only' => "* 광고성 SMS/카카오톡 수신 / 마케팅 목적의 개인정보 수집 및 이용{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.", + 'month_confirm' => "* 23개월 전(" . date('Y년 m월', strtotime('-23 month')) . ") 광고성 정보 수신 동의(이메일 또는 SMS/카카오톡)한 회원을 선택합니다." + ]; + + if (isset($_GET['ad_range_only'], $_GET['ad_range_type']) && isset($ad_range_text[$_GET['ad_range_type']])) { + echo '

    ' . $ad_range_text[$_GET['ad_range_type']] . '

    '; + } + ?> +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    + +
    + + + + + + + +
    +
    +
    +
    + + + + + + false, 'error' => '데이터가 올바르게 전달되지 않아 작업에 실패하였습니다.']); + exit; +} + +// 기존 생성된 엑셀 파일 삭제 - LOG 및 오늘 날짜 폴더 제외 +$resultExcelDelete = member_export_delete(); + +// 서버 전송 이벤트(SSE)를 위한 헤더 설정 +member_export_set_sse_headers(); + +// 모드 확인 +$mode = $_GET['mode'] ?? ''; +if ($mode !== 'start') { + member_export_send_progress("error", "잘못된 요청 입니다."); + member_export_write_log($params, ['success' => false, 'error' => '잘못된 요청 입니다.']); + exit; +} + +/** + * 회원 내보내기 처리 실행 (예외 처리 포함) + */ +try { + main_member_export($params); +} +catch (Exception $e) +{ + // 에러 로그 저장 및 SSE 에러 전송 + error_log("[Member Export Error] " . $e->getMessage()); + member_export_send_progress("error", $e->getMessage()); + member_export_write_log($params, ['success' => false, 'error' => $e->getMessage()]); +} + +/** + * 메인 내보내기 프로세스 + */ +function main_member_export($params) +{ + $total = member_export_get_total_count($params); + + if($total > MEMBER_EXPORT_MAX_SIZE){ + throw new Exception("엑셀 다운로드 가능 범위(최대 " . number_format(MEMBER_EXPORT_MAX_SIZE) . "건)를 초과했습니다.
    조건을 추가로 설정하신 후 다시 시도해 주세요."); + } + + if($total <= 0){ + throw new Exception("조회된 데이터가 없어 엑셀 파일을 생성할 수 없습니다.
    조건을 추가로 설정하신 후 다시 시도해 주세요."); + } + + $fileName = 'member_'.MEMBER_BASE_DATE; + $fileList = []; + $zipFileName = ''; + + if ($total > MEMBER_EXPORT_PAGE_SIZE) { + // 대용량 데이터 - 분할 처리 + $pages = (int)ceil($total / MEMBER_EXPORT_PAGE_SIZE); + member_export_send_progress("progress", "", 2, $total, 0, $pages, 0); + + for ($i = 1; $i <= $pages; $i++) { + $params['page'] = $i; + + member_export_send_progress("progress", "", 2, $total, ($pages == $i ? $total : $i * MEMBER_EXPORT_PAGE_SIZE), $pages, $i); + try { + $data = member_export_get_data($params); + $fileList[] = member_export_create_excel($data, $fileName, $i, $params['formatType']); + } catch (Exception $e) { + throw new Exception("총 {$pages}개 중 {$i}번째 파일을 생성하지 못했습니다
    " . $e->getMessage()); + } + } + + // 압축 파일 생성 + if (count($fileList) > 1) { + member_export_send_progress("zipping", "", 2, $total, $total, $pages, $i); + $zipResult = member_export_create_zip($fileList, $fileName); // 압축 파일 생성 + + if($zipResult['error']){ + member_export_write_log($params, ['success' => false, 'error' => $zipResult['error']]); + member_export_send_progress("zippingError", $zipResult['error']); + } + + if ($zipResult && $zipResult['result']) { + member_export_delete($fileList); // 압축 후 엑셀 파일 제거 + $zipFileName = $zipResult['zipFile']; + } + } + + } else { + // 소용량 데이터 - 단일 파일 + member_export_send_progress("progress", "", 1, $total, 0); + $data = member_export_get_data($params); + member_export_send_progress("progress", "", 1, $total, $total/2); + $fileList[] = member_export_create_excel($data, $fileName, 0, $params['formatType']); + member_export_send_progress("progress", "", 1, $total, $total); + } + + member_export_write_log($params, ['success' => true, 'total' => $total, 'files' => $fileList, 'zip' => $zipFileName ?? null]); + member_export_send_progress("done", "", 2, $total, $total, $pages, $pages, $fileList, $zipFileName); +} + +/** + * 진행률 전송 + */ +function member_export_send_progress($status, $message = "", $downloadType = 1, $total = 1, $current = 1, $totalChunks = 1, $currentChunk = 1, $files = [], $zipFile = '') +{ + // 연결 상태 확인 + if (connection_aborted()) return; + + $data = [ + 'status' => $status, + 'message' => $message, + 'downloadType' => $downloadType, + 'total' => $total, + 'current' => $current, + 'totalChunks' => $totalChunks, + 'currentChunk' => $currentChunk, + 'files' => $files, + 'zipFile' => $zipFile, + 'filePath' => G5_DATA_URL . "/" . MEMBER_BASE_DIR . "/" . MEMBER_BASE_DATE, + ]; + + echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n"; + + // 더 안정적인 플러시 + if (ob_get_level()) ob_end_flush(); + flush(); +} + +/** + * 엑셀 내보내기 설정 + */ +function member_export_get_config($type) +{ + $configs = [ + 1 => [ + 'title' => ["회원관리파일(일반)"], + 'headers' => ['아이디', '이름', '닉네임', '휴대폰번호', '전화번호', '이메일', '주소', '회원권한', '포인트', '가입일', '차단', + '광고성 이메일 수신동의', '광고성 이메일 동의일자', '광고성 SMS/카카오톡 수신동의', '광고성 SMS/카카오톡 동의일자', + '마케팅목적의개인정보수집및이용동의', '마케팅목적의개인정보수집및이용동의일자', '개인정보제3자제공동의', '개인정보제3자제공동의일자'], + 'fields' => ['mb_id', 'mb_name', 'mb_nick', 'mb_hp', 'mb_tel', 'mb_email', 'mb_addr1', 'mb_level', 'mb_point', 'mb_datetime', 'mb_intercept_date', + 'mb_mailling','mb_mailling_date', 'mb_sms','mb_sms_date', 'mb_marketing_agree', + 'mb_marketing_date', 'mb_thirdparty_agree', 'mb_thirdparty_date'], + 'widths' => [20, 20, 20, 20, 20, 30, 30, 10, 15, 25, 10, 20, 25, 20, 25, 20, 25, 20, 25], + ], + 2 => [ + 'title' => ["회원관리파일(팝빌)"], + 'headers' => ['휴대폰번호', '이름', '변수1', '변수2', '변수3'], + 'fields' => ['mb_hp', 'mb_name'], + 'widths' => [20, 15, 30, 30, 30], + ], + ]; + + return isset($configs[$type]) ? $configs[$type] : $configs[1]; +} + +/** + * SSE 헤더 설정 + */ +function member_export_set_sse_headers() +{ + header('Content-Type: text/event-stream'); + header('Cache-Control: no-cache'); + header('Connection: keep-alive'); + header('X-Accel-Buffering: no'); + + if (ob_get_level()) ob_end_flush(); + ob_implicit_flush(true); +} + +/** + * 엑셀 컬럼 문자 반환 + */ +function member_export_column_char($i) +{ + return chr(65 + $i); +} + +/** + * 회원 데이터 조회 + */ +function member_export_get_data($params) +{ + global $g5; + + $config = member_export_get_config($params['formatType']); + $fields = $config['fields']; + + // 팝빌 타입인 경우 var 추가 + if ($params['formatType'] == 2 && !empty($params['vars'])) { + $fields = array_merge($fields, array_values($params['vars'])); + } + + $fields = array_unique($fields); + + // SQL 변환 맵 (가공이 필요한 필드만 정의) + $sqlTransformMap = [ + 'mb_datetime' => "IF(mb_datetime = '0000-00-00 00:00:00', '', mb_datetime) AS mb_datetime", + 'mb_intercept_date' => "IF(mb_intercept_date != '', '차단됨', '정상') AS mb_intercept_date", + 'mb_sms' => "IF(mb_sms = '1', '동의', '미동의') AS mb_sms", + 'mb_sms_date' => "IF(mb_sms != '1' OR mb_sms_date = '0000-00-00 00:00:00', '', mb_sms_date) AS mb_sms_date", + 'mb_mailling' => "IF(mb_mailling = '1', '동의', '미동의') AS mb_mailling", + 'mb_mailling_date' => "IF(mb_mailling != '1' OR mb_mailling_date = '0000-00-00 00:00:00', '', mb_mailling_date) AS mb_mailling_date", + 'mb_marketing_agree' => "IF(mb_marketing_agree = '1', '동의', '미동의') AS mb_marketing_agree", + 'mb_marketing_date' => "IF(mb_marketing_agree != '1' OR mb_marketing_date = '0000-00-00 00:00:00', '', mb_marketing_date) AS mb_marketing_date", + 'mb_thirdparty_agree' => "IF(mb_thirdparty_agree = '1', '동의', '미동의') AS mb_thirdparty_agree", + 'mb_thirdparty_date' => "IF(mb_thirdparty_agree != '1' OR mb_thirdparty_date = '0000-00-00 00:00:00', '', mb_thirdparty_date) AS mb_thirdparty_date", + ]; + + // SQL 필드 생성 + $sqlFields = []; + foreach ($fields as $field) { + $sqlFields[] = $sqlTransformMap[$field] ?? $field; + } + $field_list = implode(', ', $sqlFields); + + $where = member_export_build_where($params); + + $page = (int)($params['page'] ?? 1); + if ($page < 1) $page = 1; + $offset = ($page - 1) * MEMBER_EXPORT_PAGE_SIZE; + + $sql = "SELECT {$field_list} FROM {$g5['member_table']} {$where} ORDER BY mb_no DESC LIMIT {$offset}, " . MEMBER_EXPORT_PAGE_SIZE; + + $result = sql_query($sql); + if (!$result) { + throw new Exception("데이터 조회에 실패하였습니다"); + } + + $excelData = [$config['title'], $config['headers']]; + + while ($row = sql_fetch_array($result)) { + $rowData = []; + foreach ($fields as $field) { + $rowData[] = $row[$field] ?? ''; + } + $excelData[] = $rowData; + } + + return $excelData; +} + +/** + * 엑셀 파일 생성 + */ +function member_export_create_excel($data, $fileName, $index = 0, $type = 1) +{ + $config = member_export_get_config($type); + + if (!class_exists('PHPExcel')) { + error_log('[Member Export Error] PHPExcel 라이브러리를 찾을 수 없습니다.'); + throw new Exception('파일 생성 중 내부 오류가 발생했습니다: PHPExcel 라이브러리를 찾을 수 없습니다.'); + } + + // 현재 설정값 백업 + $currentCache = PHPExcel_Settings::getCacheStorageMethod(); + + // 캐싱 모드 설정 (엑셀 생성 전용) + $cacheMethods = [ + PHPExcel_CachedObjectStorageFactory::cache_to_discISAM, + PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized + ]; + + foreach ($cacheMethods as $method) { + if (PHPExcel_Settings::setCacheStorageMethod($method)) { + break; + } + } + + try { + $excel = new PHPExcel(); + $sheet = $excel->setActiveSheetIndex(0); + + // 헤더 스타일 적용 + $last_char = member_export_column_char(count($config['headers']) - 1); + $sheet->getStyle("A2:{$last_char}2")->applyFromArray([ + 'fill' => [ + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => ['rgb' => 'D9E1F2'], // 연파랑 배경 + ], + ]); + + // 셀 정렬 및 줄바꿈 설정 + $sheet->getStyle("A:{$last_char}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER)->setWrapText(true); + + // 컬럼 너비 설정 + foreach ($config['widths'] as $i => $width) { + $sheet->getColumnDimension(member_export_column_char($i))->setWidth($width); + } + + // 데이터 입력 + $sheet->fromArray($data, NULL, 'A1'); + + // 디렉토리 확인 + member_export_ensure_directory(MEMBER_EXPORT_DIR); + + // 파일명 생성 + $subname = $index == 0 ? 'all' : sprintf("%02d", $index); + $filename = $fileName . "_" . $subname . ".xlsx"; + $filePath = MEMBER_EXPORT_DIR . "/" . $filename; + + // 파일 저장 + $writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007'); + $writer->setPreCalculateFormulas(false); + $writer->save($filePath); + + unset($excel, $sheet, $writer); // 생성 완료 후 메모리 해제 + } + catch (Exception $e) + { + throw new Exception("엑셀 파일 생성에 실패하였습니다: " . $e->getMessage()); + } + finally + { + // 캐싱 모드 원래 상태로 복원 + if ($currentCache) { + PHPExcel_Settings::setCacheStorageMethod($currentCache); + } + } + + return $filename; +} + +/** + * 압축 파일 생성 + */ +function member_export_create_zip($files, $zipFileName) +{ + if (!class_exists('ZipArchive')) { + error_log('[Member Export Error] ZipArchive 클래스를 사용할 수 없습니다.'); + return ['error' => '파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다.
    : ZipArchive 클래스를 사용할 수 없습니다.']; + } + + member_export_ensure_directory(MEMBER_EXPORT_DIR); + $destinationZipPath = rtrim(MEMBER_EXPORT_DIR, "/") . "/" . $zipFileName . ".zip"; + + $zip = new ZipArchive(); + if ($zip->open($destinationZipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) { + return ['error' => "파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다."]; + } + + foreach ($files as $file) { + $filePath = MEMBER_EXPORT_DIR . "/" . $file; + if (file_exists($filePath)) { + $zip->addFile($filePath, basename($filePath)); + } + } + + $result = $zip->close(); + + return [ + 'result' => $result, + 'zipFile' => $zipFileName . ".zip", + 'zipPath' => $destinationZipPath, + ]; +} + +/** + * 디렉토리 생성 및 확인 + */ +function member_export_ensure_directory($dir) +{ + if (!is_dir($dir)) { + if (!@mkdir($dir, G5_DIR_PERMISSION, true)) { + throw new Exception("디렉토리 생성 실패"); + } + @chmod($dir, G5_DIR_PERMISSION); + } + + if (!is_writable($dir)) { + throw new Exception("디렉토리 쓰기 권한 없음"); + } +} + +/** + * 파일 삭제 - 값이 있으면 해당 파일만 삭제, 없으면 디렉토리 내 모든 파일 삭제 + * - 알집 생성 완료 시 엑셀 파일 제거 + * - 작업 전 오늘 날짜 폴더 및 log 폴더를 제외한 나머지 파일 모두 제거 + */ +function member_export_delete($fileList = []) +{ + $cnt = 0; + + // 파일 리스트가 있는 경우 -> 해당 파일만 삭제 + if (!empty($fileList)) { + foreach ($fileList as $file) { + $filePath = rtrim(MEMBER_EXPORT_DIR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file; + if (file_exists($filePath) && is_file($filePath) && @unlink($filePath)) { + $cnt++; + } + } + } + // 파일 리스트가 없는 경우 -> 디렉토리 내 모든 파일 삭제 + else { + $files = glob(rtrim(G5_DATA_PATH . "/" . MEMBER_BASE_DIR, '/') . '/*'); + + function deleteFolder($dir) { + foreach (glob($dir . '/{.,}*', GLOB_BRACE) as $item) { + if (in_array(basename($item), ['.', '..'])) continue; + is_dir($item) ? deleteFolder($item) : unlink($item); + } + rmdir($dir); + } + + foreach ($files as $file) { + $name = basename($file); + + // log 폴더와 오늘 날짜로 시작하는 폴더는 제외 + if ($name === 'log' || preg_match('/^' . date('Ymd') . '\d{6}$/', $name)) continue; + + if (is_file($file) && pathinfo($file, PATHINFO_EXTENSION) !== 'log' && @unlink($file)) { + $cnt++; + } elseif (is_dir($file)) { + deleteFolder($file); // 재귀 폴더 삭제 함수 사용 + $cnt++; + } + } + } + + return $cnt; +} + +/** + * 로그 작성 + */ +function member_export_write_log($params, $result = []) +{ + global $member; + + $maxSize = 1024 * 1024 * 2; // 2MB + $maxFiles = 10; // 최대 로그 파일 수 (필요시 조정) + $username = $member['mb_id'] ?? 'guest'; + $datetime = date("Y-m-d H:i:s"); + + if (!is_dir(MEMBER_LOG_DIR)) { + @mkdir(MEMBER_LOG_DIR, G5_DIR_PERMISSION, true); + @chmod(MEMBER_LOG_DIR, G5_DIR_PERMISSION); + } + + $logFiles = glob(MEMBER_LOG_DIR . "/export_log_*.log") ?: []; + + // 최신 파일 기준 정렬 (최신 → 오래된) + usort($logFiles, fn($a, $b) => filemtime($b) - filemtime($a)); + + $latestLogFile = $logFiles[0] ?? null; + + // 용량 기준으로 새 파일 생성 + if (!$latestLogFile || filesize($latestLogFile) >= $maxSize) { + $latestLogFile = MEMBER_LOG_DIR . "/export_log_" . date("YmdHi") . ".log"; + file_put_contents($latestLogFile, ''); + array_unshift($logFiles, $latestLogFile); + } + + // 최대 파일 수 초과 시 오래된 파일 제거 + if (count($logFiles) > $maxFiles) { + $filesToDelete = array_slice($logFiles, $maxFiles); + foreach ($filesToDelete as $file) { + @unlink($file); + } + } + + $formatType = (isset($params['formatType']) && $params['formatType'] == 2) ? '팝빌' : '일반'; + $success = isset($result['success']) && $result['success'] === true; + $status = $success ? '성공' : '실패'; + + // 조건 정리 + $condition = []; + + // 검색 조건 + if ($params['use_stx'] == 1 && !empty($params['stx'])) { + $sfl_list = get_export_config('sfl_list'); + + $label = $sfl_list[$params['sfl']] ?? ''; + $condition[] = "검색({$params['stx_cond']}) : {$label} - {$params['stx']}"; + } + + // 레벨 조건 + if ($params['use_level'] == 1 && ($params['level_start'] || $params['level_end'])) { + $condition[] = "레벨: {$params['level_start']}~{$params['level_end']}"; + } + + // 가입일 조건 + if ($params['use_date'] == 1 && ($params['date_start'] || $params['date_end'])) { + $condition[] = "가입일: {$params['date_start']}~{$params['date_end']}"; + } + + // 포인트 조건 + if ($params['use_point'] == 1 && $params['point'] !== '') { + $point_cond_map = get_export_config('point_cond_map'); + $symbol = $point_cond_map[$params['point_cond']] ?? '≥'; + $condition[] = "포인트 {$symbol} {$params['point']}"; + } + + // 휴대폰 여부 + if ($params['use_hp_exist'] == 1) { + $condition[] = "휴대폰번호 있는 경우만"; + } + + // 광고 수신 동의 + if ($params['ad_range_only'] == 1) { + $ad_range_list = get_export_config('ad_range_list'); + $label = $ad_range_list[$params['ad_range_type']] ?? ''; + $condition[] = "수신동의: 예 ({$label})"; + + if ($params['ad_range_type'] == "custom_period" && ($params['agree_date_start'] || $params['agree_date_end'])) { + $condition[] = "수신동의일: {$params['agree_date_start']}~{$params['agree_date_end']}"; + } + + if (in_array($params['ad_range_type'], ["month_confirm", "custom_period"])){ + $channels = array_filter([ + !empty($params['ad_mailling']) && (int)$params['ad_mailling'] === 1 ? '이메일' : null, + !empty($params['ad_sms']) && (int)$params['ad_sms'] === 1 ? 'SMS/카카오톡' : null, + ]); + + if ($channels) { + $condition[] = '수신채널: ' . implode(', ', $channels); + } + } + } + + // 차단회원 처리 + if ($params['use_intercept'] == 1) { + $intercept_list = get_export_config('intercept_list'); + $label = $intercept_list[$params['intercept']] ?? ''; + if ($label) $condition[] = $label; + } + + $conditionStr = !empty($condition) ? implode(', ', $condition) : '없음'; + $line1 = "[{$datetime}] [{$status}] 관리자: {$username} | 형식: {$formatType}"; + + // 성공일 경우 추가 정보 + if ($success) { + $total = $result['total'] ?? 0; + $fileCount = isset($result['zip']) ? 1 : count($result['files'] ?? []); + $line1 .= " | 총 {$total}건 | 파일: {$fileCount}개"; + } + + $logEntry = $line1 . PHP_EOL; + $logEntry .= "조건: {$conditionStr}" . PHP_EOL; + + if (!$success && !empty($result['error'])) { + $logEntry .= "오류 메시지: {$result['error']}" . PHP_EOL; + } + + $logEntry .= PHP_EOL; + + // 파일에 기록 + if (@file_put_contents($latestLogFile, $logEntry, FILE_APPEND | LOCK_EX) === false) { + error_log("[Member Export Error] 로그 파일 기록 실패: {$latestLogFile}"); + } +} \ No newline at end of file diff --git a/adm/member_list_file_delete.php b/adm/member_list_file_delete.php new file mode 100644 index 000000000..d6c1fb058 --- /dev/null +++ b/adm/member_list_file_delete.php @@ -0,0 +1,72 @@ + + +
    +

    + 완료 메세지가 나오기 전에 프로그램의 실행을 중지하지 마십시오. +

    +
    + +회원관리파일를 열지못했습니다.

    '; +} + +$cnt = 0; +echo '
      ' . PHP_EOL; + +$files = glob(G5_DATA_PATH . '/member_list/*'); +$cnt = 0; + +// 폴더 및 하위 파일 재귀 삭제 함수 +function deleteFolder($folderPath) { + $items = glob($folderPath . '/*'); + foreach ($items as $item) { + if (is_dir($item)) { + deleteFolder($item); + } else { + unlink($item); + } + } + rmdir($folderPath); // 폴더 자체 삭제 +} + +if (is_array($files)) { + foreach ($files as $member_list_file) { + // log 확장자가 아닌 파일/디렉토리 처리 + $ext = strtolower(pathinfo($member_list_file, PATHINFO_EXTENSION)); + $basename = basename($member_list_file); + + if (is_file($member_list_file) && $ext !== 'log') { + unlink($member_list_file); + echo '
    • 파일 삭제: ' . $member_list_file . '
    • ' . PHP_EOL; + $cnt++; + } elseif (is_dir($member_list_file) && $basename !== 'log') { + deleteFolder($member_list_file); + echo '
    • 폴더 삭제: ' . $member_list_file . '
    • ' . PHP_EOL; + $cnt++; + } + + flush(); + + if ($cnt % 10 == 0) { + echo PHP_EOL; + } + } +} +echo '
    • 완료됨
    ' . PHP_EOL; +echo '

    회원관리파일 ' . $cnt . '건 삭제 완료됐습니다.
    프로그램의 실행을 끝마치셔도 좋습니다.

    ' . PHP_EOL; +?> + + +
    +
    +
    +

    카카오톡 발송 서비스

    +

    주문이나 배송시에 상점운영자 또는 고객에게 휴대폰으로 카카오톡을 발송합니다. 연동회원으로 선택한 후 링크아이디 [SIRSOFT]를 입력해 주세요.

    +
    +
    팝빌 카카오톡 서비스 신청하기
    +
    +
    +
    diff --git a/adm/shop_admin/itemform.php b/adm/shop_admin/itemform.php index 0f6ec2a11..1ebf105eb 100644 --- a/adm/shop_admin/itemform.php +++ b/adm/shop_admin/itemform.php @@ -680,9 +680,9 @@ $(function(){
    - + > 예
    상품명 휴대폰번호SMS전송SMS전송일시전송결과전송채널전송일시 등록일시
    자료가 없습니다.
    자료가 없습니다.
    @@ -157,6 +165,10 @@ $listall = '전체목 + + + +
    @@ -170,13 +182,18 @@ function fitemstocksms_submit(f) return false; } - if(document.pressed == "선택삭제") { - if(!confirm("선택한 자료를 정말 삭제하시겠습니까?")) { - return false; - } - } + var action = document.pressed; - return true; + switch (action) { + case "선택삭제": + return confirm("선택한 자료를 정말 삭제하시겠습니까?"); + case "선택SMS전송": + return confirm("선택한 자료에 대해서 SMS로 재입고 알림을 전송하시겠습니까?"); + case "선택알림톡전송": + return confirm("선택한 자료에 대해서 알림톡으로 재입고 알림을 전송하시겠습니까?"); + default: + return true; + } } diff --git a/adm/shop_admin/itemstocksmsupdate.php b/adm/shop_admin/itemstocksmsupdate.php index 27cbde6d9..5cfbe5f82 100644 --- a/adm/shop_admin/itemstocksmsupdate.php +++ b/adm/shop_admin/itemstocksmsupdate.php @@ -45,7 +45,8 @@ if ($_POST['act_button'] == "선택SMS전송") { // SMS 전송으로 변경함 $sql = " update {$g5['g5_shop_item_stocksms_table']} set ss_send = '1', - ss_send_time = '".G5_TIME_YMDHIS."' + ss_send_time = '".G5_TIME_YMDHIS."', + ss_channel = '1' where ss_id = '{$ss_id}' "; sql_query($sql); } @@ -98,6 +99,52 @@ if ($_POST['act_button'] == "선택SMS전송") { $SMS->Init(); // 보관하고 있던 결과값을 지웁니다. } } +} else if ($_POST['act_button'] == "선택알림톡전송") { + // 알림톡 발송 BEGIN: 재입고알림(CU-ST01) ------------------------------------- + auth_check_menu($auth, $sub_menu, 'w'); + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + + if (!$config['cf_kakaotalk_use']) { + alert('카카오톡 사용 설정이 되어 있지 않아 발송할 수 없습니다.\n[환경설정>기본환경설정>기본알림환경]에서 사용 설정을 해주세요.'); + } else { + // 프리셋 정보 가져오기 + $alimtalk = get_alimtalk_preset_info('CU-ST01'); + + if (empty($alimtalk['success'])) { + alert('재입고 알림톡 설정이 되어 있지 않아 발송할 수 없습니다.\n[환경설정>알림톡프리셋 관리]에서 설정해주세요.'); + } else { + for ($i=0; $i<$count_post_chk; $i++) { + + // 실제 번호를 넘김 + $k = isset($_POST['chk'][$i]) ? (int) $_POST['chk'][$i] : 0; + $ss_id = isset($_POST['ss_id'][$k]) ? (int) $_POST['ss_id'][$k] : 0; + + $sql = " select a.ss_id, a.ss_hp, a.ss_send, b.it_id, b.it_name + from {$g5['g5_shop_item_stocksms_table']} a left join {$g5['g5_shop_item_table']} b on ( a.it_id = b.it_id ) + where a.ss_id = '$ss_id' "; + $row = sql_fetch($sql); + + if(!$row['ss_id'] || !$row['it_id'] || $row['ss_send']) + continue; + + $conditions = ['it_id' => $row['it_id'], 'it_name' => get_text($row['it_name'])]; // 변수 치환 정보 + + $cu_atk = send_alimtalk_preset('CU-ST01', ['rcv' => $row['ss_hp']], $conditions); // 회원 + + // 성공한 건만 완료 처리 + if (!empty($cu_atk) && !empty($cu_atk['success'])) + { + sql_query(" update {$g5['g5_shop_item_stocksms_table']} + set ss_send = '1', + ss_send_time = '".G5_TIME_YMDHIS."', + ss_channel = '2' + where ss_id = '{$ss_id}' "); + } + } + } + } + // 알림톡 발송 END ------------------------------------------------------------- + } else if ($_POST['act_button'] == "선택삭제") { if ($is_admin != 'super') diff --git a/adm/shop_admin/orderalimtalk.inc.php b/adm/shop_admin/orderalimtalk.inc.php new file mode 100644 index 000000000..aebdd57a0 --- /dev/null +++ b/adm/shop_admin/orderalimtalk.inc.php @@ -0,0 +1,22 @@ + $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od_hp ?: $od['od_tel'], 'rcvnm' => $od_name], $conditions); // 회원 + // 알림톡 발송 END -------------------------------------------------------- +} + +// 배송 알림 +if($od_alimtalk_baesong_check){ + // 알림톡 발송 BEGIN: 배송중(CU-DE02) ------------------------------ + $conditions = ['od_id' => $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-DE02', ['rcv' => $od_hp ?: $od['od_tel'], 'rcvnm' => $od_name], $conditions); // 회원 + // 알림톡 발송 END -------------------------------------------------------- +} \ No newline at end of file diff --git a/adm/shop_admin/orderdeliveryupdate.php b/adm/shop_admin/orderdeliveryupdate.php index 3030de506..6b2886cd7 100644 --- a/adm/shop_admin/orderdeliveryupdate.php +++ b/adm/shop_admin/orderdeliveryupdate.php @@ -3,6 +3,7 @@ $sub_menu = '400400'; include_once('./_common.php'); include_once('./admin.shop.lib.php'); include_once(G5_LIB_PATH.'/mailer.lib.php'); +include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); auth_check_menu($auth, $sub_menu, "w"); @@ -98,6 +99,14 @@ if(isset($_FILES['excelfile']['tmp_name']) && $_FILES['excelfile']['tmp_name']) include(G5_SHOP_PATH.'/'.$od['od_pg'].'/escrow.register.php'); } + + // 알림톡 발송 BEGIN: 배송중(CU-DE02/AD-DE02) ------------------------------ + $it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명 + + $conditions = ['od_id' => $od_id, 'od_name' => $od['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-DE02', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-DE02', 'super', $conditions); // 관리자 + // 알림톡 발송 END -------------------------------------------------------- } } diff --git a/adm/shop_admin/orderform.php b/adm/shop_admin/orderform.php index fbeac0dc1..baffdc4de 100644 --- a/adm/shop_admin/orderform.php +++ b/adm/shop_admin/orderform.php @@ -1,6 +1,7 @@

    주문, 입금, 준비, 배송, 완료는 장바구니와 주문서 상태를 모두 변경하지만, 취소, 반품, 품절은 장바구니의 상태만 변경하며, 주문서 상태는 변경하지 않습니다.

    개별적인(이곳에서의) 상태 변경은 모든 작업을 수동으로 처리합니다. 예를 들어 주문에서 입금으로 상태 변경시 입금액(결제금액)을 포함한 모든 정보는 수동 입력으로 처리하셔야 합니다.

    + +

    * 알림톡 프리셋: [준비, 완료, 취소, 반품, 품절]자동으로 발송되며, [입금완료, 배송]결제상세정보에서 수동으로 발송하셔야 합니다.

    + @@ -719,6 +723,16 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
    + + + + + +
    + + @@ -826,6 +840,17 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
    + + + + + +
    + + diff --git a/adm/shop_admin/orderformcartupdate.php b/adm/shop_admin/orderformcartupdate.php index 4d47da98b..84bcb5681 100644 --- a/adm/shop_admin/orderformcartupdate.php +++ b/adm/shop_admin/orderformcartupdate.php @@ -1,6 +1,7 @@ 'DE01', '완료' => 'DE03', '취소' => 'OR05', '반품' => 'OR06', '품절' => 'OR07' ]; // 알림톡 코드 매핑 + + // 처리상품명 및 치환 변수 값 세팅 + $order = sql_fetch("select * from {$g5['g5_shop_order_table']} where od_id = '$od_id'"); // 주문 정보 조회 + $it_name = !empty($od_names) ? $od_names[0] . (count($od_names) > 1 ? ' 외 ' . (count($od_names) - 1) . '건' : '') : ''; // 상품명 + $conditions = [ 'od_id' => $od_id, 'it_name' => $it_name ]; // 변수 치환 정보 + + if (isset($alimtalk_map[$_POST['ct_status']])) { + $status_code = $alimtalk_map[$_POST['ct_status']]; + + // 고객 발송 (준비, 완료, 취소, 반품, 품절 공통) + $cu_atk = send_alimtalk_preset('CU-' . $status_code, ['rcv' => $order['od_hp'] ?: $order['od_tel'], 'rcvnm' => $order['od_name']], $conditions); // 회원 + + // 관리자 발송 (취소만) + if ($_POST['ct_status'] === '취소') { + $ad_atk = send_admin_alimtalk('AD-' . $status_code, 'super', $conditions); // 관리자 + } + } + // 알림톡 발송 END ------------------------------------------------------------------------------------------------------------------- + // 1.06.06 $od = sql_fetch(" select od_receipt_point from {$g5['g5_shop_order_table']} where od_id = '$od_id' "); if ($od['od_receipt_point']) diff --git a/adm/shop_admin/orderformreceiptupdate.php b/adm/shop_admin/orderformreceiptupdate.php index 7bfb6b787..de2ca0be8 100644 --- a/adm/shop_admin/orderformreceiptupdate.php +++ b/adm/shop_admin/orderformreceiptupdate.php @@ -161,6 +161,9 @@ include "./ordermail.inc.php"; define("_ORDERSMS_", true); include "./ordersms.inc.php"; +// 알림톡 전송 문자전송 +define("_ORDERALIMTALK_", true); +include "./orderalimtalk.inc.php"; // 에스크로 배송처리 if($posts['od_tno'] && $posts['od_escrow'] == 1) diff --git a/adm/shop_admin/orderlistupdate.php b/adm/shop_admin/orderlistupdate.php index 20929a3b1..1aa492e91 100644 --- a/adm/shop_admin/orderlistupdate.php +++ b/adm/shop_admin/orderlistupdate.php @@ -3,6 +3,7 @@ $sub_menu = '400400'; include_once('./_common.php'); include_once('./admin.shop.lib.php'); include_once(G5_LIB_PATH.'/mailer.lib.php'); +include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); check_admin_token(); @@ -48,6 +49,7 @@ for ($i=0; $i<$count_post_chk; $i++) $current_status = $od['od_status']; $change_status = isset($_POST['od_status']) ? clean_xss_tags($_POST['od_status'], 1, 1) : ''; + $it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명 switch ($current_status) { @@ -69,6 +71,12 @@ for ($i=0; $i<$count_post_chk; $i++) } } + // 알림톡 발송 BEGIN: 입금완료(CU-OR03/AD-OR03) ------------------------------ + $conditions = ['od_id' => $od_id, 'od_name' => $od['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR03', 'super', $conditions); // 관리자 + // 알림톡 발송 END -------------------------------------------------------- + // 메일 if($config['cf_email_use'] && $od_send_mail) include './ordermail.inc.php'; @@ -78,6 +86,12 @@ for ($i=0; $i<$count_post_chk; $i++) case '입금' : if ($change_status != '준비') continue 2; change_status($od_id, '입금', '준비'); + + // 알림톡 발송 BEGIN: 배송준비(CU-DE01/AD-DE01) ------------------------------ + $conditions = ['od_id' => $od_id, 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-DE01', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-DE01', 'super', $conditions); // 관리자 + // 알림톡 발송 END ------------------------------------------------------------- break; case '준비' : @@ -115,6 +129,11 @@ for ($i=0; $i<$count_post_chk; $i++) include(G5_SHOP_PATH.'/'.$od['od_pg'].'/escrow.register.php'); } + // 알림톡 발송 BEGIN: 배송중(CU-DE02/AD-DE02) ------------------------------------- + $conditions = ['od_id' => $od_id, 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-DE02', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-DE02', 'super', $conditions); // 관리자 + // 알림톡 발송 END ------------------------------------------------------------- break; case '배송' : @@ -139,6 +158,12 @@ for ($i=0; $i<$count_post_chk; $i++) sql_query($sql3); } */ + + // 알림톡 발송 BEGIN: 배송완료(CU-DE03/AD-DE03) ------------------------------------- + $conditions = ['od_id' => $od_id, 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-DE03', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-DE03', 'super', $conditions); // 관리자 + // 알림톡 발송 END ------------------------------------------------------------- break; } // switch end diff --git a/bbs/poll_etc_update.php b/bbs/poll_etc_update.php index 772826872..8e47e5954 100644 --- a/bbs/poll_etc_update.php +++ b/bbs/poll_etc_update.php @@ -43,6 +43,12 @@ if ($w == '') $from_email = $member['mb_email'] ? $member['mb_email'] : $admin['mb_email']; mailer($name, $from_email, $admin['mb_email'], '['.$config['cf_title'].'] 설문조사 기타의견 메일', $content, 1); } + + // 알림톡 발송 BEGIN: 기타의견 작성(AD-VO01) ------------------------------------- + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + $conditions = ['po_id' => $po_id, 'pc_id' => $pc_id]; // 변수 치환 정보 + $ad_atk = send_admin_alimtalk('AD-VO01', 'super', $conditions); // 관리자 + // 알림톡 발송 END ------------------------------------------------------------- } else if ($w == 'd') { diff --git a/bbs/qawrite_update.php b/bbs/qawrite_update.php index 4c6902936..7c3d8825b 100644 --- a/bbs/qawrite_update.php +++ b/bbs/qawrite_update.php @@ -472,6 +472,25 @@ if(($w == '' || $w == 'r') && trim($qaconfig['qa_admin_email'])) { mailer($config['cf_admin_email_name'], $qa_email, $qaconfig['qa_admin_email'], $subject, $content, 1); } +// 알림톡 발송 BEGIN: 1:1 문의(CU-IQ01/CU-IQ02/AD-IQ01) ------------------------------------- +include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); +$conditions = ['qa_id' => $qa_id, 'qa_name' => $write['qa_name'] ?? $member['mb_nick'], 'mb_name' => $write['qa_name'] ?? $member['mb_nick']]; // 변수 치환 정보 + +// 답변글은 질문 등록자에게 전송 +if ($w == 'a' && !empty($write['qa_hp'])) { + $cu_atk = send_alimtalk_preset('CU-IQ02', ['rcv' => $write['qa_hp'], 'rcvnm' => $write['qa_name']], $conditions); // 회원 +} + +// 문의글 등록시 질문등록자/관리자에게 전송 +if ($w == '' || $w == 'r') { + $ad_atk = send_admin_alimtalk('AD-IQ01', 'super', $conditions); // 관리자 + + if (!empty($qa_hp)) { + $cu_atk = send_alimtalk_preset('CU-IQ01', ['rcv' => $qa_hp, 'rcvnm' => $member['mb_nick']], $conditions); // 회원 + } +} +// 알림톡 발송 END ------------------------------------------------------------- + if($w == 'a') $result_url = G5_BBS_URL.'/qaview.php?qa_id='.$qa_id.$qstr; else if($w == 'u' && $write['qa_type']) diff --git a/bbs/register_form_update.php b/bbs/register_form_update.php index 2617aa4ce..3a574df67 100644 --- a/bbs/register_form_update.php +++ b/bbs/register_form_update.php @@ -50,8 +50,8 @@ $mb_addr_jibeon = isset($_POST['mb_addr_jibeon']) ? trim($_POST['mb_addr_jibeo $mb_signature = isset($_POST['mb_signature']) ? trim($_POST['mb_signature']) : ""; $mb_profile = isset($_POST['mb_profile']) ? trim($_POST['mb_profile']) : ""; $mb_recommend = isset($_POST['mb_recommend']) ? trim($_POST['mb_recommend']) : ""; -$mb_mailling = isset($_POST['mb_mailling']) ? trim($_POST['mb_mailling']) : ""; -$mb_sms = isset($_POST['mb_sms']) ? trim($_POST['mb_sms']) : ""; +$mb_mailling = isset($_POST['mb_mailling']) ? trim($_POST['mb_mailling']) : "0"; +$mb_sms = isset($_POST['mb_sms']) ? trim($_POST['mb_sms']) : "0"; $mb_open = isset($_POST['mb_open']) ? trim($_POST['mb_open']) : "0"; $mb_1 = isset($_POST['mb_1']) ? trim($_POST['mb_1']) : ""; $mb_2 = isset($_POST['mb_2']) ? trim($_POST['mb_2']) : ""; @@ -63,7 +63,6 @@ $mb_7 = isset($_POST['mb_7']) ? trim($_POST['mb_7']) $mb_8 = isset($_POST['mb_8']) ? trim($_POST['mb_8']) : ""; $mb_9 = isset($_POST['mb_9']) ? trim($_POST['mb_9']) : ""; $mb_10 = isset($_POST['mb_10']) ? trim($_POST['mb_10']) : ""; - $mb_name = clean_xss_tags($mb_name, 1, 1); $mb_email = get_email_address($mb_email); $mb_homepage = clean_xss_tags($mb_homepage, 1, 1); @@ -75,6 +74,13 @@ $mb_addr2 = clean_xss_tags($mb_addr2, 1, 1); $mb_addr3 = clean_xss_tags($mb_addr3, 1, 1); $mb_addr_jibeon = preg_match("/^(N|R)$/", $mb_addr_jibeon) ? $mb_addr_jibeon : ''; +$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? trim($_POST['mb_marketing_agree']) : "0"; +$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? trim($_POST['mb_thirdparty_agree']) : "0"; +$mb_board_post = isset($_POST['mb_board_post']) ? trim($_POST['mb_board_post']) : "0"; +$mb_board_reply = isset($_POST['mb_board_reply']) ? trim($_POST['mb_board_reply']) : "0"; +$mb_board_comment = isset($_POST['mb_board_comment']) ? trim($_POST['mb_board_comment']) : "0"; +$mb_board_recomment = isset($_POST['mb_board_recomment']) ? trim($_POST['mb_board_recomment']) : "0"; + run_event('register_form_update_before', $mb_id, $w); if ($w == '' || $w == 'u') { @@ -250,12 +256,50 @@ if ($w == '') { mb_7 = '{$mb_7}', mb_8 = '{$mb_8}', mb_9 = '{$mb_9}', - mb_10 = '{$mb_10}' + mb_10 = '{$mb_10}', + mb_marketing_agree = '{$mb_marketing_agree}', + mb_thirdparty_agree = '{$mb_thirdparty_agree}', + mb_board_post = '{$mb_board_post}', + mb_board_reply = '{$mb_board_reply}', + mb_board_comment = '{$mb_board_comment}', + mb_board_recomment = '{$mb_board_recomment}' {$sql_certify} "; // 이메일 인증을 사용하지 않는다면 이메일 인증시간을 바로 넣는다 if (!$config['cf_use_email_certify']) $sql .= " , mb_email_certify = '".G5_TIME_YMDHIS."' "; + + $agree_items = []; + // 마케팅 목적의 개인정보 수집 및 이용 + if ($mb_marketing_agree == 1) { + $sql .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(동의)"; + } + + // 광고성 이메일 수신 + if ($mb_mailling == 1) { + $sql .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(동의)"; + } + + // 광고성 SMS/카카오톡 수신 + if ($mb_sms == 1) { + $sql .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(동의)"; + } + + // 개인정보 제3자 제공 + if ($mb_thirdparty_agree == 1) { + $sql .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(동의)"; + } + + // 동의 로그 추가 + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 회원가입] " . implode(' | ', $agree_items) . "\n"; + $sql .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } + sql_query($sql); // 회원가입 포인트 부여 @@ -322,6 +366,17 @@ if ($w == '') { insert_member_cert_history($mb_id, $mb_name, $mb_hp, get_session('ss_cert_birth'), get_session('ss_cert_type') ); // 본인인증 후 정보 수정 시 내역 기록 } + // 알림톡 발송 BEGIN: 회원가입 (CU-MB01/AD-MB01) ------------------------------------- + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + $conditions = ['mb_id' => $mb_id]; // 변수 치환 정보 + + $ad_atk = send_admin_alimtalk('AD-MB01', 'super', $conditions); // 관리자 + + // 회원 - 휴대폰 번호가 있을 경우만 + if (!empty($mb_hp)) { + $cu_atk = send_alimtalk_preset('CU-MB01', ['rcv' => $mb_hp, 'rcvnm' => $mb_name], $conditions); // 회원 + } + // 알림톡 발송 END -------------------------------------------------------- } else if ($w == 'u') { if (!trim(get_session('ss_mb_id'))) alert('로그인 되어 있지 않습니다.'); @@ -346,6 +401,43 @@ if ($w == '') { if ($old_email != $mb_email && $config['cf_use_email_certify']) $sql_email_certify = " , mb_email_certify = '' "; + $agree_items = []; + + // 마케팅 목적의 개인정보 수집 및 이용 + $sql_marketing_date = ""; + if ($mb_marketing_agree_default !== $mb_marketing_agree) { + $sql_marketing_date .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(" . ($mb_marketing_agree == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 이메일 수신 + $sql_mailling_date = ""; + if ($mb_mailling_default !== $mb_mailling) { + $sql_mailling_date .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(" . ($mb_mailling == 1 ? "동의" : "철회") . ")"; + } + + // 광고성 SMS/카카오톡 수신 + $sql_sms_date = ""; + if ($mb_sms_default !== $mb_sms) { + $sql_sms_date .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(" . ($mb_sms == 1 ? "동의" : "철회") . ")"; + } + + // 개인정보 제3자 제공 + $sql_thirdparty_date = ""; + if ($mb_thirdparty_agree_default !== $mb_thirdparty_agree) { + $sql_thirdparty_date .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(" . ($mb_thirdparty_agree == 1 ? "동의" : "철회") . ")"; + } + + // 동의 로그 추가 + $sql_agree_log = ""; + if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", 회원 정보 수정] " . implode(' | ', $agree_items) . "\n"; + $sql_agree_log .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; + } + $sql = " update {$g5['member_table']} set mb_nick = '{$mb_nick}', mb_mailling = '{$mb_mailling}', @@ -371,12 +463,23 @@ if ($w == '') { mb_7 = '{$mb_7}', mb_8 = '{$mb_8}', mb_9 = '{$mb_9}', - mb_10 = '{$mb_10}' + mb_10 = '{$mb_10}', + mb_marketing_agree = '{$mb_marketing_agree}', + mb_thirdparty_agree = '{$mb_thirdparty_agree}', + mb_board_post = '{$mb_board_post}', + mb_board_reply = '{$mb_board_reply}', + mb_board_comment = '{$mb_board_comment}', + mb_board_recomment = '{$mb_board_recomment}' {$sql_password} {$sql_nick_date} {$sql_open_date} {$sql_email_certify} {$sql_certify} + {$sql_mailling_date} + {$sql_sms_date} + {$sql_marketing_date} + {$sql_thirdparty_date} + {$sql_agree_log} where mb_id = '$mb_id' "; sql_query($sql); diff --git a/bbs/write_comment_update.php b/bbs/write_comment_update.php index ed1984251..c164c8e25 100644 --- a/bbs/write_comment_update.php +++ b/bbs/write_comment_update.php @@ -260,6 +260,56 @@ if ($w == 'c') // 댓글 입력 } } + // 알림톡 발송 BEGIN: 새 댓글 작성(CU-BO02,CU-BO03) ------------------------------------- + if ($config['cf_kakaotalk_use'] && $board['bo_use_kakaotalk']) { + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + + // 댓글 작성자 ID (회원/게스트 모두 고려) + $current_mb_id = $member['mb_id'] ?? ''; + + // 방금 저장된 댓글 레코드 로드 (실패 시 중단) + $comment = get_write($write_table, $comment_id); + if ($comment && !empty($comment['wr_id'])) { + $conditions = ['bo_table' => $bo_table, 'wr_id'=> $wr['wr_id'], 'wr_name_comment' => $comment['wr_name'] ?? '']; // 변수 치환 정보 + + // 1) 원글 작성자 알림 (CU-BO02) + $post_mb_id = $wr['mb_id'] ?? ''; + if ($post_mb_id !== '' && $post_mb_id !== $current_mb_id) { + $mb_post = get_member($post_mb_id); + if (!empty($mb_post['mb_board_comment']) && !empty($mb_post['mb_hp'])) { + $cu_atk = send_alimtalk_preset('CU-BO02', ['rcv' => $mb_post['mb_hp'], 'rcvnm' => ($mb_post['mb_name'] ?? $mb_post['mb_nick'] ?? '')], $conditions); + } + } + + // 2) 직속 부모 댓글 작성자 알림 (CU-BO03) + $reply = $comment['wr_comment_reply'] ?? ''; + if ($reply !== '') { + $parent_reply_esc = sql_escape_string(substr($reply, 0, -1)); + $wr_parent = $wr['wr_id']; + $wr_comment = $comment['wr_comment']; + + $sql_parent = " + SELECT wr_id, mb_id + FROM {$write_table} + WHERE wr_parent = {$wr_parent} + AND wr_is_comment = 1 + AND wr_comment = {$wr_comment} + AND wr_comment_reply = '{$parent_reply_esc}' + LIMIT 1 + "; + $pr = sql_fetch($sql_parent); + + if (!empty($pr['mb_id']) && $pr['mb_id'] !== $current_mb_id && $pr['mb_id'] !== ($post_mb_id ?: '')) { + $mb_parent = get_member($pr['mb_id']); + if (!empty($mb_parent['mb_board_recomment']) && !empty($mb_parent['mb_hp'])) { + $cu_atk = send_alimtalk_preset('CU-BO03', ['rcv' => $mb_parent['mb_hp'], 'rcvnm' => ($mb_parent['mb_name'] ?? $mb_parent['mb_nick'] ?? '')], $conditions); + } + } + } + } + } + // 알림톡 발송 END ------------------------------------------------------------------ + // SNS 등록 include_once("./write_comment_update.sns.php"); if($wr_facebook_user || $wr_twitter_user) { diff --git a/bbs/write_update.php b/bbs/write_update.php index 9b169d732..9e7c2a64a 100644 --- a/bbs/write_update.php +++ b/bbs/write_update.php @@ -756,6 +756,45 @@ if (!($w == 'u' || $w == 'cu') && $config['cf_email_use'] && $board['bo_use_emai } } +// 알림톡 발송 BEGIN: 새 게시글/답변 작성 (CU-BO01,CU-BO04/AD-BO01,AD-BO02,AD-BO03) ------------------------------------- +if ($config['cf_kakaotalk_use'] && $board['bo_use_kakaotalk']) +{ + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + $conditions = ['bo_table' => $bo_table, 'wr_id' => $wr_id, 'mb_id' => $mb_id]; // 변수 치환 정보 + + // 새 게시글 작성 + if ($w === '') { + // 관리자 알림 + $ad_atk_super = send_admin_alimtalk('AD-BO01', 'super', $conditions); // 관리자 + $ad_atk_group = send_admin_alimtalk('AD-BO02', 'group', $conditions, ['super']); // 그룹 관리자 + $ad_atk_board = send_admin_alimtalk('AD-BO03', 'board', $conditions, ['super', 'group']); // 게시판 관리자 + + // 작성자 본인 알림 + if (!empty($mb_id)) { + $writer = get_member($mb_id); + if ($writer && !empty($writer['mb_board_post']) && !empty($writer['mb_hp'])) { + $cu_atk = send_alimtalk_preset('CU-BO01', ['rcv' => $writer['mb_hp'], 'rcvnm' => ($writer['mb_name'] ?? '')], $conditions); // 회원 + } + } + } + + // 답변 작성 + else if ($w === 'r') { + $parent_mb_id = $wr['mb_id'] ?? ''; + + if ($parent_mb_id && $parent_mb_id !== ($member['mb_id'] ?? '')) { + $conditions['mb_id'] = $parent_mb_id; + $conditions['wr_subject'] = $wr['wr_subject']; + $writer = get_member($parent_mb_id); + + if ($writer && !empty($writer['mb_board_reply']) && !empty($writer['mb_hp'])) { + $cu_atk = send_alimtalk_preset('CU-BO04', ['rcv' => $writer['mb_hp'], 'rcvnm' => ($writer['mb_name'] ?? '')], $conditions); // 회원 + } + } + } +} +// 알림톡 발송 END -------------------------------------------------------- + // 사용자 코드 실행 @include_once($board_skin_path.'/write_update.skin.php'); @include_once($board_skin_path.'/write_update.tail.skin.php'); diff --git a/css/default.css b/css/default.css index b32a453e0..05ec8c8a2 100644 --- a/css/default.css +++ b/css/default.css @@ -55,6 +55,16 @@ border:1px solid #558ab7 !important; #container_wr, #ft_wr {width:1200px} +/* 공통 - display none/block */ +.is-hidden { display: none !important; } +.is-visible { display: block !important; } + +/* 공통 - 뷰포트 (pc / mobile) 별 display none/block */ +.pc-only { display: none; } +@media (min-width: 769px) { .pc-only { display: block !important; }} +.mobile-only { display: block; } +@media (min-width: 769px) { .mobile-only { display: none !important; }} + /* 팝업레이어 */ #hd_pop {z-index:1000;position:relative;margin:0 auto;height:0} #hd_pop h2 {position:absolute;font-size:0;line-height:0;overflow:hidden} @@ -332,6 +342,8 @@ box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075); .tbl_frm01 a {text-decoration:none} .tbl_frm01 .frm_file {display:block;margin-bottom:5px} .tbl_frm01 .frm_info {display:block;padding:0 0 5px;line-height:1.4em} +.frm_info.add_info { margin-top: 10px !important; padding: 8px 12px; background: #fff; border: 1px solid #ddd; border-radius: 6px; line-height: 1.6; } +.btn_info_toggle { display: block; margin: 5px 0 0 21px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /*기본 리스트*/ .list_01 ul {border-top:1px solid #ececec} diff --git a/extend/kakao5.extend.php b/extend/kakao5.extend.php new file mode 100644 index 000000000..6355b52c1 --- /dev/null +++ b/extend/kakao5.extend.php @@ -0,0 +1,329 @@ + ['url' => G5_URL.'/shop/orderinquiryview.php?od_id={od_id}'] +// 위의 경우 $conditions['od_id'] 값이 있어야 {od_id}가 실제 주문번호로 치환됩니다. +//------------------------------------------------------------------------------ +$kakao5_preset_button_links = [ + // 공통 + '#{홈페이지}' => [ + 'url' => G5_URL, + 'description' => '홈페이지 메인으로 이동하는 링크입니다.' + ], + '#{로그인}' => [ + 'url' => G5_URL.'/bbs/login.php', + 'description' => '로그인 페이지로 이동하는 링크입니다.' + ], + '#{마이페이지}' => [ + 'url' => G5_URL.'/shop/mypage.php', + 'description' => '마이페이지로 이동하는 링크입니다.' + ], + + // 게시판 + '#{게시판}' => [ + 'url' => G5_URL.'/bbs/board.php?bo_table={bo_table}', + 'description' => '특정 게시판 목록으로 이동하는 링크입니다. {bo_table}은 게시판 아이디로 자동 치환됩니다.' + ], + '#{게시글}' => [ + 'url' => G5_URL.'/bbs/board.php?bo_table={bo_table}&wr_id={wr_id}', + 'description' => '특정 게시글 상세 페이지로 이동하는 링크입니다. {bo_table}, {wr_id}는 자동 치환됩니다.' + ], + + // 주문/쇼핑몰 + '#{주문내역}' => [ + 'url' => G5_URL.'/shop/orderinquiry.php', + 'description' => '주문내역(리스트) 페이지로 이동하는 링크입니다.' + ], + '#{주문상세}' => [ + 'url' => G5_URL.'/shop/orderinquiryview.php?od_id={od_id}', + 'description' => '주문상세 페이지로 이동하는 링크입니다. {od_id}는 주문번호로 자동 치환됩니다.' + ], + '#{장바구니}' => [ + 'url' => G5_URL.'/shop/cart.php', + 'description' => '장바구니 페이지로 이동하는 링크입니다.' + ], + '#{상품상세}' => [ + 'url' => G5_URL.'/shop/item.php?it_id={it_id}', + 'description' => '재입고된 상품의 상세 페이지로 이동하는 링크입니다. {it_id}는 상품코드로 자동 치환됩니다.' + ], + + // 1:1 문의 + '#{문의상세}' => [ + 'url' => G5_URL.'/bbs/qaview.php?qa_id={qa_id}', + 'description' => '1:1 문의 상세 페이지로 이동하는 링크입니다. {qa_id}는 문의글 ID로 자동 치환됩니다.' + ], + + // 관리자 + '#{관리자주문내역}' => [ + 'url' => G5_ADMIN_URL.'/shop_admin/orderlist.php', + 'description' => '관리자 주문내역(리스트) 페이지로 이동하는 링크입니다.' + ], + '#{관리자주문상세}' => [ + 'url' => G5_ADMIN_URL.'/shop_admin/orderform.php?od_id={od_id}', + 'description' => '관리자 주문상세 페이지로 이동하는 링크입니다. {od_id}는 주문번호로 자동 치환됩니다.' + ], + '#{투표상세}' => [ + 'url' => G5_ADMIN_URL.'/poll_form.php?w=u&po_id={po_id}', + 'description' => '관리자 투표 상세 페이지로 이동하는 링크입니다. {po_id}는 투표 ID로 자동 치환됩니다.' + ], +]; + +//------------------------------------------------------------------------------ +// 알림톡 프리셋 변수 목록 정의 파일 +// - 템플릿에서 사용할 수 있는 변수들의 목록과 각 변수의 설명, 실제 데이터베이스 테이블 및 컬럼 정보를 정의합니다. +// - 각 카테고리별로 사용 가능한 변수와 설명, 실제 매핑되는 DB 테이블/컬럼 정보를 배열로 관리합니다. +// - 알림톡 메시지 전송 시, 템플릿 내 #{변수명} 형태로 사용되며, 해당 변수에 맞는 실제 값으로 치환됩니다. +//------------------------------------------------------------------------------ +$kakao5_preset_variable_list = [ + [ + 'category' => '공통', + 'variables' => [ + [ + 'name' => '#{회사명}', + 'description' => '해당 메시지를 발송하는 회사명(브랜드명)이 표시됩니다. (예시 : [마이쇼핑])', + 'column' => 'cf_title', + 'table' => 'config_table', + 'condition_key' => '' + ], + ], + ], + [ + 'category' => '회원', + 'variables' => [ + [ + 'name' => '#{이름}', + 'description' => '회원가입 시 입력한 고객님의 이름이 표시됩니다. (예시 : [홍길동])', + 'column' => 'mb_name', + 'table' => 'member_table', + 'condition_key' => 'mb_id' + ], + [ + 'name' => '#{회원아이디}', + 'description' => '회원가입 시 입력한 아이디가 표시됩니다. (예시 : [user1234])', + 'table' => 'member_table', + 'column' => 'mb_id', + 'condition_key' => 'mb_id' + ], + [ + 'name' => '#{닉네임}', + 'description' => '회원가입 시 입력한 닉네임이 표시됩니다. (예시 : [길동이])', + 'table' => 'member_table', + 'column' => 'mb_nick', + 'condition_key' => 'mb_id' + ], + [ + 'name' => '#{이메일}', + 'description' => '회원가입 시 입력한 이메일 주소가 표시됩니다. (예시 : [hong@example.com])', + 'table' => 'member_table', + 'column' => 'mb_email', + 'condition_key' => 'mb_id' + ], + [ + 'name' => '#{가입일}', + 'description' => '회원가입한 날짜가 표시됩니다. (예시 : [2024-06-20])', + 'column' => 'mb_datetime', + 'table' => 'member_table', + 'condition_key' => 'mb_id' + ] + ] + ], + [ + 'category' => '게시판', + 'variables' => [ + [ + 'name' => '#{게시판명}', + 'description' => '게시판의 제목이 표시됩니다. (예시 : [공지사항])', + 'table' => 'board_table', + 'column' => 'bo_subject', + 'condition_key' => 'bo_table' + ], + [ + 'name' => '#{게시글제목}', + 'description' => '게시글의 제목이 표시됩니다. (예시 : [서비스 점검 안내])', + 'table' => 'write_prefix', + 'table_placeholder' => '{bo_table}', + 'column' => 'wr_subject', + 'condition_key' => 'wr_id' + ], + [ + 'name' => '#{작성자명}', + 'description' => '게시글 작성자의 이름이 표시됩니다. (예시 : [홍길동])', + 'table' => 'write_prefix', + 'table_placeholder' => '{bo_table}', + 'column' => 'wr_name', + 'condition_key' => 'wr_id' + ], + [ + 'name' => '#{작성일시}', + 'description' => '게시글이 작성된 일시가 표시됩니다. (예시 : [2024-06-20 14:35:20])', + 'table' => 'write_prefix', + 'table_placeholder' => '{bo_table}', + 'column' => 'wr_datetime', + 'condition_key' => 'wr_id', + ], + [ + 'name' => '#{댓글작성자}', + 'description' => '댓글 작성자의 이름이 표시됩니다. (예시 : [이몽룡])', + 'column' => 'wr_name_comment', + ], + ] + ], + [ + 'category' => '주문', + 'variables' => [ + [ + 'name' => '#{주문자명}', + 'description' => '주문 시 입력한 주문자의 이름이 표시됩니다. (예시 : [홍길동])', + 'column' => 'od_name', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id' + ], + [ + 'name' => '#{주문번호}', + 'description' => '해당 주문의 주문번호가 표시됩니다. (예시 : [202406190001])', + 'column' => 'od_id', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id' + ], + [ + 'name' => '#{상품명}', + 'description' => '주문한 또는 처리된 상품명이 표시됩니다. 상품이 여러개인 경우 "외 N건" 형식으로 표시됩니다. (예시: [아메리카노 외 2건])
    + - 주문 시: 주문한 상품명이 표시됩니다.
    + - 주문취소 / 반품 / 품절 처리 시: 해당 처리 대상 상품명만 표시됩니다.', + 'column' => 'it_name', + 'table' => 'g5_shop_cart_table', + 'condition_key' => 'od_id' + ], + [ + 'name' => '#{주문금액}', + 'description' => '주문의 총 결제금액이 표시됩니다. (예시 : [120,000])', + 'column' => 'od_receipt_price', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id', + 'is_price' => true + ], + [ + 'name' => '#{취소사유}', + 'description' => '고객이 주문취소 시 입력한 취소사유가 표시됩니다. (예시: [단순변심])', + 'column' => 'cancel_memo', + ] + ] + ], + [ + 'category' => '주문 - 무통장 입금 관련', + 'variables' => [ + [ + 'name' => '#{은행계좌번호}', + 'description' => '무통장 입금 시 제공되는 은행명과 계좌명이 표시됩니다. (예시 : [우리은행 123-456-7890])', + 'column' => 'od_bank_account', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id' + ], + [ + 'name' => '#{입금자명}', + 'description' => '입금자가 입력한 이름이 표시됩니다. (예시 : [홍길동])', + 'column' => 'od_deposit_name', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id' + ] + ] + ], + [ + 'category' => '배송', + 'variables' => [ + [ + 'name' => '#{택배회사}', + 'description' => '배송을 진행하는 택배사 이름이 표시됩니다. (예시 : [CJ대한통운])', + 'table' => 'g5_shop_order_table', + 'column' => 'od_delivery_company', + 'condition_key' => 'od_id' + ], + [ + 'name' => '#{운송장번호}', + 'description' => '배송 송장번호가 표시됩니다. (예시 : [123456789012])', + 'column' => 'od_invoice', + 'table' => 'g5_shop_order_table', + 'condition_key' => 'od_id' + ] + ] + ], + [ + 'category' => '투표', + 'variables' => [ + [ + 'name' => '#{투표제목}', + 'description' => '해당 의견이 속한 투표의 제목입니다. (예시 : [서비스 만족도 조사])', + 'table' => 'poll_table', + 'column' => 'po_subject', + 'condition_key' => 'po_id' + ], + [ + 'name' => '#{응답자명}', + 'description' => '기타 의견을 작성한 응답자의 이름입니다. (예시 : [홍길동])', + 'table' => 'poll_etc_table', + 'column' => 'pc_name', + 'condition_key' => 'pc_id' + ], + [ + 'name' => '#{응답일시}', + 'description' => '기타 의견이 작성된 일시입니다. (예시 : [2024-06-20 14:35:20])', + 'table' => 'poll_etc_table', + 'column' => 'pc_datetime', + 'condition_key' => 'pc_id' + ], + [ + 'name' => '#{응답내용}', + 'description' => '기타 의견으로 작성된 텍스트입니다. (예시 : [서비스가 매우 만족스러웠습니다.])', + 'table' => 'poll_etc_table', + 'column' => 'pc_idea', + 'condition_key' => 'pc_id' + ], + ] + ], + [ + 'category' => '1:1 문의', + 'variables' => [ + [ + 'name' => '#{문의제목}', + 'description' => '1:1 문의 제목이 표시됩니다. (예시 : [상품 환불 관련 문의])', + 'table' => 'qa_content_table', + 'column' => 'qa_subject', + 'condition_key' => 'qa_id' + ], + [ + 'name' => '#{문의자명}', + 'description' => '1:1 문의를 작성한 회원의 이름이 표시됩니다. (예시 : [홍길동])', + 'table' => 'qa_content_table', + 'column' => 'qa_name', + 'condition_key' => 'qa_id' + ], + [ + 'name' => '#{문의일시}', + 'description' => '회원이 1:1 문의를 작성한 일시가 표시됩니다. (예시 : [2024-06-20 14:35:20])', + 'table' => 'qa_content_table', + 'column' => 'qa_datetime', + 'condition_key' => 'qa_id' + ], + ] + ], +]; \ No newline at end of file diff --git a/install/gnuboard5.sql b/install/gnuboard5.sql index ddb7b99cf..8c2fdb570 100644 --- a/install/gnuboard5.sql +++ b/install/gnuboard5.sql @@ -92,6 +92,7 @@ CREATE TABLE IF NOT EXISTS `g5_board` ( `bo_notice` text NOT NULL, `bo_upload_count` tinyint(4) NOT NULL DEFAULT '0', `bo_use_email` tinyint(4) NOT NULL DEFAULT '0', + `bo_use_kakaotalk` tinyint(4) NOT NULL DEFAULT '0', `bo_use_cert` enum('','cert','adult','hp-cert','hp-adult') NOT NULL DEFAULT '', `bo_use_sns` tinyint(4) NOT NULL DEFAULT '0', `bo_use_captcha` tinyint(4) NOT NULL DEFAULT '0', @@ -276,6 +277,7 @@ CREATE TABLE IF NOT EXISTS `g5_config` ( `cf_max_po_id` int(11) NOT NULL DEFAULT '0', `cf_stipulation` text NOT NULL, `cf_privacy` text NOT NULL, + `cf_use_promotion` tinyint(1) NOT NULL DEFAULT '0', `cf_open_modify` int(11) NOT NULL DEFAULT '0', `cf_memo_send_point` int(11) NOT NULL DEFAULT '0', `cf_mobile_new_skin` varchar(50) NOT NULL DEFAULT '', @@ -326,6 +328,12 @@ CREATE TABLE IF NOT EXISTS `g5_config` ( `cf_captcha` varchar(100) NOT NULL DEFAULT '', `cf_recaptcha_site_key` varchar(100) NOT NULL DEFAULT '', `cf_recaptcha_secret_key` varchar(100) NOT NULL DEFAULT '', + `cf_kakaotalk_use` varchar(50) NOT NULL DEFAULT '', + `cf_kakaotalk_corpnum` varchar(50) NOT NULL DEFAULT '', + `cf_kakaotalk_sender_hp` varchar(50) NOT NULL DEFAULT '', + `cf_popbill_userid` varchar(100) NOT NULL DEFAULT '', + `cf_popbill_link_id` varchar(100) NOT NULL DEFAULT '', + `cf_popbill_secretkey` varchar(255) NOT NULL DEFAULT '', `cf_1_subj` varchar(255) NOT NULL DEFAULT '', `cf_2_subj` varchar(255) NOT NULL DEFAULT '', `cf_3_subj` varchar(255) NOT NULL DEFAULT '', @@ -520,13 +528,24 @@ CREATE TABLE IF NOT EXISTS `g5_member` ( `mb_memo` text NOT NULL, `mb_lost_certify` varchar(255) NOT NULL, `mb_mailling` tinyint(4) NOT NULL default '0', + `mb_mailling_date` datetime NOT NULL default '0000-00-00 00:00:00', `mb_sms` tinyint(4) NOT NULL default '0', + `mb_sms_date` datetime NOT NULL default '0000-00-00 00:00:00', `mb_open` tinyint(4) NOT NULL default '0', `mb_open_date` date NOT NULL default '0000-00-00', `mb_profile` text NOT NULL, `mb_memo_call` varchar(255) NOT NULL default '', `mb_memo_cnt` int(11) NOT NULL DEFAULT '0', `mb_scrap_cnt` int(11) NOT NULL default '0', + `mb_marketing_agree` tinyint(1) NOT NULL default '0', + `mb_marketing_date` datetime NOT NULL default '0000-00-00 00:00:00', + `mb_thirdparty_agree` tinyint(1) NOT NULL default '0', + `mb_thirdparty_date` datetime NOT NULL default '0000-00-00 00:00:00', + `mb_agree_log` TEXT NOT NULL, + `mb_board_post` tinyint(1) NOT NULL default '0', + `mb_board_reply` tinyint(1) NOT NULL default '0', + `mb_board_comment` tinyint(1) NOT NULL default '0', + `mb_board_recomment` tinyint(1) NOT NULL default '0', `mb_1` varchar(255) NOT NULL default '', `mb_2` varchar(255) NOT NULL default '', `mb_3` varchar(255) NOT NULL default '', @@ -542,7 +561,6 @@ CREATE TABLE IF NOT EXISTS `g5_member` ( KEY `mb_today_login` (`mb_today_login`), KEY `mb_datetime` (`mb_datetime`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - -- -------------------------------------------------------- -- @@ -957,3 +975,91 @@ CREATE TABLE IF NOT EXISTS `g5_menu` ( `me_mobile_use` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`me_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `g5_kakao5_preset` +-- + +DROP TABLE IF EXISTS `g5_kakao5_preset`; +CREATE TABLE IF NOT EXISTS `g5_kakao5_preset` ( + `kp_id` int(11) NOT NULL AUTO_INCREMENT, + `kp_type` varchar(20) NOT NULL DEFAULT '', + `kp_category` varchar(20) NOT NULL DEFAULT '', + `kp_preset_code` varchar(100) NOT NULL DEFAULT '', + `kp_preset_name` varchar(100) NOT NULL DEFAULT '', + `kp_template_name` varchar(100) NOT NULL DEFAULT '', + `kp_alt_send` varchar(100) NOT NULL DEFAULT '1', + `kp_active` tinyint(1) NOT NULL DEFAULT '1', + `kp_created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `kp_updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`kp_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Dumping data for table `g5_kakao5_preset` +-- + +INSERT INTO `g5_kakao5_preset` +(`kp_type`, `kp_category`, `kp_preset_code`, `kp_preset_name`, `kp_template_name`, `kp_alt_send`, `kp_active`, `kp_created_at`, `kp_updated_at`) +VALUES +('회원', '회원', 'CU-MB01', '회원가입완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '회원', 'AD-MB01', '회원가입완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +('작성자', '게시판', 'CU-BO01', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '게시판', 'AD-BO01', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('그룹관리자', '게시판', 'AD-BO02', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('게시판관리자', '게시판', 'AD-BO03', '새 게시글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('작성자', '게시판', 'CU-BO02', '새 댓글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('댓글 작성자', '게시판', 'CU-BO03', '새 댓글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('답변글 작성자', '게시판', 'CU-BO04', '답변글 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '투표', 'AD-VO01', '기타의견 작성', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +('주문자', '쇼핑몰', 'CU-OR01', '주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '쇼핑몰', 'AD-OR01', '주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR02', '무통장입금 주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '쇼핑몰', 'AD-OR02', '무통장입금 주문 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR03', '무통장입금 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '쇼핑몰', 'AD-OR03', '무통장입금 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR04', '(주문자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '쇼핑몰', 'AD-OR04', '(주문자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR05', '(관리자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '쇼핑몰', 'AD-OR05', '(관리자)주문 취소', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR06', '반품', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-OR07', '품절', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-DE01', '배송 준비', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-DE02', '배송중', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('주문자', '쇼핑몰', 'CU-DE03', '배송 완료', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('요청자', '쇼핑몰', 'CU-ST01', '재입고알림', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +('문의자', '1:1문의', 'CU-IQ01', '문의 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('최고관리자', '1:1문의', 'AD-IQ01', '문의 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), +('문의자', '1:1문의', 'CU-IQ02', '답변 등록', '', 1, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `g5_kakao5_preset_history` +-- + +DROP TABLE IF EXISTS `g5_kakao5_preset_history`; +CREATE TABLE IF NOT EXISTS `g5_kakao5_preset_history` ( + `ph_id` int(11) NOT NULL AUTO_INCREMENT, + `kp_id` int(11) NOT NULL DEFAULT '0', + `mb_id` varchar(20) NOT NULL DEFAULT '', + `ph_rcvnm` varchar(100) NOT NULL DEFAULT '', + `ph_rcv` varchar(100) NOT NULL DEFAULT '', + `ph_template_code` varchar(100) NOT NULL DEFAULT '', + `ph_alt_send` varchar(100) NOT NULL DEFAULT '', + `ph_request_num` varchar(100) NOT NULL DEFAULT '', + `ph_receipt_num` varchar(100) NOT NULL DEFAULT '', + `ph_send_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `ph_state` tinyint(1) NOT NULL DEFAULT '0', + `ph_log` text NOT NULL, + PRIMARY KEY (`ph_id`), + KEY `kp_id` (`kp_id`), + KEY `mb_id` (`mb_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/install/install_db.php b/install/install_db.php index f2cba5b4c..11dd47a5d 100644 --- a/install/install_db.php +++ b/install/install_db.php @@ -317,6 +317,7 @@ if ($g5_install || $is_install === false) { bo_use_list_view = '0', bo_use_list_content = '0', bo_use_email = '0', + bo_use_kakaotalk = '0', bo_table_width = '100', bo_subject_len = '60', bo_mobile_subject_len = '30', diff --git a/js/kakao5.js b/js/kakao5.js new file mode 100644 index 000000000..3a849bea4 --- /dev/null +++ b/js/kakao5.js @@ -0,0 +1,26 @@ +// 카카오톡 - URL 생성 후 팝업 오픈 +async function openKakao5PopupFromAjax(kakaoUrl, getUrlValue) { + const currentUrl = kakaoUrl + '/ajax.get_url.php'; + let response, data; + try { + response = await fetch(currentUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ get_url: getUrlValue }) + }); + data = await response.json(); + } catch (error) { + alert('서버와 통신에 실패했습니다.'); + return; + } + + if (data && data.url) { + window.open( + data.url, + 'win_template', + `width=${data.width},height=${data.height},scrollbars=yes` + ); + } else { + alert('URL 생성에 실패했습니다.'); + } +} \ No newline at end of file diff --git a/mobile/shop/orderformupdate.php b/mobile/shop/orderformupdate.php index 8e003b2ae..8a75af656 100644 --- a/mobile/shop/orderformupdate.php +++ b/mobile/shop/orderformupdate.php @@ -1,6 +1,7 @@ 0) { + // 무통장 입금일 경우 알림톡 발송 : 주문금액 - 미결제액 + $conditions = ['od_id' => $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str, 'od_receipt_price' => number_format($od_misu)]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR02', ['rcv' => $od_hp ?: $od_tel, 'rcvnm' => $od_name], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR02', 'super', $conditions); // 관리자 +}else{ + // 주문 완료 + $conditions = ['od_id' => $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str, 'od_receipt_price' => number_format($i_price)]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR01', ['rcv' => $od_hp ?: $od_tel, 'rcvnm' => $od_name], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR01', 'super', $conditions); // 관리자 +} +// 알림톡 발송 END --------------------------------------------------------------------------------------------- // orderview 에서 사용하기 위해 session에 넣고 $uid = md5($od_id.G5_TIME_YMDHIS.$REMOTE_ADDR); diff --git a/mobile/skin/member/basic/consent_modal.inc.php b/mobile/skin/member/basic/consent_modal.inc.php new file mode 100644 index 000000000..cd4f1525d --- /dev/null +++ b/mobile/skin/member/basic/consent_modal.inc.php @@ -0,0 +1,85 @@ + + + +
    +
    +

    안내

    +
    +
    +
    + + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/mobile/skin/member/basic/register.skin.php b/mobile/skin/member/basic/register.skin.php index 5fb762a94..7f0148ef7 100644 --- a/mobile/skin/member/basic/register.skin.php +++ b/mobile/skin/member/basic/register.skin.php @@ -22,7 +22,7 @@ add_stylesheet('',
    -

    회원가입약관

    +

    (필수) 회원가입약관

    @@ -31,7 +31,7 @@ add_stylesheet('',
    -

    개인정보 수집 및 이용

    +

    (필수) 개인정보 수집 및 이용

    diff --git a/mobile/skin/member/basic/register_form.skin.php b/mobile/skin/member/basic/register_form.skin.php index 90f33137b..db1f1260d 100644 --- a/mobile/skin/member/basic/register_form.skin.php +++ b/mobile/skin/member/basic/register_form.skin.php @@ -45,8 +45,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi

    개인정보 입력

      -
    • - +
    • + 간편인증'.PHP_EOL; } @@ -67,11 +68,10 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi echo '(필수)'; echo ''.PHP_EOL; - } ?> 본인확인성인인증 완료
    - + +
  • class="frm_input full_input " placeholder="이름 (필수)"> @@ -129,7 +130,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • - " maxlength="20" placeholder="전화번호 (필수)"> + " maxlength="20" placeholder="전화번호 (필수)">
  • @@ -218,26 +219,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi -
  • - class="selec_chk"> - - 정보 메일을 받겠습니다. -
  • - - -
  • - class="selec_chk"> - - 휴대폰 문자메세지를 받겠습니다. -
  • - -
  • class="selec_chk"> @@ -246,7 +227,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi 정보공개 다른분들이 나의 정보를 볼 수 있도록 합니다. - + 정보공개를 바꾸시면 앞으로 일 이내에는 변경이 안됩니다. @@ -276,20 +257,174 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • - -
  • - 자동등록방지 - -
  • + +
    +

    게시판 알림설정

    + 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    + +
      + + + +
      + + + +
    +
    + + + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + +
    +
    + + +
    +

    자동등록방지

    +
      +
    • + 자동등록방지 + +
    • +
    +
    + + + \ No newline at end of file diff --git a/mobile/skin/member/basic/style.css b/mobile/skin/member/basic/style.css index c9fc6c292..66edd7a6e 100644 --- a/mobile/skin/member/basic/style.css +++ b/mobile/skin/member/basic/style.css @@ -5,6 +5,8 @@ .mbskin h1 {font-size:1.75em;margin:40px 0 25px} .mbskin p {padding-bottom:20px;border-bottom:1px solid #c8c8c8} .mbskin p strong {color:#4162ff;padding-bottom:5px;display:block;font-size:1.083em} + +/* 버튼 */ .mbskin .btn_submit {display:block;width:100%;height:40px;line-height:40px;padding:0 10px;border:0;font-weight:bold;background:#3a8afd;color:#fff;border-radius:3px} /* ### 기본 스타일 커스터마이징 끝 ### */ @@ -68,7 +70,10 @@ .fregister_agree input[type="checkbox"]:checked + label {color:#000} .fregister_agree input[type="checkbox"]:checked + label span {background:url('./img/chk.png') no-repeat 50% 50% #3a8afd;border-color:#1471f6;border-radius:3px} .fregister_agree.chk_all input[type="checkbox"] + label span {top:15px} - +#fregisterform .consent-line {display: flex; align-items: baseline;} +#fregisterform .consent-date { margin: 5px 0 0 20px !important; } +#fregisterform .consent-group .sub-consents {padding: 10px 20px 0;} +#fregisterform .js-open-consent {flex:1 0 auto; margin-left: 10px; text-align: right; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /* 회원가입 완료 */ #reg_result {padding:20px 10px 10px} @@ -135,8 +140,9 @@ #flogin {background:#fff;margin:20px 0} #mb_login_notmb {background:#fff;border-bottom:1px solid #ccc;padding:20px} +#mb_login_notmb .chk_box input[type="checkbox"] + label{padding-left:20px} #mb_login_notmb h2 {font-size:1.25em;padding:10px;background:#f3f3f3} -#mb_login_notmb p {border:0;padding:0;margin:10px;color:#} +#mb_login_notmb p {border:0;padding:0;margin:10px;} #guest_privacy {border:1px solid #ccc;text-align:left;line-height:1.6em;color:#666;background:#fafafa;padding:10px;height:200px;margin:10px 0;overflow-y:auto} #mb_login_notmb .btn_submit {width:100%;display:block;height:40px;line-height:40px} @@ -154,7 +160,6 @@ #mb_login #sns_login .sns-icon:nth-child(odd) {margin-right:2%} #mb_login #sns_login .txt {font-size:0.95em;padding-left:5px !important;border-left:0 !important} - /* 쪽지 */ .memo_list {border-top:1px solid #ececec;} .memo_list li {border-bottom:1px solid #ececec;background:#fff;padding:10px 15px;list-style:none;position:relative} @@ -198,7 +203,7 @@ .memo_from li.memo_view_date {display:block;color:#555;line-height:24px} .memo_from li.memo_op_btn {position:absolute} .memo_from li.list_btn {right:53px;} -.memo_from li.del_btn {right:15px;padding} +.memo_from li.del_btn {right:15px;} .memo_from:after {display:block;visibility:hidden;clear:both;content:""} .memo_btn {width:100%} @@ -292,7 +297,6 @@ .chk_box input[type="radio"]:checked + label span {border-color:#3a8afd} .chk_box input[type="radio"]:checked + label span:before {width:7px;height:7px;background:#3a8afd;content:'';position:absolute;top:3px;left:3px;border-radius:50%} - /* 자기소개 */ #profile section {margin:10px} #profile h2 {margin:0} diff --git a/mobile/skin/shop/basic/item.form.skin.php b/mobile/skin/shop/basic/item.form.skin.php index 6db60f890..e2d0ad970 100644 --- a/mobile/skin/shop/basic/item.form.skin.php +++ b/mobile/skin/shop/basic/item.form.skin.php @@ -545,11 +545,11 @@ function popup_item_recommend(it_id) } } -// 재입고SMS 알림 +// 재입고 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; popup_window(url, "itemstocksms", opt); } diff --git a/mobile/skin/social/consent_modal.inc.php b/mobile/skin/social/consent_modal.inc.php new file mode 100644 index 000000000..cd4f1525d --- /dev/null +++ b/mobile/skin/social/consent_modal.inc.php @@ -0,0 +1,85 @@ + + + +
    +
    +

    안내

    +
    +
    +
    + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/mobile/skin/social/social_register_member.skin.php b/mobile/skin/social/social_register_member.skin.php index 8a8d19dc5..3f5d9e0a8 100644 --- a/mobile/skin/social/social_register_member.skin.php +++ b/mobile/skin/social/social_register_member.skin.php @@ -145,6 +145,108 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다. + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + (동의일자: ".$member['mb_marketing_date'].")"; ?> + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + (동의일자: ".$member['mb_thirdparty_date'].")"; ?> + + +
    • + +
    +
    + +
    취소 @@ -196,6 +298,8 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
    + + \ No newline at end of file diff --git a/mobile/skin/social/style.css b/mobile/skin/social/style.css index 12d53c965..f3c72683f 100644 --- a/mobile/skin/social/style.css +++ b/mobile/skin/social/style.css @@ -130,6 +130,9 @@ #fregisterform .rgs_name_li button {margin:5px 0 0;width:auto} #fregisterform .reg_mb_img_file {margin-bottom:30px} #fregisterform .reg_mb_img_file img {max-width:100%;height:auto} +#fregisterform .consent-line {display: flex; align-items: baseline;} +#fregisterform .consent-group .sub-consents {padding: 10px 20px 0;} +#fregisterform .js-open-consent {flex:1 0 auto; margin-left: 10px; text-align: right; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } .filebox .fileName {display:inline-block;position:relative;width:100%;height:45px;padding-left:10px;margin-right:5px;line-height:30px;border: 1px solid #d0d3db;background-color:#fff;color:red;vertical-align:middle} .filebox .btn_file {display:inline-block;position:absolute;right:8px;top:8px;border:1px solid #3a8afd;border-radius:3px;width:70px;height:30px;color:#3a8afd;font-size:1em;line-height:30px;font-weight:bold;text-align:center;vertical-align:middle} diff --git a/plugin/kakao5/Popbill/LICENSE.md b/plugin/kakao5/Popbill/LICENSE.md new file mode 100644 index 000000000..57638d9c2 --- /dev/null +++ b/plugin/kakao5/Popbill/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 LinkHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/plugin/kakao5/Popbill/Linkhub/LICENSE.md b/plugin/kakao5/Popbill/Linkhub/LICENSE.md new file mode 100644 index 000000000..57638d9c2 --- /dev/null +++ b/plugin/kakao5/Popbill/Linkhub/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 LinkHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/plugin/kakao5/Popbill/Linkhub/README.md b/plugin/kakao5/Popbill/Linkhub/README.md new file mode 100644 index 000000000..9e0bb56b9 --- /dev/null +++ b/plugin/kakao5/Popbill/Linkhub/README.md @@ -0,0 +1,4 @@ +linkhub.auth.php +================ + +링크허브 API 인증 SDK for PHP5 diff --git a/plugin/kakao5/Popbill/Linkhub/example.php b/plugin/kakao5/Popbill/Linkhub/example.php new file mode 100644 index 000000000..f67cfe34b --- /dev/null +++ b/plugin/kakao5/Popbill/Linkhub/example.php @@ -0,0 +1,49 @@ +getToken($ServiceID,$AccessID, array('member','110')); +}catch(LinkhubException $le) { + echo $le; + + exit(); +} +echo 'Token is issued : '.substr($Token->session_token,0,20).' ...'; +echo chr(10); + +try +{ + $balance = $Linkhub->getBalance($Token->session_token,$ServiceID); +}catch(LinkhubException $le) { + echo $le; + + exit(); +} +echo 'remainPoint is '. $balance; +echo chr(10); + +try +{ + $balance = $Linkhub->getPartnerBalance($Token->session_token,$ServiceID); +}catch(LinkhubException $le) { + echo $le; + + exit(); +} +echo 'remainPartnerPoint is '. $balance; +echo chr(10); + +?> diff --git a/plugin/kakao5/Popbill/Linkhub/linkhub.auth.php b/plugin/kakao5/Popbill/Linkhub/linkhub.auth.php new file mode 100644 index 000000000..ecf3a451a --- /dev/null +++ b/plugin/kakao5/Popbill/Linkhub/linkhub.auth.php @@ -0,0 +1,337 @@ +__SecretKey; + } + public function getLinkID(){ + return $this->__LinkID; + } + public function ServiceURL($V){ + $this->__ServiceURL = $V; + } + private static $singleton = null; + public static function getInstance($LinkID,$secretKey) + { + if(is_null(Linkhub::$singleton)) { + Linkhub::$singleton = new Linkhub(); + } + Linkhub::$singleton->__LinkID = $LinkID; + Linkhub::$singleton->__SecretKey = $secretKey; + + return Linkhub::$singleton; + } + public function gzdecode($data){ + return gzinflate(substr($data, 10, -8)); + } + + private function executeCURL($url,$header = array(),$isPost = false, $postdata = null) { + $base_header = array(); + $base_header[] = 'Accept-Encoding: gzip,deflate'; + $base_header[] = 'User-Agent: PHP5 LINKHUB SDK'; + $arr_header = $header + $base_header; + + if($this->__requestMode != "STREAM") { + $http = curl_init($url); + + if($isPost) { + curl_setopt($http, CURLOPT_POST,1); + curl_setopt($http, CURLOPT_POSTFIELDS, $postdata); + } + curl_setopt($http, CURLOPT_HTTPHEADER,$arr_header); + curl_setopt($http, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($http, CURLOPT_ENCODING, 'gzip,deflate'); + // Connection timeout 설정 + curl_setopt($http, CURLOPT_CONNECTTIMEOUT_MS, 10 * 1000); + // 통합 timeout 설정 + curl_setopt($http, CURLOPT_TIMEOUT_MS, 180 * 1000); + + $responseJson = curl_exec($http); + + // curl Error 추가 + if ($responseJson == false) { + throw new LinkhubException(curl_error($http)); + } + + $http_status = curl_getinfo($http, CURLINFO_HTTP_CODE); + + curl_close($http); + + $is_gzip = 0 === mb_strpos($responseJson, "\x1f" . "\x8b" . "\x08"); + + if ($is_gzip) { + $responseJson = $this->gzdecode($responseJson); + } + + if($http_status != 200) { + throw new LinkhubException($responseJson); + } + + return json_decode($responseJson); + + } + else { + if($isPost) { + $params = array('http' => array( + 'ignore_errors' => TRUE, + 'method' => 'POST', + 'protocol_version' => '1.0', + 'content' => $postdata, + 'timeout' => 180 + )); + } else { + $params = array('http' => array( + 'ignore_errors' => TRUE, + 'method' => 'GET', + 'protocol_version' => '1.0', + 'timeout' => 180 + )); + } + if ($arr_header !== null) { + $head = ""; + foreach($arr_header as $h) { + $head = $head . $h . "\r\n"; + } + $params['http']['header'] = substr($head,0,-2); + } + $ctx = stream_context_create($params); + $response = file_get_contents($url, false, $ctx); + + $is_gzip = 0 === mb_strpos($response , "\x1f" . "\x8b" . "\x08"); + if($is_gzip){ + $response = $this->gzdecode($response); + } + + if ($http_response_header[0] != "HTTP/1.1 200 OK") { + throw new LinkhubException($response); + } + + return json_decode($response); + } + } + + public function getTime($useStaticIP = false, $useLocalTimeYN = true, $useGAIP = false) { + if($useLocalTimeYN) { + $replace_search = array("@","#"); + $replace_target = array("T","Z"); + + $date = new DateTime('now', new DateTimeZone('UTC')); + + return str_replace($replace_search, $replace_target, $date->format('Y-m-d@H:i:s#')); + } + if($this->__requestMode != "STREAM") { + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + + $http = curl_init($targetURL.'/Time'); + + curl_setopt($http, CURLOPT_RETURNTRANSFER, TRUE); + // Read timeout 설정 + curl_setopt($http, CURLOPT_TIMEOUT_MS, 180 * 1000); + // Connection timeout 설정 + curl_setopt($http, CURLOPT_CONNECTTIMEOUT_MS, 10 * 1000); + + $response = curl_exec($http); + + // curl Error 추가 + if ($response == false) { + throw new LinkhubException(curl_error($http)); + } + + $http_status = curl_getinfo($http, CURLINFO_HTTP_CODE); + + curl_close($http); + + if($http_status != 200) { + throw new LinkhubException($response); + } + return $response; + + } else { + $header = array(); + $header[] = 'Connection: close'; + $params = array('http' => array( + 'ignore_errors' => TRUE, + 'protocol_version' => '1.0', + 'method' => 'GET', + 'timeout' => 180 + )); + if ($header !== null) { + $head = ""; + foreach($header as $h) { + $head = $head . $h . "\r\n"; + } + $params['http']['header'] = substr($head,0,-2); + } + + $ctx = stream_context_create($params); + + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + + $response = (file_get_contents( $targetURL.'/Time', false, $ctx)); + + if ($http_response_header[0] != "HTTP/1.1 200 OK") { + throw new LinkhubException($response); + } + return $response; + } + } + + public function getToken($ServiceID, $access_id, array $scope = array() , $forwardIP = null, $useStaticIP = false, $useLocalTimeYN = true, $useGAIP = false) + { + $xDate = $this->getTime($useStaticIP, $useLocalTimeYN, $useGAIP); + + $uri = '/' . $ServiceID . '/Token'; + $header = array(); + + $TokenRequest = new TokenRequest(); + $TokenRequest->access_id = $access_id; + $TokenRequest->scope = $scope; + + $postdata = json_encode($TokenRequest); + + $digestTarget = 'POST'.chr(10); + $digestTarget = $digestTarget.base64_encode(hash('sha256',$postdata,true)).chr(10); + $digestTarget = $digestTarget.$xDate.chr(10); + if(!(is_null($forwardIP) || $forwardIP == '')) { + $digestTarget = $digestTarget.$forwardIP.chr(10); + } + $digestTarget = $digestTarget.Linkhub::VERSION.chr(10); + $digestTarget = $digestTarget.$uri; + + $digest = base64_encode(hash_hmac('sha256',$digestTarget,base64_decode(strtr($this->__SecretKey, '-_', '+/')),true)); + + $header[] = 'x-lh-date: '.$xDate; + $header[] = 'x-lh-version: '.Linkhub::VERSION; + if(!(is_null($forwardIP) || $forwardIP == '')) { + $header[] = 'x-lh-forwarded: '.$forwardIP; + } + + $header[] = 'Authorization: LINKHUB '.$this->__LinkID.' '.$digest; + $header[] = 'Content-Type: Application/json'; + $header[] = 'Connection: close'; + + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + + return $this->executeCURL($targetURL.$uri , $header,true,$postdata); + } + + + public function getBalance($bearerToken, $ServiceID, $useStaticIP = false, $useGAIP = false) + { + $header = array(); + $header[] = 'Authorization: Bearer '.$bearerToken; + $header[] = 'Connection: close'; + + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + $uri = '/'.$ServiceID.'/Point'; + + $response = $this->executeCURL($targetURL.$uri,$header); + return $response->remainPoint; + + } + + public function getPartnerBalance($bearerToken, $ServiceID, $useStaticIP = false, $useGAIP = false) + { + $header = array(); + $header[] = 'Authorization: Bearer '.$bearerToken; + $header[] = 'Connection: close'; + + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + $uri = '/'.$ServiceID.'/PartnerPoint'; + + $response = $this->executeCURL($targetURL.$uri,$header); + return $response->remainPoint; + } + + /* + * 파트너 포인트 충전 팝업 URL 추가 (2017/08/29) + */ + public function getPartnerURL($bearerToken, $ServiceID, $TOGO, $useStaticIP = false, $useGAIP = false) + { + $header = array(); + $header[] = 'Authorization: Bearer '.$bearerToken; + $header[] = 'Connection: close'; + + $targetURL = $this->getTargetURL($useStaticIP, $useGAIP); + $uri = '/'.$ServiceID.'/URL?TG='.$TOGO; + + $response = $this->executeCURL($targetURL.$uri, $header); + return $response->url; + } + + private function getTargetURL($useStaticIP, $useGAIP){ + if(isset($this->__ServiceURL)) { + return $this->__ServiceURL; + } + + if($useGAIP){ + return Linkhub::ServiceURL_GA; + } else if($useStaticIP){ + return Linkhub::ServiceURL_Static; + } else { + return Linkhub::ServiceURL; + } + } +} + +class TokenRequest +{ + public $access_id; + public $scope; +} + +class LinkhubException extends Exception +{ + public function __construct($response, Exception $previous = null) { + $Err = json_decode($response); + if(is_null($Err)) { + parent::__construct($response, -99999999); + } + else { + parent::__construct($Err->message, $Err->code); + } + } + + public function __toString() { + return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; + } +} + +?> diff --git a/plugin/kakao5/Popbill/PopbillKakao.php b/plugin/kakao5/Popbill/PopbillKakao.php new file mode 100644 index 000000000..e472bf02c --- /dev/null +++ b/plugin/kakao5/Popbill/PopbillKakao.php @@ -0,0 +1,679 @@ +AddScope('153'); + $this->AddScope('154'); + $this->AddScope('155'); + } + + // 전송 단가 확인 + public function GetUnitCost($CorpNum, $MessageType) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($MessageType)) { + throw new PopbillException('카카오톡 전송유형이 입력되지 않았습니다.'); + } + + return $this->executeCURL('/KakaoTalk/UnitCost?Type=' . $MessageType, $CorpNum)->unitCost; + } + + // 알림톡/친구톡 전송내역 확인 + public function GetMessages($CorpNum, $ReceiptNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('카카오톡 접수번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/KakaoTalk/' . $ReceiptNum, $CorpNum, $UserID); + $DetailInfo = new KakaoSentInfo(); + $DetailInfo->fromJsonInfo($response); + + return $DetailInfo; + } + + // 알림톡/친구톡 전송내역 확인 (요청번호 할당) + public function GetMessagesRN($CorpNum, $RequestNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('카카오톡 전송요청번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/KakaoTalk/Get/' . $RequestNum, $CorpNum, $UserID); + $DetailInfo = new KakaoSentInfo(); + $DetailInfo->fromJsonInfo($response); + + return $DetailInfo; + } + + // 카카오톡 채널 목록 확인 + public function ListPlusFriendID($CorpNum) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $PlusFriendList = array(); + $response = $this->executeCURL('/KakaoTalk/ListPlusFriendID', $CorpNum); + + for ($i = 0; $i < Count($response); $i++) { + $PlusFriendObj = new PlusFriend(); + $PlusFriendObj->fromJsonInfo($response[$i]); + $PlusFriendList[$i] = $PlusFriendObj; + } + + return $PlusFriendList; + } + + // 알림톡 템플릿 목록 확인 + public function ListATSTemplate($CorpNum) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $result = $this->executeCURL('/KakaoTalk/ListATSTemplate', $CorpNum); + + $TemplateList = array(); + for ($i = 0; $i < Count($result); $i++) { + $TemplateObj = new ATSTemplate(); + $TemplateObj->fromJsonInfo($result[$i]); + $TemplateList[$i] = $TemplateObj; + } + + return $TemplateList; + } + + // 발신번호 등록여부 확인 + public function CheckSenderNumber($CorpNum, $SenderNumber, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($SenderNumber)) { + throw new PopbillException('발신번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/KakaoTalk/CheckSenderNumber/' . $SenderNumber, $CorpNum, $UserID); + } + + // 발신번호 목록 확인 + public function GetSenderNumberList($CorpNum) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/SenderNumber', $CorpNum); + } + + // 예약전송 취소 (접수번호) + public function CancelReserve($CorpNum, $ReceiptNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('예약전송을 취소할 접수번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/KakaoTalk/' . $ReceiptNum . '/Cancel', $CorpNum, $UserID); + } + + // 예약전송 전체 취소 (전송 요청번호) + public function CancelReserveRN($CorpNum, $RequestNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('예약전송을 취소할 전송요청번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/KakaoTalk/Cancel/' . $RequestNum, $CorpNum, $UserID); + } + + // 예약전송 일부 취소 (접수번호) + public function CancelReservebyRCV($CorpNum, $ReceiptNum, $ReceiveNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('예약전송 취소할 접수번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiveNum)) { + throw new PopbillException('예약전송 취소할 수신번호가 입력되지 않았습니다.'); + } + + $postdata = json_encode($ReceiveNum); + + return $this->executeCURL('/KakaoTalk/' . $ReceiptNum . '/Cancel', $CorpNum, $UserID, true, null, $postdata); + } + + // 예약전송 일부 취소 (전송 요청번호) + public function CancelReserveRNbyRCV($CorpNum, $RequestNum, $ReceiveNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('예약전송 취소할 전송요청번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiveNum)) { + throw new PopbillException('예약전송 취소할 수신번호가 입력되지 않았습니다.'); + } + + $postdata = json_encode($ReceiveNum); + + return $this->executeCURL('/KakaoTalk/Cancel/' . $RequestNum, $CorpNum, $UserID, true, null, $postdata); + } + + public function GetURL($CorpNum, $UserID, $TOGO) + { + $URI = '/KakaoTalk/?TG='; + + if ($TOGO == "SENDER") { + $URI = '/Message/?TG='; + } + + $response = $this->executeCURL($URI . $TOGO, $CorpNum, $UserID); + return $response->url; + } + + // 플러스친구 계정관리 팝업 URL + public function GetPlusFriendMgtURL($CorpNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/KakaoTalk/?TG=PLUSFRIEND', $CorpNum, $UserID); + return $response->url; + } + + // 발신번호 관리 팝업 URL + public function GetSenderNumberMgtURL($CorpNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/Message/?TG=SENDER', $CorpNum, $UserID); + return $response->url; + } + + // 알림톡 템플릿관리 팝업 URL + public function GetATSTemplateMgtURL($CorpNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/KakaoTalk/?TG=TEMPLATE', $CorpNum, $UserID); + return $response->url; + } + + // 알림톡 템플릿 정보 확인 + public function GetATSTemplate($CorpNum, $TemplateCode, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($TemplateCode)) { + throw new PopbillException('템플릿코드가 입력되지 않았습니다.'); + } + + $result = $this->executeCURL('/KakaoTalk/GetATSTemplate/'.$TemplateCode, $CorpNum, $UserID); + + $TemplateInfo = new ATSTemplate(); + $TemplateInfo->fromJsonInfo($result); + + return $TemplateInfo; + } + + // 카카오톡 전송내역 팝업 URL + public function GetSentListURL($CorpNum, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/KakaoTalk/?TG=BOX', $CorpNum, $UserID); + return $response->url; + } + + // 전송내역 목록 조회 + public function Search($CorpNum, $SDate, $EDate, $State = array(), $Item = array(), $ReserveYN = null, $SenderYN = false, $Page = null, $PerPage = null, $Order = null, $UserID = null, $QString = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($SDate)) { + throw new PopbillException('시작일자가 입력되지 않았습니다.'); + } + if(!$this->isValidDate($SDate)) { + throw new PopbillException('시작일자가 유효하지 않습니다.'); + } + if($this->isNullOrEmpty($EDate)) { + throw new PopbillException('종료일자가 입력되지 않았습니다.'); + } + if(!$this->isValidDate($EDate)) { + throw new PopbillException('종료일자가 유효하지 않습니다.'); + } + if($this->isNullOrEmpty($State)) { + throw new PopbillException('전송상태가 입력되지 않았습니다.'); + } + + $uri = '/KakaoTalk/Search'; + $uri .= '?SDate=' . $SDate; + $uri .= '&EDate=' . $EDate; + $uri .= '&State=' . implode(',', $State); + + if(!$this->isNullOrEmpty($Item)) { + $uri .= '&Item=' . implode(',', $Item); + } + if(!is_null($ReserveYN) && $ReserveYN != "") { + if($ReserveYN) { + $uri .= '&ReserveYN=1'; + }else{ + $uri .= '&ReserveYN=0'; + } + } + if ($SenderYN) { + $uri .= '&SenderOnly=1'; + } else { + $uri .= '&SenderOnly=0'; + } + if(!$this->isNullOrEmpty($Page)) { + $uri .= '&Page=' . $Page; + } + if(!$this->isNullOrEmpty($PerPage)) { + $uri .= '&PerPage=' . $PerPage; + } + if(!$this->isNullOrEmpty($Order)) { + $uri .= '&Order=' . $Order; + } + if(!$this->isNullOrEmpty($QString)) { + $uri .= '&QString=' . urlencode($QString); + } + + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $SearchList = new KakaoSearchResult(); + $SearchList->fromJsonInfo($response); + + return $SearchList; + + } + + // 과금정보 확인 + public function GetChargeInfo($CorpNum, $MessageType, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($MessageType)) { + throw new PopbillException('카카오톡 전송유형이 입력되지 않았습니다.'); + } + + $uri = '/KakaoTalk/ChargeInfo?Type=' . $MessageType; + + $response = $this->executeCURL($uri, $CorpNum, $UserID); + $ChargeInfo = new ChargeInfo(); + $ChargeInfo->fromJsonInfo($response); + + return $ChargeInfo; + } + + // 친구톡(이미지) + public function SendFMS($CorpNum, $PlusFriendID, $Sender = null, $Content = null, $AltContent = null, $AltSendType = null, $AdsYN = false, $Messages = array(), $Btns = array(), $ReserveDT = null, $FilePaths = array(), $ImageURL = null, $UserID = null, $RequestNum = null, $AltSubject = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($PlusFriendID)) { + throw new PopbillException('카카오톡 채널 검색용 아이디가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($Messages)) { + throw new PopbillException('카카오톡 전송정보가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($FilePaths)) { + throw new PopbillException('전송할 이미지 파일 경로가 입력되지 않았습니다.'); + } + if(!$this->isNullOrEmpty($ReserveDT) && !$this->isValidDT($ReserveDT)) { + throw new PopbillException('전송 예약일시가 유효하지 않습니다.'); + } + + $Request = array(); + + if(!$this->isNullOrEmpty($PlusFriendID)) $Request['plusFriendID'] = $PlusFriendID; + if(!$this->isNullOrEmpty($Sender)) $Request['snd'] = $Sender; + if(!$this->isNullOrEmpty($Content)) $Request['content'] = $Content; + if(!$this->isNullOrEmpty($AltSubject)) $Request['altSubject'] = $AltSubject; + if(!$this->isNullOrEmpty($AltContent)) $Request['altContent'] = $AltContent; + if(!$this->isNullOrEmpty($AltSendType)) $Request['altSendType'] = $AltSendType; + if(!$this->isNullOrEmpty($ReserveDT)) $Request['sndDT'] = $ReserveDT; + if(!$this->isNullOrEmpty($AdsYN)) $Request['adsYN'] = $AdsYN; + if(!$this->isNullOrEmpty($ImageURL)) $Request['imageURL'] = $ImageURL; + if(!$this->isNullOrEmpty($RequestNum)) $Request['requestNum'] = $RequestNum; + if(!$this->isNullOrEmpty($Btns)) $Request['btns'] = $Btns; + + $Request['msgs'] = $Messages; + $postdata = array(); + $postdata['form'] = json_encode($Request); + + $i = 0; + + foreach ($FilePaths as $FilePath) { + $postdata['file'] = '@' . $FilePath; + } + + return $this->executeCURL('/FMS', $CorpNum, $UserID, true, null, $postdata, true)->receiptNum; + } + + // 친구톡(텍스트) + public function SendFTS($CorpNum, $PlusFriendID, $Sender = null, $Content = null, $AltContent = null, $AltSendType = null, $AdsYN = false, $Messages = array(), $Btns = array(), $ReserveDT = null, $UserID = null, $RequestNum = null, $AltSubject = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($PlusFriendID)) { + throw new PopbillException('카카오톡 채널 검색용 아이디가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($Messages)) { + throw new PopbillException('카카오톡 전송정보가 입력되지 않았습니다.'); + } + if(!$this->isNullOrEmpty($ReserveDT) && !$this->isValidDT($ReserveDT)) { + throw new PopbillException('전송 예약일시가 유효하지 않습니다.'); + } + + $Request = array(); + + if(!$this->isNullOrEmpty($PlusFriendID)) $Request['plusFriendID'] = $PlusFriendID; + if(!$this->isNullOrEmpty($Sender)) $Request['snd'] = $Sender; + if(!$this->isNullOrEmpty($Content)) $Request['content'] = $Content; + if(!$this->isNullOrEmpty($AltSubject)) $Request['altSubject'] = $AltSubject; + if(!$this->isNullOrEmpty($AltContent)) $Request['altContent'] = $AltContent; + if(!$this->isNullOrEmpty($AltSendType)) $Request['altSendType'] = $AltSendType; + if(!$this->isNullOrEmpty($ReserveDT)) $Request['sndDT'] = $ReserveDT; + if(!$this->isNullOrEmpty($AdsYN)) $Request['adsYN'] = $AdsYN; + if(!$this->isNullOrEmpty($RequestNum)) $Request['requestNum'] = $RequestNum; + if(!$this->isNullOrEmpty($Btns)) $Request['btns'] = $Btns; + + $Request['msgs'] = $Messages; + $postdata = json_encode($Request); + + return $this->executeCURL('/FTS', $CorpNum, $UserID, true, null, $postdata)->receiptNum; + } + + // 알림톡 단건전송 + public function SendATS($CorpNum, $TemplateCode, $Sender = null, $Content = null, $AltContent = null, $AltSendType = null, $Messages = array(), $ReserveDT = null, $UserID = null, $RequestNum = null, $Btns = null, $AltSubject = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($TemplateCode)) { + throw new PopbillException('승인된 알림톡 템플릿코드가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($Messages)) { + throw new PopbillException('카카오톡 전송정보가 입력되지 않았습니다.'); + } + if(!$this->isNullOrEmpty($ReserveDT) && !$this->isValidDT($ReserveDT)) { + throw new PopbillException('전송 예약일시가 유효하지 않습니다.'); + } + + $Request = array(); + + if(!$this->isNullOrEmpty($TemplateCode)) $Request['templateCode'] = $TemplateCode; + if(!$this->isNullOrEmpty($Sender)) $Request['snd'] = $Sender; + if(!$this->isNullOrEmpty($Content)) $Request['content'] = $Content; + if(!$this->isNullOrEmpty($AltSubject)) $Request['altSubject'] = $AltSubject; + if(!$this->isNullOrEmpty($AltContent)) $Request['altContent'] = $AltContent; + if(!$this->isNullOrEmpty($AltSendType)) $Request['altSendType'] = $AltSendType; + if(!$this->isNullOrEmpty($ReserveDT)) $Request['sndDT'] = $ReserveDT; + if(!$this->isNullOrEmpty($RequestNum)) $Request['requestNum'] = $RequestNum; + if(!$this->isNullOrEmpty($Btns)) $Request['btns'] = $Btns; + + $Request['msgs'] = $Messages; + + $postdata = json_encode($Request); + + return $this->executeCURL('/ATS', $CorpNum, $UserID, true, null, $postdata)->receiptNum; + } +} + +class ENumKakaoType +{ + const ATS = 'ATS'; + const FTS = 'FTS'; + const FMS = 'FMS'; +} + +class KakaoSearchResult +{ + public $code; + public $message; + public $total; + public $perPage; + public $pageNum; + public $pageCount; + + public $list; + + function fromJsonInfo($jsonInfo) + { + + isset($jsonInfo->code) ? ($this->code = $jsonInfo->code) : null; + isset($jsonInfo->message) ? ($this->message = $jsonInfo->message) : null; + isset($jsonInfo->total) ? ($this->total = $jsonInfo->total) : null; + isset($jsonInfo->perPage) ? ($this->perPage = $jsonInfo->perPage) : null; + isset($jsonInfo->pageNum) ? ($this->pageNum = $jsonInfo->pageNum) : null; + isset($jsonInfo->pageCount) ? ($this->pageCount = $jsonInfo->pageCount) : null; + + $DetailList = array(); + for ($i = 0; $i < Count($jsonInfo->list); $i++) { + $SentInfo = new KakaoSentInfoDetail(); + $SentInfo->fromJsonInfo($jsonInfo->list[$i]); + $DetailList[$i] = $SentInfo; + } + $this->list = $DetailList; + } +} + +class KakaoSentInfo +{ + public $contentType; + public $templateCode; + public $plusFriendID; + public $sendNum; + public $altSubject; + public $altContent; + public $altSendType; + public $reserveDT; + public $adsYN; + public $imageURL; + public $sendCnt; + public $successCnt; + public $failCnt; + public $altCnt; + public $cancelCnt; + + public $msgs; + public $btns; + + function fromJsonInfo($jsonInfo) + { + + isset($jsonInfo->contentType) ? ($this->contentType = $jsonInfo->contentType) : null; + isset($jsonInfo->templateCode) ? ($this->templateCode = $jsonInfo->templateCode) : null; + isset($jsonInfo->plusFriendID) ? ($this->plusFriendID = $jsonInfo->plusFriendID) : null; + isset($jsonInfo->sendNum) ? ($this->sendNum = $jsonInfo->sendNum) : null; + isset($jsonInfo->altSubject) ? ($this->altSubject = $jsonInfo->altSubject) : null; + isset($jsonInfo->altContent) ? ($this->altContent = $jsonInfo->altContent) : null; + isset($jsonInfo->altSendType) ? ($this->altSendType = $jsonInfo->altSendType) : null; + isset($jsonInfo->reserveDT) ? ($this->reserveDT = $jsonInfo->reserveDT) : null; + isset($jsonInfo->adsYN) ? ($this->adsYN = $jsonInfo->adsYN) : null; + isset($jsonInfo->imageURL) ? ($this->imageURL = $jsonInfo->imageURL) : null; + isset($jsonInfo->sendCnt) ? ($this->sendCnt = $jsonInfo->sendCnt) : null; + isset($jsonInfo->successCnt) ? ($this->successCnt = $jsonInfo->successCnt) : null; + isset($jsonInfo->failCnt) ? ($this->failCnt = $jsonInfo->failCnt) : null; + isset($jsonInfo->altCnt) ? ($this->altCnt = $jsonInfo->altCnt) : null; + isset($jsonInfo->cancelCnt) ? ($this->cancelCnt = $jsonInfo->cancelCnt) : null; + + if (isset($jsonInfo->msgs)) { + $msgsList = array(); + for ($i = 0; $i < Count($jsonInfo->msgs); $i++) { + $kakaoDetail = new KakaoSentInfoDetail(); + $kakaoDetail->fromJsonInfo($jsonInfo->msgs[$i]); + $msgsList[$i] = $kakaoDetail; + } + $this->msgs = $msgsList; + } // end of if + + if (isset($jsonInfo->btns)) { + $btnsList = array(); + for ($i = 0; $i < Count($jsonInfo->btns); $i++) { + $buttonDetail = new KakaoButton(); + $buttonDetail->fromJsonInfo($jsonInfo->btns[$i]); + $btnsList[$i] = $buttonDetail; + } + $this->btns = $btnsList; + } + + } + +} // end of KakaoSentInfo class + +class KakaoSentInfoDetail +{ + public $state; + public $sendDT; + public $receiveNum; + public $receiveName; + public $content; + public $result; + public $resultDT; + public $altSubject; + public $altContent; + public $contentType; + public $altContentType; + public $altSendDT; + public $altResult; + public $altResultDT; + public $reserveDT; + public $receiptNum; + public $requestNum; + public $interOPRefKey; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->state) ? ($this->state = $jsonInfo->state) : null; + isset($jsonInfo->sendDT) ? ($this->sendDT = $jsonInfo->sendDT) : null; + isset($jsonInfo->receiveNum) ? ($this->receiveNum = $jsonInfo->receiveNum) : null; + isset($jsonInfo->receiveName) ? ($this->receiveName = $jsonInfo->receiveName) : null; + isset($jsonInfo->content) ? ($this->content = $jsonInfo->content) : null; + isset($jsonInfo->result) ? ($this->result = $jsonInfo->result) : null; + isset($jsonInfo->resultDT) ? ($this->resultDT = $jsonInfo->resultDT) : null; + isset($jsonInfo->altSubject) ? ($this->altSubject = $jsonInfo->altSubject) : null; + isset($jsonInfo->altContent) ? ($this->altContent = $jsonInfo->altContent) : null; + isset($jsonInfo->contentType) ? ($this->contentType = $jsonInfo->contentType) : null; + isset($jsonInfo->altContentType) ? ($this->altContentType = $jsonInfo->altContentType) : null; + isset($jsonInfo->altSendDT) ? ($this->altSendDT = $jsonInfo->altSendDT) : null; + isset($jsonInfo->altResult) ? ($this->altResult = $jsonInfo->altResult) : null; + isset($jsonInfo->altResultDT) ? ($this->altResultDT = $jsonInfo->altResultDT) : null; + isset($jsonInfo->reserveDT) ? ($this->reserveDT = $jsonInfo->reserveDT) : null; + isset($jsonInfo->receiptNum) ? ($this->receiptNum = $jsonInfo->receiptNum) : null; + isset($jsonInfo->requestNum) ? ($this->requestNum = $jsonInfo->requestNum) : null; + isset($jsonInfo->interOPRefKey) ? ($this->interOPRefKey = $jsonInfo->interOPRefKey) : null; + } +} + +class ATSTemplate +{ + public $templateCode; + public $templateName; + public $template; + public $plusFriendID; + public $ads; + public $appendix; + public $btns; + public $secureYN; + public $state; + public $stateDT; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->templateCode) ? $this->templateCode = $jsonInfo->templateCode : null; + isset($jsonInfo->templateName) ? $this->templateName = $jsonInfo->templateName : null; + isset($jsonInfo->template) ? $this->template = $jsonInfo->template : null; + isset($jsonInfo->plusFriendID) ? $this->plusFriendID = $jsonInfo->plusFriendID : null; + isset($jsonInfo->ads) ? $this->ads = $jsonInfo->ads : null; + isset($jsonInfo->appendix) ? $this->appendix = $jsonInfo->appendix : null; + isset($jsonInfo->secureYN) ? $this->secureYN = $jsonInfo->secureYN : null; + isset($jsonInfo->state) ? $this->state = $jsonInfo->state : null; + isset($jsonInfo->stateDT) ? $this->stateDT = $jsonInfo->stateDT : null; + + if(isset($jsonInfo->btns)){ + $InfoList = array(); + for ($i = 0; $i < Count($jsonInfo->btns); $i++) { + $InfoObj = new KakaoButton(); + $InfoObj->fromJsonInfo($jsonInfo->btns[$i]); + $InfoList[$i] = $InfoObj; + } + $this->btns = $InfoList; + } + } +} + +class KakaoButton +{ + public $n; + public $t; + public $u1; + public $u2; + public $tg; + + function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->n) ? $this->n = $jsonInfo->n : null; + isset($jsonInfo->t) ? $this->t = $jsonInfo->t : null; + isset($jsonInfo->u1) ? $this->u1 = $jsonInfo->u1 : null; + isset($jsonInfo->u2) ? $this->u2 = $jsonInfo->u2 : null; + isset($jsonInfo->tg) ? $this->tg = $jsonInfo->tg : null; + } +} + +class PlusFriend +{ + public $plusFriendID; + public $plusFriendName; + public $regDT; + public $state; + public $stateDT; + + function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->plusFriendID) ? $this->plusFriendID = $jsonInfo->plusFriendID : null; + isset($jsonInfo->plusFriendName) ? $this->plusFriendName = $jsonInfo->plusFriendName : null; + isset($jsonInfo->regDT) ? $this->regDT = $jsonInfo->regDT : null; + isset($jsonInfo->state) ? $this->state = $jsonInfo->state : null; + isset($jsonInfo->stateDT) ? $this->stateDT = $jsonInfo->stateDT : null; + } +} + + +?> diff --git a/plugin/kakao5/Popbill/PopbillMessaging.php b/plugin/kakao5/Popbill/PopbillMessaging.php new file mode 100644 index 000000000..39abde98e --- /dev/null +++ b/plugin/kakao5/Popbill/PopbillMessaging.php @@ -0,0 +1,619 @@ +AddScope('150'); + $this->AddScope('151'); + $this->AddScope('152'); + } + + // 전송단가 확인 + public function GetUnitCost($CorpNum, $MessageType) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($MessageType)) { + throw new PopbillException('문자 전송유형이 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/UnitCost?Type=' . $MessageType, $CorpNum)->unitCost; + } + + // 발신번호 등록여부 확인 + public function CheckSenderNumber($CorpNum, $SenderNumber, $UserID = null) { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($SenderNumber)) { + throw new PopbillException('발신번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/CheckSenderNumber/' . $SenderNumber, $CorpNum, $UserID); + } + + /* 단문메시지 전송 + * $CorpNum => 발송사업자번호 + * $Sender => 동보전송용 발신번호 미기재시 개별메시지 발신번호로 전송. 발신번호가 없는 개별메시지에만 동보처리함. + * $Content => 동보전송용 발신내용 미기재시 개별메시지 내용으로 전송, 발신내용이 없는 개별메시지에만 동보처리함. + * $Messages => 발신메시지 최대 1000건, 배열 + * 'snd' => 개별발신번호 + * 'sndnm'=> 발신자명 + * 'rcv' => 수신번호, 필수 + * 'rcvnm'=> 수신자 성명 + * 'msg' => 메시지 내용, 미기재시 동보메시지로 전송함. + * 'sjt' => 메시지 제목(SMS 사용 불가, 미입력시 팝빌에서 설정한 기본값 사용) + * 'interOPRefKey'=> 파트너 지정 키(SMS/LMS/MMS 대량/동보전송시 파트너가 개별건마다 입력할 수 있는 값) + * $ReserveDT => 예약전송시 예약시간 yyyyMMddHHmmss 형식으로 기재 + * $adsYN => 광고메시지 전송여부, true:광고/false:일반 중 택 1 + * $UserID => 발신자 팝빌 회원아이디 + * $SenderName=> 동보전송용 발신자명 미기재시 개별메시지 발신자명으로 전송 + * $requestNum=> 전송 요청번호 + */ + public function SendSMS($CorpNum, $Sender = null, $Content = null, $Messages = array(), $ReserveDT = null, $adsYN = false, $UserID = null, $SenderName = null, $RequestNum = null) + { + return $this->SendMessage(ENumMessageType::SMS, $CorpNum, $Sender, $SenderName, null, $Content, $Messages, $ReserveDT, $adsYN, $UserID, $RequestNum); + } + + /* 장문메시지 전송 + * $CorpNum => 발송사업자번호 + * $Sender => 동보전송용 발신번호 미기재시 개별메시지 발신번호로 전송. 발신번호가 없는 개별메시지에만 동보처리함. + * $Subject => 동보전송용 제목 미기재시 개별메시지 제목으로 전송, 제목이 없는 개별메시지에만 동보처리함. + * $Content => 동보전송용 발신내용 미기재시 개별베시지 내용으로 전송, 발신내용이 없는 개별메시지에만 동보처리함. + * $Messages => 발신메시지 최대 1000건, 배열 + * 'snd' => 개별발신번호 + * 'sndnm'=> 발신자명 + * 'rcv' => 수신번호, 필수 + * 'rcvnm'=> 수신자 성명 + * 'msg' => 메시지 내용, 미기재시 동보메시지로 전송함. + * 'sjt' => 메시지 제목(SMS 사용 불가, 미입력시 팝빌에서 설정한 기본값 사용) + * 'interOPRefKey'=> 파트너 지정 키(SMS/LMS/MMS 대량/동보전송시 파트너가 개별건마다 입력할 수 있는 값) + * $ReserveDT => 예약전송시 예약시간 yyyyMMddHHmmss 형식으로 기재 + * $adsYN => 광고메시지 전송여부, true:광고/false:일반 중 택 1 + * $UserID => 발신자 팝빌 회원아이디 + * $SenderName=> 동보전송용 발신자명 미기재시 개별메시지 발신자명으로 전송 + * $requestNum=> 전송 요청번호 + */ + public function SendLMS($CorpNum, $Sender = null, $Subject = null, $Content = null, $Messages = array(), $ReserveDT = null, $adsYN = false, $UserID = null, $SenderName = null, $RequestNum = null) + { + return $this->SendMessage(ENumMessageType::LMS, $CorpNum, $Sender, $SenderName, $Subject, $Content, $Messages, $ReserveDT, $adsYN, $UserID, $RequestNum); + } + + /* 장/단문메시지 전송 - 메지시 길이에 따라 단문과 장문을 선택하여 전송합니다. + * $CorpNum => 발송사업자번호 + * $Sender => 동보전송용 발신번호 미기재시 개별메시지 발신번호로 전송. 발신번호가 없는 개별메시지에만 동보처리함. + * $Subject => 동보전송용 제목 미기재시 개별메시지 제목으로 전송, 제목이 없는 개별메시지에만 동보처리함. + * $Content => 동보전송용 발신내용 미기재시 개별베시지 내용으로 전송, 발신내용이 없는 개별메시지에만 동보처리함. + * $Messages => 발신메시지 최대 1000건, 배열 + * 'snd' => 개별발신번호 + * 'sndnm'=> 발신자명 + * 'rcv' => 수신번호, 필수 + * 'rcvnm'=> 수신자 성명 + * 'msg' => 메시지 내용, 미기재시 동보메시지로 전송함. + * 'sjt' => 메시지 제목(SMS 사용 불가, 미입력시 팝빌에서 설정한 기본값 사용) + * 'interOPRefKey'=> 파트너 지정 키(SMS/LMS/MMS 대량/동보전송시 파트너가 개별건마다 입력할 수 있는 값) + * $ReserveDT => 예약전송시 예약시간 yyyyMMddHHmmss 형식으로 기재 + * $adsYN => 광고메시지 전송여부, true:광고/false:일반 중 택 1 + * $UserID => 발신자 팝빌 회원아이디 + * $SenderName=> 동보전송용 발신자명 미기재시 개별메시지 발신자명으로 전송 + * $requestNum=> 전송 요청번호 + */ + public function SendXMS($CorpNum, $Sender = null, $Subject = null, $Content = null, $Messages = array(), $ReserveDT = null, $adsYN = false, $UserID = null, $SenderName = null, $RequestNum = null) + { + return $this->SendMessage(ENumMessageType::XMS, $CorpNum, $Sender, $SenderName, $Subject, $Content, $Messages, $ReserveDT, $adsYN, $UserID, $RequestNum); + } + + /* MMS 메시지 전송 + * $CorpNum => 발송사업자번호 + * $Sender => 동보전송용 발신번호 미기재시 개별메시지 발신번호로 전송. 발신번호가 없는 개별메시지에만 동보처리함. + * $Subject => 동보전송용 제목 미기재시 개별메시지 제목으로 전송, 제목이 없는 개별메시지에만 동보처리함. + * $Content => 동보전송용 발신내용 미기재시 개별베시지 내용으로 전송, 발신내용이 없는 개별메시지에만 동보처리함. + * $Messages => 발신메시지 최대 1000건, 배열 + * 'snd' => 개별발신번호 + * 'sndnm'=> 발신자명 + * 'rcv' => 수신번호, 필수 + * 'rcvnm'=> 수신자 성명 + * 'msg' => 메시지 내용, 미기재시 동보메시지로 전송함. + * 'sjt' => 메시지 제목(SMS 사용 불가, 미입력시 팝빌에서 설정한 기본값 사용) + * 'interOPRefKey'=> 파트너 지정 키(SMS/LMS/MMS 대량/동보전송시 파트너가 개별건마다 입력할 수 있는 값) + * $FilePaths => 전송할 파일경로 문자열 + * $ReserveDT => 예약전송시 예약시간 yyyyMMddHHmmss 형식으로 기재 + * $adsYN => 광고메시지 전송여부, true:광고/false:일반 중 택 1 + * $UserID => 발신자 팝빌 회원아이디 + * $SenderName => 동보전송용 발신자명 미기재시 개별메시지 발신자명으로 전송 + * $requestNum => 전송 요청번호 + */ + public function SendMMS($CorpNum, $Sender = null, $Subject = null, $Content = null, $Messages = array(), $FilePaths = array(), $ReserveDT = null, $adsYN = false, $UserID = null, $SenderName = null, $RequestNum = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($Messages)) { + throw new PopbillException('전송할 메시지가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($FilePaths)) { + throw new PopbillException('전송할 이미지 파일 경로가 입력되지 않았습니다.'); + } + if(!$this->isNullOrEmpty($ReserveDT) && !$this->isValidDT($ReserveDT)) { + throw new PopbillException('전송 예약일시가 유효하지 않습니다.'); + } + + $Request = array(); + + if(!$this->isNullOrEmpty($Sender)) $Request['snd'] = $Sender; + if(!$this->isNullOrEmpty($Subject)) $Request['subject'] = $Subject; + if(!$this->isNullOrEmpty($Content)) $Request['content'] = $Content; + if(!$this->isNullOrEmpty($ReserveDT)) $Request['sndDT'] = $ReserveDT; + if(!$this->isNullOrEmpty($SenderName)) $Request['sndnm'] = $SenderName; + if(!$this->isNullOrEmpty($RequestNum)) $Request['requestNum'] = $RequestNum; + + if ($adsYN) $Request['adsYN'] = $adsYN; + + $Request['msgs'] = $Messages; + + $postdata = array(); + $postdata['form'] = json_encode($Request); + + $i = 0; + + foreach ($FilePaths as $FilePath) { + $postdata['file'] = '@' . $FilePath; + } + + return $this->executeCURL('/MMS', $CorpNum, $UserID, true, null, $postdata, true)->receiptNum; + } + + /* 전송메시지 내역 및 전송상태 확인 + * $CorpNum => 발송사업자번호 + * $ReceiptNum=> 접수번호 + * $UserID => 팝빌 회원아이디 + */ + public function GetMessages($CorpNum, $ReceiptNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('접수번호가 입력되지 않았습니다.'); + } + + $result = $this->executeCURL('/Message/' . $ReceiptNum, $CorpNum, $UserID); + + $MessageInfoList = array(); + + for ($i = 0; $i < Count($result); $i++) { + $MsgInfo = new MessageInfo(); + $MsgInfo->fromJsonInfo($result[$i]); + $MessageInfoList[$i] = $MsgInfo; + } + return $MessageInfoList; + } + + /* 전송메시지 내역 및 전송상태 확인 + * $CorpNum => 발송사업자번호 + * $RequestNum=> 전송요청번호 + * $UserID => 팝빌 회원아이디 + */ + public function GetMessagesRN($CorpNum, $RequestNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('전송요청번호가 입력되지 않았습니다.'); + } + + $result = $this->executeCURL('/Message/Get/' . $RequestNum, $CorpNum, $UserID); + + $MessageInfoList = array(); + + for ($i = 0; $i < Count($result); $i++) { + $MsgInfo = new MessageInfo(); + $MsgInfo->fromJsonInfo($result[$i]); + $MessageInfoList[$i] = $MsgInfo; + } + return $MessageInfoList; + } + + /* 예약전송 취소 + * $CorpNum => 발송사업자번호 + * $ReceiptNum=> 접수번호 + * $UserID => 팝빌 회원아이디 + */ + public function CancelReserve($CorpNum, $ReceiptNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('예약전송 취소할 접수번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/' . $ReceiptNum . '/Cancel', $CorpNum, $UserID); + } + + /* 예약전송 취소 + * $CorpNum => 발송사업자번호 + * $RequestNum=> 전송요청번호 + * $UserID => 팝빌 회원아이디 + */ + public function CancelReserveRN($CorpNum, $RequestNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('예약전송 취소할 전송요청번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/Cancel/' . $RequestNum, $CorpNum, $UserID); + } + + /* 예약전송 취소 + * $CorpNum => 발송사업자번호 + * $ReceiptNum => 접수번호 + * $ReceiveNum => 수신번호 + * $UserID => 팝빌 회원아이디 + */ + public function CancelReservebyRCV($CorpNum, $ReceiptNum, $ReceiveNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiptNum)) { + throw new PopbillException('예약전송 취소할 접수번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiveNum)) { + throw new PopbillException('예약전송 취소할 수신번호가 입력되지 않았습니다.'); + } + + $postdata = json_encode($ReceiveNum); + + return $this->executeCURL('/Message/' . $ReceiptNum . '/Cancel', $CorpNum, $UserID, true, null, $postdata); + } + + /* 예약전송 취소 + * $CorpNum => 발송사업자번호 + * $RequestNum => 전송요청번호 + * $ReceiveNum => 수신번호 + * $UserID => 팝빌 회원아이디 + */ + public function CancelReserveRNbyRCV($CorpNum, $RequestNum, $ReceiveNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($RequestNum)) { + throw new PopbillException('예약전송 취소할 전송요청번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($ReceiveNum)) { + throw new PopbillException('예약전송 취소할 수신번호가 입력되지 않았습니다.'); + } + + $postdata = json_encode($ReceiveNum); + + return $this->executeCURL('/Message/Cancel/' . $RequestNum, $CorpNum, $UserID, true, null, $postdata); + } + + + private function SendMessage($MessageType, $CorpNum, $Sender, $SenderName, $Subject, $Content, $Messages = array(), $ReserveDT = null, $adsYN = false, $UserID = null, $RequestNum = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($Messages)) { + throw new PopbillException('전송할 메시지가 입력되지 않았습니다.'); + } + if(!$this->isNullOrEmpty($ReserveDT) && !$this->isValidDT($ReserveDT)) { + throw new PopbillException('전송 예약일시가 유효하지 않습니다.'); + } + + $Request = array(); + + if(!$this->isNullOrEmpty($Sender)) $Request['snd'] = $Sender; + if(!$this->isNullOrEmpty($SenderName)) $Request['sndnm'] = $SenderName; + if(!$this->isNullOrEmpty($Content)) $Request['content'] = $Content; + if(!$this->isNullOrEmpty($Subject)) $Request['subject'] = $Subject; + if(!$this->isNullOrEmpty($ReserveDT)) $Request['sndDT'] = $ReserveDT; + if(!$this->isNullOrEmpty($RequestNum)) $Request['requestNum'] = $RequestNum; + + if ($adsYN) $Request['adsYN'] = $adsYN; + + $Request['msgs'] = $Messages; + + $postdata = json_encode($Request); + return $this->executeCURL('/' . $MessageType, $CorpNum, $UserID, true, null, $postdata)->receiptNum; + } + + // 문자 관련 URL함수 + public function GetURL($CorpNum, $UserID = null, $TOGO) + { + $response = $this->executeCURL('/Message/?TG=' . $TOGO, $CorpNum, $UserID); + return $response->url; + } + + // 문자 전송내역 팝업 URL + public function GetSentListURL($CorpNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/Message/?TG=BOX', $CorpNum, $UserID); + return $response->url; + } + + // 발신번호 관리 팝업 URL + public function GetSenderNumberMgtURL($CorpNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + $response = $this->executeCURL('/Message/?TG=SENDER', $CorpNum, $UserID); + return $response->url; + } + + // 문자 전송내역 조회 + public function Search($CorpNum, $SDate, $EDate, $State = array(), $Item = array(), $ReserveYN = null, $SenderYN = false, $Page = null, $PerPage = null, $Order = null, $UserID = null, $QString = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($SDate)) { + throw new PopbillException('시작일자가 입력되지 않았습니다.'); + } + if(!$this->isValidDate($SDate)) { + throw new PopbillException('시작일자가 유효하지 않습니다.'); + } + if($this->isNullOrEmpty($EDate)) { + throw new PopbillException('종료일자가 입력되지 않았습니다.'); + } + if(!$this->isValidDate($EDate)) { + throw new PopbillException('종료일자가 유효하지 않습니다.'); + } + if($this->isNullOrEmpty($State)) { + throw new PopbillException('전송상태가 입력되지 않았습니다.'); + } + + $uri = '/Message/Search'; + $uri .= '?SDate=' . $SDate; + $uri .= '&EDate=' . $EDate; + $uri .= '&State=' . implode(',', $State); + + if(!$this->isNullOrEmpty($Item)) { + $uri .= '&Item=' . implode(',', $Item); + } + if(!is_null($ReserveYN)) { + if ($ReserveYN) { + $uri .= '&ReserveYN=1'; + } else { + $uri .= '&ReserveYN=0'; + } + } + if ($SenderYN) { + $uri .= '&SenderOnly=1'; + } else { + $uri .= '&SenderOnly=0'; + } + + if(!$this->isNullOrEmpty($Page)) { + $uri .= '&Page=' . $Page; + } + if(!$this->isNullOrEmpty($PerPage)) { + $uri .= '&PerPage=' . $PerPage; + } + if(!$this->isNullOrEmpty($Order)) { + $uri .= '&Order=' . $Order; + } + if(!$this->isNullOrEmpty($QString)) { + $uri .= '&QString=' . urlencode($QString); + } + + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $SearchList = new MsgSearchResult(); + $SearchList->fromJsonInfo($response); + + return $SearchList; + } + + // 080 수신거부목록 조회 + public function GetAutoDenyList($CorpNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/Denied', $CorpNum, $UserID); + } + + // 080 수신거부 조회 + public function CheckAutoDenyNumber($CorpNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/AutoDenyNumberInfo', $CorpNum, $UserID); + } + + public function GetChargeInfo($CorpNum, $MessageType, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + if($this->isNullOrEmpty($MessageType)) { + throw new PopbillException('문자 전송유형이 입력되지 않았습니다.'); + } + + $uri = '/Message/ChargeInfo?Type=' . $MessageType; + + $response = $this->executeCURL($uri, $CorpNum, $UserID); + $ChargeInfo = new ChargeInfo(); + $ChargeInfo->fromJsonInfo($response); + + return $ChargeInfo; + } + + // 발신번호 목록 조회 + public function GetSenderNumberList($CorpNum, $UserID = null) + { + if($this->isNullOrEmpty($CorpNum)) { + throw new PopbillException('팝빌회원 사업자번호가 입력되지 않았습니다.'); + } + + return $this->executeCURL('/Message/SenderNumber', $CorpNum, $UserID); + } + + // 문자전송결과 + public function GetStates($CorpNum, $ReceiptNumList = array(), $UserID = null) + { + if (is_null($ReceiptNumList) || empty($ReceiptNumList)) { + throw new PopbillException('접수번호가 입력되지 않았습니다.'); + } + + $postdata = json_encode($ReceiptNumList); + $result = $this->executeCURL('/Message/States', $CorpNum, $UserID, true, null, $postdata); + $MsgInfoList = array(); + + for ($i = 0; $i < Count($result); $i++) { + $MsgInfo = new MessageBriefInfo(); + $MsgInfo->fromJsonInfo($result[$i]); + $MsgInfoList[$i] = $MsgInfo; + } + + return $MsgInfoList; + } +} + +class ENumMessageType +{ + const SMS = 'SMS'; + const LMS = 'LMS'; + const XMS = 'XMS'; + const MMS = 'MMS'; +} + +class MsgSearchResult +{ + public $code; + public $total; + public $perPage; + public $pageNum; + public $pageCount; + public $message; + public $list; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->code) ? $this->code = $jsonInfo->code : null; + isset($jsonInfo->total) ? $this->total = $jsonInfo->total : null; + isset($jsonInfo->perPage) ? $this->perPage = $jsonInfo->perPage : null; + isset($jsonInfo->pageCount) ? $this->pageCount = $jsonInfo->pageCount : null; + isset($jsonInfo->pageNum) ? $this->pageNum = $jsonInfo->pageNum : null; + isset($jsonInfo->message) ? $this->message = $jsonInfo->message : null; + + $InfoList = array(); + + for ($i = 0; $i < Count($jsonInfo->list); $i++) { + $InfoObj = new MessageInfo(); + $InfoObj->fromJsonInfo($jsonInfo->list[$i]); + $InfoList[$i] = $InfoObj; + } + $this->list = $InfoList; + } +} + + +class MessageInfo +{ + public $state; + public $result; + public $subject; + public $type; + public $content; + public $tranNet; + public $sendNum; + public $senderName; + public $receiveNum; + public $receiveName; + public $reserveDT; + public $sendDT; + public $resultDT; + public $sendResult; + public $receiptDT; + public $receiptNum; + public $requestNum; + public $interOPRefKey; + + function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->state) ? $this->state = $jsonInfo->state : null; + isset($jsonInfo->result) ? $this->result = $jsonInfo->result : null; + isset($jsonInfo->subject) ? $this->subject = $jsonInfo->subject : null; + isset($jsonInfo->tranNet) ? $this->tranNet = $jsonInfo->tranNet : null; + isset($jsonInfo->type) ? $this->type = $jsonInfo->type : null; + isset($jsonInfo->content) ? $this->content = $jsonInfo->content : null; + isset($jsonInfo->sendNum) ? $this->sendNum = $jsonInfo->sendNum : null; + isset($jsonInfo->senderName) ? $this->senderName = $jsonInfo->senderName : null; + isset($jsonInfo->receiveNum) ? $this->receiveNum = $jsonInfo->receiveNum : null; + isset($jsonInfo->receiveName) ? $this->receiveName = $jsonInfo->receiveName : null; + isset($jsonInfo->reserveDT) ? $this->reserveDT = $jsonInfo->reserveDT : null; + isset($jsonInfo->sendDT) ? $this->sendDT = $jsonInfo->sendDT : null; + isset($jsonInfo->resultDT) ? $this->resultDT = $jsonInfo->resultDT : null; + isset($jsonInfo->sendResult) ? $this->sendResult = $jsonInfo->sendResult : null; + isset($jsonInfo->receiptDT) ? $this->receiptDT = $jsonInfo->receiptDT : null; + isset($jsonInfo->receiptNum) ? $this->receiptNum = $jsonInfo->receiptNum : null; + isset($jsonInfo->requestNum) ? $this->requestNum = $jsonInfo->requestNum : null; + isset($jsonInfo->interOPRefKey) ? $this->interOPRefKey = $jsonInfo->interOPRefKey : null; + } +} + +class MessageBriefInfo +{ + public $sn; + public $rNum; + public $stat; + public $sDT; + public $rDT; + public $rlt; + public $net; + public $srt; + + function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->sn) ? $this->sn = $jsonInfo->sn : null; + isset($jsonInfo->rNum) ? $this->rNum = $jsonInfo->rNum : null; + isset($jsonInfo->stat) ? $this->stat = $jsonInfo->stat : null; + isset($jsonInfo->sDT) ? $this->sDT = $jsonInfo->sDT : null; + isset($jsonInfo->rDT) ? $this->rDT = $jsonInfo->rDT : null; + isset($jsonInfo->rlt) ? $this->rlt = $jsonInfo->rlt : null; + isset($jsonInfo->net) ? $this->net = $jsonInfo->net : null; + isset($jsonInfo->srt) ? $this->srt = $jsonInfo->srt : null; + } +} + +?> diff --git a/plugin/kakao5/Popbill/README.md b/plugin/kakao5/Popbill/README.md new file mode 100644 index 000000000..847044a4a --- /dev/null +++ b/plugin/kakao5/Popbill/README.md @@ -0,0 +1,2 @@ +# popbill.sdk.php5 +팝빌 SDK for PHP5 diff --git a/plugin/kakao5/Popbill/crypto.php b/plugin/kakao5/Popbill/crypto.php new file mode 100644 index 000000000..04072f546 --- /dev/null +++ b/plugin/kakao5/Popbill/crypto.php @@ -0,0 +1,44 @@ +keyInstance($key); + + openssl_public_encrypt($data, $encrypted, $publickey, OPENSSL_PKCS1_OAEP_PADDING); + + return base64_encode($encrypted); + } + + public function keyInstance($publickey) { + // startline , endline 설정 + $start_line = "-----BEGIN PUBLIC KEY-----"; + $end_line = "-----END PUBLIC KEY-----"; + + // key 추출 (정규식) + $pattern = "/-+([a-zA-Z\s]*)-+([^-]*)-+([a-zA-Z\s]*)-+/"; + if(preg_match($pattern, $publickey, $matches)) { + $splitKey = $matches[2]; + $splitKey = preg_replace('/\s+/', '', $splitKey); + } else { + return null; + } + + $key = ""; + + for($pos=1; $pos <= strlen($splitKey); $pos++) { + if($pos % 64 == 0) { + $key = $key . $splitKey[$pos-1] . "\n"; + } else { + $key = $key . $splitKey[$pos-1]; + } + } + + // startline, endline 추가 + $key = $start_line . "\n" . $key . "\n" . $end_line; + + return $key; + } +} + +?> \ No newline at end of file diff --git a/plugin/kakao5/Popbill/popbill.php b/plugin/kakao5/Popbill/popbill.php new file mode 100644 index 000000000..7a66d9203 --- /dev/null +++ b/plugin/kakao5/Popbill/popbill.php @@ -0,0 +1,930 @@ +Linkhub = Linkhub::getInstance($LinkID, $SecretKey); + $this->scopes[] = 'member'; + } + + public function IsTest($T) + { + $this->IsTest = $T; + } + + public function IPRestrictOnOff($V) + { + $this->IPRestrictOnOff = $V; + } + + public function UseStaticIP($V) + { + $this->UseStaticIP = $V; + } + + public function UseGAIP($V) + { + $this->UseGAIP = $V; + } + + public function UseLocalTimeYN($V) + { + $this->UseLocalTimeYN = $V; + } + + protected function AddScope($scope) + { + $this->scopes[] = $scope; + } + + private function getsession_Token($CorpNum) + { + $targetToken = null; + + if (array_key_exists($CorpNum, $this->Token_Table)) { + $targetToken = $this->Token_Table[$CorpNum]; + } + + $Refresh = false; + + if (is_null($targetToken)) { + $Refresh = true; + } else { + $Expiration = new DateTime($targetToken->expiration, new DateTimeZone("UTC")); + + $now = $this->Linkhub->getTime($this->UseStaticIP, $this->UseLocalTimeYN, $this->UseGAIP); + $Refresh = $Expiration < $now; + } + + if ($Refresh) { + try { + $targetToken = $this->Linkhub->getToken($this->IsTest ? PopbillBase::ServiceID_TEST : PopbillBase::ServiceID_REAL, $CorpNum, $this->scopes, $this->IPRestrictOnOff ? null : "*", $this->UseStaticIP, $this->UseLocalTimeYN, $this->UseGAIP); + } catch (LinkhubException $le) { + throw new PopbillException($le->getMessage(), $le->getCode()); + } + $this->Token_Table[$CorpNum] = $targetToken; + } + return $targetToken->session_token; + } + + // ID 중복 확인 + public function CheckID($ID) + { + if (is_null($ID) || empty($ID)) { + throw new PopbillException('조회할 아이디가 입력되지 않았습니다.'); + } + return $this->executeCURL('/IDCheck?ID=' . $ID); + } + + // 담당자 추가 + public function RegistContact($CorpNum, $ContactInfo, $UserID = null) + { + $postdata = json_encode($ContactInfo); + return $this->executeCURL('/IDs/New', $CorpNum, $UserID, true, null, $postdata); + } + + // 담당자 정보 수정 + public function UpdateContact($CorpNum, $ContactInfo, $UserID) + { + $postdata = json_encode($ContactInfo); + return $this->executeCURL('/IDs', $CorpNum, $UserID, true, null, $postdata); + } + + // 담당자 정보 확인 + public function GetContactInfo($CorpNum, $ContactID, $UserID = null) + { + $postdata = '{"id":' . '"' . $ContactID . '"}'; + return $this->executeCURL('/Contact', $CorpNum, $UserID, true, null, $postdata); + } + + // 담당자 목록 조회 + public function ListContact($CorpNum, $UserID = null) + { + $ContactInfoList = array(); + + $response = $this->executeCURL('/IDs', $CorpNum, $UserID); + + for ($i = 0; $i < Count($response); $i++) { + $ContactInfo = new ContactInfo(); + $ContactInfo->fromJsonInfo($response[$i]); + $ContactInfoList[$i] = $ContactInfo; + } + + return $ContactInfoList; + } + + // 회사정보 확인 + public function GetCorpInfo($CorpNum, $UserID = null) + { + $response = $this->executeCURL('/CorpInfo', $CorpNum, $UserID); + + $CorpInfo = new CorpInfo(); + $CorpInfo->fromJsonInfo($response); + return $CorpInfo; + } + + // 회사정보 수정 + public function UpdateCorpInfo($CorpNum, $CorpInfo, $UserID = null) + { + $postdata = json_encode($CorpInfo); + return $this->executeCURL('/CorpInfo', $CorpNum, $UserID, true, null, $postdata); + } + + //팝빌 연결 URL함수 + public function GetPopbillURL($CorpNum, $UserID, $TOGO) + { + $response = $this->executeCURL('/Member?TG=' . $TOGO, $CorpNum, $UserID); + return $response->url; + } + + //팝빌 로그인 URL + public function GetAccessURL($CorpNum, $UserID) + { + $response = $this->executeCURL('/Member?TG=LOGIN', $CorpNum, $UserID); + return $response->url; + } + + // 연동회원 포인트 충전 팝업 URL + public function GetChargeURL($CorpNum, $UserID) + { + $response = $this->executeCURL('/Member?TG=CHRG', $CorpNum, $UserID); + return $response->url; + } + + // 연동회원 포인트 결제내역 팝업 URL + public function GetPaymentURL($CorpNum, $UserID) + { + $response = $this->executeCURL('/Member?TG=PAYMENT', $CorpNum, $UserID); + return $response->url; + } + + // 연동회원 포인트 사용내역 팝업 URL + public function GetUseHistoryURL($CorpNum, $UserID) + { + $response = $this->executeCURL('/Member?TG=USEHISTORY', $CorpNum, $UserID); + return $response->url; + } + + //가입여부 확인 + public function CheckIsMember($CorpNum, $LinkID) + { + return $this->executeCURL('/Join?CorpNum=' . $CorpNum . '&LID=' . $LinkID); + } + + //회원가입 + public function JoinMember($JoinForm) + { + $postdata = json_encode($JoinForm); + return $this->executeCURL('/Join', null, null, true, null, $postdata); + } + + // 연동회원 잔여포인트 확인 + public function GetBalance($CorpNum) + { + try { + return $this->Linkhub->getBalance($this->getsession_Token($CorpNum), $this->IsTest ? PopbillBase::ServiceID_TEST : PopbillBase::ServiceID_REAL, $this->UseStaticIP, $this->UseGAIP); + } catch (LinkhubException $le) { + throw new PopbillException($le->getMessage(), $le->getCode()); + } + } + + // 연동회원 포인트 사용내역 확인 + public function GetUseHistory($CorpNum, $SDate, $EDate, $Page = null, $PerPage = null, $Order = null, $UserID = null) + { + $uri = '/UseHistory'; + $uri .= '?SDate=' . $SDate; + $uri .= '&EDate=' . $EDate; + $uri .= '&Page=' . $Page; + $uri .= '&PerPage=' . $PerPage; + $uri .= '&Order=' . $Order; + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $UseHistoryResult = new UseHistoryResult(); + $UseHistoryResult->fromJsonInfo($response); + + return $UseHistoryResult; + } + + // 연동회원 포인트 결제내역 확인 + public function GetPaymentHistory($CorpNum, $SDate, $EDate, $Page = null, $PerPage = null, $UserID = null) + { + $uri = '/PaymentHistory'; + $uri .= '?SDate=' . $SDate; + $uri .= '&EDate=' . $EDate; + $uri .= '&Page=' . $Page; + $uri .= '&PerPage=' . $PerPage; + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $PaymentHistoryResult = new PaymentHistoryResult(); + $PaymentHistoryResult->fromJsonInfo($response); + + return $PaymentHistoryResult; + } + + // 연동회원 포인트 환불내역 확인 + public function GetRefundHistory($CorpNum, $Page = null, $PerPage = null, $UserID = null) + { + $uri = '/RefundHistory'; + $uri .= '?Page=' . $Page; + $uri .= '&PerPage=' . $PerPage; + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $RefundHistoryResult = new RefundHistoryResult(); + $RefundHistoryResult->fromJsonInfo($response); + + return $RefundHistoryResult; + } + + // 연동회원 포인트 환불신청 + public function Refund($CorpNum, $RefundForm, $UserID = null) + { + $postdata = json_encode($RefundForm); + + return $this->executeCURL('/Refund', $CorpNum, $UserID, true, null, $postdata); + } + + // 연동회원 무통장 입금신청 + public function PaymentRequest($CorpNum, $PaymentForm, $UserID = null) + { + $postdata = json_encode($PaymentForm); + + return $this->executeCURL('/Payment', $CorpNum, $UserID, true, null, $postdata); + } + + // 연동회원 무통장 입금신청 정보확인 + public function GetSettleResult($CorpNum, $SettleCode, $UserID = null) + { + $uri = '/Payment/' . $SettleCode; + $response = $this->executeCURL($uri, $CorpNum, $UserID); + + $PaymentHistory = new PaymentHistory(); + $PaymentHistory->fromJsonInfo($response); + + return $PaymentHistory; + } + + // 파트너 포인트충전 팝업 URL + // - 2017/08/29 추가 + public function GetPartnerURL($CorpNum, $TOGO) + { + try { + return $this->Linkhub->getPartnerURL($this->getsession_Token($CorpNum), $this->IsTest ? PopbillBase::ServiceID_TEST : PopbillBase::ServiceID_REAL, $TOGO, $this->UseStaticIP, $this->UseGAIP); + } catch (LinkhubException $le) { + throw new PopbillException($le->getMessage(), $le->getCode()); + } + } + + // 파트너 잔여포인트 확인 + public function GetPartnerBalance($CorpNum) + { + try { + return $this->Linkhub->getPartnerBalance($this->getsession_Token($CorpNum), $this->IsTest ? PopbillBase::ServiceID_TEST : PopbillBase::ServiceID_REAL, $this->UseStaticIP, $this->UseGAIP); + } catch (LinkhubException $le) { + throw new PopbillException($le->getMessage(), $le->getCode()); + } + } + + // 회원 탈퇴 + public function QuitMember($CorpNum, $QuitReason, $UserID = null) + { + $postData = json_encode(array("quitReason" => $QuitReason)); + try { + $response = $this->executeCURL('/QuitRequest', $CorpNum, $UserID, true, null, $postData); + if($response->code == 1) { + unset($this-> Token_Table[$CorpNum]); + } + } catch (LinkhubException $le) { + throw new PopbillException($le->getMessage(), $le->getCode()); + } + return $response; + } + + // 환불가능 포인트 조회 + public function GetRefundableBalance($CorpNum, $UserID = null) + { + return $this->executeCURL('/RefundPoint', $CorpNum, $UserID, false, null)->refundableBalance; + } + + // 환불 신청 상태 조회 + public function GetRefundInfo($CorpNum, $RefundCode, $UserID = null) + { + if (is_null($RefundCode) || empty($RefundCode)) { + throw new PopbillException('조회할 환불코드가 입력되지 않았습니다.'); + } + return $this->executeCURL('/Refund/' . $RefundCode, $CorpNum, $UserID, false, null, null); + } + + protected function executeCURL($uri, $CorpNum = null, $userID = null, $isPost = false, $action = null, $postdata = null, $isMultiPart = false, $contentsType = null, $isBinary = false, $SubmitID = null) + { + if ($this->__requestMode != "STREAM") { + + $targetURL = $this->getTargetURL(); + + $http = curl_init($targetURL . $uri); + $header = array(); + + $header[] = 'User-Agent: PHP5 POPBILL SDK'; + if (is_null($CorpNum) == false) { + $header[] = 'Authorization: Bearer ' . $this->getsession_Token($CorpNum); + } + if (is_null($userID) == false) { + $header[] = 'x-pb-userid: ' . $userID; + } + if (is_null($action) == false) { + $header[] = 'X-HTTP-Method-Override: ' . $action; + if ($action == 'BULKISSUE') { + $header[] = 'x-pb-message-digest: ' . base64_encode(hash('sha1', $postdata, true)); + $header[] = 'x-pb-submit-id: ' . $SubmitID; + } + } + + if ($isMultiPart == false) { + if (is_null($contentsType) == false) { + $header[] = 'Content-Type: ' . $contentsType; + } else { + $header[] = 'Content-Type: Application/json'; + } + } else { + if ($isBinary) { + $boundary = md5(time()); + $header[] = "Content-Type: multipart/form-data; boundary=" . $boundary; + $postbody = $this->binaryPostbody($boundary, $postdata); + } else { + // PHP 5.6 이상 CURL 파일전송 처리 + if ((version_compare(PHP_VERSION, '5.5') >= 0)) { + curl_setopt($http, CURLOPT_SAFE_UPLOAD, true); + foreach ($postdata as $key => $value) { + if (strpos($value, '@') === 0) { + $filename = ltrim($value, '@'); + if ($key == 'Filedata') { + $filename = substr($filename, 0, strpos($filename, ';filename')); + } + $displayName = substr($value, strpos($value, 'filename=') + strlen('filename=')); + $postdata[$key] = new CURLFile($filename, null, $displayName); + } + } // end of foreach + } + } + } + + if ($isPost) { + curl_setopt($http, CURLOPT_POST, 1); + if ($isBinary) { + curl_setopt($http, CURLOPT_POSTFIELDS, $postbody); + } else { + curl_setopt($http, CURLOPT_POSTFIELDS, $postdata); + } + } + + curl_setopt($http, CURLOPT_HTTPHEADER, $header); + curl_setopt($http, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($http, CURLOPT_ENCODING, 'gzip,deflate'); + // Connection timeout 설정 + curl_setopt($http, CURLOPT_CONNECTTIMEOUT_MS, 10 * 1000); + // 통합 timeout 설정 + curl_setopt($http, CURLOPT_TIMEOUT_MS, 180 * 1000); + + $responseJson = curl_exec($http); + + // curl Error 추가 + if ($responseJson == false) { + throw new PopbillException(curl_error($http)); + } + + $http_status = curl_getinfo($http, CURLINFO_HTTP_CODE); + + $is_gzip = 0 === mb_strpos($responseJson, "\x1f" . "\x8b" . "\x08"); + + if ($is_gzip) { + $responseJson = $this->Linkhub->gzdecode($responseJson); + } + + $contentType = strtolower(curl_getinfo($http, CURLINFO_CONTENT_TYPE)); + + curl_close($http); + if ($http_status != 200) { + throw new PopbillException($responseJson); + } + + if (0 === mb_strpos($contentType, 'application/pdf')) { + return $responseJson; + } + + return json_decode($responseJson); + } else { + $header = array(); + + $header[] = 'Accept-Encoding: gzip,deflate'; + $header[] = 'Connection: close'; + $header[] = 'User-Agent: PHP5 POPBILL SDK'; + if (is_null($CorpNum) == false) { + $header[] = 'Authorization: Bearer ' . $this->getsession_Token($CorpNum); + } + if (is_null($userID) == false) { + $header[] = 'x-pb-userid: ' . $userID; + } + if (is_null($action) == false) { + $header[] = 'X-HTTP-Method-Override: ' . $action; + if ($action == 'BULKISSUE') { + $header[] = 'x-pb-message-digest: ' . base64_encode(hash('sha1', $postdata, true)); + $header[] = 'x-pb-submit-id: ' . $SubmitID; + } + } + if ($isMultiPart == false) { + if (is_null($contentsType) == false) { + $header[] = 'Content-Type: ' . $contentsType; + } else { + $header[] = 'Content-Type: Application/json'; + } + $postbody = $postdata; + } else { //Process MultipartBody. + $eol = "\r\n"; + $mime_boundary = md5(time()); + $header[] = "Content-Type: multipart/form-data; boundary=" . $mime_boundary . $eol; + if ($isBinary) { + $postbody = $this->binaryPostbody($mime_boundary, $postdata); + } else { + $postbody = ''; + if (array_key_exists('form', $postdata)) { + $postbody .= '--' . $mime_boundary . $eol; + $postbody .= 'content-disposition: form-data; name="form"' . $eol; + $postbody .= 'content-type: Application/json;' . $eol . $eol; + $postbody .= $postdata['form'] . $eol; + foreach ($postdata as $key => $value) { + if (substr($key, 0, 4) == 'file') { + if (substr($value, 0, 1) == '@') { + $value = substr($value, 1); + } + if (file_exists($value) == FALSE) { + throw new PopbillException("전송할 파일이 존재하지 않습니다.", -99999999); + } + $displayName = substr($value, strpos($value, 'filename=') + strlen('filename=')); + $fileContents = file_get_contents($value); + $postbody .= '--' . $mime_boundary . $eol; + $postbody .= "Content-Disposition: form-data; name=\"file\"; filename=\"" .$displayName . "\"" . $eol; + $postbody .= "Content-Type: Application/octet-stream" . $eol . $eol; + $postbody .= $fileContents . $eol; + } + } + } + + if (array_key_exists('Filedata', $postdata)) { + $postbody .= '--' . $mime_boundary . $eol; + if (substr($postdata['Filedata'], 0, 1) == '@') { + $value = substr($postdata['Filedata'], 1); + $splitStr = explode(';', $value); + $path = $splitStr[0]; + $fileName = substr($splitStr[1], 9); + } + if (file_exists($path) == FALSE) { + throw new PopbillException("전송할 파일이 존재하지 않습니다.", -99999999); + } + $fileContents = file_get_contents($path); + $postbody .= 'content-disposition: form-data; name="Filedata"; filename="' . $this->GetBasename($fileName) . '"' . $eol; + $postbody .= 'content-type: Application/octet-stream;' . $eol . $eol; + $postbody .= $fileContents . $eol; + } + $postbody .= '--' . $mime_boundary . '--' . $eol; + } + } + + $params = array( + 'http' => array( + 'ignore_errors' => TRUE, + 'protocol_version' => '1.0', + 'method' => 'GET', + 'timeout' => 180 + ) + ); + + if ($isPost) { + $params['http']['method'] = 'POST'; + $params['http']['content'] = $postbody; + } + + + if ($header !== null) { + $head = ""; + foreach ($header as $h) { + $head = $head . $h . "\r\n"; + } + $params['http']['header'] = substr($head, 0, -2); + } + + $ctx = stream_context_create($params); + + $targetURL = $this->getTargetURL(); + + $response = file_get_contents($targetURL . $uri, false, $ctx); + + $is_gzip = 0 === mb_strpos($response, "\x1f" . "\x8b" . "\x08"); + + if ($is_gzip) { + $response = $this->Linkhub->gzdecode($response); + } + + if ($http_response_header[0] != "HTTP/1.1 200 OK") { + throw new PopbillException($response); + } + + foreach ($http_response_header as $k => $v) { + $t = explode(':', $v, 2); + if (preg_match('/^Content-Type:/i', $v, $out)) { + $contentType = trim($t[1]); + if (0 === mb_strpos($contentType, 'application/pdf')) { + return $response; + } + } + } + + return json_decode($response); + } + } + // build multipart/formdata , multipart 폼데이터 만들기 + protected function binaryPostbody($mime_boundary, $postdata) + { + $postbody = ''; + $eol = "\r\n"; + $postbody .= "--" . $mime_boundary . $eol + . 'Content-Disposition: form-data; name="form"' . $eol . $eol . $postdata['form'] . $eol; + + foreach ($postdata as $key => $value) { + if (substr($key, 0, 4) == 'name') { + $fileName = $value; + } + if (substr($key, 0, 4) == 'file') { + $postbody .= "--" . $mime_boundary . $eol + . 'Content-Disposition: form-data; name="' . 'file' . '"; filename="' . $fileName . '"' . $eol + . 'Content-Type: Application/octetstream' . $eol . $eol; + $postbody .= $value . $eol; + } + } + $postbody .= "--" . $mime_boundary . "--" . $eol; + + return $postbody; + } + + //파일명 추출 + protected function GetBasename($path) + { + $pattern = '/[^\/\\\\]*$/'; + if (preg_match($pattern, $path, $matches)) { + return $matches[0]; + } + throw new PopbillException("파일명 추출에 실패 하였습니다.", -99999999); + } + + // 서비스 URL + private function getTargetURL() + { + if ($this->UseGAIP) { + return ($this->IsTest ? PopbillBase::ServiceURL_GA_TEST : PopbillBase::ServiceURL_GA_REAL); + } else if ($this->UseStaticIP) { + return ($this->IsTest ? PopbillBase::ServiceURL_Static_TEST : PopbillBase::ServiceURL_Static_REAL); + } else { + return ($this->IsTest ? PopbillBase::ServiceURL_TEST : PopbillBase::ServiceURL_REAL); + } + } + + public function isNullOrEmpty($value) + { + if(is_bool($value)) { + return is_null($value) || $value === ''; + } + return is_null($value) || empty($value); + } + + public function isValidDate($date) + { + return preg_match('/(\d{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/', $date); + } + + public function isValidDT($datetime) + { + return preg_match('/(\d{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])(0[0-9]|1[0-9]|2[0-3])([0-5][0-9])([0-5][0-9])/', $datetime); + } +} + +class JoinForm +{ + public $LinkID; + public $CorpNum; + public $CEOName; + public $CorpName; + public $Addr; + public $ZipCode; + public $BizType; + public $BizClass; + public $ContactName; + public $ContactEmail; + public $ContactTEL; + public $contactHP; + public $contactFAX; + public $ID; + public $PWD; + public $Password; +} + +class UseHistoryResult +{ + public $code; + public $total; + public $perPage; + public $pageNum; + public $pageCount; + public $list; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->code) ? $this->code = $jsonInfo->code : null; + isset($jsonInfo->total) ? $this->total = $jsonInfo->total : null; + isset($jsonInfo->perPage) ? $this->perPage = $jsonInfo->perPage : null; + isset($jsonInfo->pageNum) ? $this->pageNum = $jsonInfo->pageNum : null; + isset($jsonInfo->pageCount) ? $this->pageCount = $jsonInfo->pageCount : null; + + $HistoryList = array(); + + for ($i = 0; $i < Count($jsonInfo->list); $i++) { + $HistoryObj = new UseHistory(); + $HistoryObj->fromJsonInfo($jsonInfo->list[$i]); + $HistoryList[$i] = $HistoryObj; + } + $this->list = $HistoryList; + } +} + +class UseHistory +{ + public $itemCode; + public $txType; + public $txPoint; + public $balance; + public $txDT; + public $userID; + public $userName; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->itemCode) ? $this->itemCode = $jsonInfo->itemCode : null; + isset($jsonInfo->txType) ? $this->txType = $jsonInfo->txType : null; + isset($jsonInfo->txPoint) ? $this->txPoint = $jsonInfo->txPoint : null; + isset($jsonInfo->balance) ? $this->balance = $jsonInfo->balance : null; + isset($jsonInfo->txDT) ? $this->txDT = $jsonInfo->txDT : null; + isset($jsonInfo->userID) ? $this->userID = $jsonInfo->userID : null; + isset($jsonInfo->userName) ? $this->userName = $jsonInfo->userName : null; + } +} + +class PaymentHistoryResult +{ + public $code; + public $total; + public $perPage; + public $pageNum; + public $pageCount; + public $list; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->code) ? $this->code = $jsonInfo->code : null; + isset($jsonInfo->total) ? $this->total = $jsonInfo->total : null; + isset($jsonInfo->perPage) ? $this->perPage = $jsonInfo->perPage : null; + isset($jsonInfo->pageNum) ? $this->pageNum = $jsonInfo->pageNum : null; + isset($jsonInfo->pageCount) ? $this->pageCount = $jsonInfo->pageCount : null; + + $HistoryList = array(); + + for ($i = 0; $i < Count($jsonInfo->list); $i++) { + $HistoryObj = new PaymentHistory(); + $HistoryObj->fromJsonInfo($jsonInfo->list[$i]); + $HistoryList[$i] = $HistoryObj; + } + $this->list = $HistoryList; + } +} + +class PaymentHistory +{ + public $productType; + public $productName; + public $settleType; + public $settlerName; + public $settlerEmail; + public $settleCost; + public $settlePoint; + public $settleState; + public $regDT; + public $stateDT; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->productType) ? $this->productType = $jsonInfo->productType : null; + isset($jsonInfo->productName) ? $this->productName = $jsonInfo->productName : null; + isset($jsonInfo->settleType) ? $this->settleType = $jsonInfo->settleType : null; + isset($jsonInfo->settlerName) ? $this->settlerName = $jsonInfo->settlerName : null; + isset($jsonInfo->settlerEmail) ? $this->settlerEmail = $jsonInfo->settlerEmail : null; + isset($jsonInfo->settleCost) ? $this->settleCost = $jsonInfo->settleCost : null; + isset($jsonInfo->settlePoint) ? $this->settlePoint = $jsonInfo->settlePoint : null; + isset($jsonInfo->settleState) ? $this->settleState = $jsonInfo->settleState : null; + isset($jsonInfo->regDT) ? $this->regDT = $jsonInfo->regDT : null; + isset($jsonInfo->stateDT) ? $this->stateDT = $jsonInfo->stateDT : null; + } +} + +class RefundHistoryResult +{ + public $code; + public $total; + public $perPage; + public $pageNum; + public $pageCount; + public $list; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->code) ? $this->code = $jsonInfo->code : null; + isset($jsonInfo->total) ? $this->total = $jsonInfo->total : null; + isset($jsonInfo->perPage) ? $this->perPage = $jsonInfo->perPage : null; + isset($jsonInfo->pageNum) ? $this->pageNum = $jsonInfo->pageNum : null; + isset($jsonInfo->pageCount) ? $this->pageCount = $jsonInfo->pageCount : null; + + $HistoryList = array(); + + for ($i = 0; $i < Count($jsonInfo->list); $i++) { + $HistoryObj = new RefundHistory(); + $HistoryObj->fromJsonInfo($jsonInfo->list[$i]); + $HistoryList[$i] = $HistoryObj; + } + $this->list = $HistoryList; + } +} + +class RefundHistory +{ + public $reqDT; + public $requestPoint; + public $accountBank; + public $accountNum; + public $accountName; + public $state; + public $reason; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->reqDT) ? $this->reqDT = $jsonInfo->reqDT : null; + isset($jsonInfo->requestPoint) ? $this->requestPoint = $jsonInfo->requestPoint : null; + isset($jsonInfo->accountBank) ? $this->accountBank = $jsonInfo->accountBank : null; + isset($jsonInfo->accountNum) ? $this->accountNum = $jsonInfo->accountNum : null; + isset($jsonInfo->accountName) ? $this->accountName = $jsonInfo->accountName : null; + isset($jsonInfo->state) ? $this->state = $jsonInfo->state : null; + isset($jsonInfo->reason) ? $this->reason = $jsonInfo->reason : null; + } +} + +class PaymentForm +{ + public $settlerName; + public $settlerEmail; + public $notifyHP; + public $paymentName; + public $settleCost; +} + +class RefundForm +{ + public $contactname; + public $tel; + public $requestpoint; + public $accountbank; + public $accountnum; + public $accountname; + public $reason; +} + +class CorpInfo +{ + public $ceoname; + public $corpName; + public $addr; + public $bizType; + public $bizClass; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->ceoname) ? $this->ceoname = $jsonInfo->ceoname : null; + isset($jsonInfo->corpName) ? $this->corpName = $jsonInfo->corpName : null; + isset($jsonInfo->addr) ? $this->addr = $jsonInfo->addr : null; + isset($jsonInfo->bizType) ? $this->bizType = $jsonInfo->bizType : null; + isset($jsonInfo->bizClass) ? $this->bizClass = $jsonInfo->bizClass : null; + } +} + +class ContactInfo +{ + public $id; + public $pwd; + public $Password; + public $email; + public $hp; + public $personName; + public $searchAllAllowYN; + public $searchRole; + public $tel; + public $fax; + public $mgrYN; + public $regDT; + public $state; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->id) ? $this->id = $jsonInfo->id : null; + isset($jsonInfo->email) ? $this->email = $jsonInfo->email : null; + isset($jsonInfo->hp) ? $this->hp = $jsonInfo->hp : null; + isset($jsonInfo->personName) ? $this->personName = $jsonInfo->personName : null; + isset($jsonInfo->searchAllAllowYN) ? $this->searchAllAllowYN = $jsonInfo->searchAllAllowYN : null; + isset($jsonInfo->searchRole) ? $this->searchRole = $jsonInfo->searchRole : null; + isset($jsonInfo->tel) ? $this->tel = $jsonInfo->tel : null; + isset($jsonInfo->fax) ? $this->fax = $jsonInfo->fax : null; + isset($jsonInfo->mgrYN) ? $this->mgrYN = $jsonInfo->mgrYN : null; + isset($jsonInfo->regDT) ? $this->regDT = $jsonInfo->regDT : null; + isset($jsonInfo->state) ? $this->state = $jsonInfo->state : null; + } +} + +class ChargeInfo +{ + public $unitCost; + public $chargeMethod; + public $rateSystem; + + public function fromJsonInfo($jsonInfo) + { + isset($jsonInfo->unitCost) ? $this->unitCost = $jsonInfo->unitCost : null; + isset($jsonInfo->chargeMethod) ? $this->chargeMethod = $jsonInfo->chargeMethod : null; + isset($jsonInfo->rateSystem) ? $this->rateSystem = $jsonInfo->rateSystem : null; + } +} + +class PopbillException extends Exception +{ + public function __construct($response, $code = -99999999, Exception $previous = null) + { + $Err = json_decode($response); + if (is_null($Err)) { + parent::__construct($response, $code); + } else { + parent::__construct($Err->message, $Err->code); + } + } + + public function __toString() + { + return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; + } +} diff --git a/plugin/kakao5/_common.php b/plugin/kakao5/_common.php new file mode 100644 index 000000000..469a85969 --- /dev/null +++ b/plugin/kakao5/_common.php @@ -0,0 +1,7 @@ + $check_result['error']))); +} else { + $charge_url = get_popbill_point_URL(); // 포인트 충전 팝업 URL + die(json_encode(array('balance' => $check_result['balance'], 'charge_url' => $charge_url))); +} \ No newline at end of file diff --git a/plugin/kakao5/ajax.get_url.php b/plugin/kakao5/ajax.get_url.php new file mode 100644 index 000000000..79a9f9955 --- /dev/null +++ b/plugin/kakao5/ajax.get_url.php @@ -0,0 +1,38 @@ + $url, 'width' => $width, 'height' => $height))); \ No newline at end of file diff --git a/plugin/kakao5/kakao5.lib.php b/plugin/kakao5/kakao5.lib.php new file mode 100644 index 000000000..0d4d78d53 --- /dev/null +++ b/plugin/kakao5/kakao5.lib.php @@ -0,0 +1,449 @@ + false, 'msg' => '알림톡 사용이 설정되어 있지 않습니다.'); + } + + // 프리셋 코드로 프리셋 정보 확인 + $preset_info = get_alimtalk_preset_info($preset_code); + if (isset($preset_info['error'])) { + return array('success' => false, 'msg' => $preset_info['error'], 'data' => $preset_info); + } + $template_code = $preset_info['template_code']; // 템플릿 코드 + + // 수신자 정리 (전화번호 숫자만) + $receiver_hp = preg_replace('/[^0-9]/', '', $recipient['rcv'] ?? ''); + $receiver_nm = $recipient['rcvnm'] ?? ''; + + // 수신자 정보 배열 구성 + $messages = [['rcv' => $receiver_hp, 'rcvnm' => $receiver_nm]]; + + // 주문내역에서 mb_id 조회 + if (empty($conditions['mb_id']) && !empty($conditions['od_id'])) { + $sql = "SELECT mb_id FROM {$g5['g5_shop_order_table']} WHERE od_id = '" . sql_escape_string($conditions['od_id']) . "' LIMIT 1"; + $row = sql_fetch($sql); + if ($row && !empty($row['mb_id'])) { + $conditions['mb_id'] = $row['mb_id']; + } else { + $conditions['mb_id'] = $member['mb_id'] ?? 'GUEST'; + } + } + + // 전송요청번호 생성 + $request_num = generate_alimtalk_request_id($conditions['mb_id'], $preset_code); + + // 전송 내역 초기 저장 + $history_id = save_alimtalk_history($preset_info['preset_id'], $template_code, $preset_info['alt_send'], $request_num, $receiver_nm, $receiver_hp, $conditions['mb_id']); + + // 템플릿 정보 조회 + $full_template_info = ''; + if($config['cf_kakaotalk_use'] === 'popbill'){ // 팝빌 + $full_template_info = get_popbill_template_info($template_code); + } + + // 템플릿 정보를 못 불러 올 경우 - 발송 취소 + if (is_array($full_template_info) && isset($full_template_info['error'])) { + // 탬플릿 정보 조회 실패: 알림톡 전송내역 업데이트 + $messages = "템플릿 정보 조회 실패: ".$full_template_info['error']; + update_alimtalk_history($history_id, ['ph_log' => $messages]); + return array('success' => false, 'msg' => $messages, 'data' => $full_template_info); + } + + // 템플릿 내용 변수 치환 + $content = replace_alimtalk_content_vars($full_template_info->template, $conditions); + + // 버튼 링크 치환 + $buttons = set_alimtalk_button_links($full_template_info->btns, $conditions); + + try { + // 알림톡 전송 정보 + $data = [ + 'template_code' => $template_code, + 'sender_hp' => $sender_hp, + 'content' => $content, + 'alt_content' => $content, + 'alt_send' => ($preset_info['alt_send'] == '1') ? 'C' : null, + 'messages' => $messages, + 'reserveDT' => null, + 'request_num' => $request_num, + 'buttons' => $buttons, + 'alt_subject' => $preset_info['preset_name'] + ]; + + $receipt_num = ''; + if ($config['cf_kakaotalk_use'] === 'popbill') { // 팝빌 전송 + $receipt_num = send_popbill_alimtalk($data); + } + + // 전송 결과 처리 + if ((is_array($receipt_num) && isset($receipt_num['error'])) || empty($receipt_num)) { + // 전송 실패: 알림톡 전송내역 업데이트 + $error_message = is_array($receipt_num) && isset($receipt_num['error']) ? $receipt_num['error'] : '알림톡 전송 결과가 비어 있습니다.'; + $messages = '알림톡 전송 실패하였습니다.\n' . $error_message; + update_alimtalk_history($history_id, ['ph_log' => $messages, 'ph_state' => 2]); + return array('success' => false, 'msg' => $messages, 'code' => (is_array($receipt_num) && isset($receipt_num['code']) ? $receipt_num['code'] : null)); + } else { + // 전송 성공: 알림톡 전송내역 업데이트 + $messages = '알림톡이 정상적으로 전송되었습니다.'; + update_alimtalk_history($history_id, ['ph_receipt_num' => $receipt_num, 'ph_state' => 1, 'ph_log' => $content]); + return array('success' => true, 'msg' => $messages, 'receipt_num' => $receipt_num); + } + } catch (Exception $e) { + // 전송 오류: 알림톡 전송내역 업데이트 + $messages = '알림톡 전송 중 오류가 발생하였습니다.\n' . $e->getMessage(); + update_alimtalk_history($history_id, ['ph_log' => $messages, 'ph_state' => 2]); + return array('success' => false, 'msg' => $messages, 'code' => $e->getCode()); + } +} + +/** + * 프리셋 코드로 프리셋 정보 확인 + */ +function get_alimtalk_preset_info($preset_code) +{ + global $g5; + + if (empty($preset_code)) { + return array('error' => '프리셋 코드가 입력되지 않았습니다.'); + } + + // 프리셋 코드로 프리셋 정보 조회 + $sql = "SELECT * FROM {$g5['kakao5_preset_table']} WHERE kp_preset_code = '" . sql_escape_string($preset_code) . "'"; + $preset = sql_fetch($sql); + + if (!$preset) { + return array('error' => '해당 프리셋 코드(' . $preset_code . ')가 존재하지 않습니다.'); + } + + // 활성화 상태 확인 + if ($preset['kp_active'] != '1') { + return array('error' => '프리셋(' . $preset['kp_preset_name'] . ')이 비활성화되어 있습니다.'); + } + + // 템플릿 코드 확인 + if (empty($preset['kp_template_name'])) { + return array('error' => '프리셋(' . $preset['kp_preset_name'] . ')에 템플릿이 설정되지 않았습니다.'); + } + + // 모든 조건을 만족하면 프리셋 정보 반환 + return array( + 'success' => true, + 'preset' => $preset, + 'preset_id' => $preset['kp_id'], + 'preset_name' => $preset['kp_preset_name'], + 'preset_code' => $preset['kp_preset_code'], + 'template_code' => $preset['kp_template_name'], + 'alt_send' => $preset['kp_alt_send'], + 'type' => $preset['kp_type'] + ); +} + +/** + * 템플릿 내용 변수 치환 + */ +function replace_alimtalk_content_vars($content, $conditions = []) +{ + global $g5, $kakao5_preset_variable_list; + + $replacements = []; + + // 1. 템플릿에서 변수 추출 + if (!preg_match_all('/#\{(.*?)\}/', $content, $matches) || empty($matches[1])) { + return $content; + } + $found_vars = array_unique($matches[1]); + + // 2. 변수 정의 맵 캐싱 + static $var_info_map = null; + if ($var_info_map === null) { + $var_info_map = []; + foreach ($kakao5_preset_variable_list as $category) { + foreach ($category['variables'] as $var) { + if (preg_match('/#\{(.*?)\}/', $var['name'], $match) && isset($match[1])) { + $var_info_map[$match[1]] = $var; + } + } + } + } + + // 3. 쿼리 맵 구성 및 치환값 우선 결정 + $query_map = []; + $var_to_query = []; + foreach ($found_vars as $var_name) { + $replacement_key = "#{{$var_name}}"; + + // 1순위: 변수 정의가 있고, $conditions에 column값이 있으면 바로 치환 + if (isset($var_info_map[$var_name])) { + $var = $var_info_map[$var_name]; + $column = $var['column']; + $table = $g5[$var['table'] ?? '']; + $condition_key = $var['condition_key'] ?? ''; + + if (isset($conditions[$column])) { + $replacements[$replacement_key] = $conditions[$column]; + continue; + } + + // 테이블명에 게시판과 같이 뒤에 붙는 변수가 있을 경우 사용 + $table_placeholder = isset($var['table_placeholder']) ? trim($var['table_placeholder'], '{}') : ''; + if ($table_placeholder && !empty($conditions[$table_placeholder])) { + $table .= $conditions[$table_placeholder]; + } + + // 2순위: 변수정의에 따라 DB 조회 필요 + $where = ''; + if(!empty($condition_key)) { + if (!isset($conditions[$condition_key])) { + $replacements[$replacement_key] = ''; + continue; + } + $cond_val = sql_escape_string($conditions[$condition_key]); + $where = "{$condition_key} = '{$cond_val}'"; + } + $query_key = "{$table}|{$where}"; + + if (!isset($query_map[$query_key])) { + $query_map[$query_key] = [ + 'table' => $table, + 'where' => $where, + 'columns' => [], + 'is_price' => $var['is_price'] ?? false, + ]; + } + $query_map[$query_key]['columns'][$var_name] = $column; + $var_to_query[$var_name] = $query_key; + continue; + } + + // 4. 조건값이 없으면 조회 불가 → 빈값 + $replacements[$replacement_key] = ''; + } + + // 4. DB 조회 (필요한 경우만) + $query_results = []; + foreach ($query_map as $query_key => $info) { + $table = $info['table']; + $where = $info['where']; + $columns = array_unique(array_values($info['columns'])); + $column_sql = implode(',', $columns); + + $sql = "SELECT {$column_sql} FROM {$table}"; + if (!empty($where)) { + $sql .= " WHERE {$where}"; + } + $sql .= " LIMIT 1"; + $query_results[$query_key] = sql_fetch($sql) ?: []; + } + + // 5. DB 결과로 치환값 보완 + foreach ($found_vars as $var_name) { + $replacement_key = "#{{$var_name}}"; + + if (isset($replacements[$replacement_key])) continue; // 이미 치환된 값 있음 + + if (isset($var_to_query[$var_name])) { + $query_key = $var_to_query[$var_name]; + $column = $query_map[$query_key]['columns'][$var_name]; + $value = $query_results[$query_key][$column] ?? ''; + // is_price일경우 숫자(정수 또는 실수)라면 number_format 적용 + if (isset($var_info_map[$var_name]['is_price']) && $var_info_map[$var_name]['is_price'] && is_numeric($value) && $value !== '') { + $value = number_format($value); + } + $replacements[$replacement_key] = $value; + } else { + $replacements[$replacement_key] = ''; + } + } + + return strtr($content, $replacements); +} + +/** + * 버튼 링크 치환 + */ +function set_alimtalk_button_links($btns, $conditions = []) +{ + // [정의] $kakao5_preset_button_links - extend/kakao5.extend.php + global $kakao5_preset_button_links; + + $buttons = []; + if (!empty($btns)) { + foreach ($btns as $idx => $btn) { + // 버튼의 u1, u2에 대해 #{...} 플레이스홀더를 찾아 알맞은 URL로 치환 + foreach (['u1', 'u2'] as $field) { + if (isset($btn->$field)) { + if (preg_match('/#\{(.*?)\}/', $btn->$field, $match)) { + $placeholder = $match[0]; + if (isset($kakao5_preset_button_links[$placeholder])) { + $url = $kakao5_preset_button_links[$placeholder]['url']; + // URL 내 {변수} 치환 + if (preg_match_all('/\{(.*?)\}/', $url, $url_vars)) { + foreach ($url_vars[1] as $var_name) { + // 치환할 값이 없으면 빈 문자열 처리 + $replace_val = $conditions[$var_name] ?? ''; + + // URL로 쓰일 수 있으므로 안전하게 인코딩 + $url = str_replace('{' . $var_name . '}', urlencode($replace_val), $url); + } + } + $btn->$field = $url; + } + } + } + } + $buttons[] = (array)$btn; + } + } + + return $buttons; +} + +/** + * 전송요청번호 생성 (고유성 보장) + */ +function generate_alimtalk_request_id($mb_id, $preset_code) +{ + $prefix = substr($preset_code, 0, 1); // 사용자 구분 + $mb_hash = substr(md5($mb_id), 0, 4); // mb_id 해시 4자리 + $dateTimeStr = date('ymdHis') . sprintf('%03d', (microtime(true) * 1000) % 1000); // 날짜(초) + 마이크로초(밀리초 3자리) + $requestNum = "{$prefix}{$mb_hash}{$dateTimeStr}"; + + return substr($requestNum, 0, 20); +} + +/** + * 알림톡 프리셋 전송 이력 저장 + */ +function save_alimtalk_history($preset_id, $template_code, $alt_send, $request_num, $rcvnm, $rcv, $mb_id = '') +{ + global $g5; + + $sql = "INSERT INTO {$g5['kakao5_preset_history_table']} + (mb_id, kp_id, ph_rcvnm, ph_rcv, ph_template_code, ph_alt_send, ph_request_num, ph_send_datetime, ph_state) + VALUES + ('" . sql_escape_string($mb_id) . "', + '" . (int)$preset_id . "', + '" . sql_escape_string($rcvnm) . "', + '" . sql_escape_string($rcv) . "', + '" . sql_escape_string($template_code) . "', + '" . sql_escape_string($alt_send) . "', + '" . sql_escape_string($request_num) . "', + NOW(), + 0)"; + + $result = sql_query($sql); + + if ($result) { + return sql_insert_id(); + } + + return false; +} + +/** + * 전송내역 업데이트 + */ +function update_alimtalk_history($history_id, $update_data = []) +{ + global $g5; + + if (!$history_id) { + return false; + } + + $set_arr = []; + + // update_data가 들어오면 해당 값들로 업데이트 + if (!empty($update_data) && is_array($update_data)) { + foreach ($update_data as $key => $val) { + $set_arr[] = sql_escape_string($key) . " = '" . sql_escape_string($val) . "'"; + } + } + + // 업데이트할 내용이 없음 + if (empty($set_arr)) { + return false; + } + + $sql = "UPDATE {$g5['kakao5_preset_history_table']} + SET " . implode(', ', $set_arr) . " + WHERE ph_id = '" . (int)$history_id . "'"; + + return sql_query($sql); +} + +/** + * 알림톡용 상품명 생성 (2개 이상일 경우 '외 N건' 추가) + */ +function get_alimtalk_cart_item_name($od_id) +{ + global $g5; + + $sql = "SELECT it_name FROM {$g5['g5_shop_cart_table']} WHERE od_id = '" . sql_escape_string($od_id) . "'"; + $res = sql_query($sql); + + $names = array(); + while ($row = sql_fetch_array($res)) $names[] = $row['it_name']; + if (!$names) return ''; + + return $names[0] . ($names[1] ? ' 외 ' . (count($names)-1) . '건' : ''); +} + +/** + * 관리자 정보로 알림톡 발송 + * + * @param string $tpl 템플릿 코드 (예: AD-OR01) + * @param string $type 관리자 유형 (super|group|board) + * @param array $conditions 치환 변수 배열 + * @param array $otherTypes 발송 중복 확인용 관리자 유형 ['super', 'group', 'board'] + * @return array|false send_alimtalk_preset 반환값 또는 false + */ +function send_admin_alimtalk($tpl, $type = 'super', $conditions = [], $otherTypes = []) +{ + $admin = get_admin($type); + + // 연락처가 없으면 발송하지 않음 + if (empty($admin['mb_hp'])) return false; + + // 다른 관리자 정보가 겹치는 게 있으면 발송 안함 + if(!empty($otherTypes)){ + foreach($otherTypes as $otherType){ + // 자기 자신은 비교하지 않음 + if ($otherType == $type) continue; + + $other = get_admin($otherType, 'mb_id, mb_hp'); + if (empty($other)) continue; + + // 다른 역할과 동일 인물(또는 동일 번호)이라면 발송하지 않음 + $sameId = !empty($admin['mb_id']) && !empty($other['mb_id']) && $admin['mb_id'] == $other['mb_id']; + $sameHp = !empty($other['mb_hp']) && $admin['mb_hp'] == $other['mb_hp']; + + if ($sameId || $sameHp) return false; + } + } + + return send_alimtalk_preset( + $tpl, + [ + 'rcv' => $admin['mb_hp'], + 'rcvnm' => $admin['mb_name'] ?? '' + ], + $conditions + ); +} \ No newline at end of file diff --git a/plugin/kakao5/kakao5_popbill.lib.php b/plugin/kakao5/kakao5_popbill.lib.php new file mode 100644 index 000000000..6c33c5c18 --- /dev/null +++ b/plugin/kakao5/kakao5_popbill.lib.php @@ -0,0 +1,238 @@ +IsTest(G5_KAKAO5_IS_TEST); + + return $KakaoService; +} + +/** + * 팝빌 정보 확인 + */ +function get_popbill_service_info(){ + global $userID, $corpnum; + + if (empty($userID) || strlen($userID) < 4) { + return array('error' => '연결 실패: 회원아이디가 없거나 올바르지 않습니다. 회원아이디를 확인해주세요.'); + } + + try { + $KakaoService = get_kakao_service_instance(); + $corpInfo = $KakaoService->GetCorpInfo($corpnum, $userID); + $balance = $KakaoService->GetBalance($corpnum); + + if ($balance === false || $balance < 0) { + return array('error' => '팝빌 API 연결에 실패했습니다. 설정값을 확인해주세요.'); + } + + return array('success' => true, 'balance' => $balance, 'corpInfo' => $corpInfo); + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 팝빌 템플릿 목록 조회 + */ +function get_popbill_template_list(){ + global $corpnum; + + try { + $KakaoService = get_kakao_service_instance(); + $templates = $KakaoService->ListATSTemplate($corpnum); + + if (empty($templates)) { + return array('error' => '템플릿 목록을 가져올 수 없습니다.'); + } + + return $templates; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 포인트 충전 팝업 URL + */ +function get_popbill_point_URL(){ + global $corpnum, $userID; + + try { + $KakaoService = get_kakao_service_instance(); + $url = $KakaoService->GetChargeURL($corpnum, $userID); + + if (empty($url)) { + return array('error' => '포인트 충전 URL을 가져올 수 없습니다.'); + } + + return $url; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 템플릿 정보 확인 + */ +function get_popbill_template_info($template_code, $type = ''){ + global $corpnum; + + try { + $KakaoService = get_kakao_service_instance(); + $info = $KakaoService->GetATSTemplate($corpnum, $template_code); + + if (empty($info)) { + return array('error' => '해당 템플릿 정보를 가져올 수 없습니다.'); + } + + if ($type) { + if (is_object($info) && isset($info->$type)) { + return $info->$type; + } else if (is_array($info) && isset($info[$type])) { + return $info[$type]; + } else { + return array('error' => '요청하신 타입의 정보가 없습니다.'); + } + } + + return $info; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 템플릿 관리 팝업 URL + */ +function get_popbill_template_manage_URL(){ + global $corpnum, $userID; + + try { + $KakaoService = get_kakao_service_instance(); + $url = $KakaoService->GetATSTemplateMgtURL($corpnum, $userID); + + if (empty($url)) { + return array('error' => '템플릿관리 URL을 가져올 수 없습니다.'); + } + + return $url; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 플러스친구 관리 팝업 URL + */ +function get_popbill_plusfriend_manage_URL(){ + global $corpnum, $userID; + try { + $KakaoService = get_kakao_service_instance(); + $url = $KakaoService->GetPlusFriendMgtURL($corpnum, $userID); + if (empty($url)) { + return array('error' => '플러스친구 관리 URL을 가져올 수 없습니다.'); + } + return $url; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 전송내역 관리 팝업 URL + */ +function get_popbill_send_manage_URL(){ + global $corpnum, $userID; + try { + $KakaoService = get_kakao_service_instance(); + $url = $KakaoService->GetSentListURL($corpnum, $userID); + if (empty($url)) { + return array('error' => '전송내역 URL을 가져올 수 없습니다.'); + } + return $url; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/** + * 발신번호 등록 팝업 URL + */ +function get_popbill_sender_number_URL(){ + global $corpnum, $userID; + try { + $KakaoService = get_kakao_service_instance(); + $url = $KakaoService->GetSenderNumberMgtURL($corpnum, $userID); + if (empty($url)) { + return array('error' => '발신번호 등록 URL을 가져올 수 없습니다.'); + } + return $url; + } catch (Exception $e) { + return array('error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), 'code' => $e->getCode()); + } +} + +/************************************************************************* +** +** 알림톡 : 팝빌 카카오톡 발송 +** +*************************************************************************/ +/** + * 팝빌 알림톡 전송 함수 (SendATS 파라미터를 배열에서 바로 전달, 예외처리 포함) + */ +function send_popbill_alimtalk($params = []){ + global $corpnum, $userID; + + try { + $KakaoService = get_kakao_service_instance(); + + $receipt_num = $KakaoService->SendATS( + $corpnum, + $params['template_code'], + $params['sender_hp'], + $params['content'], + isset($params['alt_content']) ? $params['alt_content'] : '', + isset($params['alt_send']) ? $params['alt_send'] : null, + $params['messages'], + isset($params['reserveDT']) ? $params['reserveDT'] : null, + $userID, + isset($params['request_num']) ? $params['request_num'] : null, + isset($params['buttons']) ? $params['buttons'] : null, + isset($params['alt_subject']) ? $params['alt_subject'] : '' + ); + + if ($receipt_num) { + return $receipt_num; + } else { + return [ 'error' => '알림톡 전송에 실패했습니다.' ]; + } + } catch (Exception $e) { + return [ + 'error' => '팝빌 서비스 처리 중 오류가 발생했습니다: ' . $e->getMessage(), + 'code' => $e->getCode() + ]; + } +} \ No newline at end of file diff --git a/plugin/social/register_member_update.php b/plugin/social/register_member_update.php index 2c61e2a8b..801060f76 100644 --- a/plugin/social/register_member_update.php +++ b/plugin/social/register_member_update.php @@ -97,6 +97,44 @@ if( defined('G5_SOCIAL_CERTIFY_MAIL') && G5_SOCIAL_CERTIFY_MAIL && $config['cf_u $mb_mailling = (isset($_POST['mb_mailling']) && $_POST['mb_mailling']) ? 1 : 0; //회원 정보 공개 $mb_open = (isset($_POST['mb_open']) && $_POST['mb_open']) ? 1 : 0; +//회원 SMS 동의 +$mb_sms = isset($_POST['mb_sms']) ? trim($_POST['mb_sms']) : "0"; +//마케팅 목적의 개인정보 수집 및 이용 동의 +$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? trim($_POST['mb_marketing_agree']) : "0"; +//개인정보 제3자 제공 동의 +$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? trim($_POST['mb_thirdparty_agree']) : "0"; + +$agree_items = []; +$sql_agree = ""; +// 마케팅 목적의 개인정보 수집 및 이용 +if ($mb_marketing_agree == 1) { + $sql_agree .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(동의)"; +} + +// 광고성 이메일 수신 +if ($mb_mailling == 1) { + $sql_agree .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 이메일 수신(동의)"; +} + +// 광고성 SMS/카카오톡 수신 +if ($mb_sms == 1) { + $sql_agree .= " , mb_sms_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "광고성 SMS/카카오톡 수신(동의)"; +} + +// 개인정보 제3자 제공 +if ($mb_thirdparty_agree == 1) { + $sql_agree .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' "; + $agree_items[] = "개인정보 제3자 제공(동의)"; +} + +// 동의 로그 추가 +if (!empty($agree_items)) { + $agree_log = "[".G5_TIME_YMDHIS.", ". $provider_name ." 회원가입] " . implode(' | ', $agree_items) . "\n"; + $sql_agree .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))"; +} //=============================================================== // 본인확인 @@ -164,9 +202,12 @@ $sql = " insert into {$g5['member_table']} mb_level = '{$config['cf_register_level']}', mb_login_ip = '{$_SERVER['REMOTE_ADDR']}', mb_mailling = '{$mb_mailling}', - mb_sms = '0', + mb_sms = '{$mb_sms}', mb_open = '{$mb_open}', - mb_open_date = '".G5_TIME_YMD."' + mb_open_date = '".G5_TIME_YMD."', + mb_marketing_agree = '{$mb_marketing_agree}', + mb_thirdparty_agree = '{$mb_thirdparty_agree}' + {$sql_agree} {$sql_certify} "; $result = sql_query($sql, false); @@ -290,6 +331,18 @@ if($result) { } } + // 알림톡 발송 BEGIN: 회원가입 (CU-MB01/AD-MB01) ------------------------------------- + include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); + $conditions = ['mb_id' => $mb_id]; // 변수 치환 정보 + + $ad_atk = send_admin_alimtalk('AD-MB01', 'super', $conditions); // 관리자 + + // 회원 - 휴대폰 번호가 있을 경우만 + if (!empty($mb_hp)) { + $cu_atk = send_alimtalk_preset('CU-MB01', ['rcv' => $mb_hp, 'rcvnm' => $mb_name], $conditions); // 회원 + } + // 알림톡 발송 END -------------------------------------------------------- + // 사용자 코드 실행 @include_once ($member_skin_path.'/register_form_update.tail.skin.php'); diff --git a/shop/itemstocksms.php b/shop/itemstocksms.php index f62191706..961fcbda2 100644 --- a/shop/itemstocksms.php +++ b/shop/itemstocksms.php @@ -3,7 +3,7 @@ include_once('./_common.php'); $it_id = isset($_REQUEST['it_id']) ? safe_replace_regex($_REQUEST['it_id'], 'it_id') : ''; -$g5['title'] = '상품 재입고 알림 (SMS)'; +$g5['title'] = '상품 재입고 알림'; include_once(G5_PATH.'/head.sub.php'); // 상품정보 @@ -13,7 +13,7 @@ if(! (isset($it['it_id']) && $it['it_id'])) alert_close('상품정보가 존재하지 않습니다.'); if(!$it['it_soldout'] || !$it['it_stock_sms']) - alert_close('재입고SMS 알림을 신청할 수 없는 상품입니다.'); + alert_close('재입고 알림을 신청할 수 없는 상품입니다.'); // add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨 add_stylesheet('', 0); @@ -35,6 +35,9 @@ if (G5_IS_MOBILE) {
  • +
  • + * 재입고 알림은 SMS 문자 또는 카카오 알림톡으로 발송됩니다. +
  • @@ -66,7 +69,7 @@ function fstocksms_submit(f) return false; } - if(confirm("재입고SMS 알림 요청을 등록하시겠습니까?")) { + if(confirm("재입고 알림 요청을 등록하시겠습니까?")) { return true; } else { window.close(); diff --git a/shop/itemstocksmsupdate.php b/shop/itemstocksmsupdate.php index 0735d7792..53481bf38 100644 --- a/shop/itemstocksmsupdate.php +++ b/shop/itemstocksmsupdate.php @@ -11,7 +11,7 @@ if(! (isset($it['it_id']) && $it['it_id'])) alert_close('상품정보가 존재하지 않습니다.'); if(!$it['it_soldout'] || !$it['it_stock_sms']) - alert_close('재입고SMS 알림을 신청할 수 없는 상품입니다.'); + alert_close('재입고 알림을 신청할 수 없는 상품입니다.'); $ss_hp = hyphen_hp_number($ss_hp); if(!$ss_hp) @@ -39,4 +39,4 @@ $sql = " insert into {$g5['g5_shop_item_stocksms_table']} ss_datetime = '".G5_TIME_YMDHIS."' "; sql_query($sql); -alert_close('재입고SMS 알림 요청 등록이 완료됐습니다.'); \ No newline at end of file +alert_close('재입고 알림 요청 등록이 완료됐습니다.'); \ No newline at end of file diff --git a/shop/orderformupdate.php b/shop/orderformupdate.php index bcd35f646..4e84ba4a8 100644 --- a/shop/orderformupdate.php +++ b/shop/orderformupdate.php @@ -1,6 +1,7 @@ 고객님의 주문 정보를 처리하는 중 오류가 발생해서 주문이 완료되지 않았습니다.

    '.strtoupper($od_pg).'를 이용한 전자결제(신용카드, 계좌이체, 가상계좌 등)은 자동 취소되었습니다.'); } @@ -896,6 +900,19 @@ if($config['cf_sms_use'] && ($default['de_sms_use2'] || $default['de_sms_use3']) } // SMS END -------------------------------------------------------- +// 알림톡 발송 BEGIN: 주문완료[CU-OR01/AD-OR01] / 무통장입금 요청[CU-OR02/AD-OR02] ------------------------- +if($od_settle_case == '무통장' && $od_misu > 0) { + // 무통장 입금일 경우 알림톡 발송 : 주문금액 - 미결제액 + $conditions = ['od_id' => $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str, 'od_receipt_price' => number_format($od_misu)]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR02', ['rcv' => $od_hp ?: $od_tel, 'rcvnm' => $od_name], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR02', 'super', $conditions); // 관리자 +}else{ + // 주문 완료 + $conditions = ['od_id' => $od_id, 'od_name' => $od_name, 'it_name' => $it_name_str, 'od_receipt_price' => number_format($i_price)]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR01', ['rcv' => $od_hp ?: $od_tel, 'rcvnm' => $od_name], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR01', 'super', $conditions); // 관리자 +} +// 알림톡 발송 END --------------------------------------------------------------------------------------------- // orderview 에서 사용하기 위해 session에 넣고 $uid = md5($od_id.G5_TIME_YMDHIS.$REMOTE_ADDR); diff --git a/shop/orderinquirycancel.php b/shop/orderinquirycancel.php index aefa943a1..dda82c10b 100644 --- a/shop/orderinquirycancel.php +++ b/shop/orderinquirycancel.php @@ -1,5 +1,6 @@ 0) insert_point($member['mb_id'], $od['od_receipt_point'], "주문번호 $od_id 본인 취소"); +// 알림톡 발송 BEGIN: 고객주문취소완료(CU-OR04/AD-OR04) ------------------------------------- +$it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명 + +// 총구매액 +$tot_price = $od['od_cart_price'] + $od['od_send_cost'] + $od['od_send_cost2'] - $od['od_cart_coupon'] - $od['od_coupon'] - $od['od_send_coupon'] - $od['od_cancel_price']; + +$conditions = ['od_id' => $od_id, 'cancel_memo' => $cancel_memo, 'it_name' => $it_name_str, 'od_receipt_price' => number_format($tot_price)]; // 변수 치환 정보 + +$cu_atk = send_alimtalk_preset('CU-OR04', ['rcv' => $od['od_hp'] ?: $od['od_tel'], 'rcvnm' => $od['od_name']], $conditions); // 회원 +$ad_atk = send_admin_alimtalk('AD-OR04', 'super', $conditions); // 관리자 +// 알림톡 발송 END ------------------------------------------------------------- + goto_url(G5_SHOP_URL."/orderinquiryview.php?od_id=$od_id&uid=$uid"); \ No newline at end of file diff --git a/shop/settle_inicis_common.php b/shop/settle_inicis_common.php index e6069e049..e072de4a1 100644 --- a/shop/settle_inicis_common.php +++ b/shop/settle_inicis_common.php @@ -1,5 +1,6 @@ $od_id, 'od_name' => $od_result['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od_result['od_hp'] ?: $od_result['od_tel'], 'rcvnm' => $od_result['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR03', 'super', $conditions); // 관리자 + } + // 알림톡 발송 END ------------------------------------------------------- } if($INIpayLog) { diff --git a/shop/settle_kcp_common.php b/shop/settle_kcp_common.php index 523a716dd..d3b727d7a 100644 --- a/shop/settle_kcp_common.php +++ b/shop/settle_kcp_common.php @@ -2,6 +2,7 @@ include_once('./_common.php'); include_once(G5_LIB_PATH.'/etc.lib.php'); include_once(G5_LIB_PATH.'/mailer.lib.php'); +include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); /*------------------------------------------------------------------------------ ※ KCP 에서 가맹점의 결과처리 페이지로 데이터를 전송할 때에, 아래와 같은 @@ -211,6 +212,19 @@ if(!$default['de_card_test']) { sql_query($sql, FALSE); } } + + // 알림톡 발송 BEGIN: 입금완료(CU-OR03/AD-OR03) ------------------------------ + // 주문정보 체크 + $sql = "select od_name, od_hp, od_tel from {$g5['g5_shop_order_table']} where od_id = '$od_id' limit 1"; + $od_result = sql_fetch($sql); + $it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명 + + if (isset($od_result)) { + $conditions = ['od_id' => $od_id, 'od_name' => $od_result['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od_result['od_hp'] ?: $od_result['od_tel'], 'rcvnm' => $od_result['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR03', 'super', $conditions); // 관리자 + } + // 알림톡 발송 END -------------------------------------------------------- } /* = -------------------------------------------------------------------------- = */ diff --git a/shop/settle_lg_common.php b/shop/settle_lg_common.php index 21d6662c3..6230285f2 100644 --- a/shop/settle_lg_common.php +++ b/shop/settle_lg_common.php @@ -1,5 +1,6 @@ $od_id, 'od_name' => $od_result['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od_result['od_hp'] ?: $od_result['od_tel'], 'rcvnm' => $od_result['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR03', 'super', $conditions); // 관리자 + } + // 알림톡 발송 END -------------------------------------------------------- } //if( 무통장 입금 성공 상점처리결과 성공 ) diff --git a/shop/settle_nicepay_common.php b/shop/settle_nicepay_common.php index 72b543583..e44648573 100644 --- a/shop/settle_nicepay_common.php +++ b/shop/settle_nicepay_common.php @@ -1,5 +1,6 @@ $od_id, 'od_name' => $od_result['od_name'], 'it_name' => $it_name_str]; // 변수 치환 정보 + $cu_atk = send_alimtalk_preset('CU-OR03', ['rcv' => $od_result['od_hp'] ?: $od_result['od_tel'], 'rcvnm' => $od_result['od_name']], $conditions); // 회원 + $ad_atk = send_admin_alimtalk('AD-OR03', 'super', $conditions); // 관리자 + } + // 알림톡 발송 END -------------------------------------------------------- } if($NICEPAY_payLog) { diff --git a/skin/member/basic/consent_modal.inc.php b/skin/member/basic/consent_modal.inc.php new file mode 100644 index 000000000..2d9d63a5c --- /dev/null +++ b/skin/member/basic/consent_modal.inc.php @@ -0,0 +1,85 @@ + + +

    +
    +
    +

    안내

    +
    +
    +
    + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/skin/member/basic/register.skin.php b/skin/member/basic/register.skin.php index 662c8da40..f337fe9ce 100644 --- a/skin/member/basic/register.skin.php +++ b/skin/member/basic/register.skin.php @@ -17,7 +17,7 @@ add_stylesheet('', @include_once(get_social_skin_path().'/social_register.skin.php'); ?>
    -

    회원가입약관

    +

    (필수) 회원가입약관

    @@ -25,8 +25,8 @@ add_stylesheet('',
    -
    -

    개인정보 수집 및 이용

    +
    +

    (필수) 개인정보 수집 및 이용

  • 개인정보 수집 및 이용
    diff --git a/skin/member/basic/register_form.skin.php b/skin/member/basic/register_form.skin.php index f8f73b6d3..7f85dadfb 100644 --- a/skin/member/basic/register_form.skin.php +++ b/skin/member/basic/register_form.skin.php @@ -51,8 +51,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi

    개인정보 입력

      -
    • - +
    • + 간편인증'.PHP_EOL; } @@ -72,12 +73,11 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi echo ''.PHP_EOL; echo '(필수)'; - echo ''.PHP_EOL; - } - ?> - 본인확인을 위해서는 자바스크립트 사용이 가능해야합니다.'.PHP_EOL; + ?> + 본인확인성인인증 완료
    - + +
  • class="frm_input full_input " size="10" placeholder="이름"> @@ -135,13 +136,12 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • +
  • - - class="frm_input full_input " maxlength="20" placeholder="전화번호"> -
  • +
  • @@ -227,26 +227,6 @@ gif, jpg, png파일만 가능하며 용량 - -
  • - class="selec_chk"> - - 정보 메일을 받겠습니다. -
  • - - -
  • - class="selec_chk"> - - 휴대폰 문자메세지를 받겠습니다. -
  • -
  • @@ -288,13 +268,167 @@ gif, jpg, png파일만 가능하며 용량
  • - -
  • - 자동등록방지 - -
  • + + +
    +

    + 게시판 알림설정 + + 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    +

    +
      + + + +
      + + + +
    +
    + + + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + +
    +
    + + +
    +

    자동등록방지

    +
      +
    • + 자동등록방지 + +
    • +
    +
    취소 @@ -302,6 +436,9 @@ gif, jpg, png파일만 가능하며 용량
    + + + \ No newline at end of file diff --git a/skin/member/basic/style.css b/skin/member/basic/style.css index 7ceba47e5..80cab2ea2 100644 --- a/skin/member/basic/style.css +++ b/skin/member/basic/style.css @@ -13,7 +13,6 @@ .mbskin .frm_input {width:100%} .mbskin .btn_submit {width:100%;margin:10px 0 0;height:45px;font-weight:bold;font-size:1.25em} .mbskin h1 {margin:60px 0 30px;font-size:2em} -.mbskin .tbl_frm01 th {width:85px} /* ### 기본 스타일 커스터마이징 끝 ### */ /* 회원가입 약관 */ @@ -31,12 +30,11 @@ #fregister_chkall {position:relative;text-align:center;background:#f5f7fa;line-height:50px;border:1px solid #e5e9f0;border-radius:3px;margin-bottom:15px} #fregister h2 {text-align:left;padding:20px;border-bottom:1px solid #dde7e9;font-size:1.2em} #fregister textarea {display:block;padding:20px;width:100%;height:150px;background:#fff;border:0;line-height:1.6em} -#fregister_private {position:relative} -#fregister_private div {padding:20px;background:#fff} -#fregister_private table {width:100%;border-collapse:collapse;font-size:1em;} -#fregister_private table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} -#fregister_private table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} -#fregister_private table td {border:1px solid #e7e9ec;padding:10px;border-top:0} +.fregister_terms div {padding:20px;background:#fff} +.fregister_terms table {width:100%;border-collapse:collapse;font-size:1em;} +.fregister_terms table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} +.fregister_terms table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} +.fregister_terms table td {border:1px solid #e7e9ec;padding:10px;border-top:0} .fregister_agree {position:absolute;top:0;right:0} .fregister_agree input[type="checkbox"] + label {color:#676e70} @@ -70,6 +68,11 @@ #fregisterform .captcha {display:block;margin:5px 0 0} #fregisterform .reg_mb_img_file img {max-width:100%;height:auto} #reg_mb_icon, #reg_mb_img {float:right} +#fregisterform .consent-line {display: flex; margin: 0 !important;} +#fregisterform .consent-line .chk_li {padding-left: 0;} +#fregisterform .consent-date { margin: 5px 0 0 20px !important; } +#fregisterform .consent-group .sub-consents {padding: 0 20px 0px} +#fregisterform .js-open-consent {display: block; margin-left: 10px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /* 회원가입 완료 */ #reg_result {padding:40px 30px;text-align:center;background:#edf3fc;border:1px solid #d6e2f4;border-radius:5px} @@ -93,7 +96,7 @@ .tooltip_icon {display:inline-block;vertical-align:baseline;color:#b3b5b8;border:0;font-size:1.4em;background:transparent;cursor:pointer} .tooltip_icon:hover {color:#448bf5} -.tooltip {position:relative;width:auto;color:#fff;background:#000;padding:10px;font-size:small;line-height:18px;display:none;position:absolute;z-index:9;font-weight:normal;margin-left:15px;margin-top:10px} +.tooltip {position:absolute;width:auto;color:#fff;background:#000;padding:10px;font-size:small;line-height:18px;display:none;z-index:9;font-weight:normal;margin-left:15px;margin-top:10px} .tooltip:before {content:"";position:absolute;top:0;left:-10px;width:0;height:0;border-style:solid;border-top:0px solid transparent;border-bottom:10px solid transparent;border-left:0;border-right:10px solid #000} /* 아이디/비밀번호 찾기 */ @@ -152,12 +155,12 @@ #login_info .login_if_lpl {float:right} #login_password_lost {display:inline-block;border:1px solid #d5d9dd;color:#3a8afd;border-radius:2px;padding:2px 5px;line-height:20px} -#mb_login_notmb {margin:30px auto;padding:20px 30px} +#mb_login_notmb {margin:30px auto;padding:20px 30px;border: 1px solid #dde7e9} #mb_login_notmb h2 {font-size:1.25em;margin:20px 0 10px} #guest_privacy {border:1px solid #ccc;text-align:left;line-height:1.6em;color:#666;background:#fafafa;padding:10px;height:150px;margin:10px 0;overflow-y:auto} #mb_login_notmb .btn_submit {display:block;text-align:center;line-height:45px} -#mb_login_od_wr {margin:30px auto;padding:20px 30px} +#mb_login_od_wr {margin:30px auto;padding:20px 30px;border: 1px solid #dde7e9} #mb_login_od_wr h2 {font-size:1.25em;margin:20px 0 10px} #mb_login_od_wr .frm_input {margin:10px 0 0} #mb_login_od_wr p {background:#f3f3f3;margin:20px 0 0;padding:15px 20px;line-height:1.5em} diff --git a/skin/outlogin/basic/style.css b/skin/outlogin/basic/style.css index f233abd23..453770ccd 100644 --- a/skin/outlogin/basic/style.css +++ b/skin/outlogin/basic/style.css @@ -59,6 +59,6 @@ .chk_box {position:relative} .chk_box input[type="checkbox"] + label {padding-left:20px;color:#676e70} .chk_box input[type="checkbox"] + label:hover{color:#2172f8} -.chk_box input[type="checkbox"] + label span {position:absolute;top:2px;left:0;width:15px;height:15px;display:block;margin:0;background:#fff;border:1px solid #d0d4df;border-radius:3px} +.chk_box input[type="checkbox"] + label span {position:absolute;top:3px;left:0;width:15px;height:15px;display:block;margin:0;background:#fff;border:1px solid #d0d4df;border-radius:3px} .chk_box input[type="checkbox"]:checked + label {color:#000} .chk_box input[type="checkbox"]:checked + label span {background:url(./img/chk.png) no-repeat 50% 50% #3a8afd;border-color:#1471f6;border-radius:3px} diff --git a/skin/shop/basic/item.form.skin.php b/skin/shop/basic/item.form.skin.php index 4f1095fb6..797b59805 100644 --- a/skin/shop/basic/item.form.skin.php +++ b/skin/shop/basic/item.form.skin.php @@ -340,11 +340,11 @@ add_stylesheet('', 0 } } - // 재입고SMS 알림 + // 재입고 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; popup_window(url, "itemstocksms", opt); } diff --git a/skin/social/consent_modal.inc.php b/skin/social/consent_modal.inc.php new file mode 100644 index 000000000..2d9d63a5c --- /dev/null +++ b/skin/social/consent_modal.inc.php @@ -0,0 +1,85 @@ + + + +
    +
    +

    안내

    +
    +
    +
    + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/skin/social/social_register_member.skin.php b/skin/social/social_register_member.skin.php index b5363e79c..5579cf341 100644 --- a/skin/social/social_register_member.skin.php +++ b/skin/social/social_register_member.skin.php @@ -33,7 +33,7 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다. -
    +

    개인정보 수집 및 이용

    개인정보 수집 및 이용
    @@ -144,8 +144,106 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다. - + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + +
    • + +
    +
    + + +
    취소 @@ -197,6 +295,8 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
    + + \ No newline at end of file diff --git a/skin/social/style.css b/skin/social/style.css index d331544fa..a6f1eeb34 100644 --- a/skin/social/style.css +++ b/skin/social/style.css @@ -111,12 +111,11 @@ #fregister_chkall {position:relative;text-align:center;background:#f5f7fa;line-height:50px;border:1px solid #e5e9f0;border-radius:3px;margin-bottom:15px} #fregisterform h2 {text-align:left;padding:20px;border-bottom:1px solid #dde7e9;font-size:1.2em} #fregisterform textarea {display:block;padding:20px;width:100%;height:150px;background:#fff;border:0;line-height:1.6em} -#fregister_private {position:relative} -#fregister_private div {padding:20px;background:#fff} -#fregister_private table {width:100%;border-collapse:collapse;font-size:1em;} -#fregister_private table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} -#fregister_private table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} -#fregister_private table td {border:1px solid #e7e9ec;padding:10px;border-top:0} +.fregister_terms div {padding:20px;background:#fff} +.fregister_terms table {width:100%;border-collapse:collapse;font-size:1em;} +.fregister_terms table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} +.fregister_terms table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} +.fregister_terms table td {border:1px solid #e7e9ec;padding:10px;border-top:0} .fregister_agree {position:absolute;top:0;right:0} .fregister_agree input[type="checkbox"] + label {color:#676e70} @@ -150,6 +149,11 @@ #fregisterform .captcha {display:block;margin:5px 0 0} #fregisterform .reg_mb_img_file img {max-width:100%;height:auto} #reg_mb_icon, #reg_mb_img {float:right} +#fregisterform .consent-line {display: flex; margin: 0 !important;} +#fregisterform .consent-line .chk_li {padding-left: 0;} +#fregisterform .consent-date { margin: 5px 0 0 20px !important; } +#fregisterform .consent-group .sub-consents {padding: 0 20px 0px} +#fregisterform .js-open-consent {display: block; margin-left: 10px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } .tooltip_icon {display:inline-block;vertical-align:baseline;color:#b3b5b8;border:0;font-size:1.4em;background:transparent;cursor:pointer} .tooltip_icon:hover {color:#448bf5} diff --git a/theme/basic/css/default.css b/theme/basic/css/default.css index 9efda9dce..2902165c8 100644 --- a/theme/basic/css/default.css +++ b/theme/basic/css/default.css @@ -55,6 +55,16 @@ border:1px solid #558ab7 !important; #container_wr, #ft_wr {width:1200px} +/* 공통 - display none/block */ +.is-hidden { display: none !important; } +.is-visible { display: block !important; } + +/* 공통 - 뷰포트 (pc / mobile) 별 display none/block */ +.pc-only { display: none; } +@media (min-width: 769px) { .pc-only { display: block !important; }} +.mobile-only { display: block; } +@media (min-width: 769px) { .mobile-only { display: none !important; }} + /* 팝업레이어 */ #hd_pop {z-index:1000;position:relative;margin:0 auto;height:0} #hd_pop h2 {position:absolute;font-size:0;line-height:0;overflow:hidden} @@ -333,6 +343,8 @@ box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075); .tbl_frm01 a {text-decoration:none} .tbl_frm01 .frm_file {display:block;margin-bottom:5px} .tbl_frm01 .frm_info {display:block;padding:0 0 5px;line-height:1.4em} +.frm_info.add_info { margin-top: 10px !important; padding: 8px 12px; background: #fff; border: 1px solid #ddd; border-radius: 6px; line-height: 1.6; } +.btn_info_toggle { display: block; margin: 5px 0 0 21px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /*기본 리스트*/ .list_01 ul {border-top:1px solid #ececec} diff --git a/theme/basic/css/mobile.css b/theme/basic/css/mobile.css index fe161442d..e1dd1ecca 100644 --- a/theme/basic/css/mobile.css +++ b/theme/basic/css/mobile.css @@ -27,6 +27,16 @@ ul {list-style:none} box-sizing:border-box; } +/* 공통 - display none/block */ +.is-hidden { display: none !important; } +.is-visible { display: block !important; } + +/* 공통 - 뷰포트 (pc / mobile) 별 display none/block */ +.pc-only { display: none; } +@media (min-width: 769px) { .pc-only { display: block !important; }} +.mobile-only { display: block; } +@media (min-width: 769px) { .mobile-only { display: none !important; }} + /* 팝업레이어 */ #hd_pop {z-index:1000;position:relative;margin:0 auto;width:100%;height:0} #hd_pop h2 {position:absolute;font-size:0;text-indent:-9999em;line-height:0;overflow:hidden} @@ -267,6 +277,8 @@ transition:background-color 0.3s ease-out} .form_01 .frm_file {display:block;margin-bottom:5px;width:100%} .form_01 select {height:40px;background-color:#fff} .form_01 .frm_info {font-size:0.92em;color:#3a8afd;text-align:left;margin:3px 0 10px;display:block;line-height:1.3em} +.form_01 .frm_info.add_info { padding: 8px 12px; background: #fff; border: 1px solid #ddd; border-radius: 6px; line-height: 1.6; } +.btn_info_toggle { display: block; margin: 5px 0 0 21px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /* 자료 없는 목록 */ .empty_table {padding:100px 0 !important;color:#777;text-align:center} diff --git a/theme/basic/mobile/skin/member/basic/consent_modal.inc.php b/theme/basic/mobile/skin/member/basic/consent_modal.inc.php new file mode 100644 index 000000000..cd4f1525d --- /dev/null +++ b/theme/basic/mobile/skin/member/basic/consent_modal.inc.php @@ -0,0 +1,85 @@ + + + +
    +
    +

    안내

    +
    +
    +
    + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/theme/basic/mobile/skin/member/basic/register.skin.php b/theme/basic/mobile/skin/member/basic/register.skin.php index 5fb762a94..7f0148ef7 100644 --- a/theme/basic/mobile/skin/member/basic/register.skin.php +++ b/theme/basic/mobile/skin/member/basic/register.skin.php @@ -22,7 +22,7 @@ add_stylesheet('',
    -

    회원가입약관

    +

    (필수) 회원가입약관

    @@ -31,7 +31,7 @@ add_stylesheet('',
    -

    개인정보 수집 및 이용

    +

    (필수) 개인정보 수집 및 이용

    diff --git a/theme/basic/mobile/skin/member/basic/register_form.skin.php b/theme/basic/mobile/skin/member/basic/register_form.skin.php index 534a63933..1830eeff4 100644 --- a/theme/basic/mobile/skin/member/basic/register_form.skin.php +++ b/theme/basic/mobile/skin/member/basic/register_form.skin.php @@ -45,8 +45,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi

    개인정보 입력

      -
    • - +
    • + 간편인증'.PHP_EOL; } @@ -67,11 +68,10 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi echo '(필수)'; echo ''.PHP_EOL; - } ?> 본인확인성인인증 완료
    - + +
  • class="frm_input full_input " placeholder="이름 (필수)"> @@ -217,26 +218,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • -
  • - class="selec_chk"> - - 정보 메일을 받겠습니다. -
  • - - -
  • - class="selec_chk"> - - 휴대폰 문자메세지를 받겠습니다. -
  • - -
  • class="selec_chk"> @@ -245,7 +226,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi 정보공개 다른분들이 나의 정보를 볼 수 있도록 합니다. - + 정보공개를 바꾸시면 앞으로 일 이내에는 변경이 안됩니다. @@ -275,20 +256,174 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • - -
  • - 자동등록방지 - -
  • + +
    +

    게시판 알림설정

    + 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    + +
      + + + +
      + + + +
    +
    + + + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + +
    +
    + + +
    +

    자동등록방지

    +
      +
    • + 자동등록방지 + +
    • +
    +
    + + + \ No newline at end of file diff --git a/theme/basic/mobile/skin/member/basic/style.css b/theme/basic/mobile/skin/member/basic/style.css index c0565e156..66edd7a6e 100644 --- a/theme/basic/mobile/skin/member/basic/style.css +++ b/theme/basic/mobile/skin/member/basic/style.css @@ -70,6 +70,10 @@ .fregister_agree input[type="checkbox"]:checked + label {color:#000} .fregister_agree input[type="checkbox"]:checked + label span {background:url('./img/chk.png') no-repeat 50% 50% #3a8afd;border-color:#1471f6;border-radius:3px} .fregister_agree.chk_all input[type="checkbox"] + label span {top:15px} +#fregisterform .consent-line {display: flex; align-items: baseline;} +#fregisterform .consent-date { margin: 5px 0 0 20px !important; } +#fregisterform .consent-group .sub-consents {padding: 10px 20px 0;} +#fregisterform .js-open-consent {flex:1 0 auto; margin-left: 10px; text-align: right; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /* 회원가입 완료 */ #reg_result {padding:20px 10px 10px} diff --git a/theme/basic/mobile/skin/shop/basic/item.form.skin.php b/theme/basic/mobile/skin/shop/basic/item.form.skin.php index 79aa47569..aaf8c17bb 100644 --- a/theme/basic/mobile/skin/shop/basic/item.form.skin.php +++ b/theme/basic/mobile/skin/shop/basic/item.form.skin.php @@ -545,11 +545,11 @@ function popup_item_recommend(it_id) } } -// 재입고SMS 알림 +// 재입고 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; popup_window(url, "itemstocksms", opt); } diff --git a/theme/basic/skin/member/basic/consent_modal.inc.php b/theme/basic/skin/member/basic/consent_modal.inc.php new file mode 100644 index 000000000..2d9d63a5c --- /dev/null +++ b/theme/basic/skin/member/basic/consent_modal.inc.php @@ -0,0 +1,85 @@ + + + +
    +
    +

    안내

    +
    +
    +
    + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/theme/basic/skin/member/basic/register.skin.php b/theme/basic/skin/member/basic/register.skin.php index 662c8da40..f337fe9ce 100644 --- a/theme/basic/skin/member/basic/register.skin.php +++ b/theme/basic/skin/member/basic/register.skin.php @@ -17,7 +17,7 @@ add_stylesheet('', @include_once(get_social_skin_path().'/social_register.skin.php'); ?>
    -

    회원가입약관

    +

    (필수) 회원가입약관

    @@ -25,8 +25,8 @@ add_stylesheet('',
    -
    -

    개인정보 수집 및 이용

    +
    +

    (필수) 개인정보 수집 및 이용

    개인정보 수집 및 이용
    diff --git a/theme/basic/skin/member/basic/register_form.skin.php b/theme/basic/skin/member/basic/register_form.skin.php index 7b79c61f4..63a2073f8 100644 --- a/theme/basic/skin/member/basic/register_form.skin.php +++ b/theme/basic/skin/member/basic/register_form.skin.php @@ -51,18 +51,19 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi

    개인정보 입력

      -
    • - 본인확인 시 자동입력'; $desc_phone = ' 본인확인 시 자동입력'; - + if (!$config['cf_cert_simple'] && !$config['cf_cert_hp'] && $config['cf_cert_ipin']) { $desc_phone = ''; } - + ?> +
    • + 간편인증'.PHP_EOL; } @@ -73,11 +74,10 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi echo '(필수)'; echo ''.PHP_EOL; - } ?> 본인확인성인인증 완료
    - + +
  • - class="frm_input full_input " size="10" placeholder="이름"> + class="frm_input full_input " size="10" placeholder="이름">
  • @@ -135,13 +136,12 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
  • +
  • - - class="frm_input full_input " maxlength="20" placeholder="전화번호"> -
  • +
  • @@ -227,26 +227,6 @@ gif, jpg, png파일만 가능하며 용량 - -
  • - class="selec_chk"> - - 정보 메일을 받겠습니다. -
  • - - -
  • - class="selec_chk"> - - 휴대폰 문자메세지를 받겠습니다. -
  • -
  • @@ -288,13 +268,167 @@ gif, jpg, png파일만 가능하며 용량
  • - -
  • - 자동등록방지 - -
  • + + +
    +

    + 게시판 알림설정 + + 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    +

    +
      + + + +
      + + + +
    +
    + + + + +
    +

    수신설정

    + +
      + +
    • + + +
      마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + + + + + + + '아이코드', 'popbill' => '팝빌']; + + $usedCompanies = []; + foreach ($configKeys as $key) { + if (!empty($config[$key]) && isset($companies[$config[$key]])) { + $usedCompanies[] = $companies[$config[$key]]; + } + } + ?> + +
    • + + +
      개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.
      + + + +
    • + +
    +
    + + +
    +

    자동등록방지

    +
      +
    • + 자동등록방지 + +
    • +
    +
    취소 @@ -302,6 +436,9 @@ gif, jpg, png파일만 가능하며 용량
    + + + \ No newline at end of file diff --git a/theme/basic/skin/member/basic/style.css b/theme/basic/skin/member/basic/style.css index ae2158460..1e846bf01 100644 --- a/theme/basic/skin/member/basic/style.css +++ b/theme/basic/skin/member/basic/style.css @@ -30,12 +30,11 @@ #fregister_chkall {position:relative;text-align:center;background:#f5f7fa;line-height:50px;border:1px solid #e5e9f0;border-radius:3px;margin-bottom:15px} #fregister h2 {text-align:left;padding:20px;border-bottom:1px solid #dde7e9;font-size:1.2em} #fregister textarea {display:block;padding:20px;width:100%;height:150px;background:#fff;border:0;line-height:1.6em} -#fregister_private {position:relative} -#fregister_private div {padding:20px;background:#fff} -#fregister_private table {width:100%;border-collapse:collapse;font-size:1em;} -#fregister_private table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} -#fregister_private table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} -#fregister_private table td {border:1px solid #e7e9ec;padding:10px;border-top:0} +.fregister_terms div {padding:20px;background:#fff} +.fregister_terms table {width:100%;border-collapse:collapse;font-size:1em;} +.fregister_terms table caption {position:absolute;font-size:0;line-height:0;overflow:hidden} +.fregister_terms table th {background:#f7f7f9;width:33.33%;color:#000;padding:10px;border:1px solid #d8dbdf} +.fregister_terms table td {border:1px solid #e7e9ec;padding:10px;border-top:0} .fregister_agree {position:absolute;top:0;right:0} .fregister_agree input[type="checkbox"] + label {color:#676e70} @@ -69,6 +68,11 @@ #fregisterform .captcha {display:block;margin:5px 0 0} #fregisterform .reg_mb_img_file img {max-width:100%;height:auto} #reg_mb_icon, #reg_mb_img {float:right} +#fregisterform .consent-line {display: flex; margin: 0 !important;} +#fregisterform .consent-line .chk_li {padding-left: 0;} +#fregisterform .consent-date { margin: 5px 0 0 20px !important; } +#fregisterform .consent-group .sub-consents {padding: 0 20px 0px} +#fregisterform .js-open-consent {display: block; margin-left: 10px; font-size: 12px; color: #3f51b5; background: none; border: none; cursor: pointer; text-decoration: underline; } /* 회원가입 완료 */ #reg_result {padding:40px 30px;text-align:center;background:#edf3fc;border:1px solid #d6e2f4;border-radius:5px} diff --git a/theme/basic/skin/outlogin/basic/style.css b/theme/basic/skin/outlogin/basic/style.css index f233abd23..453770ccd 100644 --- a/theme/basic/skin/outlogin/basic/style.css +++ b/theme/basic/skin/outlogin/basic/style.css @@ -59,6 +59,6 @@ .chk_box {position:relative} .chk_box input[type="checkbox"] + label {padding-left:20px;color:#676e70} .chk_box input[type="checkbox"] + label:hover{color:#2172f8} -.chk_box input[type="checkbox"] + label span {position:absolute;top:2px;left:0;width:15px;height:15px;display:block;margin:0;background:#fff;border:1px solid #d0d4df;border-radius:3px} +.chk_box input[type="checkbox"] + label span {position:absolute;top:3px;left:0;width:15px;height:15px;display:block;margin:0;background:#fff;border:1px solid #d0d4df;border-radius:3px} .chk_box input[type="checkbox"]:checked + label {color:#000} .chk_box input[type="checkbox"]:checked + label span {background:url(./img/chk.png) no-repeat 50% 50% #3a8afd;border-color:#1471f6;border-radius:3px} diff --git a/theme/basic/skin/shop/basic/item.form.skin.php b/theme/basic/skin/shop/basic/item.form.skin.php index 4f1095fb6..797b59805 100644 --- a/theme/basic/skin/shop/basic/item.form.skin.php +++ b/theme/basic/skin/shop/basic/item.form.skin.php @@ -340,11 +340,11 @@ add_stylesheet('', 0 } } - // 재입고SMS 알림 + // 재입고 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; popup_window(url, "itemstocksms", opt); } From fa792efacb0e254dfd6a25481a49340ef8cb1b52 Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 8 Sep 2025 09:37:43 +0900 Subject: [PATCH 19/31] =?UTF-8?q?kcaptcha=20html=20=EC=97=90=20hook=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/kcaptcha/kcaptcha.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/kcaptcha/kcaptcha.lib.php b/plugin/kcaptcha/kcaptcha.lib.php index 92707012f..6f883f3b5 100644 --- a/plugin/kcaptcha/kcaptcha.lib.php +++ b/plugin/kcaptcha/kcaptcha.lib.php @@ -257,7 +257,7 @@ function captcha_html($class="captcha") $html .= "\n".''; $html .= "\n".'자동등록방지 숫자를 순서대로 입력하세요.'; $html .= "\n".''; - return $html; + return run_replace('kcaptcha_captcha_html', $html, $class); } From f6a6a5622d7ef3b7f010e615ed6a99aec781715f Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 8 Sep 2025 09:59:55 +0900 Subject: [PATCH 20/31] =?UTF-8?q?=EB=B2=84=EC=A0=84=205.6.21=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index cf470e4e2..472e33b45 100644 --- a/version.php +++ b/version.php @@ -2,7 +2,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 define('G5_VERSION', '그누보드5'); -define('G5_GNUBOARD_VER', '5.6.17'); +define('G5_GNUBOARD_VER', '5.6.21'); // 그누보드5.4.5.5 버전과 영카트5.4.5.5.1 버전을 합쳐서 그누보드5.4.6 버전에서 시작함 (kagla-210617) // G5_YOUNGCART_VER 이 상수를 사용하는 곳이 있으므로 주석 처리 해제함 // 그누보드5.4.6 이상 버전 부터는 영카트를 그누보드에 포함하여 배포하므로 영카트5의 버전은 의미가 없습니다. From ef364e1430c6ee27072662d3481c114b5354ef21 Mon Sep 17 00:00:00 2001 From: chym1217 Date: Mon, 8 Sep 2025 16:37:03 +0900 Subject: [PATCH 21/31] =?UTF-8?q?fix:=20=EC=9E=AC=EC=9E=85=EA=B3=A0=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20DB=20=EC=97=85=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=E2=80=93=20=EC=98=81=EC=B9=B4=ED=8A=B8=20=EB=AF=B8?= =?UTF-8?q?=EC=84=A4=EC=B9=98=20=EC=8B=9C=20=EB=B0=9C=EC=83=9D=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/dbupgrade.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/adm/dbupgrade.php b/adm/dbupgrade.php index 7b99b4b1a..a26c55ec1 100644 --- a/adm/dbupgrade.php +++ b/adm/dbupgrade.php @@ -438,14 +438,18 @@ if (!isset($member['mb_board_post'])) { } // 재입고 알림 - 채널 구분 (1=SMS, 2=알림톡) -if(sql_query(" DESC {$g5['g5_shop_item_stocksms_table']} ", false) && !sql_query(" select ss_channel from {$g5['g5_shop_item_stocksms_table']} limit 1", false)) { - sql_query( - " ALTER TABLE `{$g5['g5_shop_item_stocksms_table']}` - ADD `ss_channel` tinyint(4) NOT NULL DEFAULT '1' AFTER `ss_ip` ", - true - ); +if (defined('G5_USE_SHOP') && G5_USE_SHOP) { + if(sql_query(" DESC {$g5['g5_shop_item_stocksms_table']} ", false)) { + if(!sql_query(" select ss_channel from {$g5['g5_shop_item_stocksms_table']} limit 1", false)) { + sql_query( + " ALTER TABLE `{$g5['g5_shop_item_stocksms_table']}` + ADD `ss_channel` tinyint(4) NOT NULL DEFAULT '1' AFTER `ss_ip` ", + true + ); - $is_check = true; + $is_check = true; + } + } } From 8a1f350d6790980cf96d6a9fb79ca6f474ff4997 Mon Sep 17 00:00:00 2001 From: chym1217 Date: Tue, 9 Sep 2025 09:36:42 +0900 Subject: [PATCH 22/31] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90/=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=ED=8C=90=20=EC=B6=94=EA=B0=80=20=EC=8B=9C=EC=8B=9C=20?= =?UTF-8?q?Undefined=20array=20key=20=EB=8B=A4=EC=88=98=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/board_form.php | 1 + adm/member_form.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/adm/board_form.php b/adm/board_form.php index 1cd57e81b..600fde032 100644 --- a/adm/board_form.php +++ b/adm/board_form.php @@ -136,6 +136,7 @@ $board_default = array( 'bo_mobile_content_tail'=>'', 'bo_insert_content'=>'', 'bo_sort_field'=>'', +'bo_use_kakaotalk'=>0, ); for ($i = 0; $i <= 10; $i++) { diff --git a/adm/member_form.php b/adm/member_form.php index 4bc216120..2ba6d3f22 100644 --- a/adm/member_form.php +++ b/adm/member_form.php @@ -55,6 +55,8 @@ if ($w == '') { $mb['mb_sms'] = 1; $mb['mb_open'] = 1; $mb['mb_level'] = $config['cf_register_level']; + $mb['mb_marketing_agree'] = 0; + $mb['mb_thirdparty_agree'] = 0; $mb['mb_board_post'] = 1; $mb['mb_board_reply'] = 1; $mb['mb_board_comment'] = 1; From 633ff46596763ed46328f3cc4a63318b075ce2f3 Mon Sep 17 00:00:00 2001 From: thisgun Date: Fri, 12 Sep 2025 10:50:08 +0900 Subject: [PATCH 23/31] =?UTF-8?q?=EC=A0=84=EC=B2=B4=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=8B=9C=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.lib.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/common.lib.php b/lib/common.lib.php index ddb8bbace..291af89f3 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -3429,11 +3429,8 @@ function clean_xss_tags($str, $check_entities=0, $is_remove_tags=0, $cur_str_len $result = preg_replace('#([^\p{L}]|^)(?:javascript|jar|applescript|vbscript|vbs|wscript|jscript|behavior|mocha|livescript|view-source)\s*:(?:.*?([/\\\;()\'">]|$))#ius', '$1$2', $result); - // 이벤트 핸들러 속성 제거 (예: onclick=, onerror= 등) - $result = preg_replace('/on\w+\s*=\s*(".*?"|\'.*?\'|[^\s>]+)/i', '', $result); - - // 속성 제거 (CSS 기반 인젝션 차단) - $result = preg_replace('/\s*style\s*=\s*(".*?"|\'.*?\'|[^\s>]+)/i', '', $result); + // 따옴표 + 속성으로 강제 진입 차단 (예: "style=..., 'onerror=...) + $result = preg_replace('/["\']\s*(?:on\w+|style)\s*=\s*/i', '', $result); if((string)$result === (string)$str) break; From a71192a63ba578722a1ae37a9dd87bc3e0bd1369 Mon Sep 17 00:00:00 2001 From: thisgun Date: Fri, 12 Sep 2025 13:56:31 +0900 Subject: [PATCH 24/31] =?UTF-8?q?=EB=B2=84=EC=A0=84=205.6.22=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 472e33b45..29d68051d 100644 --- a/version.php +++ b/version.php @@ -2,7 +2,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 define('G5_VERSION', '그누보드5'); -define('G5_GNUBOARD_VER', '5.6.21'); +define('G5_GNUBOARD_VER', '5.6.22'); // 그누보드5.4.5.5 버전과 영카트5.4.5.5.1 버전을 합쳐서 그누보드5.4.6 버전에서 시작함 (kagla-210617) // G5_YOUNGCART_VER 이 상수를 사용하는 곳이 있으므로 주석 처리 해제함 // 그누보드5.4.6 이상 버전 부터는 영카트를 그누보드에 포함하여 배포하므로 영카트5의 버전은 의미가 없습니다. From 1eee11e4333f5d023c11140ab595b9f187ccd949 Mon Sep 17 00:00:00 2001 From: thisgun Date: Fri, 12 Sep 2025 14:27:11 +0900 Subject: [PATCH 25/31] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20export=20=EC=97=90=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/member_list_exel_export.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adm/member_list_exel_export.php b/adm/member_list_exel_export.php index 78422b90b..ce451bc36 100644 --- a/adm/member_list_exel_export.php +++ b/adm/member_list_exel_export.php @@ -1,8 +1,12 @@ Date: Tue, 16 Sep 2025 16:34:15 +0900 Subject: [PATCH 26/31] =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EB=A8=BC=EC=B8=A0=20v2=20=EA=B2=B0=EC=A0=9C=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/css/admin.css | 3 +- adm/shop_admin/admin.shop.lib.php | 4 + adm/shop_admin/configform.php | 40 ++- adm/shop_admin/configformupdate.php | 6 +- adm/shop_admin/orderform.php | 10 +- adm/shop_admin/orderformcartupdate.php | 4 + adm/shop_admin/personalpayform.php | 2 + install/gnuboard5.sql | 2 + mobile/shop/orderform.sub.php | 63 +++- mobile/shop/orderformupdate.php | 28 ++ mobile/shop/orderinquiryview.php | 6 + mobile/shop/personalpayform.sub.php | 57 +++- mobile/shop/personalpayformupdate.php | 40 ++- mobile/shop/personalpayresult.php | 6 + mobile/shop/settle_toss.inc.php | 15 + mobile/shop/toss/_common.php | 6 + mobile/shop/toss/orderform.1.php | 33 ++ mobile/shop/toss/orderform.2.php | 37 +++ mobile/shop/toss/orderform.3.php | 3 + mobile/shop/toss/returnurl.php | 77 +++++ mobile/shop/toss/toss_approval.php | 192 +++++++++++ shop/orderform.sub.php | 93 +++++- shop/orderformupdate.php | 31 ++ shop/orderinquirycancel.php | 4 + shop/orderinquiryview.php | 6 + shop/personalpayform.sub.php | 87 ++++- shop/personalpayformupdate.php | 50 ++- shop/personalpayresult.php | 6 + shop/settle_toss.inc.php | 11 + shop/settle_toss_common.php | 422 +++++++++++++++++++++++++ shop/toss/_common.php | 2 + shop/toss/orderform.1.php | 111 +++++++ shop/toss/orderform.2.php | 31 ++ shop/toss/orderform.3.php | 16 + shop/toss/orderform.4.php | 3 + shop/toss/orderpartcancel.inc.php | 61 ++++ shop/toss/returnurl.php | 67 ++++ shop/toss/taxsave_form.php | 197 ++++++++++++ shop/toss/taxsave_result.php | 192 +++++++++++ shop/toss/toss.inc.php | 373 ++++++++++++++++++++++ shop/toss/toss_approval.php | 79 +++++ shop/toss/toss_cancel.php | 38 +++ shop/toss/toss_result.php | 108 +++++++ theme/basic/shop/orderinquiryview.php | 12 +- 44 files changed, 2614 insertions(+), 20 deletions(-) create mode 100644 mobile/shop/settle_toss.inc.php create mode 100644 mobile/shop/toss/_common.php create mode 100644 mobile/shop/toss/orderform.1.php create mode 100644 mobile/shop/toss/orderform.2.php create mode 100644 mobile/shop/toss/orderform.3.php create mode 100644 mobile/shop/toss/returnurl.php create mode 100644 mobile/shop/toss/toss_approval.php create mode 100644 shop/settle_toss.inc.php create mode 100644 shop/settle_toss_common.php create mode 100644 shop/toss/_common.php create mode 100644 shop/toss/orderform.1.php create mode 100644 shop/toss/orderform.2.php create mode 100644 shop/toss/orderform.3.php create mode 100644 shop/toss/orderform.4.php create mode 100644 shop/toss/orderpartcancel.inc.php create mode 100644 shop/toss/returnurl.php create mode 100644 shop/toss/taxsave_form.php create mode 100644 shop/toss/taxsave_result.php create mode 100644 shop/toss/toss.inc.php create mode 100644 shop/toss/toss_approval.php create mode 100644 shop/toss/toss_cancel.php create mode 100644 shop/toss/toss_result.php diff --git a/adm/css/admin.css b/adm/css/admin.css index 20802f756..6df934090 100644 --- a/adm/css/admin.css +++ b/adm/css/admin.css @@ -743,7 +743,7 @@ a.nicepay_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#0 ul.de_pg_tab{margin:0;padding:0;zoom:1} ul.de_pg_tab:after{display:block;visibility:hidden;clear:both;content:"";} -ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;width:120px} +ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;width:140px} ul.de_pg_tab li a{margin:0 2px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:2.5;background-color:#f7f7f7;color:#74777b;font-weight:bold;font-size:1.2em;text-decoration:none} ul.de_pg_tab li a:hover{text-decoration:none} ul.de_pg_tab li.tab-current a{background:#2CC185;color:#fff} @@ -751,6 +751,7 @@ ul.de_pg_tab li.tab-current a{background:#2CC185;color:#fff} .pg_info_fld{position:relative} .kcp_info_fld th{background-color:#F6FCFF} .lg_info_fld th{background-color:#FFF4FA} +.lg_info_fld_v2 th{background-color:#ffe8f5} .inicis_info_fld th{background-color:#F6F1FF} .kakao_info_fld th{background-color:#FFFCED} .naver_info_fld th{background-color:#F3FFF3} diff --git a/adm/shop_admin/admin.shop.lib.php b/adm/shop_admin/admin.shop.lib.php index a6310b125..737d0d95c 100644 --- a/adm/shop_admin/admin.shop.lib.php +++ b/adm/shop_admin/admin.shop.lib.php @@ -170,6 +170,10 @@ function is_cancel_shop_pg_order($od){ $is_od_pg_cancel = true; } + if($od['od_pg'] === 'toss' && in_array($od['od_settle_case'], array('계좌이체', '휴대폰'))) { + $is_od_pg_cancel = true; + } + return $is_od_pg_cancel; } diff --git a/adm/shop_admin/configform.php b/adm/shop_admin/configform.php index 041a53d36..788f7288e 100644 --- a/adm/shop_admin/configform.php +++ b/adm/shop_admin/configform.php @@ -215,6 +215,14 @@ if (! isset($default['de_nicepay_mid'])) { sql_query($sql, false); } +// 토스페이먼츠 버전 2 client, secret key 추가 +if( ! isset($config['cf_toss_client_key']) ){ + $sql = "ALTER TABLE `{$g5['config_table']}` + ADD COLUMN `cf_toss_client_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_lg_mert_key`, + ADD COLUMN `cf_toss_secret_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_toss_client_key`; "; + sql_query($sql, false); +} + if( function_exists('pg_setting_check') ){ pg_setting_check(true); } @@ -636,17 +644,23 @@ if(!$default['de_kakaopay_cancelpwd']){ /settle_kcp_common.php - + - + + + + + + + + + + + + + + - + @@ -790,8 +790,8 @@ if(!$default['de_kakaopay_cancelpwd']){ @@ -849,23 +849,23 @@ if(!$default['de_kakaopay_cancelpwd']){ - + - - + + - + @@ -1805,7 +1805,11 @@ function fconfig_check(f) } } else if ( f.de_pg_service.value == "lg" ) { if( f.cf_lg_mid.value && f.cf_lg_mert_key.value && parseInt(f.de_card_test.value) > 0 ){ - pg_msg = "토스페이먼츠"; + pg_msg = "토스페이먼츠(구버전)"; + } + } else if ( f.de_pg_service.value == "toss" ) { + if( f.cf_lg_mid.value && f.cf_toss_client_key.value && f.cf_toss_secret_key.value && parseInt(f.de_card_test.value) > 0 ){ + msg += "(주의!) 토스페이먼츠 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.\n상점 API키를 [테스트]키로 설정한 후 테스트결제를 진행해주세요.\n쇼핑몰 운영중이면 반드시 실결제 전환 및 [라이브]키로 설정하여 운영하셔야 합니다.\n실결제로 변경하려면 결제설정 탭 -> 결제 테스트에서 실결제를 선택해 주세요.\n정말로 테스트결제로 설정하시겠습니까?"; } } else if ( f.de_pg_service.value == "inicis" ) { if( f.de_inicis_mid.value && f.de_inicis_sign_key.value && parseInt(f.de_card_test.value) > 0 ){ diff --git a/adm/shop_admin/orderform.php b/adm/shop_admin/orderform.php index 02e231006..97cd04610 100644 --- a/adm/shop_admin/orderform.php +++ b/adm/shop_admin/orderform.php @@ -515,7 +515,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js switch($od['od_pg']) { case 'lg': $pg_url = 'https://app.tosspayments.com'; - $pg_test = '토스페이먼츠'; + $pg_test = '토스페이먼츠(구버전)'; if ($default['de_card_test']) { $pg_url = 'https://pgweb.tosspayments.com/tmert'; $pg_test .= ' 테스트 '; diff --git a/shop/settle_toss_common.php b/shop/settle_toss_common.php index 07684cbfe..2c7f48f71 100644 --- a/shop/settle_toss_common.php +++ b/shop/settle_toss_common.php @@ -13,7 +13,7 @@ $payLog = true; // 로그 사용 여부 $log_file = G5_DATA_PATH . '/log/tosspayment_result_log.txt'; /** - * 토스페이먼츠 로그 기록 함수 + * 로그 기록 함수 */ function write_toss_log($reason, $orderId = '', $status = '') { @@ -32,7 +32,7 @@ function write_toss_log($reason, $orderId = '', $status = '') } } -// 토스페이먼츠 입금통보 결과 데이터 읽기 +// 입금통보 결과 데이터 읽기 $raw = file_get_contents('php://input'); if ($raw == false) { write_toss_log("입력 데이터 읽기 실패"); @@ -375,7 +375,7 @@ elseif($TOSS_STATUS == "CANCELED") // 위에서 상점 데이터베이스에 등록 성공유무에 따라서 성공시에는 성공응답인 `HTTP 200` 상태 코드를 리턴해야 합니다. // (주의) 성공응답인 `HTTP 200` 상태 코드를 리턴하지 않으면 토스페이먼츠에서 7회까지 재전송에 실패하면 웹훅 상태가 실패로 변경됩니다. -// 토스페이먼츠 로그 기록 (nicepay 형태) +// 로그 기록 if($payLog) { $logfile = fopen($log_file, "a+"); From 7e8eff53953a6c4ba8095b89337d0954a70cbc30 Mon Sep 17 00:00:00 2001 From: chym1217 Date: Thu, 18 Sep 2025 12:50:17 +0900 Subject: [PATCH 28/31] =?UTF-8?q?=ED=8C=9D=EB=B9=8C=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=ED=86=A1=20=EC=A0=9C=EA=B1=B0=20=EC=99=84=EB=A3=8C=20-=20?= =?UTF-8?q?=EA=B4=91=EA=B3=A0=EC=84=B1=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=8C=8C=EC=9D=BC(=EC=B9=9C=EA=B5=AC?= =?UTF-8?q?=ED=86=A1=20=EC=BD=94=EB=93=9C=EB=A7=8C=20=EC=A0=9C=EA=B1=B0)?= =?UTF-8?q?=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/admin.menu100.php | 1 - adm/admin.menu500.shop_2of2.php | 2 +- adm/alimtalkpreset.php | 57 -- adm/alimtalkpreset_popbill.php | 300 ------ adm/alimtalkpresetupdate.php | 37 - adm/board_copy_update.php | 1 - adm/board_form.php | 19 - adm/board_form_update.php | 4 - adm/config_form.php | 152 +-- adm/config_form_update.php | 14 +- adm/css/admin.css | 27 +- adm/dbupgrade.php | 136 --- adm/img/svc_btn_07.jpg | Bin 6137 -> 0 bytes adm/member_form.php | 53 - adm/member_form_update.php | 10 - adm/member_list_exel.lib.php | 3 +- adm/member_list_exel.php | 105 +- adm/member_list_exel_export.php | 28 +- adm/service.php | 10 - adm/shop_admin/itemform.php | 4 +- adm/shop_admin/itemstocksms.php | 22 +- adm/shop_admin/itemstocksmsupdate.php | 49 +- adm/shop_admin/orderalimtalk.inc.php | 22 - adm/shop_admin/orderdeliveryupdate.php | 9 - adm/shop_admin/orderform.php | 23 - adm/shop_admin/orderformcartupdate.php | 27 +- adm/shop_admin/orderformreceiptupdate.php | 3 - adm/shop_admin/orderlistupdate.php | 25 - bbs/poll_etc_update.php | 6 - bbs/qawrite_update.php | 19 - bbs/register_form_update.php | 27 +- bbs/write_comment_update.php | 50 - bbs/write_update.php | 39 - extend/kakao5.extend.php | 329 ------- install/gnuboard5.sql | 101 +- install/install_db.php | 1 - js/kakao5.js | 26 - mobile/shop/orderformupdate.php | 17 - .../skin/member/basic/register_form.skin.php | 51 +- mobile/skin/shop/basic/item.form.skin.php | 4 +- .../social/social_register_member.skin.php | 6 +- plugin/kakao5/Popbill/LICENSE.md | 9 - plugin/kakao5/Popbill/Linkhub/LICENSE.md | 9 - plugin/kakao5/Popbill/Linkhub/README.md | 4 - plugin/kakao5/Popbill/Linkhub/example.php | 49 - .../kakao5/Popbill/Linkhub/linkhub.auth.php | 337 ------- plugin/kakao5/Popbill/PopbillKakao.php | 679 ------------- plugin/kakao5/Popbill/PopbillMessaging.php | 619 ------------ plugin/kakao5/Popbill/README.md | 2 - plugin/kakao5/Popbill/crypto.php | 44 - plugin/kakao5/Popbill/popbill.php | 930 ------------------ plugin/kakao5/_common.php | 7 - plugin/kakao5/ajax.check_popbill.php | 14 - plugin/kakao5/ajax.get_url.php | 38 - plugin/kakao5/kakao5.lib.php | 449 --------- plugin/kakao5/kakao5_popbill.lib.php | 238 ----- plugin/social/register_member_update.php | 12 - shop/itemstocksms.php | 9 +- shop/itemstocksmsupdate.php | 4 +- shop/orderformupdate.php | 18 - shop/orderinquirycancel.php | 13 - shop/settle_inicis_common.php | 14 - shop/settle_kcp_common.php | 14 - shop/settle_lg_common.php | 15 +- shop/settle_nicepay_common.php | 14 - skin/member/basic/register_form.skin.php | 53 +- skin/shop/basic/item.form.skin.php | 4 +- skin/social/social_register_member.skin.php | 6 +- .../skin/member/basic/register_form.skin.php | 51 +- .../mobile/skin/shop/basic/item.form.skin.php | 4 +- .../skin/member/basic/register_form.skin.php | 53 +- .../basic/skin/shop/basic/item.form.skin.php | 4 +- 72 files changed, 73 insertions(+), 5462 deletions(-) delete mode 100644 adm/alimtalkpreset.php delete mode 100644 adm/alimtalkpreset_popbill.php delete mode 100644 adm/alimtalkpresetupdate.php delete mode 100644 adm/img/svc_btn_07.jpg delete mode 100644 adm/shop_admin/orderalimtalk.inc.php delete mode 100644 extend/kakao5.extend.php delete mode 100644 js/kakao5.js delete mode 100644 plugin/kakao5/Popbill/LICENSE.md delete mode 100644 plugin/kakao5/Popbill/Linkhub/LICENSE.md delete mode 100644 plugin/kakao5/Popbill/Linkhub/README.md delete mode 100644 plugin/kakao5/Popbill/Linkhub/example.php delete mode 100644 plugin/kakao5/Popbill/Linkhub/linkhub.auth.php delete mode 100644 plugin/kakao5/Popbill/PopbillKakao.php delete mode 100644 plugin/kakao5/Popbill/PopbillMessaging.php delete mode 100644 plugin/kakao5/Popbill/README.md delete mode 100644 plugin/kakao5/Popbill/crypto.php delete mode 100644 plugin/kakao5/Popbill/popbill.php delete mode 100644 plugin/kakao5/_common.php delete mode 100644 plugin/kakao5/ajax.check_popbill.php delete mode 100644 plugin/kakao5/ajax.get_url.php delete mode 100644 plugin/kakao5/kakao5.lib.php delete mode 100644 plugin/kakao5/kakao5_popbill.lib.php diff --git a/adm/admin.menu100.php b/adm/admin.menu100.php index aa153c4fd..8363f0af0 100644 --- a/adm/admin.menu100.php +++ b/adm/admin.menu100.php @@ -6,7 +6,6 @@ $menu['menu100'] = array( array('100280', '테마설정', G5_ADMIN_URL . '/theme.php', 'cf_theme', 1), array('100290', '메뉴설정', G5_ADMIN_URL . '/menu_list.php', 'cf_menu', 1), array('100300', '메일 테스트', G5_ADMIN_URL . '/sendmail_test.php', 'cf_mailtest'), - array('100320', '알림톡프리셋관리', G5_ADMIN_URL . '/alimtalkpreset.php', 'alimtalk_preset'), array('100310', '팝업레이어관리', G5_ADMIN_URL . '/newwinlist.php', 'scf_poplayer'), array('100800', '세션파일 일괄삭제', G5_ADMIN_URL . '/session_file_delete.php', 'cf_session', 1), array('100900', '캐시파일 일괄삭제', G5_ADMIN_URL . '/cache_file_delete.php', 'cf_cache', 1), diff --git a/adm/admin.menu500.shop_2of2.php b/adm/admin.menu500.shop_2of2.php index 863dd9cf6..95068e48a 100644 --- a/adm/admin.menu500.shop_2of2.php +++ b/adm/admin.menu500.shop_2of2.php @@ -8,7 +8,7 @@ $menu['menu500'] = array( array('500110', '매출현황', G5_ADMIN_URL . '/shop_admin/sale1.php', 'sst_order_stats'), array('500100', '상품판매순위', G5_ADMIN_URL . '/shop_admin/itemsellrank.php', 'sst_rank'), array('500120', '주문내역출력', G5_ADMIN_URL . '/shop_admin/orderprint.php', 'sst_print_order', 1), - array('500400', '재입고알림', G5_ADMIN_URL . '/shop_admin/itemstocksms.php', 'sst_stock_sms', 1), + array('500400', '재입고SMS알림', G5_ADMIN_URL . '/shop_admin/itemstocksms.php', 'sst_stock_sms', 1), array('500300', '이벤트관리', G5_ADMIN_URL . '/shop_admin/itemevent.php', 'scf_event'), array('500310', '이벤트일괄처리', G5_ADMIN_URL . '/shop_admin/itemeventlist.php', 'scf_event_mng'), array('500500', '배너관리', G5_ADMIN_URL . '/shop_admin/bannerlist.php', 'scf_banner', 1), diff --git a/adm/alimtalkpreset.php b/adm/alimtalkpreset.php deleted file mode 100644 index 24c5571ee..000000000 --- a/adm/alimtalkpreset.php +++ /dev/null @@ -1,57 +0,0 @@ - '쇼핑몰'"; - -if ($stx) { - $sql_search .= " and ( "; - switch ($sfl) { - default: - $sql_search .= " ({$sfl} like '%{$stx}%') "; - break; - } - $sql_search .= " ) "; -} - -if ($sst) { - $sql_order = " order by {$sst} {$sod} "; -} else { - $sql_order = " order by kp_id asc "; -} - -// 프리셋 테이블 조회 -$result = sql_query("SELECT * FROM {$g5['kakao5_preset_table']} {$sql_search} {$sql_order}"); -?> - - -

    카카오톡 프리셋 DB가 설치되지 않았습니다.

    -
    -

    카카오톡 프리셋 DB가 설치되지 않아 프리셋을 사용할 수 없습니다. -
    DB 업그레이드를 진행해주세요.

    -
    - - - - -

    카카오톡 발송 서비스를 사용할 수 없습니다.

    -
    -

    카카오톡 을 사용하지 않고 있기 때문에, 카카오톡 전송을 할 수 없습니다. -
    카카오톡 사용 설정은 환경설정 > 기본환경설정 > 기본알림환경 에서 카카오톡 사용을 변경해 주셔야 사용하실수 있습니다.

    -
    - - -', 1); // 카카오톡5 솔루션 js 추가 -?> - - -

    팝빌 카카오톡 발송 서비스를 사용할 수 없습니다.

    -
    -

    팝빌 서비스 설정이 되어 있지 않아, 프리셋 서비스를 사용할 수 없습니다. -
    팝빌 설정은 환경설정 > 기본환경설정 > 기본알림환경 에서 확인 및 설정해 주셔야 사용하실 수 있습니다.


    -

    * 설정 오류 내용 :

    -
    - -
    -

    템플릿관리를 통해 신청 후 승인된 템플릿만 사용가능합니다.
    템플릿 내용 작성 시, 동일한 [구분]에 속한 변수만 사용 가능하며, 아래에 제공된 변수 외의 항목을 입력할 경우 적용되지 않습니다.


    -

    아래 표의 #{변수명}만 템플릿 내용에 사용할 수 있으며, 실제 발송 시 값으로 자동 치환됩니다.
    ※ 표에 없는 변수는 치환되지 않습니다.

    -

    [사용 가능한 변수 리스트]

    -
    -
    개인정보 수집 및 이용
    KG이니시스 가상계좌 입금통보 URLKG이니시스 가상계좌
    입금통보 URL
    KG이니시스 관리자 > 거래내역 > 가상계좌 > 입금통보방식선택 > URL 수신 설정에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?> /settle_inicis_common.php
    NICEPAY 가상계좌 입금통보 URLNICEPAY 가상계좌
    입금통보 URL
    NICEPAY 관리자 > 가맹점관리자페이지 설정 (메인화면 → 가맹점정보 클릭)에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?> /settle_nicepay_common.php
    토스페이먼츠v2 가상계좌
    입금통보 URL
    + 토스페이먼츠 상점관리자 > 개발자센터 > 웹훅 > 웹훅 등록하기에 URL에 넣으시고, 구독할 이벤트를 [DEPOSIT_CALLBACK]을 선택하셔야 상점에 자동으로 입금 통보됩니다."); ?> + /settle_toss_common.php
    @@ -687,6 +701,7 @@ if(!$default['de_kakaopay_cancelpwd']){
    @@ -776,6 +791,7 @@ if(!$default['de_kakaopay_cancelpwd']){ @@ -839,6 +855,20 @@ if(!$default['de_kakaopay_cancelpwd']){
    + 개발자센터 -> API키 -> 클라이언트 키에서 확인하실 수 있습니다. 예) live_ck_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_ck_tosspayment"); ?> + +
    + 개발자센터 -> API키 -> 시크릿 키에서 확인하실 수 있습니다. 예) live_sk_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_sk_tosspayment"); ?> + +

    @@ -1070,6 +1100,9 @@ if(!$default['de_kakaopay_cancelpwd']){ 실결제 관리자 테스트 관리자 + @@ -1095,6 +1128,9 @@ if(!$default['de_kakaopay_cancelpwd']){
    • 테스트결제의 상점관리자 로그인 정보는 토스페이먼츠 상점아이디 첫 글자에 t를 추가해서 로그인하시기 바랍니다. 예) tsi_lguplus
    +
      +
    • 테스트 결제 시 상점관리자 로그인 정보는 실결제용 키와는 다르니 반드시 [테스트] API 연동 키로 로그인해야 합니다. 예) test_ck_toss
    • +
    • 일반결제의 테스트 사이트 mid는 INIpayTest 이며, 에스크로 결제의 테스트 사이트 mid는 iniescrow0 입니다.
    diff --git a/adm/shop_admin/configformupdate.php b/adm/shop_admin/configformupdate.php index edbe82937..adfb06ea8 100644 --- a/adm/shop_admin/configformupdate.php +++ b/adm/shop_admin/configformupdate.php @@ -159,6 +159,8 @@ $check_sanitize_keys = array( 'de_kcp_site_key', //NHN KCP SITE KEY 'cf_lg_mid', //LG유플러스 상점아이디 'cf_lg_mert_key', //LG유플러스 MERT KEY +'cf_toss_client_key', //토스페이먼츠 MERT KEY +'cf_toss_secret_key', //토스페이먼츠 MERT KEY 'de_inicis_mid', //KG이니시스 상점아이디 'de_inicis_iniapi_key', //KG이니시스 INIAPI KEY 'de_inicis_iniapi_iv', //KG이니시스 INIAPI IV @@ -465,7 +467,9 @@ $sql = " update {$g5['config_table']} cf_icode_server_port = '{$_POST['cf_icode_server_port']}', cf_icode_token_key = '{$cf_icode_token_key}', cf_lg_mid = '{$cf_lg_mid}', - cf_lg_mert_key = '{$cf_lg_mert_key}' "; + cf_lg_mert_key = '{$cf_lg_mert_key}', + cf_toss_client_key = '{$cf_toss_client_key}', + cf_toss_secret_key = '{$cf_toss_secret_key}' "; sql_query($sql); run_event('shop_admin_configformupdate'); diff --git a/adm/shop_admin/orderform.php b/adm/shop_admin/orderform.php index baffdc4de..02e231006 100644 --- a/adm/shop_admin/orderform.php +++ b/adm/shop_admin/orderform.php @@ -283,7 +283,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js

    주문, 입금, 준비, 배송, 완료는 장바구니와 주문서 상태를 모두 변경하지만, 취소, 반품, 품절은 장바구니의 상태만 변경하며, 주문서 상태는 변경하지 않습니다.

    개별적인(이곳에서의) 상태 변경은 모든 작업을 수동으로 처리합니다. 예를 들어 주문에서 입금으로 상태 변경시 입금액(결제금액)을 포함한 모든 정보는 수동 입력으로 처리하셔야 합니다.

    - +

    * 알림톡 프리셋: [준비, 완료, 취소, 반품, 품절]자동으로 발송되며, [입금완료, 배송]결제상세정보에서 수동으로 발송하셔야 합니다.

    @@ -533,6 +533,10 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js $pg_url = 'https://npg.nicepay.co.kr/'; $pg_test = 'NICEPAY'; break; + case 'toss': + $pg_url = 'https://app.tosspayments.com'; + $pg_test = '토스페이먼츠 '; + break; default: $pg_url = 'http://admin8.kcp.co.kr'; $pg_test = 'KCP'; @@ -634,6 +638,8 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$od['od_id'].'\',\''.$od['od_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($od['od_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$od['od_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $cash = unserialize($od['od_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; @@ -844,7 +850,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js diff --git a/adm/shop_admin/orderformcartupdate.php b/adm/shop_admin/orderformcartupdate.php index 84bcb5681..055213e45 100644 --- a/adm/shop_admin/orderformcartupdate.php +++ b/adm/shop_admin/orderformcartupdate.php @@ -224,6 +224,10 @@ if (in_array($_POST['ct_status'], $status_cancel)) { $pg_res_msg = $xpay->Response_Msg(); } break; + case 'toss': + $cancel_msg = '쇼핑몰 운영자 승인 취소'; + include_once(G5_SHOP_PATH.'/toss/toss_cancel.php'); + break; case 'inicis': include_once(G5_SHOP_PATH.'/settle_inicis.inc.php'); $cancel_msg = '쇼핑몰 운영자 승인 취소'; diff --git a/adm/shop_admin/personalpayform.php b/adm/shop_admin/personalpayform.php index 6c0005511..d67df8d51 100644 --- a/adm/shop_admin/personalpayform.php +++ b/adm/shop_admin/personalpayform.php @@ -210,6 +210,8 @@ if(!sql_query(" select pp_cash from {$g5['g5_shop_personalpay_table']} limit 1 " break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$pp['pp_id'].'\',\''.$pp['pp_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$pp['pp_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $cash = unserialize($pp['pp_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; diff --git a/install/gnuboard5.sql b/install/gnuboard5.sql index 8c2fdb570..5b11e86ff 100644 --- a/install/gnuboard5.sql +++ b/install/gnuboard5.sql @@ -300,6 +300,8 @@ CREATE TABLE IF NOT EXISTS `g5_config` ( `cf_cert_kcp_enckey` varchar(100) NOT NULL DEFAULT '', `cf_lg_mid` varchar(100) NOT NULL DEFAULT '', `cf_lg_mert_key` varchar(100) NOT NULL DEFAULT '', + `cf_toss_client_key` varchar(100) NOT NULL DEFAULT '', + `cf_toss_secret_key` varchar(100) NOT NULL DEFAULT '', `cf_cert_limit` int(11) NOT NULL DEFAULT '0', `cf_cert_req` tinyint(4) NOT NULL DEFAULT '0', `cf_sms_use` varchar(255) NOT NULL DEFAULT '', diff --git a/mobile/shop/orderform.sub.php b/mobile/shop/orderform.sub.php index d4befcae4..e6f9008b9 100644 --- a/mobile/shop/orderform.sub.php +++ b/mobile/shop/orderform.sub.php @@ -62,6 +62,9 @@ ob_start(); $comm_free_mny = 0; // 면세금액 $tot_tax_mny = 0; + // 토스페이먼츠 escrowProducts 배열 생성 + $escrow_products = array(); + for ($i=0; $row=sql_fetch_array($result); $i++) { // 합계금액 계산 @@ -114,6 +117,15 @@ ob_start(); $point = $sum['point']; $sell_price = $sum['price']; + + // 토스페이먼츠 escrowProducts 배열에 상품 정보 추가 + $escrow_products[] = array( + 'id' => $row['ct_id'], + 'name' => $row['it_name'], + 'code' => $row['it_id'], + 'unitPrice' => (int) $row['ct_price'], + 'quantity' => (int) $row['ct_qty'] + ); $cp_button = ''; // 쿠폰 @@ -577,7 +589,7 @@ if($is_kakaopay_use) { // 계좌이체 사용 if ($default['de_iche_use']) { $multi_settle++; - echo '
  • '.PHP_EOL; + echo '
  • '.PHP_EOL; $checked = ''; } @@ -1412,6 +1424,55 @@ function pay_approval() f.LGD_TAXFREEAMOUNT.value = pf.comm_free_mny.value; + + var pay_method = ""; + switch(settle_method) { + case "계좌이체": + pay_method = "TRANSFER"; + break; + case "가상계좌": + pay_method = "VIRTUAL_ACCOUNT"; + break; + case "휴대폰": + pay_method = "MOBILE_PHONE"; + break; + case "신용카드": + pay_method = "CARD"; + break; + case "간편결제": + pay_method = "CARD"; + break; + } + f.method.value = pay_method; + f.orderId.value = ''; + f.orderName.value = ''; + + f.customerName.value = pf.od_name.value; + f.customerEmail.value = pf.od_email.value; + f.customerMobilePhone.value = pf.od_hp.value.replace(/[^0-9]/g, ''); + if (f.customerMobilePhone.value == '') { + f.customerMobilePhone.value = pf.od_tel.value.replace(/[^0-9]/g, ''); + } + + f.cardUseCardPoint.value = false; + f.cardUseAppCardOnly.value = false; + + + f.cardUseEscrow.value = 'true'; + f.escrowProducts.value = JSON.stringify(); + + + if(settle_method == "간편결제") { + f.cardflowMode.value = 'DIRECT'; + } + + f.amountCurrency.value = 'KRW'; + f.amountValue.value = f.good_mny.value; + + f.taxFreeAmount.value = pf.comm_free_mny.value; + + f.windowTarget.value = 'self'; + var paymethod = ""; var width = 330; diff --git a/mobile/shop/orderformupdate.php b/mobile/shop/orderformupdate.php index 8a75af656..e80d2bb98 100644 --- a/mobile/shop/orderformupdate.php +++ b/mobile/shop/orderformupdate.php @@ -8,6 +8,7 @@ $post_enc_data = isset($_POST['enc_data']) ? $_POST['enc_data'] : ''; $post_enc_info = isset($_POST['enc_info']) ? $_POST['enc_info'] : ''; $post_tran_cd = isset($_POST['tran_cd']) ? $_POST['tran_cd'] : ''; $post_lgd_paykey = isset($_POST['LGD_PAYKEY']) ? $_POST['LGD_PAYKEY'] : ''; +$paymentKey = isset($_POST['paymentKey']) ? $_POST['paymentKey'] : ''; //삼성페이 또는 lpay 또는 이니시스 카카오페이 요청으로 왔다면 현재 삼성페이 또는 lpay 또는 이니시스 카카오페이는 이니시스 밖에 없으므로 $default['de_pg_service'] 값을 이니시스로 변경한다. if( is_inicis_order_pay($od_settle_case) && !empty($_POST['P_HASH']) ){ @@ -42,6 +43,9 @@ if($od_settle_case != '무통장' && $od_settle_case != 'KAKAOPAY') { if($default['de_pg_service'] == 'lg' && ! $post_lgd_paykey) alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); + if($default['de_pg_service'] == 'toss' && ! $paymentKey) + alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); + if($default['de_pg_service'] == 'inicis' && ! $post_p_hash) alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); } @@ -358,6 +362,9 @@ else if ($od_settle_case == "계좌이체") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -387,6 +394,9 @@ else if ($od_settle_case == "가상계좌") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -416,6 +426,9 @@ else if ($od_settle_case == "휴대폰") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -443,6 +456,9 @@ else if ($od_settle_case == "신용카드") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -472,6 +488,9 @@ else if ($od_settle_case == "간편결제") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -543,6 +562,9 @@ if($tno) { 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; @@ -699,6 +721,9 @@ if(! $result || ! (isset($exists_order['od_id']) && $od_id && $exists_order['od_ 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; @@ -761,6 +786,9 @@ if(!$result) { 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; diff --git a/mobile/shop/orderinquiryview.php b/mobile/shop/orderinquiryview.php index c3f328c1a..185ad2534 100644 --- a/mobile/shop/orderinquiryview.php +++ b/mobile/shop/orderinquiryview.php @@ -338,6 +338,8 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $hp_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($od['od_pg'] == 'toss') { + $hp_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/phone?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $hp_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { @@ -359,6 +361,8 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $card_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($od['od_pg'] == 'toss') { + $card_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/redirection?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { $card_receipt_script = 'window.open(\'https://npg.nicepay.co.kr/issue/IssueLoader.do?type=0&TID='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { @@ -432,6 +436,8 @@ if($od['od_pg'] == 'lg') { break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$od['od_id'].'\',\''.$od['od_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($od['od_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$od['od_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $cash = unserialize($od['od_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; diff --git a/mobile/shop/personalpayform.sub.php b/mobile/shop/personalpayform.sub.php index 3034a7b00..43b54aef6 100644 --- a/mobile/shop/personalpayform.sub.php +++ b/mobile/shop/personalpayform.sub.php @@ -53,8 +53,18 @@ $tablet_size = "1.0"; // 화면 사이즈 조정 - 기기화면에 맞게 수정 $checked = ''; $escrow_title = ""; + $escrow_products = array(); // 토스페이먼츠 escrowProducts 배열 생성 if ($default['de_escrow_use']) { $escrow_title = "에스크로 "; + + // 토스페이먼츠 escrowProducts 배열에 상품 정보 추가 + $escrow_products[] = array( + 'id' => $pp['pp_id'], + 'name' => $pp['pp_name'].'님 개인결제', + 'code' => $pp['pp_id'], + 'unitPrice' => (int) $pp['pp_price'], + 'quantity' => (int) 1 + ); } if ($default['de_vbank_use'] || $default['de_iche_use'] || $default['de_card_use'] || $default['de_hp_use']) { @@ -73,7 +83,7 @@ $tablet_size = "1.0"; // 화면 사이즈 조정 - 기기화면에 맞게 수정 // 계좌이체 사용 if ($default['de_iche_use']) { $multi_settle++; - echo '
  • '.PHP_EOL; + echo '
  • '.PHP_EOL; $checked = ''; } @@ -174,6 +184,51 @@ function pay_approval() f.LGD_TAXFREEAMOUNT.value = pf.comm_free_mny.value; + + var pay_method = ""; + switch(settle_method) { + case "계좌이체": + pay_method = "TRANSFER"; + break; + case "가상계좌": + pay_method = "VIRTUAL_ACCOUNT"; + break; + case "휴대폰": + pay_method = "MOBILE_PHONE"; + break; + case "신용카드": + pay_method = "CARD"; + break; + case "간편결제": + pay_method = "CARD"; + break; + } + f.method.value = pay_method; + f.orderId.value = ''; + f.orderName.value = ''; + + f.customerName.value = pf.pp_name.value; + f.customerEmail.value = pf.pp_email.value; + f.customerMobilePhone.value = pf.pp_hp.value.replace(/[^0-9]/g, ''); + + f.cardUseCardPoint.value = false; + f.cardUseAppCardOnly.value = false; + + + f.cardUseEscrow.value = 'true'; + f.escrowProducts.value = JSON.stringify(); + + + if(settle_method == "간편결제") { + f.cardflowMode.value = 'DIRECT'; + } + + f.amountCurrency.value = 'KRW'; + f.amountValue.value = f.good_mny.value; + + f.taxFreeAmount.value = pf.comm_free_mny.value; + + f.windowTarget.value = 'self'; var paymethod = ""; var width = 330; diff --git a/mobile/shop/personalpayformupdate.php b/mobile/shop/personalpayformupdate.php index aa3acace8..c0da09f7b 100644 --- a/mobile/shop/personalpayformupdate.php +++ b/mobile/shop/personalpayformupdate.php @@ -9,6 +9,7 @@ $post_enc_info = isset($_POST['enc_info']) ? $_POST['enc_info'] : ''; $post_enc_data = isset($_POST['enc_data']) ? $_POST['enc_data'] : ''; $post_lgd_paykey = isset($_POST['LGD_PAYKEY']) ? $_POST['LGD_PAYKEY'] : ''; +$paymentKey = isset($_POST['paymentKey']) ? $_POST['paymentKey'] : ''; $post_p_hash = isset($_POST['P_HASH']) ? $_POST['P_HASH'] : ''; @@ -22,6 +23,9 @@ if($default['de_pg_service'] == 'kcp' && ($post_tran_cd === '' || $post_enc_info if($default['de_pg_service'] == 'lg' && ! $post_lgd_paykey) alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); +if($default['de_pg_service'] == 'toss' && ! $paymentKey) + alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); + if($default['de_pg_service'] == 'inicis' && ! $post_p_hash) alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); @@ -32,6 +36,13 @@ $pp = sql_fetch($sql); if(! (isset($pp['pp_id']) && $pp['pp_id'])) alert('개인결제 정보가 존재하지 않습니다.', G5_SHOP_URL.'/personalpay.php'); +// PG사의 가상계좌 또는 계좌이체의 자동 현금영수증 초기배열값 +$pg_receipt_infos = array( + 'od_cash' => 0, + 'od_cash_no' => '', + 'od_cash_info' => '', +); + $hash_data = md5($pp_id.$good_mny.$pp['pp_time']); if($pp['pp_tno']){ @@ -54,6 +65,9 @@ if ($pp_settle_case == "계좌이체") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -79,6 +93,9 @@ else if ($pp_settle_case == "가상계좌") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -105,6 +122,9 @@ else if ($pp_settle_case == "휴대폰") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -128,6 +148,9 @@ else if ($pp_settle_case == "신용카드") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_MSHOP_PATH.'/inicis/pay_result.php'; break; @@ -159,6 +182,9 @@ if((int)$pp['pp_price'] !== (int)$pg_price) { case 'lg': include G5_SHOP_PATH.'/lg/xpay_cancel.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; break; @@ -186,7 +212,10 @@ $sql = " update {$g5['g5_shop_personalpay_table']} pp_bank_account = '$pp_bank_account', pp_deposit_name = '$pp_deposit_name', pp_receipt_time = '$pp_receipt_time', - pp_receipt_ip = '{$_SERVER['REMOTE_ADDR']}' + pp_receipt_ip = '{$_SERVER['REMOTE_ADDR']}', + pp_cash = '{$pg_receipt_infos['od_cash']}', + pp_cash_no = '{$pg_receipt_infos['od_cash_no']}', + pp_cash_info = '{$pg_receipt_infos['od_cash_info']}' where pp_id = '{$pp['pp_id']}' "; $result = sql_query($sql, false); @@ -197,6 +226,9 @@ if(!$result) { 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; @@ -224,6 +256,9 @@ if($pp_receipt_price > 0 && $pp['pp_id'] && $pp['od_id']) { od_settle_case = '$pp_settle_case', od_deposit_name = '$pp_deposit_name', od_bank_account = '$pp_bank_account', + od_cash = '{$pg_receipt_infos['od_cash']}', + od_cash_no = '{$pg_receipt_infos['od_cash_no']}', + od_cash_info = '{$pg_receipt_infos['od_cash_info']}', od_shop_memo = concat(od_shop_memo, \"\\n개인결제 ".$pp['pp_id']." 로 결제완료 - ".$pp_receipt_time."\") where od_id = '{$pp['od_id']}' "; $result = sql_query($sql, false); @@ -235,6 +270,9 @@ if($pp_receipt_price > 0 && $pp['pp_id'] && $pp['od_id']) { case 'lg': include G5_SHOP_PATH.'/lg/xpay_cancel.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; break; diff --git a/mobile/shop/personalpayresult.php b/mobile/shop/personalpayresult.php index 4e15dab76..856abf3bc 100644 --- a/mobile/shop/personalpayresult.php +++ b/mobile/shop/personalpayresult.php @@ -145,6 +145,8 @@ if($pp['pp_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $hp_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $hp_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/phone?transactionId='.$pp['pp_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $hp_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$pp['pp_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'nicepay') { @@ -166,6 +168,8 @@ if($pp['pp_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $card_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $card_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/redirection?transactionId='.$pp['pp_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'nicepay') { $card_receipt_script = 'window.open(\'https://npg.nicepay.co.kr/issue/IssueLoader.do?type=0&TID='.$pp['pp_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { @@ -221,6 +225,8 @@ if($pp['pp_pg'] == 'lg') { break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$pp['pp_id'].'\',\''.$pp['pp_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$pp['pp_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $cash = unserialize($pp['pp_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; diff --git a/mobile/shop/settle_toss.inc.php b/mobile/shop/settle_toss.inc.php new file mode 100644 index 000000000..59e47a8e0 --- /dev/null +++ b/mobile/shop/settle_toss.inc.php @@ -0,0 +1,15 @@ +setPaymentHeader(); +?> \ No newline at end of file diff --git a/mobile/shop/toss/_common.php b/mobile/shop/toss/_common.php new file mode 100644 index 000000000..7c37c6ca3 --- /dev/null +++ b/mobile/shop/toss/_common.php @@ -0,0 +1,6 @@ +쇼핑몰 설치 후 이용해 주십시오.

    '); +define('_SHOP_', true); \ No newline at end of file diff --git a/mobile/shop/toss/orderform.1.php b/mobile/shop/toss/orderform.1.php new file mode 100644 index 000000000..d3b6e9017 --- /dev/null +++ b/mobile/shop/toss/orderform.1.php @@ -0,0 +1,33 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/mobile/shop/toss/orderform.2.php b/mobile/shop/toss/orderform.2.php new file mode 100644 index 000000000..6587ed56b --- /dev/null +++ b/mobile/shop/toss/orderform.2.php @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + 취소 +
    diff --git a/mobile/shop/toss/orderform.3.php b/mobile/shop/toss/orderform.3.php new file mode 100644 index 000000000..0f09cf7f0 --- /dev/null +++ b/mobile/shop/toss/orderform.3.php @@ -0,0 +1,3 @@ + diff --git a/mobile/shop/toss/returnurl.php b/mobile/shop/toss/returnurl.php new file mode 100644 index 000000000..e62a5cf78 --- /dev/null +++ b/mobile/shop/toss/returnurl.php @@ -0,0 +1,77 @@ + + +'.PHP_EOL; + +echo make_order_field($data, $exclude); + +echo ''.PHP_EOL; +?> + +
    +
    + + 주문완료 중입니다. 잠시만 기다려 주십시오. +
    +
    + + + + + +
    + $value) { + if (isset($_REQUEST[$key]) && $_REQUEST[$key]) { + $value = $_REQUEST[$key]; + } + if (is_array($value)) { + $value = implode(',', $value); + } + if ($key === 'escrowProducts') { + $value = str_replace("\\", "", $value); + echo ''.PHP_EOL; + } else { + echo ''.PHP_EOL; + } +} +?> +
    + + + + + + $row['ct_id'], + 'name' => $row['it_name'], + 'code' => $row['it_id'], + 'unitPrice' => (int) $row['ct_price'], + 'quantity' => (int) $row['ct_qty'] + ); + // 쿠폰 $cp_button = ''; if($is_member) { @@ -596,7 +608,8 @@ if($is_kakaopay_use) { // 계좌이체 사용 if ($default['de_iche_use']) { $multi_settle++; - echo ' '.PHP_EOL; + // 토스페이먼츠 v2 - 퀵계좌이체 명칭 사용 + echo ' '.PHP_EOL; $checked = ''; } @@ -1541,6 +1554,28 @@ function forderform_check(f) f.LGD_CUSTOM_FIRSTPAY.value = "무통장"; break; } + + switch(settle_method) + { + case "계좌이체": + f.method.value = "TRANSFER"; + break; + case "가상계좌": + f.method.value = "VIRTUAL_ACCOUNT"; + break; + case "휴대폰": + f.method.value = "MOBILE_PHONE"; + break; + case "신용카드": + f.method.value = "CARD"; + break; + case "간편결제": + f.method.value = "CARD"; + break; + default: + f.method.value = "무통장"; + break; + } switch(settle_method) { @@ -1676,6 +1711,62 @@ function forderform_check(f) f.submit(); } + + + f.orderId.value = ''; + f.orderName.value = ''; + + f.customerName.value = f.od_name.value; + f.customerEmail.value = f.od_email.value; + f.customerMobilePhone.value = f.od_hp.value.replace(/[^0-9]/g, ''); + if (f.customerMobilePhone.value == '') { + f.customerMobilePhone.value = f.od_tel.value.replace(/[^0-9]/g, ''); + } + + f.cardUseCardPoint.value = false; + f.cardUseAppCardOnly.value = false; + + + f.cardUseEscrow.value = 'true'; + f.escrowProducts.value = JSON.stringify(); + + + if(settle_method == "간편결제") { + f.cardflowMode.value = 'DIRECT'; + } + + f.amountCurrency.value = 'KRW'; + f.amountValue.value = f.good_mny.value; + + f.taxFreeAmount.value = f.comm_free_mny.value; + + f.windowTarget.value = 'iframe'; + + if(f.method.value != "무통장") { + // 주문정보 임시저장 + var order_data = $(f).serialize(); + var save_result = ""; + $.ajax({ + type: "POST", + data: order_data, + url: g5_url+"/shop/ajax.orderdatasave.php", + cache: false, + async: false, + success: function(data) { + save_result = data; + } + }); + + if(save_result) { + alert(save_result); + return false; + } + + launchCrossPlatform(f); + } else { + f.submit(); + } + f.price.value = f.good_mny.value; diff --git a/shop/orderformupdate.php b/shop/orderformupdate.php index 4e84ba4a8..cac0daf16 100644 --- a/shop/orderformupdate.php +++ b/shop/orderformupdate.php @@ -20,6 +20,12 @@ if(($od_settle_case != '무통장' && $od_settle_case != 'KAKAOPAY') && $default alert('결제등록 요청 후 주문해 주십시오.'); } +// 토스 v2 대응 +if(($od_settle_case != '무통장' && $od_settle_case != 'KAKAOPAY') && $default['de_pg_service'] == 'toss' && !$_POST['paymentKey']){ + if(function_exists('add_order_post_log')) add_order_post_log('결제등록 요청 후 주문해 주십시오.'); + alert('결제등록 요청 후 주문해 주십시오.'); +} + // 장바구니가 비어있는가? if (get_session("ss_direct")) $tmp_cart_id = get_session('ss_cart_direct'); @@ -337,6 +343,9 @@ else if ($od_settle_case == "계좌이체") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -367,6 +376,10 @@ else if ($od_settle_case == "가상계좌") include G5_SHOP_PATH.'/lg/xpay_result.php'; $od_receipt_time = '0000-00-00 00:00:00'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + $od_receipt_time = '0000-00-00 00:00:00'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; $od_app_no = $app_no; @@ -395,6 +408,9 @@ else if ($od_settle_case == "휴대폰") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -422,6 +438,9 @@ else if ($od_settle_case == "신용카드") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -451,6 +470,9 @@ else if ($od_settle_case == "간편결제" || (($od_settle_case == "lpay" || $od case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -510,6 +532,9 @@ if($tno) { 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; @@ -653,6 +678,9 @@ if(! $result || ! (isset($exists_order['od_id']) && $od_id && $exists_order['od_ 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; @@ -712,6 +740,9 @@ if(!$result) { 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; diff --git a/shop/orderinquirycancel.php b/shop/orderinquirycancel.php index dda82c10b..cf562b900 100644 --- a/shop/orderinquirycancel.php +++ b/shop/orderinquirycancel.php @@ -65,6 +65,10 @@ if($od['od_tno']) { alert($msg); } break; + case 'toss': + $cancel_msg = '주문자 본인 취소-'.$cancel_memo; + include_once(G5_SHOP_PATH.'/toss/toss_cancel.php'); + break; case 'inicis': include_once(G5_SHOP_PATH.'/settle_inicis.inc.php'); $cancel_msg = '주문자 본인 취소-'.$cancel_memo; diff --git a/shop/orderinquiryview.php b/shop/orderinquiryview.php index 297c5208d..bba85f21f 100644 --- a/shop/orderinquiryview.php +++ b/shop/orderinquiryview.php @@ -334,6 +334,8 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $hp_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($od['od_pg'] == 'toss') { + $hp_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/phone?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $hp_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { @@ -355,6 +357,8 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $card_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($od['od_pg'] == 'toss') { + $card_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/redirection?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $card_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { @@ -429,6 +433,8 @@ if($od['od_pg'] == 'lg') { break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$od['od_id'].'\',\''.$od['od_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($od['od_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$od['od_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'inicis') { $cash = unserialize($od['od_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; diff --git a/shop/personalpayform.sub.php b/shop/personalpayform.sub.php index a80153d8d..4d37d8387 100644 --- a/shop/personalpayform.sub.php +++ b/shop/personalpayform.sub.php @@ -62,8 +62,18 @@ require_once(G5_SHOP_PATH.'/'.$default['de_pg_service'].'/orderform.1.php'); $checked = ''; $escrow_title = ""; + $escrow_products = array(); // 토스페이먼츠 escrowProducts 배열 생성 if ($default['de_escrow_use']) { $escrow_title = "에스크로
    "; + + // 토스페이먼츠 escrowProducts 배열에 상품 정보 추가 + $escrow_products[] = array( + 'id' => $pp['pp_id'], + 'name' => $pp['pp_name'].'님 개인결제', + 'code' => $pp['pp_id'], + 'unitPrice' => (int) $pp['pp_price'], + 'quantity' => (int) 1 + ); } if ($default['de_vbank_use'] || $default['de_iche_use'] || $default['de_card_use'] || $default['de_hp_use']) { @@ -89,7 +99,7 @@ require_once(G5_SHOP_PATH.'/'.$default['de_pg_service'].'/orderform.1.php'); // 계좌이체 사용 if ($default['de_iche_use']) { $multi_settle++; - echo ' '.PHP_EOL; + echo ' '.PHP_EOL; $checked = ''; } ?> @@ -233,6 +243,28 @@ function forderform_check(f) f.LGD_CUSTOM_FIRSTPAY.value = "무통장"; break; } + + switch(settle_method) + { + case "계좌이체": + f.method.value = "TRANSFER"; + break; + case "가상계좌": + f.method.value = "VIRTUAL_ACCOUNT"; + break; + case "휴대폰": + f.method.value = "MOBILE_PHONE"; + break; + case "신용카드": + f.method.value = "CARD"; + break; + case "간편결제": + f.method.value = "CARD"; + break; + default: + f.method.value = "무통장"; + break; + } switch(settle_method) { @@ -310,6 +342,59 @@ function forderform_check(f) f.submit(); } + + + f.orderId.value = ''; + f.orderName.value = ''; + + f.customerName.value = f.pp_name.value; + f.customerEmail.value = f.pp_email.value; + f.customerMobilePhone.value = f.pp_hp.value.replace(/[^0-9]/g, ''); + + f.cardUseCardPoint.value = false; + f.cardUseAppCardOnly.value = false; + + + f.cardUseEscrow.value = 'true'; + f.escrowProducts.value = JSON.stringify(); + + + if(settle_method == "간편결제") { + f.cardflowMode.value = 'DIRECT'; + } + + f.amountCurrency.value = 'KRW'; + f.amountValue.value = f.good_mny.value; + + f.taxFreeAmount.value = f.comm_free_mny.value; + + f.windowTarget.value = 'iframe'; + + if(f.method.value != "무통장") { + // 주문정보 임시저장 + var order_data = $(f).serialize(); + var save_result = ""; + $.ajax({ + type: "POST", + data: order_data, + url: g5_url+"/shop/ajax.orderdatasave.php", + cache: false, + async: false, + success: function(data) { + save_result = data; + } + }); + + if(save_result) { + alert(save_result); + return false; + } + + launchCrossPlatform(f); + } else { + f.submit(); + } + f.price.value = f.good_mny.value; f.buyername.value = f.pp_name.value; diff --git a/shop/personalpayformupdate.php b/shop/personalpayformupdate.php index 6a25e8af6..2e09e09d5 100644 --- a/shop/personalpayformupdate.php +++ b/shop/personalpayformupdate.php @@ -2,28 +2,42 @@ include_once('./_common.php'); include_once(G5_LIB_PATH.'/mailer.lib.php'); +$page_return_url = G5_SHOP_URL.'/personalpayform.php?pp_id='.get_session('ss_personalpay_id'); + $pp_id = $_POST['pp_id'] = isset($_POST['pp_id']) ? preg_replace('/[^0-9]/', '', $_POST['pp_id']) : 0; $good_mny = $_POST['good_mny'] = isset($_POST['good_mny']) ? preg_replace('/[^0-9]/', '', $_POST['good_mny']) : 0; $post_lgd_paykey = isset($_POST['LGD_PAYKEY']) ? $_POST['LGD_PAYKEY'] : ''; +$paymentKey = isset($_POST['paymentKey']) ? $_POST['paymentKey'] : ''; $pp_deposit_name = ''; if($default['de_pg_service'] == 'lg' && ! $post_lgd_paykey) - alert('결제등록 요청 후 결제해 주십시오.'); + alert('결제등록 요청 후 결제해 주십시오.', $page_return_url); + +if($default['de_pg_service'] == 'toss' && ! $paymentKey) + alert('결제등록 요청 후 주문해 주십시오.', $page_return_url); + +set_session('ss_order_id', $pp_id); // 개인결제 정보 $pp_check = false; $sql = " select * from {$g5['g5_shop_personalpay_table']} where pp_id = '{$pp_id}' and pp_use = '1' "; $pp = sql_fetch($sql); if(! (isset($pp['pp_id']) && $pp['pp_id'])) - alert('개인결제 정보가 존재하지 않습니다.'); + alert('개인결제 정보가 존재하지 않습니다.', $page_return_url); if($pp['pp_tno']) - alert('이미 결제하신 개인결제 내역입니다.'); + alert('이미 결제하신 개인결제 내역입니다.', $page_return_url); $hash_data = md5($pp_id.$good_mny.$pp['pp_time']); if($pp_id != get_session('ss_personalpay_id') || $hash_data != get_session('ss_personalpay_hash')) die('개인결제 정보가 올바르지 않습니다.'); +// PG사의 가상계좌 또는 계좌이체의 자동 현금영수증 초기배열값 +$pg_receipt_infos = array( + 'od_cash' => 0, + 'od_cash_no' => '', + 'od_cash_info' => '', +); if ($pp_settle_case == "계좌이체") { @@ -31,6 +45,9 @@ if ($pp_settle_case == "계좌이체") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -56,6 +73,9 @@ else if ($pp_settle_case == "가상계좌") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -81,6 +101,9 @@ else if ($pp_settle_case == "휴대폰") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -104,6 +127,9 @@ else if ($pp_settle_case == "신용카드") case 'lg': include G5_SHOP_PATH.'/lg/xpay_result.php'; break; + case 'toss': + include G5_SHOP_PATH.'/toss/toss_result.php'; + break; case 'inicis': include G5_SHOP_PATH.'/inicis/inistdpay_result.php'; break; @@ -134,6 +160,9 @@ if((int)$pp['pp_price'] !== (int)$pg_price) { 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; @@ -164,7 +193,10 @@ $sql = " update {$g5['g5_shop_personalpay_table']} pp_bank_account = '$pp_bank_account', pp_deposit_name = '$pp_deposit_name', pp_receipt_time = '$pp_receipt_time', - pp_receipt_ip = '{$_SERVER['REMOTE_ADDR']}' + pp_receipt_ip = '{$_SERVER['REMOTE_ADDR']}', + pp_cash = '{$pg_receipt_infos['od_cash']}', + pp_cash_no = '{$pg_receipt_infos['od_cash_no']}', + pp_cash_info = '{$pg_receipt_infos['od_cash_info']}' where pp_id = '{$pp['pp_id']}' "; $result = sql_query($sql, false); @@ -202,6 +234,9 @@ if($pp_receipt_price > 0 && $pp['pp_id'] && $pp['od_id']) { od_settle_case = '$pp_settle_case', od_deposit_name = '$pp_deposit_name', od_bank_account = '$pp_bank_account', + od_cash = '{$pg_receipt_infos['od_cash']}', + od_cash_no = '{$pg_receipt_infos['od_cash_no']}', + od_cash_info = '{$pg_receipt_infos['od_cash_info']}', od_shop_memo = concat(od_shop_memo, \"\\n개인결제 ".$pp['pp_id']." 로 결제완료 - ".$pp_receipt_time."\") where od_id = '{$pp['od_id']}' "; $result = sql_query($sql, false); @@ -213,9 +248,12 @@ if($pp_receipt_price > 0 && $pp['pp_id'] && $pp['od_id']) { 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; + include G5_SHOP_PATH.'/inicis/inipay_cancel.php'; + break; default: include G5_SHOP_PATH.'/kcp/pp_ax_hub_cancel.php'; break; diff --git a/shop/personalpayresult.php b/shop/personalpayresult.php index 9113bd97d..150eb4eae 100644 --- a/shop/personalpayresult.php +++ b/shop/personalpayresult.php @@ -149,6 +149,8 @@ if($pp['pp_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $hp_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $hp_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/phone?transactionId='.$pp['pp_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $hp_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$pp['pp_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'nicepay') { @@ -170,6 +172,8 @@ if($pp['pp_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $card_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $card_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/redirection?transactionId='.$pp['pp_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $card_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$pp['pp_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'nicepay') { @@ -225,6 +229,8 @@ if($pp['pp_pg'] == 'lg') { break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$pp['pp_id'].'\',\''.$pp['pp_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; + } else if($pp['pp_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$pp['pp_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; } else if($pp['pp_pg'] == 'inicis') { $cash = unserialize($pp['pp_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; diff --git a/shop/settle_toss.inc.php b/shop/settle_toss.inc.php new file mode 100644 index 000000000..205853004 --- /dev/null +++ b/shop/settle_toss.inc.php @@ -0,0 +1,11 @@ +setPaymentHeader(); \ No newline at end of file diff --git a/shop/settle_toss_common.php b/shop/settle_toss_common.php new file mode 100644 index 000000000..07684cbfe --- /dev/null +++ b/shop/settle_toss_common.php @@ -0,0 +1,422 @@ +setPaymentHeader(); + +$orderResult = $toss->getPaymentByOrderId($TOSS_ORDERID); +$order_info = $toss->responseData; + +if (!$orderResult || $order_info['secret'] !== $TOSS_SECRET) { + $error_msg = isset($order_info['message']) ? $order_info['message'] : '주문 정보 조회 실패'; + $error_code = isset($order_info['code']) ? $order_info['code'] : 'UNKNOWN_ERROR'; + write_toss_log("주문 정보 조회 실패 - {$error_code} : {$error_msg}", $TOSS_ORDERID, $TOSS_STATUS); + http_response_code(400); + exit; +} + +// 결제 정보 +$paymentKey = isset($order_info["paymentKey"]) ? clean_xss_tags($order_info["paymentKey"]) : ''; // 결제 키 +$customerName = isset($order_info["virtualAccount"]["customerName"]) ? clean_xss_tags($order_info["virtualAccount"]["customerName"]) : ''; // 주문자명 (가상계좌 발급 시 고객명) +$depositorName = isset($order_info["virtualAccount"]["depositorName"]) ? clean_xss_tags($order_info["virtualAccount"]["depositorName"]) : ''; // 입금자명 (실제 입금자 입력 이름) +$totalAmount = isset($order_info["totalAmount"]) ? clean_xss_tags($order_info["totalAmount"]) : ''; // 입금 금액 (결제 총액) +$bankCode = isset($order_info["virtualAccount"]["bankCode"]) ? clean_xss_tags($order_info["virtualAccount"]["bankCode"]) : ''; // 은행코드 (가상계좌 발급 은행, 예: 11 → 농협) +$accountNumber = isset($order_info["virtualAccount"]["accountNumber"]) ? clean_xss_tags($order_info["virtualAccount"]["accountNumber"]) : ''; // 가상계좌 입금계좌번호 +$approvedAt = isset($order_info['approvedAt']) ? clean_xss_tags($order_info['approvedAt']) : ''; //입금일시 +$dueDate = isset($order_info["virtualAccount"]['dueDate']) ? clean_xss_tags($order_info['virtualAccount']['dueDate']) : ''; // 만료일시 +$receipt_time = $approvedAt ? (strtotime($approvedAt) !== false ? date("Y-m-d H:i:s", strtotime($approvedAt)) : '') : ''; +$due_time = $dueDate ? (strtotime($dueDate) !== false ? date("Y-m-d H:i:s", strtotime($dueDate)) : '') : ''; + +// 가상계좌 채번시 현금영수증 자동발급신청이 되었을 경우 전달되며 +// RcptTID에 값이 있는 경우만 발급처리 됨 +$RcptTID = isset($order_info['cashReceipt']['receiptKey']) ? clean_xss_tags($order_info['cashReceipt']['receiptKey']) : ''; // 현금영수증 거래번호 +$RcptAuthCode = isset($order_info['cashReceipt']['issueNumber']) ? clean_xss_tags($order_info['cashReceipt']['issueNumber']) : ''; // 현금영수증 승인번호 +// 현금영수증 구분(0:미발행, 1:소득공제용, 2:지출증빙용) +$RcptType = isset($order_info['cashReceipt']['type']) ? clean_xss_tags($order_info['cashReceipt']['type'] === '소득공제' ? '1' : ($order_info['cashReceipt']['type'] === '지출증빙' ? '2' : '0')) : '0'; +$RcptReceiptUrl = isset($order_info['cashReceipt']['receiptUrl']) ? clean_xss_tags($order_info['cashReceipt']['receiptUrl']) : ''; // 현금영수증 URL + +$result = false; + +/** + * 입금 완료 처리 + */ +if($TOSS_STATUS == "DONE"){ + + // 입금결과 처리 + $sql = " select pp_id, od_id from {$g5['g5_shop_personalpay_table']} where pp_id = '{$TOSS_ORDERID}' and pp_tno = '{$paymentKey}'"; + $row = sql_fetch($sql); + + if($row['pp_id']) { + // 개인결제 UPDATE + $add_update_sql = ''; + + // 현금영수증 발급시 1 또는 2 이면 + if ($RcptType) { + $add_update_sql = " + , pp_cash = '1', + pp_cash_no = '".$RcptAuthCode."', + pp_cash_info = '".serialize(array('TID'=>$RcptTID, 'ApplNum'=>$RcptAuthCode, 'AuthDate'=>$approvedAt, 'receiptUrl'=>$RcptReceiptUrl))."' + "; + } + + $sql = " update {$g5['g5_shop_personalpay_table']} + set pp_receipt_price = '$totalAmount', + pp_receipt_time = '$receipt_time', + pp_deposit_name = '$depositorName' + $add_update_sql + where pp_id = '$TOSS_ORDERID'"; + $result = sql_query($sql, false); + + if($row['od_id']) { + // 주문서 UPDATE + $sql = " update {$g5['g5_shop_order_table']} + set od_receipt_price = od_receipt_price + '$totalAmount', + od_receipt_time = '$receipt_time', + od_deposit_name = '$depositorName', + od_shop_memo = concat(od_shop_memo, \"\\n개인결제 ".$row['pp_id']." 로 결제완료 - ".$receipt_time."\") + where od_id = '{$row['od_id']}' "; + $result = sql_query($sql, FALSE); + } + } else { + // 주문내역에 secret 검증 추가 + $sql = " select od_id from {$g5['g5_shop_order_table']} where od_id = '$TOSS_ORDERID' and od_tno = '$paymentKey'"; + $row = sql_fetch($sql); + if(!$row['od_id']) { + write_toss_log("주문내역 조회 실패", $TOSS_ORDERID, $TOSS_STATUS); + http_response_code(400); + exit; + } + + // 주문서 UPDATE + $sql = " update {$g5['g5_shop_order_table']} + set od_receipt_price = '$totalAmount', + od_receipt_time = '$receipt_time', + od_deposit_name = '$depositorName' + where od_id = '$TOSS_ORDERID' + and od_tno = '$paymentKey'"; + $result = sql_query($sql, FALSE); + } + + if($result) { + if (isset($row['od_id']) && $row['od_id']) + $od_id = $row['od_id']; + else + $od_id = $TOSS_ORDERID; + + // 주문정보 체크 + $sql = " select count(od_id) as cnt + from {$g5['g5_shop_order_table']} + where od_id = '$od_id' + and od_status = '주문' "; + $row = sql_fetch($sql); + + if($row['cnt'] == 1) { + // 미수금 정보 업데이트 + $info = get_order_info($od_id); + + $add_update_sql = ''; + + // 현금영수증 발급시 1 또는 2 이면 + if ($RcptType) { + $add_update_sql = " + , od_cash = '1', + od_cash_no = '".$RcptAuthCode."', + od_cash_info = '".serialize(array('TID'=>$RcptTID, 'ApplNum'=>$RcptAuthCode, 'AuthDate'=>$approvedAt, 'receiptUrl'=>$RcptReceiptUrl))."' + "; + } + + $sql = " update {$g5['g5_shop_order_table']} + set od_misu = '{$info['od_misu']}' $add_update_sql "; + if($info['od_misu'] == 0) + $sql .= " , od_status = '입금' "; + $sql .= " where od_id = '$od_id' "; + sql_query($sql, FALSE); + + // 장바구니 상태변경 + if($info['od_misu'] == 0) { + $sql = " update {$g5['g5_shop_cart_table']} + set ct_status = '입금' + where od_id = '$od_id' "; + sql_query($sql, FALSE); + } + } + } +} + +/** + * 입금 오류 처리 (입금 오류로 인해 WAITING_FOR_DEPOSIT으로 되돌아온 경우) + */ +elseif($TOSS_STATUS == "WAITING_FOR_DEPOSIT") +{ + // 개인결제 정보 조회 + $sql = " select pp_id, od_id, pp_name, pp_hp, pp_tel from {$g5['g5_shop_personalpay_table']} where pp_id = '{$TOSS_ORDERID}' and pp_tno = '{$paymentKey}'"; + $row = sql_fetch($sql); + + if($row['pp_id']) { + // 개인결제 정보 롤백 + $sql = " update {$g5['g5_shop_personalpay_table']} + set pp_receipt_price = 0, + pp_receipt_time = '', + pp_cash = 0, + pp_cash_no = '', + pp_cash_info = '' + where pp_id = '{$TOSS_ORDERID}' and pp_tno = '{$paymentKey}'"; + $result = sql_query($sql, FALSE); + + if($row['od_id']) { + // 주문서에서 개인결제 금액 차감 + $sql = " update {$g5['g5_shop_order_table']} + set od_receipt_price = od_receipt_price - '$totalAmount', + od_shop_memo = concat(od_shop_memo, \"\\n개인결제 ".$row['pp_id']." 가상계좌 입금 오류로 취소 - ".date('Y-m-d H:i:s')."\") + where od_id = '{$row['od_id']}' "; + $result = sql_query($sql, FALSE); + } + } else { + // 일반 주문 롤백 전에 데이터 존재 확인 + $sql = " select od_id, od_name, od_hp, od_tel from {$g5['g5_shop_order_table']} where od_id = '{$TOSS_ORDERID}' and od_tno = '{$paymentKey}'"; + $row = sql_fetch($sql); + if(empty($row['od_id'])) { + write_toss_log("주문 데이터가 존재하지 않음", $TOSS_ORDERID, $TOSS_STATUS); + http_response_code(400); + exit; + } + + // 일반 주문 입금완료 - 주문 상태 롤백 (입금 → 주문) + $sql = " update {$g5['g5_shop_order_table']} + set od_status = '주문', + od_receipt_price = 0, + od_receipt_time = '', + od_shop_memo = concat(od_shop_memo, \"\\n가상계좌 입금 오류로 취소 - ".date('Y-m-d H:i:s')."\"), + od_cash = 0, + od_cash_no = '', + od_cash_info = '' + where od_id = '{$TOSS_ORDERID}' and od_tno = '{$paymentKey}' "; + $result = sql_query($sql, FALSE); + } + + // 공통 처리: 미수금 정보 재계산 및 상태 롤백 + if($result) { + if (isset($row['od_id']) && $row['od_id']) + $od_id = $row['od_id']; + else + $od_id = $TOSS_ORDERID; + + // 미수금 정보 재계산 + $info = get_order_info($od_id); + + if($info) { + $sql = " update {$g5['g5_shop_order_table']} + set od_misu = '{$info['od_misu']}', + od_status = '주문', + od_cash = 0, + od_cash_no = '', + od_cash_info = '' + where od_id = '{$od_id}' "; + sql_query($sql, FALSE); + + // 장바구니 상태 롤백 (입금 → 주문) + $sql = " update {$g5['g5_shop_cart_table']} + set ct_status = '주문' + where od_id = '{$od_id}' "; + sql_query($sql, FALSE); + } + + // SMS 발송 - 재입금 안내 + $sms_message = ''; + + // 개인결제인지 일반주문인지 확인하여 연락처 조회 + if($row['pp_id']) { + // 개인결제인 경우 + $customer_name = $row['pp_name']; + $customer_phone = $row['pp_hp'] ? $row['pp_hp'] : ($row['pp_tel'] ? $row['pp_tel'] : ''); + $title = "개인결제번호 {$TOSS_ORDERID}"; + } else { + // 일반주문인 경우 + $customer_name = $row['od_name']; + $customer_phone = $row['od_hp'] ? $row['od_hp'] : ($row['od_tel'] ? $row['od_tel'] : ''); + $title = "주문번호 {$od_id}"; + } + + if($customer_phone) { + $sms_message = "{$customer_name}님, {$title} 가상계좌 입금이 완료되지 않았습니다. 재입금 또는 관리자 문의 바랍니다.\n"; + $sms_message .= $default['de_admin_company_name']; + } + + // 전화번호가 있고 SMS 발송 설정이 활성화된 경우에만 발송 + if($customer_phone && $sms_message && $config['cf_icode_id'] && $config['cf_icode_pw']) { + // SMS 발송 + $sms_messages = array(); + $receive_number = preg_replace("/[^0-9]/", "", $customer_phone); // 수신자번호 + $send_number = preg_replace("/[^0-9]/", "", $default['de_admin_company_tel']); // 발신자번호 + $sms_messages[] = array('recv' => $receive_number, 'send' => $send_number, 'cont' => $sms_message); + + // SMS 발송 처리 + if($config['cf_sms_type'] == 'LMS') { + include_once(G5_LIB_PATH.'/icode.lms.lib.php'); + + $port_setting = get_icode_port_type($config['cf_icode_id'], $config['cf_icode_pw']); + + if($port_setting !== false) { + $SMS = new LMS; + $SMS->SMS_con($config['cf_icode_server_ip'], $config['cf_icode_id'], $config['cf_icode_pw'], $port_setting); + + for($s=0; $sAdd($strDest, $strCallBack, $strCaller, $strSubject, $strURL, $strData, $strDate, $nCount); + $SMS->Send(); + $SMS->Init(); + } + } + } else { + include_once(G5_LIB_PATH.'/icode.sms.lib.php'); + + $SMS = new SMS; + $SMS->SMS_con($config['cf_icode_server_ip'], $config['cf_icode_id'], $config['cf_icode_pw'], $config['cf_icode_server_port']); + + for($s=0; $sAdd($recv_number, $send_number, $config['cf_icode_id'], $sms_content, ""); + } + + $SMS->Send(); + $SMS->Init(); + } + + // SMS 발송 로그 기록 + write_toss_log("가상계좌 재입금 안내 SMS 발송 완료", $TOSS_ORDERID, "SMS_SENT"); + } + } +} + +/** + * 입금 전 취소 처리 + */ +elseif($TOSS_STATUS == "CANCELED") +{ + $sql = " update {$g5['g5_shop_order_table']} + set od_shop_memo = concat(od_shop_memo, \"\\n가상계좌 입금 전 취소 - ".date('Y-m-d H:i:s')."\") + where od_id = '{$TOSS_ORDERID}' "; + $result = sql_query($sql, FALSE); +} + +//************************************************************************************ +// 위에서 상점 데이터베이스에 등록 성공유무에 따라서 성공시에는 성공응답인 `HTTP 200` 상태 코드를 리턴해야 합니다. +// (주의) 성공응답인 `HTTP 200` 상태 코드를 리턴하지 않으면 토스페이먼츠에서 7회까지 재전송에 실패하면 웹훅 상태가 실패로 변경됩니다. + +// 토스페이먼츠 로그 기록 (nicepay 형태) +if($payLog) { + $logfile = fopen($log_file, "a+"); + + // 은행명 조회 + $bankName = ''; + if($bankCode && isset($toss->bankCode[$bankCode])) { + $bankName = $toss->bankCode[$bankCode]; + } + + fwrite( $logfile,"************************************************\r\n"); + fwrite( $logfile,"GoodsName : 토스페이먼츠 가상계좌\r\n"); + fwrite( $logfile,"OrderId : ".$TOSS_ORDERID."\r\n"); + fwrite( $logfile,"Status : ".$TOSS_STATUS."\r\n"); + fwrite( $logfile,"ResultMsg : ".($result ? "SUCCESS" : "FAIL")."\r\n"); + fwrite( $logfile,"Amt : ".$totalAmount."\r\n"); + fwrite( $logfile,"name : ".$customerName."\r\n"); + fwrite( $logfile,"TID : ".$paymentKey."\r\n"); + fwrite( $logfile,"AuthDate : ".$approvedAt."\r\n"); + fwrite( $logfile,"VbankNum : ".$accountNumber."\r\n"); + fwrite( $logfile,"VbankCode : ".$bankCode."\r\n"); + fwrite( $logfile,"VbankName : ".$bankName."\r\n"); + fwrite( $logfile,"VbankInputName: ".$depositorName."\r\n"); + fwrite( $logfile,"RcptTID : ".$RcptTID."\r\n"); + fwrite( $logfile,"RcptAuthCode : ".$RcptAuthCode."\r\n"); + fwrite( $logfile,"RcptType : ".$RcptType."\r\n"); + fwrite( $logfile,"************************************************\r\n"); + + fclose( $logfile ); +} + +if ($result) +{ + http_response_code(200); // 절대로 지우지마세요 + echo "OK"; + exit; +} +else +{ + http_response_code(400); + echo "FAIL"; + exit; +} + +//************************************************************************************* \ No newline at end of file diff --git a/shop/toss/_common.php b/shop/toss/_common.php new file mode 100644 index 000000000..c7ed3c4ee --- /dev/null +++ b/shop/toss/_common.php @@ -0,0 +1,2 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/shop/toss/orderform.3.php b/shop/toss/orderform.3.php new file mode 100644 index 000000000..04b7d01e6 --- /dev/null +++ b/shop/toss/orderform.3.php @@ -0,0 +1,16 @@ + + + + + + diff --git a/shop/toss/orderform.4.php b/shop/toss/orderform.4.php new file mode 100644 index 000000000..4e5ea2114 --- /dev/null +++ b/shop/toss/orderform.4.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/shop/toss/orderpartcancel.inc.php b/shop/toss/orderpartcancel.inc.php new file mode 100644 index 000000000..59044418e --- /dev/null +++ b/shop/toss/orderpartcancel.inc.php @@ -0,0 +1,61 @@ +setPaymentHeader(); + +$od_id = isset($od['od_id']) ? $od['od_id'] : (isset($pp['pp_id']) ? $pp['pp_id'] : ''); + +if (!$toss->getPaymentByOrderId($od_id)) { + alert('결제정보를 가져올 수 없습니다.'); +} + +$toss->setCancelData(array( + 'paymentKey' => $toss->responseData['paymentKey'], + 'cancelReason' => $mod_memo, + 'cancelAmount' => (int)$tax_mny + (int)$free_mny, + 'taxFreeAmount' => (int)$free_mny, +)); +if (!$toss->cancelPayment()) { + $msg = '결제 부분취소 요청이 실패하였습니다.\\n\\n'; + if (isset($toss->responseData['message'])) { + $msg .= '사유 : ' . $toss->responseData['message'] . '\\n'; + } + if (isset($toss->responseData['code'])) { + $msg .= '코드 : ' . $toss->responseData['code']; + } + alert($msg); +} + +// 환불금액 기록 +$mod_mny = (int)$tax_mny + (int)$free_mny; +$sql = " update {$g5['g5_shop_order_table']} + set od_refund_price = od_refund_price + '$mod_mny', + od_shop_memo = concat(od_shop_memo, \"$mod_memo\") + where od_id = '{$od['od_id']}'"; +sql_query($sql); + +// 미수금 등의 정보 업데이트 +$info = get_order_info($od_id); + +$sql = " update {$g5['g5_shop_order_table']} + set od_misu = '{$info['od_misu']}', + od_tax_mny = '{$info['od_tax_mny']}', + od_vat_mny = '{$info['od_vat_mny']}', + od_free_mny = '{$info['od_free_mny']}' + where od_id = '$od_id' "; +sql_query($sql); \ No newline at end of file diff --git a/shop/toss/returnurl.php b/shop/toss/returnurl.php new file mode 100644 index 000000000..434cf9ad5 --- /dev/null +++ b/shop/toss/returnurl.php @@ -0,0 +1,67 @@ + + +'.PHP_EOL; + +echo make_order_field($data, $exclude); + +echo ''.PHP_EOL; +?> + + + + + + + +
    +

    + +
    +

    주문정보

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    주문 번호
    상품 정보
    주문자 이름
    주문자 E-Mail
    주문자 전화번호
    +
    +
    + +
    +

    현금영수증 발급 정보

    + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    원 거래 시각
    발행 용도 + + + + +
    + + + ("-" 생략) +
    거래금액 총합
    공급가액
    봉사료
    부가가치세
    +
    + +
    + + + + +
    + +
    +
    + +
    \ No newline at end of file diff --git a/shop/toss/taxsave_result.php b/shop/toss/taxsave_result.php new file mode 100644 index 000000000..188c0a283 --- /dev/null +++ b/shop/toss/taxsave_result.php @@ -0,0 +1,192 @@ +개인결제 내역이 존재하지 않습니다.

    '); + + $od_tno = $od['pp_tno']; + $goods_name = $od['pp_name'].'님 개인결제'; + $settle_case = $od['pp_settle_case']; + $order_price = $od['pp_receipt_price']; + $od_casseqno = $od['pp_casseqno']; + $od_name = $od['pp_name']; + $od_email = $od['pp_email']; + $od_tel = $od['pp_hp']; +} else { + $od = sql_fetch(" select * from {$g5['g5_shop_order_table']} where od_id = '$od_id' "); + if (!$od) + die('

    주문서가 존재하지 않습니다.

    '); + + $od_tno = $od['od_tno']; + $goods = get_goods($od['od_id']); + $goods_name = $goods['full_name']; + $settle_case = $od['od_settle_case']; + $order_price = $od['od_tax_mny'] + $od['od_vat_mny'] + $od['od_free_mny']; + $od_casseqno = $od['od_casseqno']; + $od_name = $od['od_name']; + $od_email = $od['od_email']; + $od_tel = $od['od_tel']; +} + +switch($settle_case) { + case '가상계좌': + case '계좌이체': + case '무통장': + // 토스페이먼츠는 결제수단 구분 없이 현금영수증 발급 가능 + break; + default: + die('

    현금영수증은 무통장, 가상계좌, 계좌이체에 한해 발급요청이 가능합니다.

    '); + break; +} + +// 토스페이먼츠 현금영수증 발급 요청 +$orderId = $od_id; +$amount = $order_price; +$type = ($_POST['tr_code'] == '0') ? '소득공제' : '지출증빙'; +$customerIdentityNumber = $_POST['id_info']; +$orderName = $od_name; +$customerEmail = $_POST['buyeremail'] ?: $od_email; +$customerMobilePhone = $_POST['buyertel'] ?: $od_tel; + +// 토스페이먼츠 현금영수증 발급 API 호출 +$toss->setCashReceiptsData([ + 'orderId' => $orderId, + 'amount' => $amount, + 'type' => $type, + 'customerIdentityNumber' => $customerIdentityNumber, + 'orderName' => $goods_name, +]); +$toss_result = $toss->issueCashReceipt(); + +/* + * 토스페이먼츠 현금영수증 발급 요청 결과처리 + */ +if ($toss_result && isset($toss->responseData['receiptKey'])) { + // 현금영수증 발급 성공 + $data = $toss->responseData; + $receiptKey = $data['receiptKey']; // 현금영수증 발급 키 + $cash_no = $data['issueNumber']; // 현금영수증 발급 번호 + $approvedAt = $data['requestedAt']; + + $cash = array(); + $cash['receiptKey'] = $receiptKey; + $cash['approvedAt'] = $approvedAt; + $cash['orderId'] = $data['orderId']; + $cash['amount'] = $data['amount']; + $cash['type'] = $data['type']; + $cash['receiptUrl'] = $data['receiptUrl']; + $cash_info = serialize($cash); + + if($tx == 'personalpay') { + $sql = " update {$g5['g5_shop_personalpay_table']} + set pp_cash = '1', + pp_cash_no = '$cash_no', + pp_cash_info = '$cash_info' + where pp_id = '$orderId' "; + } else { + $sql = " update {$g5['g5_shop_order_table']} + set od_cash = '1', + od_cash_no = '$cash_no', + od_cash_info = '$cash_info' + where od_id = '$orderId' "; + } + + $result = sql_query($sql, false); + + if(!$result) { // DB 정보갱신 실패시 취소 + $cancel_result = $toss->cancelCashReceipt($receiptKey, 'DB 업데이트 실패로 인한 취소'); + + if (!$cancel_result) { + $msg = '현금영수증 취소 요청처리가 정상적으로 완료되지 않았습니다.\\n'. $toss->responseData['message']; + alert_close($msg); + } + } + +} else { + // API 요청 실패 화면처리 + $msg = '현금영수증 발급 요청처리가 정상적으로 완료되지 않았습니다.\\n'. $toss->responseData['message']; + alert($msg); +} + +$g5['title'] = ''; +include_once(G5_PATH.'/head.sub.php'); +?> + + + + +
    +

    현금영수증 - 토스페이먼츠

    + +
    + + + + + + + responseData['receiptKey'])): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    결과발급 완료
    현금영수증 발급번호responseData['issueNumber']; ?>
    주문번호responseData['orderId']; ?>
    발급 유형responseData['type']; ?>
    금액responseData['amount']); ?>원
    승인시간responseData['requestedAt'])); ?>
    현금영수증 확인 + +

    영수증 확인은 실 등록의 경우에만 가능합니다.

    +
    결과발급 실패
    오류 메시지
    +
    + +
    + + '한국산업은행', + '03' => 'IBK기업은행', + '06' => 'KB국민은행', + '07' => 'Sh수협은행', + '11' => 'NH농협은행', + '12' => '단위농협(지역농축협)', + '20' => '우리은행', + '23' => 'SC제일은행', + '27' => '씨티은행', + '31' => 'iM뱅크(대구)', + '32' => '부산은행', + '34' => '광주은행', + '35' => '제주은행', + '37' => '전북은행', + '39' => '경남은행', + '45' => '새마을금고', + '48' => '신협', + '50' => '저축은행중앙회', + '54' => '홍콩상하이은행', + '64' => '산림조합', + '71' => '우체국예금보험', + '81' => '하나은행', + '88' => '신한은행', + '89' => '케이뱅크', + '90' => '카카오뱅크', + '92' => '토스뱅크', + + // 증권 + 'S0' => '유안타증권', + 'S2' => '신한금융투자', + 'S3' => '삼성증권', + 'S4' => 'KB증권', + 'S5' => '미래에셋증권', + 'S6' => '한국투자증권', + 'S8' => '교보증권', + 'S9' => '아이엠증권', + 'SA' => '현대차증권', + 'SB' => '키움증권', + 'SD' => 'SK증권', + 'SE' => '대신증권', + 'SG' => '한화투자증권', + 'SH' => '하나금융투자', + 'SI' => 'DB금융투자', + 'SJ' => '유진투자증권', + 'SK' => '메리츠증권', + 'SM' => '부국증권', + 'SN' => '신영증권', + 'SO' => 'LIG투자증권', + 'SP' => 'KTB투자증권(다올투자증권)', + 'SQ' => '카카오페이증권', + 'SR' => '펀드온라인코리아(한국포스증권)', + 'ST' => '토스증권' + ); + + public array $cardCode = array( + '3K' => '기업 BC', + '46' => '광주은행', + '71' => '롯데카드', + '30' => '한국산업은행', + '31' => 'BC카드', + '51' => '삼성카드', + '38' => '새마을금고', + '41' => '신한카드', + '62' => '신협', + '36' => '씨티카드', + '33' => '우리BC카드(BC 매입)', + 'W1' => '우리카드(우리 매입)', + '37' => '우체국예금보험', + '39' => '저축은행중앙회', + '35' => '전북은행', + '42' => '제주은행', + '15' => '카카오뱅크', + '3A' => '케이뱅크', + '24' => '토스뱅크', + '21' => '하나카드', + '61' => '현대카드', + '11' => 'KB국민카드', + '91' => 'NH농협카드', + '34' => 'Sh수협은행', + 'PCP' => '페이코', + 'KBS' => 'KB증권' + ); + + // 간편결제 제공업체 코드 + public array $easyPayCode = array( + 'TOSSPAY' => '토스페이', + 'NAVERPAY' => '네이버페이', + 'SAMSUNGPAY' => '삼성페이', + 'APPLEPAY' => '애플페이', + 'LPAY' => '엘페이', + 'KAKAOPAY' => '카카오페이', + 'PINPAY' => '핀페이', + 'PAYCO' => '페이코', + 'SSG' => 'SSG페이' + ); + + public function __construct(string $clientKey, string $secretKey, string $mId) { + $this->clientKey = $clientKey; + $this->secretKey = $secretKey; + $this->mId = $mId; + } + + /** + * 헤더 시크릿 키 설정 + * @return void + */ + private function setHeaderSecretKey(): void + { + $this->headerSecretKey = base64_encode($this->secretKey . ':'); + } + + /** + * 헤더 설정 + * @return void + */ + public function setPaymentHeader(): void + { + $this->setHeaderSecretKey(); + + $this->headers = array( + 'Authorization: Basic ' . $this->headerSecretKey, + 'Content-Type' => 'Content-Type: application/json' + ); + } + + /** + * 결제 데이터 설정 + * + * @param array $request + * @return void + */ + public function setPaymentData(array $request): void + { + $this->paymentData = array( + 'amount' => $request['amount'], + 'orderId' => $request['orderId'], + 'paymentKey' => $request['paymentKey'], + ); + } + + /** + * 주문번호로 결제정보 조회 + * + * @param string $orderId + * @return bool + */ + public function getPaymentByOrderId(string $orderId): bool + { + if (empty($orderId)) { + return false; + } + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, str_replace('{orderId}', $orderId, $this->paymentUrl)); + curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($curl, CURLOPT_SSLVERSION, 6); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_TIMEOUT, 20); + + $response = curl_exec($curl); + + $return_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $this->responseData = json_decode($response, true); + + curl_close($curl); + + // 결제 실패 상황인 경우 + if ($return_status != 200) { + return false; + } + + return true; + } + + /** + * 결제 승인 + * + * @return bool + */ + public function approvePayment(): bool { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $this->acceptUrl); + curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->paymentData)); + curl_setopt($curl, CURLOPT_SSLVERSION, 6); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_TIMEOUT, 20); + + $response = curl_exec($curl); + + $return_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $this->responseData = json_decode($response, true); + + curl_close($curl); + + // 결제 실패 상황인 경우 + if ($return_status != 200 || ($this->responseData['status'] != 'DONE' && $this->responseData['status'] != 'WAITING_FOR_DEPOSIT')) { + return false; + } + + return true; + } + + /** + * 결제 취소 데이터 설정 + * + * @param array $request + * @return void + */ + public function setCancelData(array $request): void + { + $this->cancelData = array( + 'paymentKey' => $request['paymentKey'], + 'cancelReason' => $request['cancelReason'], + ); + + // 부분취소 금액이 있는 경우 + if (isset($request['cancelAmount']) && $request['cancelAmount'] > 0) { + $this->cancelData['cancelAmount'] = $request['cancelAmount']; + } + + // 면세금액이 있는 경우 + if (isset($request['taxFreeAmount']) && $request['taxFreeAmount'] > 0) { + $this->cancelData['taxFreeAmount'] = $request['taxFreeAmount']; + } + + // 환불 계좌정보가 있는 경우 (가상계좌) + if (isset($request['refundReceiveAccount']) && is_array($request['refundReceiveAccount'])) { + $this->cancelData['refundReceiveAccount'] = array( + 'bank' => $request['refundReceiveAccount']['bank'], + 'accountNumber' => $request['refundReceiveAccount']['accountNumber'], + 'holderName' => $request['refundReceiveAccount']['holderName'], + ); + } + } + + /** + * 결제 취소 + * + * @return bool + */ + public function cancelPayment(): bool + { + // 취소에 필요한 결제 키가 있는지 여부 + if (empty($this->cancelData['paymentKey'])) { + return false; + } + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, str_replace('{paymentKey}', $this->cancelData['paymentKey'], $this->cancelUrl)); + curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->cancelData)); + curl_setopt($curl, CURLOPT_SSLVERSION, 6); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_TIMEOUT, 20); + + $response = curl_exec($curl); + + $return_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $this->responseData = json_decode($response, true); + + curl_close($curl); + + // 결제 실패 상황인 경우 + if ($return_status != 200) { + return false; + } + + return true; + } + + /** + * 현금영수증 발급 데이터 설정 + */ + public function setCashReceiptsData(array $request): void + { + $this->cashReceiptsData = array( + 'amount' => $request['amount'], + 'orderId' => $request['orderId'], + 'type' => $request['type'], + 'customerIdentityNumber' => $request['customerIdentityNumber'], + 'orderName' => $request['orderName'], + ); + } + + /** + * 현금영수증 발급 + */ + public function issueCashReceipt(): bool + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $this->cashReceiptsUrl); + curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->cashReceiptsData)); + curl_setopt($curl, CURLOPT_SSLVERSION, 6); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_ENCODING, ""); + curl_setopt($curl, CURLOPT_MAXREDIRS, 10); + curl_setopt($curl, CURLOPT_TIMEOUT, 20); + curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST"); + + $response = curl_exec($curl); + + $return_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $this->responseData = json_decode($response, true); + + curl_close($curl); + + // 결제 실패 상황인 경우 + if ($return_status != 200) { + return false; + } + + return true; + } + + /** + * 현금영수증 발급 취소 + */ + public function cancelCashReceipt($receiptKey): bool + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $this->cashReceiptsUrl."/".$receiptKey."/cancel"); + curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers); + curl_setopt($curl, CURLOPT_SSLVERSION, 6); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_ENCODING, ""); + curl_setopt($curl, CURLOPT_MAXREDIRS, 10); + curl_setopt($curl, CURLOPT_TIMEOUT, 20); + curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST"); + + $response = curl_exec($curl); + + $return_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $this->responseData = json_decode($response, true); + + curl_close($curl); + + // 결제 실패 상황인 경우 + if ($return_status != 200) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/shop/toss/toss_approval.php b/shop/toss/toss_approval.php new file mode 100644 index 000000000..c31311ae6 --- /dev/null +++ b/shop/toss/toss_approval.php @@ -0,0 +1,79 @@ +setPaymentData([ + 'orderId' => $orderId, + 'amount' => $amount, + 'paymentKey' => $paymentKey, +]); + +// 장바구니 ID 설정 (바로구매 여부 확인) +$ss_cart_id = get_session('ss_direct') ? get_session('ss_cart_direct') : get_session('ss_cart_id'); + +// 임시데이터에 결제 데이터 저장 +$addQuery = ""; +if (isset($orderId)) { + $addQuery .= " AND od_id = '$orderId'"; +} +if (isset($ss_cart_id)) { + $addQuery .= " AND cart_id = '$ss_cart_id'"; +} +if (isset($member['mb_id'])) { + $addQuery .= " AND mb_id = '{$member['mb_id']}'"; +} + +if (empty($orderId) && empty($ss_cart_id)) { + alert('주문정보가 올바르지 않습니다.'); + exit; +} + +// 기존 dt_data 가져오기 +$sql = " + SELECT * FROM {$g5['g5_shop_order_data_table']} + WHERE 1=1 + {$addQuery} + LIMIT 1 +"; +$res = sql_fetch($sql); +$dt_data = []; +if (isset($res['dt_data'])) { + $dt_data = unserialize(base64_decode($res['dt_data'])); +} + +// dt_data 에 결제 키 추가 +if (isset($paymentKey)) { + $dt_data['paymentKey'] = $paymentKey; + $dt_data_new = base64_encode(serialize($dt_data)); + + // 업데이트 + $sql = " + UPDATE {$g5['g5_shop_order_data_table']} SET + dt_data = '".$dt_data_new."' + WHERE od_id = '$orderId' + {$addQuery} + "; + sql_query($sql); +} + +if(isset($payReqMap['pp_id']) && $payReqMap['pp_id']) { + $page_return_url = G5_SHOP_URL.'/personalpayform.php?pp_id='.$payReqMap['pp_id']; +} else { + $page_return_url = G5_SHOP_URL.'/orderform.php'; + if ($_SESSION['ss_direct']) { + $page_return_url .= '?sw_direct=1'; + } +} +?> diff --git a/shop/toss/toss_cancel.php b/shop/toss/toss_cancel.php new file mode 100644 index 000000000..8738a0597 --- /dev/null +++ b/shop/toss/toss_cancel.php @@ -0,0 +1,38 @@ +setPaymentHeader(); + +$od_id = isset($od['od_id']) ? $od['od_id'] : (isset($pp['pp_id']) ? $pp['pp_id'] : ''); + +if (!$toss->getPaymentByOrderId($od_id)) { + alert('결제정보를 가져올 수 없습니다.'); +} + +$toss->setCancelData(array( + 'paymentKey' => $toss->responseData['paymentKey'], + 'cancelReason' => $cancel_msg, +)); +if (!$toss->cancelPayment()) { + $msg = '결제 취소에 실패하였습니다.\\n'; + if (isset($toss->responseData['message'])) { + $msg .= '사유 : ' . $toss->responseData['message'] . '\\n'; + } + if (isset($toss->responseData['code'])) { + $msg .= '코드 : ' . $toss->responseData['code']; + } + alert($msg); +} \ No newline at end of file diff --git a/shop/toss/toss_result.php b/shop/toss/toss_result.php new file mode 100644 index 000000000..6e86aa212 --- /dev/null +++ b/shop/toss/toss_result.php @@ -0,0 +1,108 @@ +setPaymentData([ + 'amount' => $amount, + 'orderId' => $orderId, + 'paymentKey' => $paymentKey +]); +$toss->setPaymentHeader(); + +// 결제승인 요청 +$result = $toss->approvePayment(); + +if ($result) { + // 결제승인 성공시 처리 + $status = isset($toss->responseData['status']) ? $toss->responseData['status'] : ''; + $method = isset($toss->responseData['method']) ? $toss->responseData['method'] : ''; + + // 가상계좌(VIRTUAL_ACCOUNT)만 입금대기(WAITING_FOR_DEPOSIT) 상태 값을 가질 수 있음 + if ($status === 'DONE' || ($status === 'WAITING_FOR_DEPOSIT' && $method === '가상계좌')) { + // 공통 DB처리 변수 설정 + $tno = isset($toss->responseData['paymentKey']) ? $toss->responseData['paymentKey'] : ''; + $amount = isset($toss->responseData['totalAmount']) ? $toss->responseData['totalAmount'] : 0; + $escw_yn = $toss->responseData['useEscrow'] === true ? 'Y' : 'N'; + $app_time = isset($toss->responseData['approvedAt']) ? date('Y-m-d H:i:s', strtotime($toss->responseData['approvedAt'])) : ''; + + // 결제수단별 데이터 처리 (카드, 가상계좌, 계좌이체, 휴대폰, 간편결제 순) + if ($method === '카드') { + // 카드 + $app_no = $od_app_no = isset($toss->responseData['card']['approveNo']) ? $toss->responseData['card']['approveNo'] : '00000000'; + $card_name = isset($toss->cardCode[$toss->responseData['card']['issuerCode']]) ? $toss->cardCode[$toss->responseData['card']['issuerCode']] : ''; + } else if ($method === '가상계좌') { + // 가상계좌 + $bank_name = $bankname = isset($toss->bankCode[$toss->responseData['virtualAccount']['bankCode']]) ? $toss->bankCode[$toss->responseData['virtualAccount']['bankCode']] : ''; + $depositor = isset($toss->responseData['virtualAccount']['customerName']) ? $toss->responseData['virtualAccount']['customerName'] : ''; + $account = isset($toss->responseData['virtualAccount']['accountNumber']) ? $toss->responseData['virtualAccount']['accountNumber'] : ''; + } else if ($method === '계좌이체') { + // 계좌이체 + $bank_name = isset($toss->bankCode[$toss->responseData['transfer']['bankCode']]) ? $toss->bankCode[$toss->responseData['transfer']['bankCode']] : ''; + + // 현금영수증 데이터 처리 + $cashReceiptType = isset($toss->responseData['cashReceipt']['type']) ? $toss->responseData['cashReceipt']['type'] : ''; + $RcptType = $cashReceiptType === '소득공제' ? '1' : ($cashReceiptType === '지출증빙' ? '2' : '0'); + $RcptTID = isset($toss->responseData['cashReceipt']['receiptKey']) ? $toss->responseData['cashReceipt']['receiptKey'] : ''; // 현금영수증 TID, 현금영수증 거래인 경우 리턴 + $RcptAuthCode = isset($toss->responseData['cashReceipt']['issueNumber']) ? $toss->responseData['cashReceipt']['issueNumber'] : ''; // 현금영수증 승인번호, 현금영수증 거래인 경우 리턴 + $RcptReceiptUrl = isset($toss->responseData['cashReceipt']['receiptUrl']) ? $toss->responseData['cashReceipt']['receiptUrl'] : ''; // 현금영수증 URL + + // 현금영수증 발급시 1 또는 2 이면 + if ($RcptType) { + $pg_receipt_infos['od_cash'] = 1; // 현금영수증 발급인것으로 처리 + $pg_receipt_infos['od_cash_no'] = $RcptAuthCode; // 현금영수증 승인번호 + $pg_receipt_infos['od_cash_info'] = serialize(array('TID'=>$RcptTID, 'ApplNum'=>$RcptAuthCode, 'receiptUrl'=>$RcptReceiptUrl)); + } + } else if ($method === '휴대폰') { + // 휴대폰 + $mobile_no = isset($toss->responseData['mobilePhone']['customerMobilePhone']) ? $toss->responseData['mobilePhone']['customerMobilePhone'] : ''; + } else if ($method === '간편결제') { + // 간편결제 + $provider = isset($toss->responseData['easyPay']['provider']) ? $toss->responseData['easyPay']['provider'] : ''; + $card_name = isset($toss->easyPayCode[$provider]) ? $toss->easyPayCode[$provider] : $provider; + } + } else { + + if(G5_IS_MOBILE) { + if(isset($_POST['pp_id']) && $_POST['pp_id']) { + $page_return_url = G5_SHOP_URL.'/personalpayform.php?pp_id='.get_session('ss_personalpay_id'); + } else { + $page_return_url = G5_SHOP_URL.'/orderform.php'; + if(get_session('ss_direct')) + $page_return_url .= '?sw_direct=1'; + } + + alert($toss->responseData['message'].' 코드 : '.$toss->responseData['code'], $page_return_url); + } else { + alert($toss->responseData['message'].' 코드 : '.$toss->responseData['code'], G5_SHOP_URL.'/orderform.php'); + } + } +} else { + alert($toss->responseData['message'].' 코드 : '.$toss->responseData['code'], G5_SHOP_URL); +} \ No newline at end of file diff --git a/theme/basic/shop/orderinquiryview.php b/theme/basic/shop/orderinquiryview.php index f248543c6..868f81771 100644 --- a/theme/basic/shop/orderinquiryview.php +++ b/theme/basic/shop/orderinquiryview.php @@ -461,7 +461,9 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $hp_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; - } else if($od['od_pg'] == 'inicis') { + } else if($od['od_pg'] == 'toss') { + $hp_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/phone?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; + } else if($od['od_pg'] == 'inicis') { $hp_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { $hp_receipt_script = 'window.open(\'https://npg.nicepay.co.kr/issue/IssueLoader.do?type=0&TID='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; @@ -482,7 +484,9 @@ if($od['od_pg'] == 'lg') { $LGD_HASHDATA = md5($LGD_MID.$LGD_TID.$LGD_MERTKEY); $card_receipt_script = 'showReceiptByTID(\''.$LGD_MID.'\', \''.$LGD_TID.'\', \''.$LGD_HASHDATA.'\');'; - } else if($od['od_pg'] == 'inicis') { + } else if($od['od_pg'] == 'toss') { + $card_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/redirection?transactionId='.$od['od_tno'].'&ref=PX\',\'receipt\',\'width=430,height=700\');'; + } else if($od['od_pg'] == 'inicis') { $card_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/mCmReceipt_head.jsp?noTid='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; } else if($od['od_pg'] == 'nicepay') { $card_receipt_script = 'window.open(\'https://npg.nicepay.co.kr/issue/IssueLoader.do?type=0&TID='.$od['od_tno'].'&noMethod=1\',\'receipt\',\'width=430,height=700\');'; @@ -555,7 +559,9 @@ if($od['od_pg'] == 'lg') { break; } $cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$od['od_id'].'\',\''.$od['od_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');'; - } else if($od['od_pg'] == 'inicis') { + } else if($od['od_pg'] == 'toss') { + $cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$od['od_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');'; + } else if($od['od_pg'] == 'inicis') { $cash = unserialize($od['od_cash_info']); $cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');'; } else if($od['od_pg'] == 'nicepay') { From f4718a71a297f1679eefb0d646ecea4e4eac22e0 Mon Sep 17 00:00:00 2001 From: chym1217 Date: Wed, 17 Sep 2025 10:44:40 +0900 Subject: [PATCH 27/31] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=86=A0=EC=8A=A4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EB=A8=BC=EC=B8=A0=20=EB=AA=85=EC=B9=AD=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=95=88=EB=82=B4=20=EC=B6=94=EA=B0=80=20-=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20:=20=ED=86=A0=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EB=A8=BC=EC=B8=A0=20->=20=ED=86=A0=EC=8A=A4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EB=A8=BC=EC=B8=A0(=EA=B5=AC=EB=B2=84=EC=A0=84)=20-=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EB=A8=BC=EC=B8=A0=20V2=20->=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8E=98=EC=9D=B4=EB=A8=BC=EC=B8=A0=20API=20?= =?UTF-8?q?-=20=EA=B8=B0=ED=83=80=20css=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/css/admin.css | 4 ++-- adm/shop_admin/admin.shop.lib.php | 4 +++- adm/shop_admin/configform.php | 30 +++++++++++++++++------------- adm/shop_admin/orderform.php | 2 +- shop/settle_toss_common.php | 6 +++--- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/adm/css/admin.css b/adm/css/admin.css index 6df934090..470d65d76 100644 --- a/adm/css/admin.css +++ b/adm/css/admin.css @@ -743,8 +743,8 @@ a.nicepay_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#0 ul.de_pg_tab{margin:0;padding:0;zoom:1} ul.de_pg_tab:after{display:block;visibility:hidden;clear:both;content:"";} -ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;width:140px} -ul.de_pg_tab li a{margin:0 2px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:2.5;background-color:#f7f7f7;color:#74777b;font-weight:bold;font-size:1.2em;text-decoration:none} +ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;min-width:130px} +ul.de_pg_tab li a{margin:0 2px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:2.5;background-color:#f7f7f7;color:#74777b;font-weight:bold;font-size:1.2em;text-decoration:none; padding:0px 10px;} ul.de_pg_tab li a:hover{text-decoration:none} ul.de_pg_tab li.tab-current a{background:#2CC185;color:#fff} diff --git a/adm/shop_admin/admin.shop.lib.php b/adm/shop_admin/admin.shop.lib.php index 737d0d95c..d1aa92857 100644 --- a/adm/shop_admin/admin.shop.lib.php +++ b/adm/shop_admin/admin.shop.lib.php @@ -125,12 +125,15 @@ function pg_setting_check($is_print=false){ $msg = ''; $pg_msg = ''; + $pg_test_conf_link = G5_ADMIN_URL.'/shop_admin/configform.php#de_card_test1'; if( $default['de_card_test'] ){ if( $default['de_pg_service'] === 'kcp' && $default['de_kcp_mid'] && $default['de_kcp_site_key'] ){ $pg_msg = 'NHN KCP'; } else if ( $default['de_pg_service'] === 'lg' && $config['cf_lg_mid'] && $config['cf_lg_mert_key'] ){ $pg_msg = 'LG유플러스'; + } else if ( $default['de_pg_service'] === 'toss' && $config['cf_lg_mid'] && $config['cf_toss_client_key'] && $config['cf_toss_secret_key'] ){ + $msg .= '
    (주의!) 토스페이먼츠 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.
    반드시 상점 API키[테스트]키로 설정한 후 테스트결제를 진행해야합니다.
    쇼핑몰 운영 시에는 실결제로 전환하여 [라이브]키로 설정해 주시기 바랍니다.
    아래 링크를 클릭하여 실결제로 설정하여 운영해 주세요.
    '.$pg_test_conf_link.'
    '; } else if ( $default['de_pg_service'] === 'inicis' && $default['de_inicis_mid'] && $default['de_inicis_sign_key'] ){ $pg_msg = 'KG이니시스'; } else if ( $default['de_pg_service'] === 'nicepay' && $default['de_nicepay_mid'] && $default['de_nicepay_key'] ){ @@ -147,7 +150,6 @@ function pg_setting_check($is_print=false){ } if( $pg_msg ){ - $pg_test_conf_link = G5_ADMIN_URL.'/shop_admin/configform.php#de_card_test1'; $msg .= '
    (주의!) '.$pg_msg.' 결제의 결제 설정이 현재 테스트결제 로 되어 있습니다.
    테스트결제시 실제 결제가 되지 않으므로, 쇼핑몰 운영중이면 반드시 실결제로 설정하여 운영하셔야 합니다.
    아래 링크를 클릭하여 실결제로 설정하여 운영해 주세요.
    '.$pg_test_conf_link.'
    '; } diff --git a/adm/shop_admin/configform.php b/adm/shop_admin/configform.php index 788f7288e..c6397039a 100644 --- a/adm/shop_admin/configform.php +++ b/adm/shop_admin/configform.php @@ -215,7 +215,7 @@ if (! isset($default['de_nicepay_mid'])) { sql_query($sql, false); } -// 토스페이먼츠 버전 2 client, secret key 추가 +// 토스페이먼츠 client, secret key 추가 if( ! isset($config['cf_toss_client_key']) ){ $sql = "ALTER TABLE `{$g5['config_table']}` ADD COLUMN `cf_toss_client_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_lg_mert_key`, @@ -656,9 +656,9 @@ if(!$default['de_kakaopay_cancelpwd']){ /settle_nicepay_common.php
    토스페이먼츠v2 가상계좌
    입금통보 URL
    토스페이먼츠 가상계좌
    입금통보 URL
    - 토스페이먼츠 상점관리자 > 개발자센터 > 웹훅 > 웹훅 등록하기에 URL에 넣으시고, 구독할 이벤트를 [DEPOSIT_CALLBACK]을 선택하셔야 상점에 자동으로 입금 통보됩니다."); ?> + 토스페이먼츠 상점관리자 > 개발자센터 > 웹훅 > 웹훅 등록하기에 URL에 넣으시고, 구독할 이벤트를 [DEPOSIT_CALLBACK]을 선택하셔야 상점에 자동으로 입금 통보됩니다."); ?> /settle_toss_common.php
    - 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3"); ?> + 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3"); ?>
    - 개발자센터 -> API키 -> 클라이언트 키에서 확인하실 수 있습니다. 예) live_ck_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_ck_tosspayment"); ?> + 개발자센터 -> API키 -> 클라이언트 키에서 확인하실 수 있습니다. 예) live_ck_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_ck_tosspayment"); ?>
    - 개발자센터 -> API키 -> 시크릿 키에서 확인하실 수 있습니다. 예) live_sk_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_sk_tosspayment"); ?> + 개발자센터 -> API키 -> 시크릿 키에서 확인하실 수 있습니다. 예) live_sk_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, 테스트로 결제시에는 [테스트] 키로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_sk_tosspayment"); ?>
    - - - - - - - - - - $var) { - ?> - - - - - - - - - -
    제공 변수 목록
    구분변수명설명
    -
    -
    - -

    아래 표의 #{버튼링크명}은 버튼에 사용할 수 있으며, 실제 발송 시 지정된 URL로 자동 치환됩니다.
    ※ 표에 없는 버튼 링크 변수는 치환되지 않습니다. 등록 시 [https://#{버튼링크명}]으로 작성하시면 됩니다.

    - - - - -
    -

    * 관리자 휴대폰번호 : 관리자로 설정된 []휴대폰 번호를 사용합니다.

    -

    * 그룹 관리자 휴대폰번호 : 그룹 관리자로 지정된 아이디의 휴대폰 번호를 사용합니다.

    -

    * 게시판 관리자 휴대폰번호 : 게시판 관리자로 지정된 아이디의 휴대폰 번호를 사용합니다.

    -
    - - -
    -

    - 환경설정 > 기본환경설정 > 회원가입'; - - if (!empty($config['cf_use_hp'])) { - // 보이기만 설정된 경우 - echo '[휴대폰번호 입력][보이기]로 설정되어 있습니다. 일부 회원은 휴대폰 번호를 입력하지 않아 발송이 제한될 수 있습니다.' - . $link . '에서 [필수입력]으로 설정하는 것을 권장합니다.'; - } else { - // 둘 다 설정 안 된 경우 - echo '[휴대폰번호 입력][보이기] 또는 [필수입력]으로 설정되어 있지 않습니다. 현재 상태에서는 알림톡 발송이 불가능합니다.' - . $link . '에서 반드시 [보이기][필수입력] 중 하나 이상으로 설정해야 합니다.'; - } - ?> -

    -
    - - -
    - - - - - - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '; - ?> - -
    알림톡 프리셋 목록
    사용여부구분발송시점대상템플릿 명미리보기버튼정보문자대체발송
    - - - - - - -
    -
    - -
    - -
    - - $button) { - echo '[' . $button->n . ']
    '; - if ($button->t == 'DS') { // 배송조회 버튼 - echo '· 카카오톡검색 링크 버튼 자동생성
    '; - }else{ - echo ($button->t == 'AL' ? '· iOS링크: ' : ($button->t == 'WL' ? '· Mobile링크: ' : '· 링크: ')) . $button->u1 . - ($button->u2 ? '
    ' . ($button->t == 'AL' ? '· Android링크: ' : ($button->t == 'WL' ? '· PC링크: ' : '· 링크2: ')) . $button->u2 : '') . - ($button->tg ? '
    · 아웃링크: ' . $button->tg : '') . '
    '; - - } - } ?> - -
    - -
    등록된 프리셋이 없습니다.
    -
    -
    - - - -
    - - - \ No newline at end of file diff --git a/adm/alimtalkpresetupdate.php b/adm/alimtalkpresetupdate.php deleted file mode 100644 index 429d023c0..000000000 --- a/adm/alimtalkpresetupdate.php +++ /dev/null @@ -1,37 +0,0 @@ -'', 'bo_device'=>'', @@ -136,7 +131,6 @@ $board_default = array( 'bo_mobile_content_tail'=>'', 'bo_insert_content'=>'', 'bo_sort_field'=>'', -'bo_use_kakaotalk'=>0, ); for ($i = 0; $i <= 10; $i++) { @@ -708,19 +702,6 @@ $pg_anchor = ' - -
    -

    게시판 알림설정

    - 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    - -
      - - - -
      - - - -
    -
    - -
    @@ -370,10 +325,10 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi - + '아이코드', 'popbill' => '팝빌']; + $configKeys = ['cf_sms_use']; + $companies = ['icode' => '아이코드']; $usedCompanies = []; foreach ($configKeys as $key) { diff --git a/theme/basic/mobile/skin/shop/basic/item.form.skin.php b/theme/basic/mobile/skin/shop/basic/item.form.skin.php index aaf8c17bb..79aa47569 100644 --- a/theme/basic/mobile/skin/shop/basic/item.form.skin.php +++ b/theme/basic/mobile/skin/shop/basic/item.form.skin.php @@ -545,11 +545,11 @@ function popup_item_recommend(it_id) } } -// 재입고 알림 +// 재입고SMS 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; popup_window(url, "itemstocksms", opt); } diff --git a/theme/basic/skin/member/basic/register_form.skin.php b/theme/basic/skin/member/basic/register_form.skin.php index 63a2073f8..3b16a35f5 100644 --- a/theme/basic/skin/member/basic/register_form.skin.php +++ b/theme/basic/skin/member/basic/register_form.skin.php @@ -271,53 +271,6 @@ gif, jpg, png파일만 가능하며 용량
    - -
    -

    - 게시판 알림설정 - - 게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.
    알림은 등록된 휴대폰 번호로 발송됩니다.
    -

    -
      - - - -
      - - - -
    -
    - -
    @@ -384,10 +337,10 @@ gif, jpg, png파일만 가능하며 용량 - + '아이코드', 'popbill' => '팝빌']; + $configKeys = ['cf_sms_use']; + $companies = ['icode' => '아이코드']; $usedCompanies = []; foreach ($configKeys as $key) { diff --git a/theme/basic/skin/shop/basic/item.form.skin.php b/theme/basic/skin/shop/basic/item.form.skin.php index 797b59805..4f1095fb6 100644 --- a/theme/basic/skin/shop/basic/item.form.skin.php +++ b/theme/basic/skin/shop/basic/item.form.skin.php @@ -340,11 +340,11 @@ add_stylesheet('', 0 } } - // 재입고 알림 + // 재입고SMS 알림 function popup_stocksms(it_id) { url = "/itemstocksms.php?it_id=" + it_id; - opt = "scrollbars=yes,width=616,height=500,top=10,left=10"; + opt = "scrollbars=yes,width=616,height=420,top=10,left=10"; popup_window(url, "itemstocksms", opt); } From b98d45615cf63ee5095b478b33a8d523f30f89ad Mon Sep 17 00:00:00 2001 From: chym1217 Date: Thu, 18 Sep 2025 14:41:40 +0900 Subject: [PATCH 29/31] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=B3=91=ED=95=A9=EC=97=B0=EC=82=B0?= =?UTF-8?q?=EC=9E=90=20->=20=EC=82=BC=ED=95=AD=EC=97=B0=EC=82=B0=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/member_list.php | 4 +-- adm/member_list_exel.lib.php | 56 ++++++++++++++++----------------- adm/member_list_exel.php | 28 ++++++++--------- adm/member_list_exel_export.php | 26 +++++++-------- 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/adm/member_list.php b/adm/member_list.php index 96abd12cf..bb3be84c6 100644 --- a/adm/member_list.php +++ b/adm/member_list.php @@ -260,7 +260,7 @@ $colspan = 16; value="1" id="mb_mailling_"> - + value="1" id="mb_sms_"> - + diff --git a/adm/member_list_exel.lib.php b/adm/member_list_exel.lib.php index cdce04c8d..bd5826de4 100644 --- a/adm/member_list_exel.lib.php +++ b/adm/member_list_exel.lib.php @@ -49,7 +49,7 @@ function get_export_config($type = null) ], ]; - return $type ? ($config[$type] ?? []) : $config; + return $type ? (isset($config[$type]) ? $config[$type] : []) : $config; } /** @@ -58,7 +58,7 @@ function get_export_config($type = null) function get_member_export_params() { // 친구톡 양식 - 엑셀 양식에 포함할 항목 - $fieldArray = array_map('trim', explode(',', $_GET['fields'] ?? '')); + $fieldArray = array_map('trim', explode(',', isset($_GET['fields']) ? $_GET['fields'] : '')); $vars = []; foreach ($fieldArray as $index => $field) { if(!empty($field)){ @@ -68,29 +68,29 @@ function get_member_export_params() $params = [ 'page' => 1, - 'formatType' => (int)($_GET['formatType'] ?? 1), - 'use_stx' => $_GET['use_stx'] ?? 0, - 'stx_cond' => clean_xss_tags($_GET['stx_cond'] ?? 'like'), - 'sfl' => clean_xss_tags($_GET['sfl'] ?? ''), - 'stx' => clean_xss_tags($_GET['stx'] ?? ''), - 'use_level' => $_GET['use_level'] ?? 0, - 'level_start' => (int)($_GET['level_start'] ?? 1), - 'level_end' => (int)($_GET['level_end'] ?? 10), - 'use_date' => $_GET['use_date'] ?? 0, - 'date_start' => clean_xss_tags($_GET['date_start'] ?? ''), - 'date_end' => clean_xss_tags($_GET['date_end'] ?? ''), - 'use_point' => $_GET['use_point'] ?? 0, - 'point' => $_GET['point'] ?? '', - 'point_cond' => $_GET['point_cond'] ?? 'gte', - 'use_hp_exist' => $_GET['use_hp_exist'] ?? 0, - 'ad_range_only' => $_GET['ad_range_only'] ?? 0, - 'ad_range_type' => clean_xss_tags($_GET['ad_range_type'] ?? 'all'), - 'ad_mailling' => $_GET['ad_mailling'] ?? 0, - 'ad_sms' => $_GET['ad_sms'] ?? 0, - 'agree_date_start' => clean_xss_tags($_GET['agree_date_start'] ?? ''), - 'agree_date_end' => clean_xss_tags($_GET['agree_date_end'] ?? ''), - 'use_intercept' => $_GET['use_intercept'] ?? 0, - 'intercept' => clean_xss_tags($_GET['intercept'] ?? 'exclude'), + 'formatType' => (int)(isset($_GET['formatType']) ? $_GET['formatType'] : 1), + 'use_stx' => isset($_GET['use_stx']) ? $_GET['use_stx'] : 0, + 'stx_cond' => clean_xss_tags(isset($_GET['stx_cond']) ? $_GET['stx_cond'] : 'like'), + 'sfl' => clean_xss_tags(isset($_GET['sfl']) ? $_GET['sfl'] : ''), + 'stx' => clean_xss_tags(isset($_GET['stx']) ? $_GET['stx'] : ''), + 'use_level' => isset($_GET['use_level']) ? $_GET['use_level'] : 0, + 'level_start' => (int)(isset($_GET['level_start']) ? $_GET['level_start'] : 1), + 'level_end' => (int)(isset($_GET['level_end']) ? $_GET['level_end'] : 10), + 'use_date' => isset($_GET['use_date']) ? $_GET['use_date'] : 0, + 'date_start' => clean_xss_tags(isset($_GET['date_start']) ? $_GET['date_start'] : ''), + 'date_end' => clean_xss_tags(isset($_GET['date_end']) ? $_GET['date_end'] : ''), + 'use_point' => isset($_GET['use_point']) ? $_GET['use_point'] : 0, + 'point' => isset($_GET['point']) ? $_GET['point'] : '', + 'point_cond' => isset($_GET['point_cond']) ? $_GET['point_cond'] : 'gte', + 'use_hp_exist' => isset($_GET['use_hp_exist']) ? $_GET['use_hp_exist'] : 0, + 'ad_range_only' => isset($_GET['ad_range_only']) ? $_GET['ad_range_only'] : 0, + 'ad_range_type' => clean_xss_tags(isset($_GET['ad_range_type']) ? $_GET['ad_range_type'] : 'all'), + 'ad_mailling' => isset($_GET['ad_mailling']) ? $_GET['ad_mailling'] : 0, + 'ad_sms' => isset($_GET['ad_sms']) ? $_GET['ad_sms'] : 0, + 'agree_date_start' => clean_xss_tags(isset($_GET['agree_date_start']) ? $_GET['agree_date_start'] : ''), + 'agree_date_end' => clean_xss_tags(isset($_GET['agree_date_end']) ? $_GET['agree_date_end'] : ''), + 'use_intercept' => isset($_GET['use_intercept']) ? $_GET['use_intercept'] : 0, + 'intercept' => clean_xss_tags(isset($_GET['intercept']) ? $_GET['intercept'] : 'exclude'), 'vars' => $vars, ]; @@ -212,7 +212,7 @@ function member_export_build_where($params) // 정보수신동의 조건 if (!empty($params['ad_range_only']) && $params['ad_range_only'] === '1') { - $range = $params['ad_range_type'] ?? ''; + $range = isset($params['ad_range_type']) ? $params['ad_range_type'] : ''; // 공통: 마케팅 목적 수집·이용 동의 + (필요 시) 제3자 동의 $thirdparty_clause = $config['cf_sms_use'] !== '' ? " AND mb_thirdparty_agree = 1" : ""; @@ -241,8 +241,8 @@ function member_export_build_where($params) } else { // 수신동의기간 직접 입력 - custom_period - $date_start = $params['agree_date_start'] ?? ''; - $date_end = $params['agree_date_end'] ?? ''; + $date_start = isset($params['agree_date_start']) ? $params['agree_date_start'] : ''; + $date_end = isset($params['agree_date_end']) ? $params['agree_date_end'] : ''; if ($date_start && $date_end) { $emailDateCond = "mb_mailling_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'"; diff --git a/adm/member_list_exel.php b/adm/member_list_exel.php index db16c4e55..994ae979f 100644 --- a/adm/member_list_exel.php +++ b/adm/member_list_exel.php @@ -69,10 +69,10 @@ $colspan = 14; } ?> - + - - + +
    @@ -102,8 +102,8 @@ $colspan = 14;
    - ~ - + ~ +
    @@ -113,11 +113,11 @@ $colspan = 14;
    - + - - - + + +
    @@ -132,7 +132,7 @@ $colspan = 14; $label) { - $selected = (($_GET['intercept'] ?? '') === $val) ? 'selected' : ''; + $selected = ((isset($_GET['intercept']) ? $_GET['intercept'] : '') === $val) ? 'selected' : ''; echo ""; } ?> @@ -171,7 +171,7 @@ $colspan = 14; ~ - + ~ +

    * 광고성 정보 수신(이메일 또는 SMS/카카오톡) 동의일자 기준

    diff --git a/adm/member_list_exel_export.php b/adm/member_list_exel_export.php index 9421b5905..ffaebf3f1 100644 --- a/adm/member_list_exel_export.php +++ b/adm/member_list_exel_export.php @@ -25,7 +25,7 @@ $resultExcelDelete = member_export_delete(); member_export_set_sse_headers(); // 모드 확인 -$mode = $_GET['mode'] ?? ''; +$mode = isset($_GET['mode']) ? $_GET['mode'] : ''; if ($mode !== 'start') { member_export_send_progress("error", "잘못된 요청 입니다."); member_export_write_log($params, ['success' => false, 'error' => '잘못된 요청 입니다.']); @@ -107,7 +107,7 @@ function main_member_export($params) member_export_send_progress("progress", "", 1, $total, $total); } - member_export_write_log($params, ['success' => true, 'total' => $total, 'files' => $fileList, 'zip' => $zipFileName ?? null]); + member_export_write_log($params, ['success' => true, 'total' => $total, 'files' => $fileList, 'zip' => isset($zipFileName) ? $zipFileName : null]); member_export_send_progress("done", "", 2, $total, $total, $pages, $pages, $fileList, $zipFileName); } @@ -211,13 +211,13 @@ function member_export_get_data($params) // SQL 필드 생성 $sqlFields = []; foreach ($fields as $field) { - $sqlFields[] = $sqlTransformMap[$field] ?? $field; + $sqlFields[] = isset($sqlTransformMap[$field]) ? $sqlTransformMap[$field] : $field; } $field_list = implode(', ', $sqlFields); $where = member_export_build_where($params); - $page = (int)($params['page'] ?? 1); + $page = (int)(isset($params['page']) ? $params['page'] : 1); if ($page < 1) $page = 1; $offset = ($page - 1) * MEMBER_EXPORT_PAGE_SIZE; @@ -233,7 +233,7 @@ function member_export_get_data($params) while ($row = sql_fetch_array($result)) { $rowData = []; foreach ($fields as $field) { - $rowData[] = $row[$field] ?? ''; + $rowData[] = isset($row[$field]) ? $row[$field] : ''; } $excelData[] = $rowData; } @@ -430,7 +430,7 @@ function member_export_write_log($params, $result = []) $maxSize = 1024 * 1024 * 2; // 2MB $maxFiles = 10; // 최대 로그 파일 수 (필요시 조정) - $username = $member['mb_id'] ?? 'guest'; + $username = isset($member['mb_id']) ? $member['mb_id'] : 'guest'; $datetime = date("Y-m-d H:i:s"); if (!is_dir(MEMBER_LOG_DIR)) { @@ -443,7 +443,7 @@ function member_export_write_log($params, $result = []) // 최신 파일 기준 정렬 (최신 → 오래된) usort($logFiles, fn($a, $b) => filemtime($b) - filemtime($a)); - $latestLogFile = $logFiles[0] ?? null; + $latestLogFile = isset($logFiles[0]) ? $logFiles[0] : null; // 용량 기준으로 새 파일 생성 if (!$latestLogFile || filesize($latestLogFile) >= $maxSize) { @@ -470,7 +470,7 @@ function member_export_write_log($params, $result = []) if ($params['use_stx'] == 1 && !empty($params['stx'])) { $sfl_list = get_export_config('sfl_list'); - $label = $sfl_list[$params['sfl']] ?? ''; + $label = isset($sfl_list[$params['sfl']]) ? $sfl_list[$params['sfl']] : ''; $condition[] = "검색({$params['stx_cond']}) : {$label} - {$params['stx']}"; } @@ -487,7 +487,7 @@ function member_export_write_log($params, $result = []) // 포인트 조건 if ($params['use_point'] == 1 && $params['point'] !== '') { $point_cond_map = get_export_config('point_cond_map'); - $symbol = $point_cond_map[$params['point_cond']] ?? '≥'; + $symbol = isset($point_cond_map[$params['point_cond']]) ? $point_cond_map[$params['point_cond']] : '≥'; $condition[] = "포인트 {$symbol} {$params['point']}"; } @@ -499,7 +499,7 @@ function member_export_write_log($params, $result = []) // 광고 수신 동의 if ($params['ad_range_only'] == 1) { $ad_range_list = get_export_config('ad_range_list'); - $label = $ad_range_list[$params['ad_range_type']] ?? ''; + $label = isset($ad_range_list[$params['ad_range_type']]) ? $ad_range_list[$params['ad_range_type']] : ''; $condition[] = "수신동의: 예 ({$label})"; if ($params['ad_range_type'] == "custom_period" && ($params['agree_date_start'] || $params['agree_date_end'])) { @@ -521,7 +521,7 @@ function member_export_write_log($params, $result = []) // 차단회원 처리 if ($params['use_intercept'] == 1) { $intercept_list = get_export_config('intercept_list'); - $label = $intercept_list[$params['intercept']] ?? ''; + $label = isset($intercept_list[$params['intercept']]) ? $intercept_list[$params['intercept']] : ''; if ($label) $condition[] = $label; } @@ -530,8 +530,8 @@ function member_export_write_log($params, $result = []) // 성공일 경우 추가 정보 if ($success) { - $total = $result['total'] ?? 0; - $fileCount = isset($result['zip']) ? 1 : count($result['files'] ?? []); + $total = isset($result['total']) ? $result['total'] : 0; + $fileCount = isset($result['zip']) ? 1 : count(isset($result['files']) ? $result['files'] : []); $line1 .= " | 총 {$total}건 | 파일: {$fileCount}개"; } From 887833089b70b1b01b824c3459ca35ed86c9c6ab Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 22 Sep 2025 11:05:39 +0900 Subject: [PATCH 30/31] =?UTF-8?q?php=20warning=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/auth_update.php | 2 +- bbs/memo_form.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adm/auth_update.php b/adm/auth_update.php index ab01b0316..9096982e9 100644 --- a/adm/auth_update.php +++ b/adm/auth_update.php @@ -13,7 +13,7 @@ if ($is_admin != 'super') { } $mb = get_member($mb_id); -if (!$mb['mb_id']) { +if (!(isset($mb['mb_id']) && $mb['mb_id'])) { alert('존재하는 회원아이디가 아닙니다.'); } diff --git a/bbs/memo_form.php b/bbs/memo_form.php index 02848e129..55ea41a36 100644 --- a/bbs/memo_form.php +++ b/bbs/memo_form.php @@ -20,7 +20,7 @@ $me_id = isset($_REQUEST['me_id']) ? clean_xss_tags($_REQUEST['me_id'] if ($me_recv_mb_id) { $mb = get_member($me_recv_mb_id); - if (!$mb['mb_id']) + if (!(isset($mb['mb_id']) && $mb['mb_id'])) alert_close('회원정보가 존재하지 않습니다.\\n\\n탈퇴하였을 수 있습니다.'); if (!$mb['mb_open'] && $is_admin != 'super') From e26fb62f5dc78d4e33e87d843cce0712b248a4c0 Mon Sep 17 00:00:00 2001 From: thisgun Date: Mon, 22 Sep 2025 11:06:20 +0900 Subject: [PATCH 31/31] =?UTF-8?q?=EB=B2=84=EC=A0=84=205.6.23=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 29d68051d..5ccc21669 100644 --- a/version.php +++ b/version.php @@ -2,7 +2,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 define('G5_VERSION', '그누보드5'); -define('G5_GNUBOARD_VER', '5.6.22'); +define('G5_GNUBOARD_VER', '5.6.23'); // 그누보드5.4.5.5 버전과 영카트5.4.5.5.1 버전을 합쳐서 그누보드5.4.6 버전에서 시작함 (kagla-210617) // G5_YOUNGCART_VER 이 상수를 사용하는 곳이 있으므로 주석 처리 해제함 // 그누보드5.4.6 이상 버전 부터는 영카트를 그누보드에 포함하여 배포하므로 영카트5의 버전은 의미가 없습니다.