팝빌 알림톡 기능 추가

This commit is contained in:
chym1217
2025-09-04 12:32:31 +09:00
parent 37d0dcb48f
commit 66f6a75a10
99 changed files with 8876 additions and 260 deletions

View File

@ -1,11 +1,115 @@
function check_all(f)
{
var chk = document.getElementsByName("chk[]");
/** 공통 UI 모듈 */
window.CommonUI = {
bindTabs(tabSelector, contentSelector, options = {}) {
const tabs = document.querySelectorAll(tabSelector);
const contents = document.querySelectorAll(contentSelector);
for (i=0; i<chk.length; i++)
chk[i].checked = f.chkall.checked;
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.dataset.tab;
const target = document.getElementById(`tab-${tabName}`);
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
contents.forEach(c => c.classList.add('is-hidden'));
if (target) target.classList.remove('is-hidden');
options.onChange?.(tabName, target);
});
});
}
};
function setHtml(el, markup) {
if (!el) return;
if (markup == null || markup === '') {
el.textContent = '';
return;
}
const range = document.createRange();
range.selectNodeContents(el);
el.replaceChildren(range.createContextualFragment(markup));
}
/** 팝업 관리 모듈 */
window.PopupManager = {
open(id, options = {}) {
const el = document.getElementById(id);
if (el) {
el.classList.remove('is-hidden');
this.bindOutsideClickClose(id);
if (!options.disableOutsideClose) {
this.bindOutsideClickClose(id);
} else {
this.unbindOutsideClickClose(id);
}
}
},
close(id) {
const el = document.getElementById(id);
if (el) el.classList.add('is-hidden');
},
toggle(id) {
const el = document.getElementById(id);
if (el) el.classList.toggle('is-hidden');
},
bindOutsideClickClose(id) {
const el = document.getElementById(id);
if (!el) return;
el.onclick = () => this.close(id);
},
unbindOutsideClickClose(id) {
const el = document.getElementById(id);
if (!el) return;
el.onclick = null;
},
/**
* 팝업 콘텐츠 렌더링 (타이틀, 바디, 푸터 구성)
* @param {string} title - 팝업 제목
* @param {string} body - 팝업 본문 HTML
* @param {string} [footer] - 푸터 HTML
* @param {object} [options] - 팝업 열기 옵션
*/
render(title, body, footer = '', options = {}) {
const titleEl = document.getElementById('popupTitle');
const bodyEl = document.getElementById('popupBody');
const footerEl = document.getElementById('popupFooter');
if (titleEl) titleEl.textContent = title;
if (bodyEl) setHtml(bodyEl, body);
if (footerEl) setHtml(footerEl, footer);
this.open('popupOverlay', options);
}
};
/** 형식 체크 */
function check_all(target) {
const chkboxes = document.getElementsByName("chk[]");
let chkall;
if (target && target.tagName === "FORM") {
chkall = target.querySelector('input[name="chkall"]');
} else if (target && target.type === "checkbox") {
chkall = target;
}
if (!chkall) return;
for (const checkbox of chkboxes) {
checkbox.checked = chkall.checked;
}
}
function btn_check(f, act)
{
if (act == "update") // 선택수정

View File

@ -6,11 +6,13 @@ $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),
array('100910', '캡챠파일 일괄삭제', G5_ADMIN_URL . '/captcha_file_delete.php', 'cf_captcha', 1),
array('100920', '썸네일파일 일괄삭제', G5_ADMIN_URL . '/thumbnail_file_delete.php', 'cf_thumbnail', 1),
array('100930', '회원관리파일 일괄삭제', G5_ADMIN_URL . '/member_list_file_delete.php', 'cf_memberlist', 1),
array('100500', 'phpinfo()', G5_ADMIN_URL . '/phpinfo.php', 'cf_phpinfo')
);

View File

@ -2,6 +2,7 @@
$menu['menu200'] = array(
array('200000', '회원관리', G5_ADMIN_URL . '/member_list.php', 'member'),
array('200100', '회원관리', G5_ADMIN_URL . '/member_list.php', 'mb_list'),
array('200400', '회원관리파일', G5_ADMIN_URL . '/member_list_exel.php', 'mb_list'),
array('200300', '회원메일발송', G5_ADMIN_URL . '/mail_list.php', 'mb_mail'),
array('200800', '접속자집계', G5_ADMIN_URL . '/visit_list.php', 'mb_visit', 1),
array('200810', '접속자검색', G5_ADMIN_URL . '/visit_search.php', 'mb_search', 1),

View File

@ -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', '재입고SMS알림', G5_ADMIN_URL . '/shop_admin/itemstocksms.php', 'sst_stock_sms', 1),
array('500400', '재입고알림', 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),

View File

@ -14,7 +14,6 @@ $print_version = ($is_admin == 'super') ? 'Version ' . G5_GNUBOARD_VER : '';
<strong>자바스크립트를 사용하지 않음</strong>으로 설정하신 경우는 수정이나 삭제시 별도의 경고창이 나오지 않으므로 이점 주의하시기 바랍니다.
</p>
</noscript>
</div>
<footer id="ft">
<p>
@ -22,8 +21,25 @@ $print_version = ($is_admin == 'super') ? 'Version ' . G5_GNUBOARD_VER : '';
<button type="button" class="scroll_top"><span class="top_img"></span><span class="top_txt">TOP</span></button>
</p>
</footer>
</div>
<!-- 공통 레이어 팝업 컨테이너 -->
<div id="adminPopupContainer">
<div id="popupOverlay" class="popup-overlay is-hidden" onclick="PopupManager.close('popupOverlay')">
<div class="popup-content" onclick="event.stopPropagation()">
<div class="popup-header">
<strong id="popupTitle" class="popup-title"></strong>
<button type="button" class="popup-close-btn" onclick="PopupManager.close('popupOverlay')">
<i class="fa fa-close"></i><span class="sound_only">팝업 닫기</span>
</button>
</div>
<div class="popup-body" id="popupBody">
<!-- 동적으로 내용 주입 -->
</div>
<div class="popup-footer" id="popupFooter">
<!-- 버튼 등 동적으로 -->
</div>
</div>
</div>
</div>
<script>

57
adm/alimtalkpreset.php Normal file
View File

@ -0,0 +1,57 @@
<?php
$sub_menu = '100320';
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
auth_check_menu($auth, $sub_menu, "r");
$g5['title'] = '알림톡 프리셋 관리';
include_once(G5_ADMIN_PATH.'/admin.head.php');
// 영카트(쇼핑몰) 사용 여부 확인
$has_shop = defined('G5_USE_SHOP') && G5_USE_SHOP;
// SQL 조건
$sql_search = $has_shop ? 'where (1)' : "WHERE kp_category <> '쇼핑몰'";
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}");
?>
<?php if (!sql_query("DESC {$g5['kakao5_preset_table']}", false)) { ?>
<h2 class="h2_frm">카카오톡 프리셋 DB가 설치되지 않았습니다.</h2>
<div class="local_desc01 local_desc">
<p>카카오톡 프리셋 DB가 설치되지 않아 프리셋을 사용할 수 없습니다.
<br><a href="<?php echo G5_ADMIN_URL;?>/dbupgrade.php" class="btn_frmline">DB 업그레이드</a>를 진행해주세요.</p>
</div>
<?php include_once(G5_ADMIN_PATH.'/admin.tail.php'); exit; ?>
<?php } ?>
<?php if ($config['cf_kakaotalk_use'] == 'popbill') { // 팝빌 사용
include_once(G5_ADMIN_PATH.'/alimtalkpreset_popbill.php');
} else { ?>
<h2 class="h2_frm">카카오톡 발송 서비스를 사용할 수 없습니다.</h2>
<div class="local_desc01 local_desc">
<p>카카오톡 을 사용하지 않고 있기 때문에, 카카오톡 전송을 할 수 없습니다.
<br>카카오톡 사용 설정은 <a href="<?php echo G5_ADMIN_URL;?>/config_form.php#anc_cf_mail" class="btn_frmline">환경설정 &gt; 기본환경설정 &gt; 기본알림환경</a> 에서 카카오톡 사용을 변경해 주셔야 사용하실수 있습니다.</p>
</div>
<?php } ?>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');

View File

@ -0,0 +1,300 @@
<?php
/*
* 팝빌 - 알림톡 프리셋 설정
*/
// 템플릿 목록 조회
$templates = get_popbill_template_list();
add_javascript('<script src="'.G5_JS_URL.'/kakao5.js"></script>', 1); // 카카오톡5 솔루션 js 추가
?>
<?php
// 팝빌 연결이 제대로 안되었을 경우 아래와 같이 표시
$check_popbill = get_popbill_service_info();
if(isset($check_popbill['error'])) { ?>
<h2 class="h2_frm">팝빌 카카오톡 발송 서비스를 사용할 수 없습니다.</h2>
<div class="local_desc01 local_desc">
<p>팝빌 서비스 설정이 되어 있지 않아, 프리셋 서비스를 사용할 수 없습니다.
<br>팝빌 설정은 <a href="<?php echo G5_ADMIN_URL;?>/config_form.php#anc_cf_mail" class="btn_frmline">환경설정 &gt; 기본환경설정 &gt; 기본알림환경</a> 에서 확인 및 설정해 주셔야 사용하실 수 있습니다.</p><br>
<p>* 설정 오류 내용 : <span style="color:red"><?php echo $check_popbill["error"];?></span></p>
</div>
<?php } else { ?>
<div class="local_desc01 local_desc">
<p><a href="#" class="btn_template_manage" id="btnTemplateManageInfo">템플릿관리</a>를 통해 신청 후 승인된 템플릿만 사용가능합니다.<br>템플릿 내용 작성 시, 동일한 <b>[구분]</b>에 속한 변수만 사용 가능하며, 아래에 제공된 변수 외의 항목을 입력할 경우 적용되지 않습니다.</p><br>
<p>아래 표의 <b>#{변수명}</b>만 템플릿 내용에 사용할 수 있으며, 실제 발송 시 값으로 자동 치환됩니다.<br><span style="color:#888;">※ 표에 없는 변수는 치환되지 않습니다.</span></p>
<p id="variable_toggle" class="variable_toggle" style="cursor:pointer; font-weight:bold;">[사용 가능한 변수 리스트<span class="btn_toggle_text" style="font-weight:normal;">▼</span>]</p>
<div id="variable_table" class="variable_table">
<table class="tbl_head01 tbl_wrap">
<caption>제공 변수 목록</caption>
<thead>
<tr>
<th scope="col">구분</th>
<th scope="col">변수명</th>
<th scope="col">설명</th>
</tr>
</thead>
<tbody>
<?php
// [정의] $kakao5_preset_variable_list - extend/kakao5.extend.php
if (!empty($kakao5_preset_variable_list) && is_array($kakao5_preset_variable_list)) {
foreach ($kakao5_preset_variable_list as $category) {
$cat_name = isset($category['category']) ? $category['category'] : '';
$vars = isset($category['variables']) ? $category['variables'] : [];
$rowspan = count($vars);
foreach ($vars as $idx => $var) {
?>
<tr>
<?php if ($idx === 0) { ?>
<td rowspan="<?php echo $rowspan; ?>"><?php echo htmlspecialchars($cat_name); ?></td>
<?php } ?>
<td><?php echo htmlspecialchars($var['name'] ?? ''); ?></td>
<td style="text-align:left;"><?php echo $var['description'] ?? ''; ?></td>
</tr>
<?php
}
}
}
?>
</tbody>
</table>
</div>
<br>
<p>아래 표의 <b>#{버튼링크명}</b>은 버튼에 사용할 수 있으며, 실제 발송 시 지정된 URL로 자동 치환됩니다.<br><span style="color:#888;">※ 표에 없는 버튼 링크 변수는 치환되지 않습니다. 등록 시 [<b>https://#{버튼링크명}</b>]으로 작성하시면 됩니다.</span></p>
<p id="button_link_toggle" class="button_link_toggle" style="cursor:pointer; font-weight:bold;">[버튼 링크 치환 리스트<span class="button_link_toggle_text" style="font-weight:normal;">▼</span>]</p>
<div id="button_link_table" class="variable_table">
<table class="tbl_head01 tbl_wrap">
<caption>제공 변수 목록</caption>
<thead>
<tr>
<th scope="col">버튼링크명</th>
<th scope="col">설명</th>
</tr>
</thead>
<tbody>
<?php
// [정의] $kakao5_preset_button_links - extend/kakao5.extend.php
if (!empty($kakao5_preset_button_links) && is_array($kakao5_preset_button_links)) {
foreach ($kakao5_preset_button_links as $key => $val) {
?>
<tr>
<td><?php echo htmlspecialchars($key); ?></td>
<td style="text-align:left;"><?php echo isset($val['description']) ? $val['description'] : ''; ?><br>
<?php if (isset($val['url'])) { ?>
<span style="color:#888;">* URL: <?php echo htmlspecialchars($val['url']); ?></span>
<?php } ?>
</td>
</tr>
<?php
}
}
?>
</tbody>
</table>
</div>
</div>
<div class="local_desc01 local_desc">
<p><b>* 관리자 휴대폰번호</b> : 관리자로 설정된 <b>[<?php echo $config['cf_admin'];?>]</b>의 <b>휴대폰 번호</b>를 사용합니다.</p>
<p><b>* 그룹 관리자 휴대폰번호</b> : 그룹 관리자로 지정된 아이디의 <b>휴대폰 번호</b>를 사용합니다.</p>
<p><b>* 게시판 관리자 휴대폰번호</b> : 게시판 관리자로 지정된 아이디의 <b>휴대폰 번호</b>를 사용합니다.</p>
</div>
<?php if (empty($config['cf_req_hp'])) { ?>
<div class="admin_pg_notice od_test_caution">
<p>
<?php
$link = '<br><a href="'.G5_ADMIN_URL.'/config_form.php#anc_cf_join">환경설정 &gt; 기본환경설정 &gt; 회원가입</a>';
if (!empty($config['cf_use_hp'])) {
// 보이기만 설정된 경우
echo '<b>[휴대폰번호 입력]</b>이 <b>[보이기]</b>로 설정되어 있습니다. 일부 회원은 휴대폰 번호를 입력하지 않아 발송이 제한될 수 있습니다.'
. $link . '에서 <b>[필수입력]</b>으로 설정하는 것을 권장합니다.';
} else {
// 둘 다 설정 안 된 경우
echo '<b>[휴대폰번호 입력]</b>이 <b>[보이기]</b> 또는 <b>[필수입력]</b>으로 설정되어 있지 않습니다. 현재 상태에서는 알림톡 발송이 불가능합니다.'
. $link . '에서 반드시 <b>[보이기]</b>나 <b>[필수입력]</b> 중 하나 이상으로 설정해야 합니다.';
}
?>
</p>
</div>
<?php } ?>
<form name="falimtalkpreset" action="./alimtalkpresetupdate.php" method="post" enctype="MULTIPART/FORM-DATA">
<input type="hidden" name="token" value="">
<input type="hidden" name="sst" value="<?php echo $sst ?>">
<input type="hidden" name="sod" value="<?php echo $sod ?>">
<input type="hidden" name="sfl" value="<?php echo $sfl ?>">
<input type="hidden" name="stx" value="<?php echo $stx ?>">
<input type="hidden" name="page" value="<?php echo $page ?>">
<section id="anc_scf_customer">
<div class="tbl_head01 tbl_wrap">
<table>
<caption>알림톡 프리셋 목록</caption>
<thead>
<tr>
<th scope="col"><?php echo subject_sort_link('kp_active') ?>사용여부</a></th>
<th scope="col"><?php echo subject_sort_link('kp_category') ?>구분</a></th>
<th scope="col">발송시점</th>
<th scope="col"><?php echo subject_sort_link('kp_type') ?>대상</a></th>
<th scope="col">템플릿 명</th>
<th scope="col">미리보기</th>
<th scope="col">버튼정보</th>
<th scope="col">문자대체발송</th>
</tr>
</thead>
<tbody>
<?php
for ($ci=0; $row=sql_fetch_array($result); $ci++) {
$bg = 'bg'.($ci%2);
?>
<tr class="<?php echo $bg; ?>">
<td class="td_chk2">
<label class="preset-toggle">
<input type="checkbox" name="kp_active[<?php echo $row['kp_id']; ?>]" value="1" <?php echo $row['kp_active'] == '1' ? 'checked' : ''; ?> onchange="toggleTemplateFields(<?php echo $row['kp_id']; ?>)">
<span class="preset-slider"></span>
</label>
</td>
<td class="td_chk2"><?php echo get_sanitize_input($row['kp_category']); ?></td>
<td class="td_cnt"><?php echo get_sanitize_input($row['kp_preset_name']); ?></td>
<td class="td_chk2"><?php echo $row['kp_type']; ?></td>
<td class="td_category1">
<select name="kp_template_name[<?php echo $row['kp_id']; ?>]" id="template_<?php echo $row['kp_id']; ?>" style="min-width: 350px;">
<option value="">템플릿 선택</option>
<?php
$template_content = '';
$buttons = '';
if (!empty($templates) && is_array($templates)) {
foreach ($templates as $tpl) {
$tplName = $tpl->templateName ?? '';
$tplCode = $tpl->templateCode ?? '';
if (empty($tplCode)) continue;
$isSelected = ($row['kp_template_name'] == $tplCode);
if ($isSelected) {
$template_content = $tpl->template;
$buttons = $tpl->btns;
}
echo '<option value="' . $tplCode . '" ' . ($isSelected ? 'selected' : '') . '>[' . ($tpl->plusFriendID ?? '') . "] ". $tplName . ' (' . $tplCode . ')' . '</option>';
}
}
?>
</select>
</td>
<!-- 템플릿 보기 버튼 추가 -->
<td class="td_cnt">
<?php if (!empty($row['kp_template_name'])) { ?>
<button type="button" class="btn_template_preview_open">템플릿보기</button>
<div class="template_preview">
<div class="template_preview_txt"><?php echo nl2br($template_content); ?></div>
<button type="button" class="btn_template_preview_close">X 닫기</button>
</div>
<?php } ?>
</td>
<!-- 버튼 정보 -->
<td class="td_addr td_odrnum">
<?php if (!empty($buttons)) { ?>
<?php foreach ($buttons as $index => $button) {
echo '<strong>[' . $button->n . ']</strong><br>';
if ($button->t == 'DS') { // 배송조회 버튼
echo '<span style="color:#888;">· 카카오톡검색 링크 버튼 자동생성</span><br>';
}else{
echo ($button->t == 'AL' ? '· iOS링크: ' : ($button->t == 'WL' ? '· Mobile링크: ' : '· 링크: ')) . $button->u1 .
($button->u2 ? '<br>' . ($button->t == 'AL' ? '· Android링크: ' : ($button->t == 'WL' ? '· PC링크: ' : '· 링크2: ')) . $button->u2 : '') .
($button->tg ? '<br>· 아웃링크: ' . $button->tg : '') . '<br>';
}
} ?>
<?php } ?>
</td>
<td class="td_category1">
<select name="kp_alt_send[<?php echo $row['kp_id']; ?>]" id="alt_send_<?php echo $row['kp_id']; ?>" style="min-width: 150px;">
<option value="1" <?php echo $row['kp_alt_send'] == '1' ? 'selected' : ''; ?>>대체문자발송</option>
<option value="0" <?php echo $row['kp_alt_send'] == '0' ? 'selected' : ''; ?>>사용안함</option>
</select>
</td>
</tr>
<?php }
if ($ci == 0) echo '<tr><td colspan="7" class="empty_table">등록된 프리셋이 없습니다.</td></tr>';
?>
</tbody>
</table>
</div>
</section>
<!-- 상단 버튼 [템플릿관리, 전송내역, 일괄수정] -->
<div class="btn_fixed_top">
<a href="#" class="btn btn_02 btn_template_manage" id="btnTemplateManage">템플릿관리</a>
<a href="#" class="btn btn_02 btn_send_manage" id="btnSendManage">전송내역</a>
<input type="submit" value="일괄수정" class="btn_submit btn" accesskey="s">
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 템플릿 미리보기 열기
document.querySelectorAll('.btn_template_preview_open').forEach(button => {
button.addEventListener('click', () => {
// 모든 preview 닫기
document.querySelectorAll('.template_preview').forEach(preview => {
preview.classList.remove('is-visible');
});
const previewBox = button.nextElementSibling;
if (previewBox?.classList.contains('template_preview')) previewBox.classList.add('is-visible');
});
});
// 템플릿 미리보기 닫기
document.querySelectorAll('.btn_template_preview_close').forEach(closeBtn => {
closeBtn.addEventListener('click', () => {
const previewBox = closeBtn.closest('.template_preview');
if (previewBox) previewBox.classList.remove('is-visible')
});
});
// 변수 리스트 및 버튼 링크 리스트 토글 함수 리팩토링
function setupToggleSection(toggleId, tableId, toggleTextClass = '.btn_toggle_text') {
const toggleBtn = document.getElementById(toggleId);
const table = document.getElementById(tableId);
if (toggleBtn && table) {
toggleBtn.addEventListener('click', () => {
const toggleText = toggleBtn.querySelector(toggleTextClass);
table.classList.toggle('is-visible');
const isVisible = table.classList.contains('is-visible');
if (toggleText) toggleText.textContent = isVisible ? '▲' : '▼';
});
}
}
setupToggleSection('variable_toggle', 'variable_table');
setupToggleSection('button_link_toggle', 'button_link_table');
// 템플릿 관리 팝업: [정의] openKakao5PopupFromAjax() - js/kakao5.js
document.querySelectorAll('#btnTemplateManageInfo, #btnTemplateManage').forEach(btn => {
btn.addEventListener('click', async(e) => {
e.preventDefault();
await openKakao5PopupFromAjax('<?php echo G5_KAKAO5_URL; ?>', 1);
});
});
// 전송내역 팝업: [정의] openKakao5PopupFromAjax() - js/kakao5.js
document.querySelectorAll('#btnSendManage').forEach(btn => {
btn.addEventListener('click', async(e) => {
e.preventDefault();
await openKakao5PopupFromAjax('<?php echo G5_KAKAO5_URL; ?>', 2);
});
});
});
</script>
<?php } ?>

View File

@ -0,0 +1,37 @@
<?php
$sub_menu = "100320";
include_once('./_common.php');
auth_check_menu($auth, $sub_menu, "w");
check_demo();
check_admin_token();
$g5['title'] = "알림톡 프리셋 관리";
// 알림톡 프리셋 일괄 업데이트
$kp_active = isset($_REQUEST['kp_active']) ? $_REQUEST['kp_active'] : array();
$kp_template_name = isset($_REQUEST['kp_template_name']) ? $_REQUEST['kp_template_name'] : array();
$kp_alt_send = isset($_REQUEST['kp_alt_send']) ? $_REQUEST['kp_alt_send'] : array();
// DB에서 전체 프리셋 목록 조회
$presets = array();
$sql = "SELECT kp_id, kp_active, kp_template_name, kp_alt_send FROM {$g5['kakao5_preset_table']}";
$result = sql_query($sql);
for($i=0; $row=sql_fetch_array($result); $i++) {
$kp_id = $row['kp_id'];
$active = isset($kp_active[$kp_id]) ? (int)$kp_active[$kp_id] : 0;
$template_name = isset($kp_template_name[$kp_id]) ? sql_escape_string($kp_template_name[$kp_id]) : '';
$alt_send = isset($kp_alt_send[$kp_id]) ? (int)$kp_alt_send[$kp_id] : 0;
// 값이 하나라도 다르면 UPDATE
if ($row['kp_active'] != $active || $row['kp_template_name'] != $template_name || $row['kp_alt_send'] != $alt_send) {
$sql = "UPDATE {$g5['kakao5_preset_table']} SET kp_active = '{$active}', kp_template_name = '{$template_name}', kp_alt_send = '{$alt_send}' WHERE kp_id = '{$kp_id}'";
sql_query($sql);
}
}
goto_url(G5_ADMIN_URL.'/alimtalkpreset.php?' . $qstr);

View File

@ -112,6 +112,7 @@ $sql = " insert into {$g5['board_table']}
bo_notice = '{$board['bo_notice']}',
bo_upload_count = '{$board['bo_upload_count']}',
bo_use_email = '{$board['bo_use_email']}',
bo_use_kakaotalk = '{$board['bo_use_kakaotalk']}',
bo_use_cert = '{$board['bo_use_cert']}',
bo_use_sns = '{$board['bo_use_sns']}',
bo_use_captcha = '{$board['bo_use_captcha']}',

View File

@ -89,6 +89,11 @@ if (!isset($board['bo_select_editor'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_select_editor` VARCHAR(50) NOT NULL DEFAULT '' AFTER `bo_use_dhtml_editor` ", false);
}
// SMS/알림톡 사용 여부 추가
if (!isset($board['bo_use_kakaotalk'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_use_kakaotalk` VARCHAR(50) NOT NULL DEFAULT '' AFTER `bo_use_email` ", false);
}
$board_default = array(
'bo_mobile_subject'=>'',
'bo_device'=>'',
@ -702,6 +707,19 @@ $pg_anchor = '<ul class="anchor">
<label for="chk_all_use_email">전체적용</label>
</td>
</tr>
<tr>
<th scope="row"><label for="bo_use_kakaotalk">SMS/알림톡 사용</label></th>
<td>
<input type="checkbox" name="bo_use_kakaotalk" value="1" id="bo_use_kakaotalk" <?php echo $board['bo_use_kakaotalk']?'checked':''; ?>>
사용
</td>
<td class="td_grpset">
<input type="checkbox" name="chk_grp_use_kakaotalk" value="1" id="chk_grp_use_kakaotalk">
<label for="chk_grp_use_kakaotalk">그룹적용</label>
<input type="checkbox" name="chk_all_use_kakaotalk" value="1" id="chk_all_use_kakaotalk">
<label for="chk_all_use_kakaotalk">전체적용</label>
</td>
</tr>
<tr>
<th scope="row"><label for="bo_use_cert">본인확인 사용</label></th>
<td>

View File

@ -107,6 +107,7 @@ $bo_use_list_view = isset($_POST['bo_use_list_view']) ? (int) $_POST['bo_use_lis
$bo_use_list_file = isset($_POST['bo_use_list_file']) ? (int) $_POST['bo_use_list_file'] : 0;
$bo_use_list_content = isset($_POST['bo_use_list_content']) ? (int) $_POST['bo_use_list_content'] : 0;
$bo_use_email = isset($_POST['bo_use_email']) ? (int) $_POST['bo_use_email'] : 0;
$bo_use_kakaotalk = isset($_POST['bo_use_kakaotalk']) ? (int) $_POST['bo_use_kakaotalk'] : 0;
$bo_use_sns = isset($_POST['bo_use_sns']) ? (int) $_POST['bo_use_sns'] : 0;
$bo_use_captcha = isset($_POST['bo_use_captcha']) ? (int) $_POST['bo_use_captcha'] : 0;
$bo_table_width = isset($_POST['bo_table_width']) ? (int) $_POST['bo_table_width'] : 0;
@ -209,6 +210,7 @@ $sql_common = " gr_id = '{$gr_id}',
bo_use_list_file = '{$bo_use_list_file}',
bo_use_list_content = '{$bo_use_list_content}',
bo_use_email = '{$bo_use_email}',
bo_use_kakaotalk = '{$bo_use_kakaotalk}',
bo_use_cert = '{$bo_use_cert}',
bo_use_sns = '{$bo_use_sns}',
bo_use_captcha = '{$bo_use_captcha}',
@ -393,6 +395,7 @@ if (is_checked('chk_grp_use_list_view')) $grp_fields .= " , bo_use_list_v
if (is_checked('chk_grp_use_list_file')) $grp_fields .= " , bo_use_list_file = '{$bo_use_list_file}' ";
if (is_checked('chk_grp_use_list_content')) $grp_fields .= " , bo_use_list_content = '{$bo_use_list_content}' ";
if (is_checked('chk_grp_use_email')) $grp_fields .= " , bo_use_email = '{$bo_use_email}' ";
if (is_checked('chk_grp_use_kakaotalk')) $grp_fields .= " , bo_use_kakaotalk = '{$bo_use_kakaotalk}' ";
if (is_checked('chk_grp_use_cert')) $grp_fields .= " , bo_use_cert = '{$bo_use_cert}' ";
if (is_checked('chk_grp_use_sns')) $grp_fields .= " , bo_use_sns = '{$bo_use_sns}' ";
if (is_checked('chk_grp_use_captcha')) $grp_fields .= " , bo_use_captcha = '{$bo_use_captcha}' ";
@ -483,6 +486,7 @@ if (is_checked('chk_all_use_list_view')) $all_fields .= " , bo_use_list_v
if (is_checked('chk_all_use_list_file')) $all_fields .= " , bo_use_list_file = '{$bo_use_list_file}' ";
if (is_checked('chk_all_use_list_content')) $all_fields .= " , bo_use_list_content = '{$bo_use_list_content}' ";
if (is_checked('chk_all_use_email')) $all_fields .= " , bo_use_email = '{$bo_use_email}' ";
if (is_checked('chk_all_use_kakaotalk')) $all_fields .= " , bo_use_kakaotalk = '{$bo_use_kakaotalk}' ";
if (is_checked('chk_all_use_cert')) $all_fields .= " , bo_use_cert = '{$bo_use_cert}' ";
if (is_checked('chk_all_use_sns')) $all_fields .= " , bo_use_sns = '{$bo_use_sns}' ";
if (is_checked('chk_all_use_captcha')) $all_fields .= " , bo_use_captcha = '{$bo_use_captcha}' ";

View File

@ -8,6 +8,8 @@ if ($is_admin != 'super') {
alert('최고관리자만 접근 가능합니다.');
}
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); // 카카오톡 설정 확인
// https://github.com/gnuboard/gnuboard5/issues/296 이슈처리
$sql = " select * from {$g5['config_table']} limit 1";
$config = sql_fetch($sql);
@ -422,6 +424,44 @@ if (!isset($config['cf_cert_kcp_enckey'])) {
$config['cf_cert_kcp_enckey'] = '';
}
// 카카오톡 설정 필드 추가
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
);
}
// 광고성 정보 수신 동의 사용 필드 추가
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
);
}
// 광고성 정보 수신 동의 여부 필드 추가 + 메일 / 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
);
}
if (!$config['cf_faq_skin']) {
$config['cf_faq_skin'] = "basic";
}
@ -438,10 +478,10 @@ $pg_anchor = '<ul class="anchor">
<li><a href="#anc_cf_join">회원가입</a></li>
<li><a href="#anc_cf_cert">본인확인</a></li>
<li><a href="#anc_cf_url">짧은주소</a></li>
<li><a href="#anc_cf_mail">기본메일환경</a></li>
<li><a href="#anc_cf_article_mail">글작성메일</a></li>
<li><a href="#anc_cf_join_mail">가입메일</a></li>
<li><a href="#anc_cf_vote_mail">투표메일</a></li>
<li><a href="#anc_cf_mail">기본알림환경</a></li>
<li><a href="#anc_cf_article_mail">글작성알림</a></li>
<li><a href="#anc_cf_join_mail">가입알림</a></li>
<li><a href="#anc_cf_vote_mail">투표알림</a></li>
<li><a href="#anc_cf_sns">SNS</a></li>
<li><a href="#anc_cf_lay">레이아웃 추가설정</a></li>
<li><a href="#anc_cf_sms">SMS</a></li>
@ -963,6 +1003,17 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<th scope="row"><label for="cf_privacy">개인정보처리방침</label></th>
<td colspan="3"><textarea id="cf_privacy" name="cf_privacy" rows="10"><?php echo html_purifier($config['cf_privacy']); ?></textarea></td>
</tr>
<tr>
<th scope="row"><label for="cf_use_promotion">회원가입 약관 동의에<br>광고성 정보 수신 동의 표시 여부</label></th>
<td colspan="3">
<?php echo help('<b>광고성 정보 수신 · 마케팅 목적의 개인정보 수집 및 이용 · 개인정보 제 3자 제공</b> 여부를 설정합니다. <b>SMS 또는 카카오톡</b> 사용 시 <b>개인정보 제3자 제공</b>이 활성화됩니다.'); ?>
<?php echo help('동의한 회원에게 <b>카카오톡(친구톡)·문자</b>로 광고성 메시지를 발송할 수 있습니다.'); ?>
<?php echo help('<b>휴대전화번호</b> 사용을 위해서는 <b>기본환경설정 > 회원가입 > 휴대전화번호 입력</b>을 <b>[보이기]</b> 또는 <b>[필수입력]</b>으로 설정해야 하며, 미설정 시 수집이 불가합니다.'); ?>
<?php echo help('* 「정보통신망이용촉진및정보보호등에관한법률」에 따라 <b>광고성 정보 수신 동의</b>를 매 2년마다 반드시 확인해야 합니다.'); ?>
<input type="checkbox" name="cf_use_promotion" value="1" id="cf_use_promotion" <?php echo $config['cf_use_promotion'] ? 'checked' : ''; ?>>
<label for="cf_use_promotion">사용</label>
</td>
</tr>
</tbody>
</table>
</div>
@ -1105,12 +1156,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
?>
<section id="anc_cf_mail">
<h2 class="h2_frm">기본 메일 환경 설정</h2>
<h2 class="h2_frm">기본 알림 환경 설정</h2>
<?php echo $pg_anchor ?>
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>기본 메일 환경 설정</caption>
<caption>기본 알림 환경 설정</caption>
<colgroup>
<col class="grid_4">
<col>
@ -1138,18 +1189,85 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<input type="checkbox" name="cf_formmail_is_member" value="1" id="cf_formmail_is_member" <?php echo $config['cf_formmail_is_member'] ? 'checked' : ''; ?>> 회원만 사용
</td>
</tr>
<tr>
<th scope="row"><label for="cf_kakaotalk_use">카카오톡 사용</label></th>
<td>
<?php echo help("카카오톡 발송 서비스 회사를 선택하십시오. 서비스 회사를 선택하지 않으면, 카카오톡 발송 기능이 동작하지 않습니다."); ?>
<select name="cf_kakaotalk_use" id="cf_kakaotalk_use">
<option value="" <?php echo get_selected($config['cf_kakaotalk_use'], ''); ?>>사용안함</option>
<option value="popbill" <?php echo get_selected($config['cf_kakaotalk_use'], 'popbill'); ?>>팝빌</option>
</select>
</td>
</tr>
<tr>
<th scope="row">사업자등록번호</th>
<td>
<?php echo help('서비스 등록 시 사용했던 사업자등록번호를 입력하세요.'); ?>
<input type="text" name="cf_kakaotalk_corpnum" id="cf_kakaotalk_corpnum" value="<?php echo get_sanitize_input($config['cf_kakaotalk_corpnum']); ?>" class="frm_input" size="30">
</td>
</tr>
<tr>
<th scope="row">회신번호</th>
<td>
<?php echo help('대체문자 발송 시 사용할 회신번호를 입력하세요.'); ?>
<input type="text" name="cf_kakaotalk_sender_hp" id="cf_kakaotalk_sender_hp" value="<?php echo get_sanitize_input($config['cf_kakaotalk_sender_hp']); ?>" class="frm_input" size="30">
</td>
</tr>
<tr>
<th scope="row"><label for="cf_popbill_userid">팝빌 회원아이디</label></th>
<td>
<?php echo help('팝빌에 가입한 회원 아이디를 입력하세요.'); ?>
<input type="text" name="cf_popbill_userid" id="cf_popbill_userid" value="<?php echo get_sanitize_input($config['cf_popbill_userid']); ?>" class="frm_input" size="30">
</td>
</tr>
<tr>
<th scope="row"><label for="cf_popbill_link_id">팝빌 링크아이디(LinkID)</label></th>
<td>
<?php echo help('팝빌에서 발급한 API KEY - 링크아이디(LinkID)를 입력하세요.'); ?>
<input type="text" name="cf_popbill_link_id" id="cf_popbill_link_id" value="<?php echo get_sanitize_input($config['cf_popbill_link_id']); ?>" class="frm_input" size="30">
</td>
</tr>
<tr>
<th scope="row"><label for="cf_popbill_secretkey">팝빌 비밀키(SecretKey)</label></th>
<td>
<?php echo help('팝빌에서 발급한 API KEY - 비밀키(SecretKey)를 입력하세요.'); ?>
<input type="text" name="cf_popbill_secretkey" id="cf_popbill_secretkey" value="<?php echo get_sanitize_input($config['cf_popbill_secretkey']); ?>" class="frm_input" size="60">
</td>
</tr>
<tr>
<th scope="row"><label for="cf_phone">팝빌 설정 확인 및 포인트 조회</label></th>
<td>
<!-- 팝빌 설정 확인 및 잔여포인트 조회 -->
<?php echo help("팝빌 설정 정보를 확인하고, 잔여 포인트를 조회할 수 있습니다.<br>설정 확인 버튼을 클릭하면 팝빌 API와의 연결 상태 및 포인트 정보를 확인할 수 있습니다."); ?>
<button type="button" class="btn btn_02" id="btn_check_popbill">설정 확인</button>
<div id="popbill_check_result"></div>
</td>
</tr>
<tr>
<th scope="row">팝빌 연동신청</th>
<td>
<?php echo help('팝빌 연동신청이 필요할 경우 아래 버튼을 클릭하세요.<br>회원가입 시 연동회원으로 선택후 링크아이디 [SIRSOFT]를 넣어주세요.'); ?>
<a href="https://www.popbill.com/App/SignUp" target="_blank" class="btn_frmline">팝빌 연동신청</a>
</td>
</tr>
<tr>
<th scope="row">알림톡 프리셋 설정</th>
<td>
<a href="<?php echo G5_ADMIN_URL; ?>/alimtalkpreset.php" class="btn_frmline">환경설정 &gt; 알림톡프리셋관리</a> 에서 설정하실 수 있습니다.
</td>
</tr>
</table>
</div>
</section>
<section id="anc_cf_article_mail">
<h2 class="h2_frm">게시판 글 작성 시 메일 설정</h2>
<h2 class="h2_frm">게시판 글 작성 시 알림 설정</h2>
<?php echo $pg_anchor ?>
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>게시판 글 작성 시 메일 설정</caption>
<caption>게시판 글 작성 시 알림 설정</caption>
<colgroup>
<col class="grid_4">
<col>
@ -1197,12 +1315,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<section id="anc_cf_join_mail">
<h2 class="h2_frm">회원가입 시 메일 설정</h2>
<h2 class="h2_frm">회원가입 시 알림 설정</h2>
<?php echo $pg_anchor ?>
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>회원가입 시 메일 설정</caption>
<caption>회원가입 시 알림 설정</caption>
<colgroup>
<col class="grid_4">
<col>
@ -1229,12 +1347,12 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<section id="anc_cf_vote_mail">
<h2 class="h2_frm">투표 기타의견 작성 시 메일 설정</h2>
<h2 class="h2_frm">투표 기타의견 작성 시 알림 설정</h2>
<?php echo $pg_anchor ?>
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>투표 기타의견 작성 시 메일 설정</caption>
<caption>투표 기타의견 작성 시 알림 설정</caption>
<colgroup>
<col class="grid_4">
<col>
@ -1526,7 +1644,6 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
</div>
</section>
<section id="anc_cf_extra">
<h2 class="h2_frm">여분필드 기본 설정</h2>
<?php echo $pg_anchor ?>
@ -1732,6 +1849,50 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
});
</script>
<!-- 카카오톡 - 팝빌 설정 확인 -->
<?php add_javascript('<script src="'.G5_JS_URL.'/kakao5.js"></script>', 1); // 카카오톡5 솔루션 js 추가 ?>
<script>
$(function() {
$('#btn_check_popbill').on('click', function() {
var $resultDiv = $('#popbill_check_result');
$resultDiv.html('<i class="fa fa-spinner fa-spin"></i> 확인 중...');
$.ajax({
url: '<?php echo G5_KAKAO5_URL;?>/ajax.check_popbill.php',
type: 'POST',
dataType: 'json',
success: function(data) {
var html = '';
if (data.error) {
html = '<div class="alert alert-danger" style="margin-top:10px;"><i class="fa fa-exclamation-triangle"></i> ' + data.error + '</div>';
} else {
html = '<div class="alert alert-success"><i class="fa fa-check-circle"></i> 팝빌이 정상적으로 설정되었습니다.</div>';
if (typeof data.balance !== 'undefined') {
html += '<div class="alert alert-info"><i class="fa fa-info-circle"></i> 잔여포인트: ' + Number(data.balance).toLocaleString() + '원';
html += ' <a href="#" id="btn_point_charge" class="btn btn_02">포인트 충전하기</a>';
html += '</div>';
}
}
$resultDiv.html(html); // 팝빌 설정 결과 출력
$('#btn_check_popbill').hide();
// 포인트 충전 팝업: [정의] openKakao5PopupFromAjax() - js/kakao5.js
var $chargeBtn = $('#btn_point_charge');
if ($chargeBtn.length) {
$chargeBtn.off('click').on('click', async function(e) {
e.preventDefault();
await openKakao5PopupFromAjax('<?php echo G5_KAKAO5_URL; ?>', 5);
});
}
},
error: function() {
$resultDiv.html('<div class="alert alert-danger"><i class="fa fa-exclamation-triangle"></i> 네트워크 오류</div>');
}
});
});
});
</script>
<?php
// 본인확인 모듈 실행권한 체크
if ($config['cf_cert_use']) {

View File

@ -141,6 +141,7 @@ $check_keys = array(
'cf_visit' => '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']}',

View File

@ -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; }

View File

@ -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 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.<br>현재 DB 업그레이드가 완료된 상태입니다.';

BIN
adm/img/svc_btn_07.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -52,8 +52,13 @@ if ($w == '') {
$sound_only = '<strong class="sound_only">필수</strong>';
$mb['mb_mailling'] = 1;
$mb['mb_sms'] = 1;
$mb['mb_open'] = 1;
$mb['mb_level'] = $config['cf_register_level'];
$mb['mb_board_post'] = 1;
$mb['mb_board_reply'] = 1;
$mb['mb_board_comment'] = 1;
$mb['mb_board_recomment'] = 1;
$html_title = '추가';
} elseif ($w == 'u') {
$mb = get_member($mb_id);
@ -139,6 +144,27 @@ $mb_sms_no = !$mb['mb_sms'] ? 'checked="checked"' : '';
$mb_open_yes = $mb['mb_open'] ? 'checked="checked"' : '';
$mb_open_no = !$mb['mb_open'] ? 'checked="checked"' : '';
// 마케팅 목적의 개인정보 수집 및 이용
$mb_marketing_agree_yes = $mb['mb_marketing_agree'] ? 'checked="checked"' : '';
$mb_marketing_agree_no = !$mb['mb_marketing_agree'] ? 'checked="checked"' : '';
// 개인정보 제3자 제공 동의
$mb_thirdparty_agree_yes = $mb['mb_thirdparty_agree'] ? 'checked="checked"' : '';
$mb_thirdparty_agree_no = !$mb['mb_thirdparty_agree'] ? 'checked="checked"' : '';
// 알림 설정 (전용 컬럼 사용)
$mb_board_post_yes = !empty($mb['mb_board_post']) ? 'checked="checked"' : '';
$mb_board_post_no = empty($mb['mb_board_post']) ? 'checked="checked"' : '';
$mb_board_reply_yes = !empty($mb['mb_board_reply']) ? 'checked="checked"' : '';
$mb_board_reply_no = empty($mb['mb_board_reply']) ? 'checked="checked"' : '';
$mb_board_comment_yes = !empty($mb['mb_board_comment']) ? 'checked="checked"' : '';
$mb_board_comment_no = empty($mb['mb_board_comment']) ? 'checked="checked"' : '';
$mb_board_recomment_yes = !empty($mb['mb_board_recomment']) ? 'checked="checked"' : '';
$mb_board_recomment_no = empty($mb['mb_board_recomment']) ? 'checked="checked"' : '';
if (isset($mb['mb_certify'])) {
// 날짜시간형이라면 drop 시킴
if (preg_match("/-/", $mb['mb_certify'])) {
@ -354,21 +380,64 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
</td>
</tr>
<tr>
<th scope="row">메일 수신</th>
<th scope="row">광고성 이메일 수신</th>
<td>
<input type="radio" name="mb_mailling" value="1" id="mb_mailling_yes" <?php echo $mb_mailling_yes; ?>>
<label for="mb_mailling_yes">예</label>
<input type="radio" name="mb_mailling" value="0" id="mb_mailling_no" <?php echo $mb_mailling_no; ?>>
<label for="mb_mailling_no">아니오</label>
<?php if($w == "u" && $mb['mb_mailling_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_mailling'] == 1 ? "<br>(동의 일자: ".$mb['mb_mailling_date'].")" : '';
} ?>
</td>
<th scope="row"><label for="mb_sms_yes">SMS 수신</label></th>
<th scope="row"><label for="mb_sms_yes">광고성 SMS/카카오톡 수신</label></th>
<td>
<input type="radio" name="mb_sms" value="1" id="mb_sms_yes" <?php echo $mb_sms_yes; ?>>
<label for="mb_sms_yes">예</label>
<input type="radio" name="mb_sms" value="0" id="mb_sms_no" <?php echo $mb_sms_no; ?>>
<label for="mb_sms_no">아니오</label>
<?php if($w == "u" && $mb['mb_sms_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_sms'] == 1 ? "<br>(동의 일자: ".$mb['mb_sms_date'].")" : '';
} ?>
</td>
</tr>
<tr>
<th scope="row">마케팅 목적의<br>개인정보 수집 및 이용</th>
<td>
<input type="radio" name="mb_marketing_agree" value="1" id="mb_marketing_agree_yes" <?php echo $mb_marketing_agree_yes; ?>>
<label for="mb_marketing_agree_yes">예</label>
<input type="radio" name="mb_marketing_agree" value="0" id="mb_marketing_agree_no" <?php echo $mb_marketing_agree_no; ?>>
<label for="mb_marketing_agree_no">아니오</label>
<?php if($w == "u" && $mb['mb_marketing_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_marketing_agree'] == 1 ? "<br>(동의 일자: ".$mb['mb_marketing_date'].")" : '';
} ?>
</td>
<th scope="row"><label for="mb_sms_yes">개인정보 제3자 제공</label></th>
<td>
<input type="radio" name="mb_thirdparty_agree" value="1" id="mb_thirdparty_agree_yes" <?php echo $mb_thirdparty_agree_yes; ?>>
<label for="mb_thirdparty_agree_yes">예</label>
<input type="radio" name="mb_thirdparty_agree" value="0" id="mb_thirdparty_agree_no" <?php echo $mb_thirdparty_agree_no; ?>>
<label for="mb_thirdparty_agree_no">아니오</label>
<?php if($w == "u" && $mb['mb_thirdparty_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_thirdparty_agree'] == 1 ? "<br>(동의 일자: ".$mb['mb_thirdparty_date'].")" : '';
} ?>
</td>
</tr>
<?php if($w == "u"){?>
<tr>
<th scope="row">약관동의 변경내역</th>
<td colspan="3">
<section id="sodr_request_log_wrap" class="ad_agree_log">
<div>
<?php echo conv_content($mb['mb_agree_log'], 0); ?>
</div>
</section>
</td>
</tr>
<?php } ?>
<tr>
<th scope="row">정보 공개</th>
<td colspan="3">
@ -376,6 +445,9 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<label for="mb_open_yes">예</label>
<input type="radio" name="mb_open" value="0" id="mb_open_no" <?php echo $mb_open_no; ?>>
<label for="mb_open_no">아니오</label>
<?php if($w == "u" && $mb['mb_open_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_open'] == 1 ? "<br>(동의 일자: ".$mb['mb_open_date'].")" : '';
} ?>
</td>
</tr>
<tr>
@ -565,6 +637,42 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
run_event('admin_member_form_add', $mb, $w, 'table');
?>
<tr>
<th scope="row">게시글 작성 완료 알림</th>
<td>
<input type="radio" name="mb_board_post" value="1" id="mb_board_post_yes" <?php echo !empty($mb['mb_board_post']) ? 'checked' : ''; ?>>
<label for="mb_board_post_yes">예</label>
<input type="radio" name="mb_board_post" value="0" id="mb_board_post_no" <?php echo empty($mb['mb_board_post']) ? 'checked' : ''; ?>>
<label for="mb_board_post_no">아니오</label>
</td>
<th scope="row">게시글 답변 알림</th>
<td>
<input type="radio" name="mb_board_reply" value="1" id="mb_board_reply_yes" <?php echo !empty($mb['mb_board_reply']) ? 'checked' : ''; ?>>
<label for="mb_board_reply_yes">예</label>
<input type="radio" name="mb_board_reply" value="0" id="mb_board_reply_no" <?php echo empty($mb['mb_board_reply']) ? 'checked' : ''; ?>>
<label for="mb_board_reply_no">아니오</label>
</td>
</tr>
<tr>
<th scope="row">댓글 알림</th>
<td>
<input type="radio" name="mb_board_comment" value="1" id="mb_board_comment_yes" <?php echo !empty($mb['mb_board_comment']) ? 'checked' : ''; ?>>
<label for="mb_board_comment_yes">예</label>
<input type="radio" name="mb_board_comment" value="0" id="mb_board_comment_no" <?php echo empty($mb['mb_board_comment']) ? 'checked' : ''; ?>>
<label for="mb_board_comment_no">아니오</label>
</td>
<th scope="row">대댓글 알림</th>
<td>
<input type="radio" name="mb_board_recomment" value="1" id="mb_board_recomment_yes" <?php echo !empty($mb['mb_board_recomment']) ? 'checked' : ''; ?>>
<label for="mb_board_recomment_yes">예</label>
<input type="radio" name="mb_board_recomment" value="0" id="mb_board_recomment_no" <?php echo empty($mb['mb_board_recomment']) ? 'checked' : ''; ?>>
<label for="mb_board_recomment_no">아니오</label>
</td>
</tr>
<?php for ($i = 1; $i <= 10; $i++) { ?>
<tr>
<th scope="row"><label for="mb_<?php echo $i ?>">여분 필드 <?php echo $i ?></label></th>

View File

@ -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('이미 존재하는 이메일입니다.\\n : ' . $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 {

View File

@ -127,7 +127,7 @@ $colspan = 16;
<th scope="col" rowspan="2" id="mb_list_cert"><?php echo subject_sort_link('mb_certify', '', 'desc') ?>본인확인</a></th>
<th scope="col" id="mb_list_mailc"><?php echo subject_sort_link('mb_email_certify', '', 'desc') ?>메일인증</a></th>
<th scope="col" id="mb_list_open"><?php echo subject_sort_link('mb_open', '', 'desc') ?>정보공개</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>메일수신</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>광고성이메일수신</a></th>
<th scope="col" id="mb_list_auth">상태</th>
<th scope="col" id="mb_list_mobile">휴대폰</th>
<th scope="col" id="mb_list_lastcall"><?php echo subject_sort_link('mb_today_login', '', 'desc') ?>최종접속</a></th>
@ -137,7 +137,7 @@ $colspan = 16;
<tr>
<th scope="col" id="mb_list_name"><?php echo subject_sort_link('mb_name') ?>이름</a></th>
<th scope="col" id="mb_list_nick"><?php echo subject_sort_link('mb_nick') ?>닉네임</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>SMS수신</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>광고성SMS/카카오톡수신</a></th>
<th scope="col" id="mb_list_adultc"><?php echo subject_sort_link('mb_adult', '', 'desc') ?>성인인증</a></th>
<th scope="col" id="mb_list_auth"><?php echo subject_sort_link('mb_intercept_date', '', 'desc') ?>접근차단</a></th>
<th scope="col" id="mb_list_deny"><?php echo subject_sort_link('mb_level', '', 'desc') ?>권한</a></th>
@ -252,14 +252,15 @@ $colspan = 16;
<input type="radio" name="mb_certify[<?php echo $i; ?>]" value="ipin" id="mb_certify_ipin_<?php echo $i; ?>" <?php echo $row['mb_certify'] == 'ipin' ? 'checked' : ''; ?>>
<label for="mb_certify_ipin_<?php echo $i; ?>">아이핀</label>
</td>
<td headers="mb_list_mailc"><?php echo preg_match('/[1-9]/', $row['mb_email_certify']) ? '<span class="txt_true">Yes</span>' : '<span class="txt_false">No</span>'; ?></td>
<td headers="mb_list_open">
<td headers="mb_list_mailc" class="td_consent"><?php echo preg_match('/[1-9]/', $row['mb_email_certify']) ? '<span class="txt_true">Yes</span>' : '<span class="txt_false">No</span>'; ?></td>
<td headers="mb_list_open" class="td_consent">
<label for="mb_open_<?php echo $i; ?>" class="sound_only">정보공개</label>
<input type="checkbox" name="mb_open[<?php echo $i; ?>]" <?php echo $row['mb_open'] ? 'checked' : ''; ?> value="1" id="mb_open_<?php echo $i; ?>">
</td>
<td headers="mb_list_mailr">
<td headers="mb_list_mailr" class="td_consent">
<label for="mb_mailling_<?php echo $i; ?>" class="sound_only">메일수신</label>
<input type="checkbox" name="mb_mailling[<?php echo $i; ?>]" <?php echo $row['mb_mailling'] ? 'checked' : ''; ?> value="1" id="mb_mailling_<?php echo $i; ?>">
<input type="hidden" name="mb_mailling_default[<?php echo $i; ?>]" value="<?php echo $row['mb_mailling'] ?? '0'; ?> " id="mb_mailling_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_auth" class="td_mbstat">
<?php
@ -284,6 +285,7 @@ $colspan = 16;
<td headers="mb_list_sms">
<label for="mb_sms_<?php echo $i; ?>" class="sound_only">SMS수신</label>
<input type="checkbox" name="mb_sms[<?php echo $i; ?>]" <?php echo $row['mb_sms'] ? 'checked' : ''; ?> value="1" id="mb_sms_<?php echo $i; ?>">
<input type="hidden" name="mb_sms_default[<?php echo $i; ?>]" value="<?php echo $row['mb_sms'] ?? '0'; ?> " id="mb_sms_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_adultc">
<label for="mb_adult_<?php echo $i; ?>" class="sound_only">성인인증</label>

View File

@ -0,0 +1,289 @@
<?php
/*************************************************************************
**
** 내보내기 관련 상수 정의
**
*************************************************************************/
define('MEMBER_EXPORT_PAGE_SIZE', 10000); // 파일당 처리할 회원 수
define('MEMBER_EXPORT_MAX_SIZE', 300000); // 최대 처리할 회원 수
define('MEMBER_BASE_DIR', "member_list"); // 엑셀 베이스 폴더
define('MEMBER_BASE_DATE', date('YmdHis')); // 폴더/파일명용 날짜
define('MEMBER_EXPORT_DIR', G5_DATA_PATH . "/" . MEMBER_BASE_DIR . "/" . MEMBER_BASE_DATE); // 엑셀 파일 저장 경로
define('MEMBER_LOG_DIR', G5_DATA_PATH . "/" . MEMBER_BASE_DIR . "/" . "log"); // 로그 파일 저장 경로
/*************************************************************************
**
** 공통 함수 정의
**
*************************************************************************/
/**
* 검색 옵션 설정
*/
function get_export_config($type = null)
{
$config = [
'sfl_list' => [
'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);
}

578
adm/member_list_exel.php Normal file
View File

@ -0,0 +1,578 @@
<?php
$sub_menu = "200400";
require_once './_common.php';
require_once './member_list_exel.lib.php'; // 회원관리파일 공통 라이브러리
auth_check_menu($auth, $sub_menu, 'r');
// 파라미터 수집 및 유효성 검사
$params = get_member_export_params();
// 총건수
$total_count = 0;
$total_error = "";
try {
$total_count = member_export_get_total_count($params);
} catch (Exception $e) {
$total_error = $e->getMessage(); // 메서드 호출 괄호 필수
}
$g5['title'] = '회원관리파일';
require_once './admin.head.php';
$colspan = 14;
?>
<h2>회원 엑셀 생성</h2>
<div class="local_desc01 local_desc">
<p><b>회원수 <?php echo number_format(MEMBER_EXPORT_PAGE_SIZE);?>건 초과 시</b> <?php echo number_format(MEMBER_EXPORT_PAGE_SIZE);?>건 단위로 분리 저장되며, <b>엑셀 생성 최대 건수는 <?php echo number_format(MEMBER_EXPORT_MAX_SIZE);?>건</b>입니다. 초과 시 조건 추가 설정 후 재시도하시기 바랍니다.</p>
<p><b>수신동의 확인 대상은 만료일까지 1달 미만인 회원</b>을 기준으로 필터링됩니다.</p>
<br>
<p>파일 생성 시 서버에 임시 생성된 파일 중 <b>오늘 날짜를 제외 한 파일은 자동 삭제</b>되며, 수동 삭제 필요 시 <a href="<?php echo G5_ADMIN_URL;?>/member_list_file_delete.php"><b>회원관리파일 일괄삭제</b></a>에서 진행하시기 바랍니다.</p>
<p>회원 정보 수정은 <a href="<?php echo G5_ADMIN_URL;?>/member_list.php" class="link"><b>회원 관리</b></a>에서 진행하실 수 있습니다.</p>
<br>
<p><strong>친구톡 양식</strong>은 <b>카카오톡 사용 시</b>에만 이용 가능합니다.</p>
<?php if($config['cf_kakaotalk_use'] == "popbill") { ?>
<p><b>친구톡 (광고성 카카오톡 포함)</b>의 경우 기존 회원 데이터 엑셀 파일 다운로드 후 상단 <b>[친구톡 보내기]</b> 버튼을 누르면 <b>팝빌 홈페이지</b>로 이동하여 업로드 진행하실 수 있습니다.</p>
<?php } ?>
</div>
<?php if($config['cf_kakaotalk_use'] == "popbill") { ?>
<div class="btn_fixed_top">
<a href="https://popbill.com/App/Kakaotalk/FTS" target="_blank" class="btn btn_submit btn kakao_setting_btn" >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="kakao-send-svg">
<path d="M2 21l21-9L2 3v7l15 2-15 2z"></path>
</svg>
친구톡 전송하기
</a>
</div>
<?php } ?>
<div class="local_ov01 local_ov">
<span class="btn_ov01">
<span class="ov_txt">총건수 </span>
<?php if($total_error != "") { ?>
<span class="ov_num"> <?php echo $total_error ?></span>
<?php } else {?>
<span class="ov_num"> <?php echo number_format($total_count) ?>건</span>
<?php } ?>
</span>
</div>
<!-- 회원 검색 필터링 폼 -->
<form id="fsearch" name="fsearch" class="member_list_data" method="get">
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
<fieldset>
<legend class="sound_only">회원 검색 필터링</legend>
<div class="sch_table">
<!-- 검색어 적용 -->
<div class="sch_row">
<div class="label">
<label>
<input type="checkbox" name="use_stx" value="1" <?php echo isset($_GET['use_stx']) ? 'checked' : ''; ?>>
검색어 적용
</label>
</div>
<div class="field">
<select name="sfl">
<?php
// 검색어 옵션 : [정의] get_export_config() - adm/member_list_exel.lib.php;
foreach (get_export_config('sfl_list') as $val => $label) {
$selected = (isset($_GET['sfl']) && $_GET['sfl'] === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
<input type="text" name="stx" value="<?php echo htmlspecialchars($_GET['stx'] ?? ''); ?>" placeholder="검색어 입력">
<span class="radio_group">
<label><input type="radio" name="stx_cond" value="like" <?php echo ($_GET['stx_cond'] ?? 'like') === 'like' ? 'checked' : ''; ?>> 포함</label>
<label><input type="radio" name="stx_cond" value="equal" <?php echo ($_GET['stx_cond'] ?? '') === 'equal' ? 'checked' : ''; ?>> 일치</label>
</span>
</div>
</div>
<!-- 레벨 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_level" value="1" <?php echo isset($_GET['use_level']) ? 'checked' : ''; ?>> 레벨 적용</label>
</div>
<div class="field">
<select name="level_start">
<?php for ($i = 1; $i <= 10; $i++): ?>
<option value="<?php echo $i; ?>" <?php echo (isset($_GET['level_start']) && $_GET['level_start'] == $i) ? 'selected' : ''; ?>><?php echo $i; ?></option>
<?php endfor; ?>
</select> ~
<select name="level_end">
<?php for ($i = 1; $i <= 10; $i++): ?>
<option value="<?php echo $i; ?>" <?php echo (isset($_GET['level_end']) && $_GET['level_end'] == $i) ? 'selected' : ''; ?>><?php echo $i; ?></option>
<?php endfor; ?>
</select>
</div>
</div>
<!-- 가입기간 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_date" value="1" <?php echo isset($_GET['use_date']) ? 'checked' : ''; ?>> 가입기간 적용</label>
</div>
<div class="field">
<input type="date" name="date_start" max="9999-12-31" value="<?php echo htmlspecialchars($_GET['date_start'] ?? ''); ?>"> ~
<input type="date" name="date_end" max="9999-12-31" value="<?php echo htmlspecialchars($_GET['date_end'] ?? ''); ?>">
</div>
</div>
<!-- 포인트 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_point" value="1" <?php echo isset($_GET['use_point']) ? 'checked' : ''; ?>> 포인트 적용</label>
</div>
<div class="field">
<input type="number" name="point" value="<?php echo htmlspecialchars($_GET['point'] ?? ''); ?>" placeholder="포인트 입력">
<span class="radio_group">
<label><input type="radio" name="point_cond" value="gte" <?php echo ($_GET['point_cond'] ?? 'gte') === 'gte' ? 'checked' : ''; ?>> 이상</label>
<label><input type="radio" name="point_cond" value="lte" <?php echo ($_GET['point_cond'] ?? '') === 'lte' ? 'checked' : ''; ?>> 이하</label>
<label><input type="radio" name="point_cond" value="eq" <?php echo ($_GET['point_cond'] ?? '') === 'eq' ? 'checked' : ''; ?>> 일치</label>
</span>
</div>
</div>
<!-- 차단회원 조건 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_intercept" value="1" <?php echo isset($_GET['use_intercept']) ? 'checked' : ''; ?>> 차단회원</label>
</div>
<div class="field">
<select name="intercept" id="intercept">
<?php
// 차단회원 옵션 : [정의] get_export_config() - adm/member_list_exel.lib.php
foreach (get_export_config('intercept_list') as $val => $label) {
$selected = (($_GET['intercept'] ?? '') === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
</div>
</div>
<!-- 휴대폰 번호 조건 - 초기세팅(설정에 휴대폰번호가 보이기/필수입력이면 기본값 checked로 설정) -->
<div class="sch_row">
<div class="label">
<label>
<?php $use_hp_checked = isset($_GET['token']) ? (isset($_GET['use_hp_exist']) ? 'checked' : '') : (($config['cf_use_hp'] || $config['cf_req_hp']) ? 'checked' : '');?>
<input type="checkbox" name="use_hp_exist" value="1" <?php echo $use_hp_checked; ?>> 휴대폰 번호 있는 경우만
</label>
</div>
</div>
<!-- 정보수신동의 조건 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="ad_range_only" value="1" <?php echo isset($_GET['ad_range_only']) ? 'checked' : ''; ?>> 정보수신동의에 동의한 경우만</label>
</div>
<!-- 안내 문구 -->
<div class="field">
<p class="sch_notice">「정보통신망이용촉진및정보보호등에관한법률」에 따라 <b>광고성 정보 수신동의 여부</b>를 <b>매2년</b>마다 확인해야 합니다.</p>
</div>
</div>
<div class="sch_row <?php echo isset($_GET['ad_range_only']) ? '' : 'is-hidden'; ?>">
<div class="ad_range_wrap">
<div class="ad_range_box">
<div class="label">
<label for="ad_range_type">회원범위</label>
</div>
<div class="field">
<select name="ad_range_type" id="ad_range_type">
<?php
foreach (get_export_config('ad_range_list') as $val => $label) {
$selected = (($_GET['ad_range_type'] ?? '') === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
<div class="ad_range_wrap">
<!-- 기간 직접 입력 -->
<div class="ad_range_box <?php echo isset($_GET['ad_range_only']) && ($_GET['ad_range_type'] ?? '') == 'custom_period' ? '' : 'is-hidden'; ?>">
<div class="field">
<input type="date" name="agree_date_start" max="9999-12-31" value="<?php echo htmlspecialchars($_GET['agree_date_start'] ?? date('Y-m-d', strtotime('-1 month'))); ?>"> ~
<input type="date" name="agree_date_end" max="9999-12-31" value="<?php echo htmlspecialchars($_GET['agree_date_end'] ?? date('Y-m-d')); ?>">
<p>* 광고성 정보 수신(<b>이메일 또는 SMS/카카오톡</b>) 동의일자 기준</p>
</div>
</div>
<!-- 설명 문구 -->
<?php
$thirdpartyLbl = (!empty($config['cf_sms_use']) || !empty($config['cf_kakaotalk_use'])) ? ' / <b>개인정보 제3자 제공</b>' : '';
$ad_range_text = [
'all' => "* <b>광고성 정보 수신(이메일 또는 SMS/카카오톡)</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'mailling_only' => "* <b>광고성 이메일 수신</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'sms_only' => "* <b>광고성 SMS/카카오톡 수신</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'month_confirm' => "* 23개월 전(" . date('Y년 m월', strtotime('-23 month')) . ") <b>광고성 정보 수신 동의(이메일 또는 SMS/카카오톡)</b>한 회원을 선택합니다."
];
if (isset($_GET['ad_range_only'], $_GET['ad_range_type']) && isset($ad_range_text[$_GET['ad_range_type']])) {
echo '<div class="ad_range_box"><p>' . $ad_range_text[$_GET['ad_range_type']] . '</p></div>';
}
?>
</div>
<br>
</div>
</div>
</div>
</div>
<!-- 채널 체크박스 -->
<div class="sch_row <?php echo isset($_GET['ad_range_only']) && in_array($_GET['ad_range_type'], ['month_confirm', 'custom_period']) ? '' : 'is-hidden'; ?>">
<div class="ad_range_wrap">
<div class="ad_range_box">
<div class="label">
</div>
<div class="field">
<?php $ad_mailling_checked = isset($_GET['token']) ? (isset($_GET['ad_mailling']) ? 'checked' : '') : 'checked';?>
<?php $ad_sms_checked = isset($_GET['token']) ? (isset($_GET['ad_sms']) ? 'checked' : '') : 'checked';?>
<label><input type="checkbox" name="ad_mailling" value="1" <?php echo $ad_mailling_checked; ?>> 광고성 이메일 수신</label>
<label><input type="checkbox" name="ad_sms" value="1" <?php echo $ad_sms_checked; ?>> 광고성 SMS/카카오톡 수신</label>
</div>
</div>
</div>
</div>
<div class="sch_btn">
<button type="button" id="btnExcelDownload">엑셀파일 다운로드</button>
<?php if($config['cf_kakaotalk_use'] == "popbill") { ?>
<button type="button" id="btnExcelDownloadPopbill">엑셀파일 다운로드 (친구톡 양식)</button>
<?php } ?>
<button type="button" class="btn_reset" onclick="location.href='?'">초기화</button>
</div>
</div>
</fieldset>
</form>
<script>
document.querySelector('input[name="ad_range_only"]').addEventListener('change', function () {
document.querySelectorAll('.ad_range_wrap').forEach(el => {
el.classList.toggle('is-hidden', !this.checked);
});
});
document.querySelectorAll('#fsearch input, #fsearch select').forEach(el => {
const submit = () => document.getElementById('fsearch').submit();
el.addEventListener(el.type === 'date' ? 'blur' : 'change', submit);
el.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
submit();
}
});
});
</script>
<script>
let eventSource = null;
// 일반 엑셀 다운로드 버튼 클릭
document.getElementById('btnExcelDownload').addEventListener('click', () => {
startExcelDownload(1);
});
// 팝빌 양식 다운로드 버튼 클릭
<?php if($config['cf_kakaotalk_use'] == "popbill") { ?>
document.getElementById('btnExcelDownloadPopbill').addEventListener('click', () => {
showDownloadPopupPopill();
});
<?php } ?>
// 엑셀 다운로드 실행
// 1. 기존 SSE 종료
function closePreviousEventSource() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
// 2. FormData QueryString 변환
function buildDownloadParams(formatType, selectedFields = []) {
const formData = new FormData(document.getElementById('fsearch'));
const params = new URLSearchParams(formData);
params.append('mode', 'start');
params.append('formatType', formatType);
if (formatType === 2 && selectedFields.length > 0) {
params.append('fields', selectedFields.join(','));
}
return params.toString();
}
// 3. 메인 함수
function startExcelDownload(formatType, selectedFields = []) {
closePreviousEventSource();
const query = buildDownloadParams(formatType, selectedFields);
showDownloadPopup();
eventSource = new EventSource(`member_list_exel_export.php?${query}`);
eventSource.onmessage = handleProgressUpdate();
eventSource.onerror = handleDownloadError();
}
// 다운로드 팝업 표시
function showDownloadPopup() {
const bodyHTML = `
<div class="excel-download-progress">
<div class="progress-desc">
<p class="progress-summary">총 <strong>0</strong>개 파일로 분할됩니다</p>
<p class="progress-message"><strong>(0 / 0)</strong> 파일 다운로드 중</p>
<p class="progress-error"></p>
</div>
<div class="progress-spinner">
<div class="spinner"></div>
<p class="loading-message">
엑셀 파일을 생성 중입니다. 잠시만 기다려주세요.<br>
현재 데이터 기준으로 <strong id="estimatedTimeText"></strong> 정도 소요될 수 있습니다.<br>
<strong>페이지를 벗어나거나 닫으면 다운로드가 중단</strong>되니, 작업 완료까지 기다려 주세요.
</p>
</div>
<div class="progress-box">
<div class="progress-download-box"></div>
</div>
</div>
`;
PopupManager.render('엑셀 다운로드 진행 중', bodyHTML, '', { disableOutsideClose: true });
// 닫기 버튼 이벤트 핸들링
const closeBtn = document.querySelector('.popup-close-btn');
if (closeBtn) {
closeBtn.removeAttribute('onclick');
closeBtn.addEventListener('click', handlePopupCloseWithConfirm);
}
}
// 닫기 버튼 클릭 시 다운로드 중단 여부 확인
function handlePopupCloseWithConfirm(e) {
if (eventSource) {
const confirmClose = confirm("엑셀 다운로드가 진행 중입니다.\n정말 중지하시겠습니까?");
if (!confirmClose) {
e.preventDefault();
return;
}
eventSource.close();
eventSource = null;
alert("엑셀 다운로드가 중단되었습니다.");
}
PopupManager.close('popupOverlay');
}
// 친구톡 양식(팝빌) - 항목 선택 팝업
function showDownloadPopupPopill() {
const baseFields = [
['mb_id', '아이디'], ['mb_nick', '닉네임'], ['mb_point', '포인트'], ['mb_level', '권한'],
['mb_email', '이메일'], ['mb_homepage', '홈페이지'], ['mb_datetime', '회원가입일'], ['mb_intercept_date', '차단여부'],
['mb_mailling', '광고성 이메일 수신동의'], ['mb_mailling_date', '광고성 이메일 수신동의일자'],
['mb_sms', '광고성 SMS/카카오톡 수신동의'], ['mb_sms_date', '광고성 SMS/카카오톡 수신동의일자'],
['mb_marketing_agree', '마케팅목적의개인정보수집및이용동의여부'], ['mb_marketing_date', '마케팅목적의개인정보수집및이용동의일자'],
['mb_thirdparty_agree', '개인정보제3자제공동의여부'], ['mb_thirdparty_date', '개인정보제3자제공동의일자']
];
const extraFields = Array.from({ length: 10 }, (_, i) => [`mb_${i + 1}`, `여분 필드 ${i + 1}`]);
let baseFieldHTML = '';
baseFields.forEach(([value, label]) => {
baseFieldHTML += `<label><input type="checkbox" name="fields" value="${value}"> ${label}</label>\n`;
});
let extraFieldHTML = '';
extraFields.forEach(([value, label]) => {
extraFieldHTML += `<label><input type="checkbox" name="fields" value="${value}"> ${label}</label>\n`;
});
const bodyHTML = `
<div class="excel-download-progress">
<p>팝빌 친구톡 전송을 위한 엑셀 양식을 다운로드하실 수 있습니다.</p>
<p><b>전화번호</b>와 <b>이름</b>은 필수 입력 항목이며, 추가로 변수1부터 변수3까지 최대 3개의 선택 항목을 입력하실 수 있습니다.</p>
<div id="selectedFieldsPreview" class="selected-fields-preview"><strong>선택된 항목:</strong></div>
<div id="fieldSelectForm" class="field-select-form">
${baseFieldHTML}
<div class="field-separator"></div>
${extraFieldHTML}
</div>
</div>
`;
const footerHTML = `<button type="button" onclick="submitSelectedFields()">선택 완료</button>`;
PopupManager.render('엑셀 양식에 포함할 항목 선택', bodyHTML, footerHTML, { disableOutsideClose: true });
bindFieldSelectEvents();
}
// 체크박스 선택 시 최대 3개 제한 및 선택된 항목 미리보기 표시
function bindFieldSelectEvents() {
const fieldSelectForm = document.getElementById('fieldSelectForm');
if (!fieldSelectForm) return;
fieldSelectForm.addEventListener('change', function (e) {
if (e.target.name === 'fields') {
const selected = fieldSelectForm.querySelectorAll('input[name="fields"]:checked');
if (selected.length > 3) {
alert("최대 3개까지 선택 가능합니다.");
e.target.checked = false;
return;
}
// 선택된 항목 표시
const previewContainer = document.getElementById('selectedFieldsPreview');
let spans = '<strong>선택된 항목:</strong>';
selected.forEach(field => {
const label = field.parentElement.textContent.trim();
spans += `<span class="field-tag">${label}</span>`;
});
previewContainer.innerHTML = spans;
}
});
}
// 친구톡 양식 - 항목 선택 후 선택완료 버튼 클릭
function submitSelectedFields() {
const checkboxes = document.querySelectorAll('#fieldSelectForm input[name="fields"]:checked');
const selected = Array.from(checkboxes).map(cb => cb.value);
if (selected.length > 3) {
alert("최대 3개까지만 선택할 수 있습니다.");
return;
}
// 항목을 하나도 선택하지 않았을 때 안내 알럿
if (selected.length === 0) {
const confirmProceed = confirm("선택한 항목이 없습니다. 항목 없이 엑셀을 다운로드하시겠습니까?");
if (!confirmProceed) return;
}
PopupManager.close('popupOverlay');
startExcelDownload(2, selected); // formatType 2: Popbill 다운로드
}
// 엑셀 생성 및 다운로드 실행
function handleProgressUpdate() {
return function(e) {
const data = JSON.parse(e.data);
const { status, downloadType, message, total, current, totalChunks, currentChunk, zipFile, files, filePath } = data;
// DOM 요소 캐싱
const titleEl = document.getElementById('popupTitle');
const summaryEl = document.querySelector('.progress-summary');
const messageEl = document.querySelector('.progress-message');
const spinnerEl = document.querySelector('.progress-spinner');
const resultEl = document.querySelector('.loading-message');
const downloadBoxEl = document.querySelector('.progress-download-box');
const errorEl = document.querySelector('.progress-error');
if (status === "progress")
{
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일로 ` + (downloadType === 2 ? `분할 생성됩니다` : `다운로드됩니다`) + ` (총 ${total.toLocaleString('ko-KR')}건)`;
messageEl.innerHTML = downloadType === 2 ? `<strong>(${currentChunk} / ${totalChunks})</strong> 파일 생성 중` : `엑셀 파일 생성 중`;
/* 작업 소요 시간 : 예상 시간 (1만건당 10초) */
const sec = Math.max(5, Math.ceil(total * 0.0012 * 1.2)); // 최소 5초 보장
const text = `예상 처리 시간은 약 ${sec >= 60 ? `${Math.floor(sec / 60)}분 ${sec % 60}초` : `${sec}초`}`;
document.getElementById('estimatedTimeText').innerText = text;
}
else if (status === "zipping")
{
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일이 압축파일로 생성됩니다`;
messageEl.innerHTML = `<strong>${totalChunks}</strong> 파일 압축하는 중`;
}
else if (status === "zippingError")
{
errorEl.innerHTML = message;
}
else if (status === "error")
{
summaryEl.innerHTML = `엑셀 파일 다운로드 실패`;
resultEl.innerHTML = '';
spinnerEl?.classList.add('is-hidden');
const parts = message.split(/<br\s*\/?>/i);
messageEl.innerHTML = parts[0] || '';
errorEl.innerHTML = parts.slice(1).join('<br>') || '';
// SSE 작업 닫기
eventSource?.close();
eventSource = null;
}
else if (status === "done")
{
// SSE 작업 닫기
eventSource?.close();
eventSource = null;
titleEl.textContent = '엑셀 파일 다운로드 완료';
messageEl.innerHTML = `<strong>총 ${total.toLocaleString('ko-KR')}건의 데이터 다운로드가 완료되었습니다!</strong>`;
spinnerEl?.classList.add('is-hidden');
let html = '<p>* 자동으로 다운로드가 되지 않았다면 아래 버튼을 클릭해주세요.</p>';
const baseUrl = `<?php echo G5_DATA_URL; ?>/member_list/<?php echo date('Ymdhis'); ?>/`; // 공통 URL 분리
if (zipFile) {
const url = `${filePath}/${zipFile}`;
html += `<a href="${url}" class="btn btn_03" download>압축파일 다운로드</a>`;
downloadBoxEl.innerHTML = html;
triggerAutoDownload(url, zipFile);
} else if (files?.length) {
files.forEach((file, index) => {
const url = `${filePath}/${file}`;
html += `<a class="btn btn_03" href="${url}" download>엑셀파일 다운로드 ${index + 1}</a>`;
});
downloadBoxEl.innerHTML = html;
if (files.length === 1) {
const url = `${filePath}/${files[0]}`;
triggerAutoDownload(url, files[0]);
} else {
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일이 생성되었습니다. 아래 버튼을 눌러 다운로드 받아주세요.`;
}
}
}
}
}
// SSE 오류 처리
function handleDownloadError() {
return function(e){
const errorMessage = e?.message || e?.data || '알 수 없는 오류가 발생했습니다.';
document.querySelector('.progress-summary').innerHTML = `엑셀 파일 다운로드 실패`;
document.querySelector('.progress-message').innerHTML = `엑셀 파일 다운로드에 실패하였습니다`;
document.querySelector('.progress-error').innerHTML = errorMessage;
document.querySelector('.loading-message').innerHTML = '';
document.querySelector('.progress-spinner').classList.add('is-hidden');
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
}
// 자동 다운로드 실행
function triggerAutoDownload(url, filename) {
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
</script>
<?php
require_once './admin.tail.php';

View File

@ -0,0 +1,559 @@
<?php
require_once './_common.php';
require_once './member_list_exel.lib.php'; // 회원관리파일 공통 라이브러리 (상수, 검색 옵션 설정, SQL WHERE 등)
include_once(G5_LIB_PATH.'/PHPExcel.php');
ini_set('memory_limit', '-1');
session_write_close(); // 세션 종료 및 잠금 해제 (백그라운드 작업을 위해 필요)
// 파라미터 수집 및 유효성 검사
$params = get_member_export_params();
if (!$params || !is_array($params)) {
member_export_send_progress("error", "데이터가 올바르게 전달되지 않아 작업에 실패하였습니다.");
member_export_write_log([], ['success' => 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) . "건)를 초과했습니다.<br>조건을 추가로 설정하신 후 다시 시도해 주세요.");
}
if($total <= 0){
throw new Exception("조회된 데이터가 없어 엑셀 파일을 생성할 수 없습니다.<br>조건을 추가로 설정하신 후 다시 시도해 주세요.");
}
$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}번째 파일을 생성하지 못했습니다<br>" . $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' => '파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다.<br>: 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}");
}
}

View File

@ -0,0 +1,72 @@
<?php
$sub_menu = '100930';
include_once('./_common.php');
if ($is_admin != 'super')
alert('최고관리자만 접근 가능합니다.', G5_URL);
$g5['title'] = '회원관리파일 일괄삭제';
include_once(G5_ADMIN_PATH.'/admin.head.php');
?>
<div class="local_desc02 local_desc">
<p>
완료 메세지가 나오기 전에 프로그램의 실행을 중지하지 마십시오.
</p>
</div>
<?php
flush();
if (!$dir = @opendir(G5_DATA_PATH . '/member_list')) {
echo '<p>회원관리파일를 열지못했습니다.</p>';
}
$cnt = 0;
echo '<ul class="session_del">' . 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 '<li>파일 삭제: ' . $member_list_file . '</li>' . PHP_EOL;
$cnt++;
} elseif (is_dir($member_list_file) && $basename !== 'log') {
deleteFolder($member_list_file);
echo '<li>폴더 삭제: ' . $member_list_file . '</li>' . PHP_EOL;
$cnt++;
}
flush();
if ($cnt % 10 == 0) {
echo PHP_EOL;
}
}
}
echo '<li>완료됨</li></ul>' . PHP_EOL;
echo '<div class="local_desc01 local_desc"><p><strong>회원관리파일 ' . $cnt . '건 삭제 완료됐습니다.</strong><br>프로그램의 실행을 끝마치셔도 좋습니다.</p></div>' . PHP_EOL;
?>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');

View File

@ -27,6 +27,30 @@ if ($_POST['act_button'] == "선택수정") {
$post_mb_sms = isset($_POST['mb_sms'][$k]) ? (int) $_POST['mb_sms'][$k] : 0;
$post_mb_open = isset($_POST['mb_open'][$k]) ? (int) $_POST['mb_open'][$k] : 0;
$agree_items = [];
// 광고성 이메일 수신동의 일자 추가
$post_mb_mailling_default = isset($_POST['mb_mailling_default'][$k]) ? (int) $_POST['mb_mailling_default'][$k] : 0;
$sql_mailling_date = "";
if ($post_mb_mailling_default != $post_mb_mailling) {
$sql_mailling_date = " , mb_mailling_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 이메일 수신(" . ($post_mb_mailling == 1 ? "동의" : "철회") . ")";
}
// 광고성 SMS/카카오톡 수신동의 일자 추가
$post_mb_sms_default = isset($_POST['mb_sms_default'][$k]) ? (int) $_POST['mb_sms_default'][$k] : 0;
$sql_sms_date = "";
if ($post_mb_sms_default != $post_mb_sms) {
$sql_sms_date = " , mb_sms_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 SMS/카카오톡 수신(" . ($post_mb_sms == 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, ''))";
}
$mb_datas[] = $mb = get_member($_POST['mb_id'][$k]);
if (!(isset($mb['mb_id']) && $mb['mb_id'])) {
@ -50,6 +74,9 @@ if ($_POST['act_button'] == "선택수정") {
mb_open = '" . $post_mb_open . "',
mb_certify = '" . sql_real_escape_string($post_mb_certify) . "',
mb_adult = '{$mb_adult}'
{$sql_mailling_date}
{$sql_sms_date}
{$sql_agree_log}
where mb_id = '" . sql_real_escape_string($mb['mb_id']) . "' ";
sql_query($sql);
}

View File

@ -32,6 +32,16 @@ include_once('./admin.head.php');
</ul>
</div>
<div class="service_2">
<div class="svc_ri svc_sms">
<div class="svc_a">
<h3>카카오톡 발송 서비스</h3>
<p>주문이나 배송시에 상점운영자 또는 고객에게 휴대폰으로 카카오톡을 발송합니다. <b>연동회원</b>으로 선택한 후 링크아이디 [<b>SIRSOFT</b>]를 입력해 주세요.</p>
</div>
<div class="svc_btn2"><a href="https://www.popbill.com/App/SignUp" target="_blank"><img src="<?php echo G5_ADMIN_URL ?>/img/svc_btn_07.jpg" alt="팝빌 카카오톡 서비스 신청하기"></a></div>
</div>
</div>
<div class="service_2">
<div class="svc_ri svc_sms">
<div class="svc_a">

View File

@ -680,9 +680,9 @@ $(function(){
</td>
</tr>
<tr>
<th scope="row"><label for="it_stock_sms">재입고SMS 알림</label></th>
<th scope="row"><label for="it_stock_sms">재입고 알림</label></th>
<td colspan="2">
<?php echo help("상품이 품절인 경우에 체크해 놓으면 상품상세보기에서 고객이 재입고SMS 알림을 신청할 수 있게 됩니다."); ?>
<?php echo help("상품이 품절인 경우에 체크해 놓으면 상품상세보기에서 고객이 재입고 알림(SMS/알림톡)을 신청할 수 있게 됩니다."); ?>
<input type="checkbox" name="it_stock_sms" value="1" id="it_stock_sms" <?php echo ($it['it_stock_sms']) ? "checked" : ""; ?>> 예
</td>
</tr>

View File

@ -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 = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
</th>
<th scope="col">상품명</th>
<th scope="col">휴대폰번호</th>
<th scope="col">SMS전송</th>
<th scope="col">SMS전송일시</th>
<th scope="col">전송결과</th>
<th scope="col">전송채널</th>
<th scope="col">전송일시</th>
<th scope="col">등록일시</th>
</tr>
</thead>
@ -139,13 +146,14 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
<td class="td_left"><?php echo $it_name; ?></td>
<td class="td_telbig"><?php echo $row['ss_hp']; ?></td>
<td class="td_stat"><?php echo ($row['ss_send'] ? '전송완료' : '전송전'); ?></td>
<td class="td_stat"><?php echo ($row['ss_send'] ? ($row['ss_channel'] == 2 ? " 알림톡" : " SMS") : ''); ?></td>
<td class="td_datetime"><?php echo (is_null_time($row['ss_send_time']) ? '' : $row['ss_send_time']); ?></td>
<td class="td_datetime"><?php echo (is_null_time($row['ss_datetime']) ? '' : $row['ss_datetime']); ?></td>
</tr>
<?php
}
if (!$i)
echo '<tr><td colspan="6" class="empty_table"><span>자료가 없습니다.</span></td></tr>';
echo '<tr><td colspan="7" class="empty_table"><span>자료가 없습니다.</span></td></tr>';
?>
</tbody>
</table>
@ -157,6 +165,10 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
<input type="submit" name="act_button" value="선택삭제" onclick="document.pressed=this.value" class="btn btn_02">
<?php } ?>
<input type="submit" name="act_button" value="선택SMS전송" class="btn_submit btn" onclick="document.pressed=this.value">
<?php if($config['cf_kakaotalk_use']) { ?>
<input type="submit" name="act_button" value="선택알림톡전송" class="btn_submit btn" onclick="document.pressed=this.value">
<?php } ?>
</div>
</form>
@ -170,13 +182,18 @@ function fitemstocksms_submit(f)
return false;
}
if(document.pressed == "선택삭제") {
if(!confirm("선택한 자료를 정말 삭제하시겠습니까?")) {
return false;
}
}
var action = document.pressed;
switch (action) {
case "선택삭제":
return confirm("선택한 자료를 정말 삭제하시겠습니까?");
case "선택SMS전송":
return confirm("선택한 자료에 대해서 SMS로 재입고 알림을 전송하시겠습니까?");
case "선택알림톡전송":
return confirm("선택한 자료에 대해서 알림톡으로 재입고 알림을 전송하시겠습니까?");
default:
return true;
}
}
</script>

View File

@ -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')

View File

@ -0,0 +1,22 @@
<?php
if (!defined("_GNUBOARD_")) exit; // 개별 페이지 접근 불가
if (!defined("_ORDERALIMTALK_")) exit;
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
$it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명
// 입금 알림
if($od_alimtalk_ipgum_check){
// 알림톡 발송 BEGIN: 입금완료(CU-OR03 / AD-OR03) ------------------------------
$conditions = ['od_id' => $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 --------------------------------------------------------
}

View File

@ -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 --------------------------------------------------------
}
}

View File

@ -1,6 +1,7 @@
<?php
$sub_menu = '400400';
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
$cart_title3 = '주문번호';
$cart_title4 = '배송완료';
@ -282,6 +283,9 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<div class="local_desc01 local_desc">
<p>주문, 입금, 준비, 배송, 완료는 장바구니와 주문서 상태를 모두 변경하지만, 취소, 반품, 품절은 장바구니의 상태만 변경하며, 주문서 상태는 변경하지 않습니다.</p>
<p>개별적인(이곳에서의) 상태 변경은 모든 작업을 수동으로 처리합니다. 예를 들어 주문에서 입금으로 상태 변경시 입금액(결제금액)을 포함한 모든 정보는 수동 입력으로 처리하셔야 합니다.</p>
<?php if ($config['cf_kakaotalk_use']) { ?>
<p>* <b>알림톡 프리셋</b>: <b>[준비, 완료, 취소, 반품, 품절]</b>은 <b>자동</b>으로 발송되며, <b>[입금완료, 배송]</b>은 <b>결제상세정보에서 수동</b>으로 발송하셔야 합니다.</p>
<?php } ?>
</div>
</form>
@ -719,6 +723,16 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<label for="od_sms_ipgum_check">SMS 입금 문자전송</label>
<br>
<?php } ?>
<!-- 입금완료 알림톡 전송 -->
<?php
$alimtalk = get_alimtalk_preset_info('CU-OR03');
if ($config['cf_kakaotalk_use'] && isset($alimtalk['success'])) {?>
<input type="checkbox" name="od_alimtalk_ipgum_check" id="od_alimtalk_ipgum_check">
<label for="od_alimtalk_ipgum_check">입금완료 알림톡전송</label>
<br>
<?php } ?>
<input type="text" name="od_deposit_name" value="<?php echo get_text($od['od_deposit_name']); ?>" id="od_deposit_name" class="frm_input">
</td>
</tr>
@ -826,6 +840,17 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<label for="od_sms_baesong_check">SMS 배송 문자전송</label>
<br>
<?php } ?>
<!-- 배송 알림톡 전송 -->
<?php
$alimtalk = get_alimtalk_preset_info('CU-DE02');
if ($config['cf_kakaotalk_use'] && isset($alimtalk['success'])) {
?>
<input type="checkbox" name="od_alimtalk_baesong_check" id="od_alimtalk_baesong_check">
<label for="od_alimtalk_baesong_check">배송 알림톡전송</label>
<br>
<?php } ?>
<input type="text" name="od_invoice" value="<?php echo $od['od_invoice']; ?>" id="od_invoice" class="frm_input">
</td>
</tr>

View File

@ -1,6 +1,7 @@
<?php
$sub_menu = '400400';
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
auth_check_menu($auth, $sub_menu, "w");
@ -25,6 +26,7 @@ $sort2 = isset($_REQUEST['sort2']) ? clean_xss_tags($_REQUEST['sort2'], 1, 1) :
$sel_field = isset($_REQUEST['sel_field']) ? clean_xss_tags($_REQUEST['sel_field'], 1, 1) : '';
$mod_history = '';
$od_names = [];
$cnt = (isset($_POST['ct_id']) && is_array($_POST['ct_id'])) ? count($_POST['ct_id']) : 0;
$arr_it_id = array();
@ -155,6 +157,8 @@ for ($i=0; $i<$cnt; $i++)
// it_id를 배열에 저장
if($ct_status == '주문' || $ct_status == '취소' || $ct_status == '반품' || $ct_status == '품절' || $ct_status == '완료')
$arr_it_id[] = $ct['it_id'];
$od_names[] = $ct['it_name']; // 상품명 배열 : 알림톡 사용
}
// 상품 판매수량 반영
@ -376,6 +380,27 @@ $url = "./orderform.php?od_id=$od_id&amp;$qstr";
if($pg_cancel == 1 && $pg_res_cd && $pg_res_msg) {
alert('오류코드 : '.$pg_res_cd.' 오류내용 : '.$pg_res_msg, $url);
} else {
// 알림톡 발송 BEGIN: 배송준비(DE01) | 배송완료(DE03) | 관리자 주문취소(OR05) | 반품처리(OR06) | 품절안내(OR07) ------------------------------
$alimtalk_map = [ '준비' => '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'])

View File

@ -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)

View File

@ -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

View File

@ -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')
{

View File

@ -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'])

View File

@ -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);

View File

@ -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) {

View File

@ -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');

View File

@ -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}

329
extend/kakao5.extend.php Normal file
View File

@ -0,0 +1,329 @@
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
//------------------------------------------------------------------------------
// 카카오톡 알림 메시지 상수 모음 시작
//------------------------------------------------------------------------------
define('G5_KAKAO5_DIR', 'kakao5');
define('G5_KAKAO5_PATH', G5_PLUGIN_PATH.'/'.G5_KAKAO5_DIR);
define('G5_KAKAO5_URL', G5_PLUGIN_URL.'/'.G5_KAKAO5_DIR);
// 연동환경 설정, true-테스트, false-운영(Production), (기본값:false)
define('G5_KAKAO5_IS_TEST', false);
// 카카오톡 알림 메시지 테이블명
$g5['kakao5_prefix'] = G5_TABLE_PREFIX.'kakao5_';
$g5['kakao5_preset_table'] = $g5['kakao5_prefix'] . 'preset'; // 알림톡 - 프리셋 관리
$g5['kakao5_preset_history_table'] = $g5['kakao5_prefix'] . 'preset_history'; // 알림톡 - 프리셋 전송내역 관리
//------------------------------------------------------------------------------
// 알림톡 프리셋 버튼 링크 매핑
// - 버튼 템플릿에서 #{주문상세}와 같은 플레이스홀더를 사용할 때, 실제 URL로 치환하기 위한 매핑입니다.
// - URL 내에 {필드명} 형태의 변수가 포함된 경우, 해당 값은 $conditions 배열에 전달되어야 정상적으로 치환됩니다.
// 예시: '#{주문상세}' => ['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건])<br>
- <b>주문 시</b>: 주문한 상품명이 표시됩니다.<br>
- <b>주문취소 / 반품 / 품절 처리 시</b>: 해당 처리 대상 상품명만 표시됩니다.',
'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' => '<b>고객이 주문취소 시</b> 입력한 취소사유가 표시됩니다. (예시: [단순변심])',
'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'
],
]
],
];

View File

@ -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;

View File

@ -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',

26
js/kakao5.js Normal file
View File

@ -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 생성에 실패했습니다.');
}
}

View File

@ -1,6 +1,7 @@
<?php
include_once('./_common.php');
include_once(G5_LIB_PATH.'/mailer.lib.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
$post_p_hash = isset($_POST['P_HASH']) ? $_POST['P_HASH'] : '';
$post_enc_data = isset($_POST['enc_data']) ? $_POST['enc_data'] : '';
@ -581,6 +582,9 @@ if( !$od_id ){
die("주문번호가 없습니다.");
}
// 주문 상품명 및 개수 조회
$it_name_str = get_alimtalk_cart_item_name($od_id); // 상품명
$od_escrow = 0;
if(isset($escw_yn) && $escw_yn == 'Y')
$od_escrow = 1;
@ -944,6 +948,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);

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; word-break:keep-all; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -22,7 +22,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</div>
<section id="fregister_term">
<h2>회원가입약관</h2>
<h2>(필수) 회원가입약관</h2>
<textarea readonly><?php echo get_text($config['cf_stipulation']) ?></textarea>
<fieldset class="fregister_agree">
<input type="checkbox" name="agree" value="1" id="agree11" class="selec_chk">
@ -31,7 +31,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</section>
<section id="fregister_private">
<h2>개인정보 수집 및 이용</h2>
<h2>(필수) 개인정보 수집 및 이용</h2>
<div class="tbl_head01 tbl_wrap">
<table>
<caption>개인정보 수집 및 이용</caption>

View File

@ -45,7 +45,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<div class="form_01">
<h2>개인정보 입력</h2>
<ul>
<li>
<?php
$desc_name = '';
$desc_phone = '';
@ -56,7 +55,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
if (!$config['cf_cert_simple'] && !$config['cf_cert_hp'] && $config['cf_cert_ipin']) {
$desc_phone = '';
}
?>
<li>
<?php
if($config['cf_cert_simple']) {
echo '<button type="button" id="win_sa_kakao_cert" class="btn_frmline btn win_sa_cert" data-type="">간편인증</button>'.PHP_EOL;
}
@ -67,10 +68,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
echo '<span class="cert_req">(필수)</span>';
echo '<noscript>본인확인을 위해서는 자바스크립트 사용이 가능해야합니다.</noscript>'.PHP_EOL;
}
?>
<?php
if ($config['cf_cert_use'] && $member['mb_certify']) {
if ($member['mb_certify']) {
switch ($member['mb_certify']) {
case "simple":
$mb_cert = "간편인증";
@ -88,6 +88,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
<?php } ?>
</li>
<?php } ?>
<li class="rgs_name_li">
<label for="reg_mb_name" class="sound_only">이름 (필수)<?php echo $desc_name ?></label>
<input type="text" id="reg_mb_name" name="mb_name" value="<?php echo get_text($member['mb_name']) ?>" <?php echo $required ?> <?php echo $name_readonly; ?> class="frm_input full_input <?php echo $required ?> <?php echo $name_readonly ?>" placeholder="이름 (필수)<?php echo $desc_name ?>">
@ -129,7 +130,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<?php if ($config['cf_use_tel']) { ?>
<li>
<label for="reg_mb_tel" class="sound_only">전화번호<?php if ($config['cf_req_tel']) { ?> (필수)<?php } ?></label>
<input type="text" name="mb_tel" value="<?php echo get_text($member['mb_tel']) ?>" id="reg_mb_tel" class="frm_input full_input <?php echo $config['cf_req_tel']?"required":""; ?>" <?php if ($config['cf_cert_use'] && ($config['cf_cert_hp'] || $config['cf_cert_simple']) && $member['mb_certify']) { echo "readonly"; } ?> maxlength="20" <?php echo $config['cf_req_tel']?"required":""; ?> placeholder="전화번호<?php if ($config['cf_req_tel']) { ?> (필수)<?php } ?>">
<input type="text" name="mb_tel" value="<?php echo get_text($member['mb_tel']) ?>" id="reg_mb_tel" class="frm_input full_input <?php echo $config['cf_req_tel']?"required":""; ?>" maxlength="20" <?php echo $config['cf_req_tel']?"required":""; ?> placeholder="전화번호<?php if ($config['cf_req_tel']) { ?> (필수)<?php } ?>">
</li>
<?php } ?>
@ -218,26 +219,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</li>
<?php } ?>
<li class="chk_box">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo ($w=='' || $member['mb_mailling'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_mailling">
<span></span>
<b class="sound_only">메일링서비스</b>
</label>
<span class="chk_li">정보 메일을 받겠습니다.</span>
</li>
<?php if ($config['cf_use_hp']) { ?>
<li class="chk_box">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo ($w=='' || $member['mb_sms'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_sms">
<span></span>
<b class="sound_only">SMS 수신여부</b>
</label>
<span class="chk_li">휴대폰 문자메세지를 받겠습니다.</span>
</li>
<?php } ?>
<?php if (isset($member['mb_open_date']) && $member['mb_open_date'] <= date("Y-m-d", G5_SERVER_TIME - ($config['cf_open_modify'] * 86400)) || empty($member['mb_open_date'])) { // 정보공개 수정일이 지났다면 수정가능 ?>
<li class="chk_box">
<input type="checkbox" name="mb_open" value="1" id="reg_mb_open" <?php echo ($w=='' || $member['mb_open'])?'checked':''; ?> class="selec_chk">
@ -246,7 +227,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<b class="sound_only">정보공개</b>
</label>
<span class="chk_li">다른분들이 나의 정보를 볼 수 있도록 합니다.</span>
<span class="frm_info">
<span class="frm_info add_info">
정보공개를 바꾸시면 앞으로 <?php echo (int)$config['cf_open_modify'] ?>일 이내에는 변경이 안됩니다.
</span>
<input type="hidden" name="mb_open_default" value="<?php echo $member['mb_open'] ?>">
@ -276,7 +257,159 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<input type="text" name="mb_recommend" id="reg_mb_recommend" class="frm_input full_input" placeholder="추천인아이디">
</li>
<?php } ?>
</ul>
</div>
<?php if($config['cf_kakaotalk_use'] != "") { ?>
<div class="form_01">
<h2>게시판 알림설정</h2>
<span class="frm_info add_info">게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.<br>알림은 등록된 휴대폰 번호로 발송됩니다.</span>
<ul>
<!-- 게시글 알림 -->
<li class="chk_box consent-group">
<label><b>게시글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_post" value="1" id="mb_board_post" <?php echo ($w=='' || $member['mb_board_post'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_post"><span></span><b class="sound_only">내 게시글 작성 완료 알림</b></label>
<span class="chk_li">내 게시글 작성 완료 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_reply" value="1" id="mb_board_reply" <?php echo ($w=='' || $member['mb_board_reply'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_reply"><span></span><b class="sound_only">내 게시글에 달린 답변 알림</b></label>
<span class="chk_li">내 게시글에 달린 답변 알림</span>
</li>
</ul>
</li>
<br>
<!-- 댓글 알림 -->
<li class="chk_box consent-group">
<label><b>댓글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_comment" value="1" id="mb_board_comment" <?php echo ($w=='' || $member['mb_board_comment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_comment"><span></span><b class="sound_only">내 게시글에 달린 댓글 알림</b></label>
<span class="chk_li">내 게시글에 달린 댓글 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_recomment" value="1" id="mb_board_recomment" <?php echo ($w=='' || $member['mb_board_recomment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_recomment"><span></span><b class="sound_only">댓글에 대댓글 알림</b></label>
<span class="chk_li">내 댓글에 달린 대댓글 알림</span>
</li>
</ul>
</li>
</ul>
</div>
<?php } ?>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="form_01">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_marketing_agree'] == 1 && $member['mb_marketing_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_marketing_date'].")"; ?></div>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_mailling'] == 1 && $member['mb_mailling_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_mailling_date'].")"; ?></div>
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_sms'] == 1 && $member['mb_sms_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_sms_date'].")"; ?></div>
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_thirdparty_agree'] == 1 && $member['mb_thirdparty_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_thirdparty_date'].")"; ?></div>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
<div class="form_01">
<h2>자동등록방지</h2>
<ul>
<li class="is_captcha_use">
<span class="frm_label">자동등록방지</span>
<?php echo captcha_html(); ?>
@ -290,6 +423,8 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
</form>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
$("#reg_zip_find").css("display", "inline-block");
@ -513,5 +648,29 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
}
$(this).siblings('.fileName').val(filename);
});
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
</div>

View File

@ -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}

View File

@ -545,11 +545,11 @@ function popup_item_recommend(it_id)
}
}
// 재입고SMS 알림
// 재입고 알림
function popup_stocksms(it_id)
{
url = "<?php echo G5_SHOP_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);
}

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; word-break:keep-all; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -145,6 +145,108 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
</ul>
</div>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="form_01">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<?php if ($member['mb_marketing_agree'] == 1 && $member['mb_marketing_date'] != "0000-00-00 00:00:00") echo "<br>(동의일자: ".$member['mb_marketing_date'].")"; ?>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
<?php if ($w == 'u' && $member['mb_mailling'] == 1 && $member['mb_mailling_date'] != "0000-00-00 00:00:00") echo "<br>(동의일자: ".$member['mb_mailling_date'].")"; ?>
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
<?php if ($w == 'u' && $member['mb_sms'] == 1 && $member['mb_sms_date'] != "0000-00-00 00:00:00") echo "<br>(동의일자: ".$member['mb_sms_date'].")"; ?>
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<?php if ($member['mb_thirdparty_agree'] == 1 && $member['mb_thirdparty_date'] != "0000-00-00 00:00:00") echo "<br>(동의일자: ".$member['mb_thirdparty_date'].")"; ?>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
<div class="btn_confirm">
<a href="<?php echo G5_URL ?>" class="btn_cancel">취소</a>
<button type="submit" id="btn_submit" class="btn_submit" accesskey="s"><?php echo $w == '' ? '회원가입' : '정보수정'; ?></button>
@ -196,6 +298,8 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
</div>
</div>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
// 모두선택
@ -335,6 +439,30 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
return true;
}
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
<!-- } 회원정보 입력/수정 끝 -->

View File

@ -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}

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,4 @@
linkhub.auth.php
================
링크허브 API 인증 SDK for PHP5

View File

@ -0,0 +1,49 @@
<?php
require_once 'linkhub.auth.php';
$ServiceID = 'POPBILL_TEST';
$LinkID = 'TESTER';
$SecretKey = 'SwWxqU+0TErBXy/9TVjIPEnI0VTUMMSQZtJf3Ed8q3I=';
//통신방식 기본은 CURL , curl 사용에 문제가 있을경우 STREAM 사용가능.
//STREAM 사용시에는 allow_fopen_url = on 으로 설정해야함.
define('LINKHUB_COMM_MODE','STREAM');
$AccessID = '1231212312';
$Linkhub = Linkhub::getInstance($LinkID,$SecretKey);
try
{
$Token = $Linkhub->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);
?>

View File

@ -0,0 +1,337 @@
<?php
/**
* =====================================================================================
* Class for develop interoperation with Linkhub APIs.
* Functionalities are authentication for Linkhub api products, and to support
* several base infomation(ex. Remain point).
*
* This module uses curl and openssl for HTTPS Request. So related modules must
* be installed and enabled.
*
* http://www.linkhub.co.kr
* Author : Kim Seongjun
* Contributor : Jeong Yohan (code@linkhubcorp.com)
* Contributor : Jeong Wooseok (code@linkhubcorp.com)
* Written : 2017-08-29
* Updated : 2024-10-15
*
* Thanks for your interest.
* We welcome any suggestions, feedbacks, blames or anythings.
*
* Update Log
* - 2017/08/29 GetPartnerURL API added
* - 2023/02/10 Request Header User-Agent added
* - 2023/08/02 AuthURL Setter added
* - 2023/08/11 ServiceURL Rename
* - 2024/09/26 Timeout added
* ======================================================================================
*/
class Linkhub
{
const VERSION = '2.0';
const ServiceURL = 'https://auth.linkhub.co.kr';
const ServiceURL_Static = 'https://static-auth.linkhub.co.kr';
const ServiceURL_GA = 'https://ga-auth.linkhub.co.kr';
private $__LinkID;
private $__SecretKey;
private $__requestMode = LINKHUB_COMM_MODE;
private $__ServiceURL;
public function getSecretKey(){
return $this->__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";
}
}
?>

View File

@ -0,0 +1,679 @@
<?php
/**
* =====================================================================================
* Class for base module for Popbill API SDK. It include base functionality for
* RESTful web service request and parse json result. It uses Linkhub module
* to accomplish authentication APIs.
*
* This module uses curl and openssl for HTTPS Request. So related modules must
* be installed and enabled.
*
* http://www.linkhub.co.kr
* Author : Jeong YoHan (code@linkhubcorp.com)
* Written : 2018-03-02
* Updated : 2025-01-13
*
* Thanks for your interest.
* We welcome any suggestions, feedbacks, blames or anything.
* ======================================================================================
*/
require_once 'popbill.php';
class KakaoService extends PopbillBase {
public function __construct($LinkID, $SecretKey)
{
parent::__construct($LinkID, $SecretKey);
$this->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;
}
}
?>

View File

@ -0,0 +1,619 @@
<?php
/**
* =====================================================================================
* Class for base module for Popbill API SDK. It include base functionality for
* RESTful web service request and parse json result. It uses Linkhub module
* to accomplish authentication APIs.
*
* This module uses curl and openssl for HTTPS Request. So related modules must
* be installed and enabled.
*
* http://www.linkhub.co.kr
* Author : Kim Seongjun
* Written : 2014-04-15
* Contributor : Jeong YoHan (code@linkhubcorp.com)
* Updated : 2025-01-13
*
* Thanks for your interest.
* We welcome any suggestions, feedbacks, blames or anything.
* ======================================================================================
*/
require_once 'popbill.php';
class MessagingService extends PopbillBase {
public function __construct($LinkID, $SecretKey)
{
parent::__construct($LinkID, $SecretKey);
$this->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;
}
}
?>

View File

@ -0,0 +1,2 @@
# popbill.sdk.php5
팝빌 SDK for PHP5

View File

@ -0,0 +1,44 @@
<?php
class cryptor {
public function encrypt($key, $data) {
// 키 설정
$publickey = $this->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;
}
}
?>

View File

@ -0,0 +1,930 @@
<?php
/**
* =====================================================================================
* Class for base module for Popbill API SDK. It include base functionality for
* RESTful web service request and parse json result. It uses Linkhub module
* to accomplish authentication APIs.
*
* This module uses curl and openssl for HTTPS Request. So related modules must
* be installed and enabled.
*
* http://www.linkhub.co.kr
* Author : Kim Seongjun
* Written : 2014-04-15
* Contributor : Jeong YoHan (code@linkhubcorp.com)
* Updated : 2025-01-13
*
* Thanks for your interest.
* We welcome any suggestions, feedbacks, blames or anythings.
* ======================================================================================
*/
require_once 'Linkhub/linkhub.auth.php';
class PopbillBase
{
const ServiceID_REAL = 'POPBILL';
const ServiceID_TEST = 'POPBILL_TEST';
const ServiceURL_REAL = 'https://popbill.linkhub.co.kr';
const ServiceURL_TEST = 'https://popbill-test.linkhub.co.kr';
const ServiceURL_Static_REAL = 'https://static-popbill.linkhub.co.kr';
const ServiceURL_Static_TEST = 'https://static-popbill-test.linkhub.co.kr';
const ServiceURL_GA_REAL = 'https://ga-popbill.linkhub.co.kr';
const ServiceURL_GA_TEST = 'https://ga-popbill-test.linkhub.co.kr';
const Version = '1.0';
private $Token_Table = array();
private $Linkhub;
private $IsTest = false;
private $IPRestrictOnOff = true;
private $UseStaticIP = false;
private $UseGAIP = false;
private $UseLocalTimeYN = true;
private $scopes = array();
private $__requestMode = LINKHUB_COMM_MODE;
public function __construct($LinkID, $SecretKey)
{
$this->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";
}
}

View File

@ -0,0 +1,7 @@
<?php
// 팝빌 설정값 가져오기
$userID = isset($config['cf_popbill_userid']) && $config['cf_popbill_userid'] !== '' ? $config['cf_popbill_userid'] : '';
$linkID = isset($config['cf_popbill_link_id']) && $config['cf_popbill_link_id'] !== '' ? $config['cf_popbill_link_id'] : '';
$secretKey = isset($config['cf_popbill_secretkey']) && $config['cf_popbill_secretkey'] !== '' ? $config['cf_popbill_secretkey'] : '';
$corpnum = isset($config['cf_kakaotalk_corpnum']) && $config['cf_kakaotalk_corpnum'] !== '' ? preg_replace('/[^0-9]/', '', $config['cf_kakaotalk_corpnum']) : '';
$sender_hp = isset($config['cf_kakaotalk_sender_hp']) && $config['cf_kakaotalk_sender_hp'] !== '' ? preg_replace('/[^0-9]/', '', $config['cf_kakaotalk_sender_hp']) : '';

View File

@ -0,0 +1,14 @@
<?php
include_once('../../common.php');
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
// 팝빌 정보 확인
$check_result = get_popbill_service_info();
if (isset($check_result['error'])) {
die(json_encode(array('error' => $check_result['error'])));
} else {
$charge_url = get_popbill_point_URL(); // 포인트 충전 팝업 URL
die(json_encode(array('balance' => $check_result['balance'], 'charge_url' => $charge_url)));
}

View File

@ -0,0 +1,38 @@
<?php
/* 팝빌 URL 출력 */
include_once('../../common.php');
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php'); // 팝빌 카카오톡 솔루션 라이브러리
$url = null;
$width = '1200';
$height = '800';
$get_url = isset($_POST['get_url']) ? $_POST['get_url'] : null;
if ($config['cf_kakaotalk_use'] == 'popbill') {
switch ($get_url) {
case '1': // 템플릿 목록 URL
$url = get_popbill_template_manage_URL();
break;
case '2': // 전송내역 URL
$url = get_popbill_send_manage_URL();
$width = '1350';
break;
case '3': // 플러스친구 관리 URL
$url = get_popbill_plusfriend_manage_URL();
break;
case '4': // 발신번호 등록 URL
$url = get_popbill_sender_number_URL();
break;
case '5': // 포인트 충전 URL
$url = get_popbill_point_URL();
$width = '800';
$height = '700';
break;
default:
$url = null;
break;
}
}
die(json_encode(array('url' => $url, 'width' => $width, 'height' => $height)));

View File

@ -0,0 +1,449 @@
<?php
if (!defined('_GNUBOARD_')) exit;
include_once(G5_KAKAO5_PATH.'/_common.php');
require_once(G5_KAKAO5_PATH.'/kakao5_popbill.lib.php');
/*************************************************************************
**
** 알림톡 함수 모음
**
*************************************************************************/
/**
* 프리셋 코드를 사용하여 알림톡을 전송하는 함수
*/
function send_alimtalk_preset($preset_code, array $recipient, $conditions = [])
{
global $g5, $sender_hp, $member, $config;
// 알림톡 사용 설정 확인
if (empty($config['cf_kakaotalk_use'])) {
return array('success' => 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
);
}

View File

@ -0,0 +1,238 @@
<?php
include_once(G5_KAKAO5_PATH . '/Popbill/PopbillKakao.php');
/*************************************************************************
**
** 공통 : 팝빌 카카오톡 발송
**
*************************************************************************/
/**
* kakao 서비스 인스턴스 생성
*/
function get_kakao_service_instance() {
global $linkID, $secretKey;
// 이미 생성된 인스턴스가 있으면 반환
static $KakaoService = null;
if ($KakaoService !== null) {
return $KakaoService;
}
// 통신방식 기본은 CURL , curl 사용에 문제가 있을경우 STREAM 사용가능.
define('LINKHUB_COMM_MODE','CURL');
$KakaoService = new KakaoService($linkID, $secretKey);
// 연동환경 설정, true-테스트, false-운영(Production), (기본값:false)
$KakaoService->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()
];
}
}

View File

@ -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');

View File

@ -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('<link rel="stylesheet" href="'.G5_SHOP_SKIN_URL.'/style.css">', 0);
@ -35,6 +35,9 @@ if (G5_IS_MOBILE) {
<li class="prd_name">
<?php echo $it['it_name']; ?>
</li>
<li>
<strong>* 재입고 알림은 SMS 문자 또는 카카오 알림톡으로 발송됩니다.</strong>
</li>
<li>
<label for="ss_hp" class="sound_only">휴대폰번호<strong> 필수</strong></label>
<input type="text" name="ss_hp" value="<?php echo $member['mb_hp']; ?>" id="ss_hp" required class="required frm_input full_input" placeholder="휴대폰번호">
@ -66,7 +69,7 @@ function fstocksms_submit(f)
return false;
}
if(confirm("재입고SMS 알림 요청을 등록하시겠습니까?")) {
if(confirm("재입고 알림 요청을 등록하시겠습니까?")) {
return true;
} else {
window.close();

View File

@ -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 알림 요청 등록이 완료됐습니다.');
alert_close('재입고 알림 요청 등록이 완료됐습니다.');

View File

@ -1,6 +1,7 @@
<?php
include_once('./_common.php');
include_once(G5_LIB_PATH.'/mailer.lib.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
//이니시스 lpay 요청으로 왔다면 $default['de_pg_service'] 값을 이니시스로 변경합니다.
if( in_array($od_settle_case, array('lpay', 'inicis_kakaopay')) ){
@ -541,6 +542,9 @@ else
// 주문번호를 얻는다.
$od_id = get_session('ss_order_id');
// 주문 상품명 및 개수 조회
$it_name_str = get_alimtalk_cart_item_name($tmp_cart_id);
$od_escrow = 0;
if(isset($escw_yn) && $escw_yn === 'Y')
$od_escrow = 1;
@ -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);

View File

@ -1,5 +1,6 @@
<?php
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
$od_id = isset($_REQUEST['od_id']) ? safe_replace_regex($_REQUEST['od_id'], 'od_id') : '';
@ -173,4 +174,16 @@ sql_query($sql);
if ($od['od_receipt_point'] > 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&amp;uid=$uid");

View File

@ -1,5 +1,6 @@
<?php
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
//**********************************************************************************
//이니시스가 전달하는 가상계좌이체의 결과를 수신하여 DB 처리 하는 부분 입니다.
@ -127,6 +128,19 @@ if (in_array($_SERVER['REMOTE_ADDR'], $allowed_ips)) { //PG에서 보냈는지
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 -------------------------------------------------------
}
if($INIpayLog) {

View File

@ -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 --------------------------------------------------------
}
/* = -------------------------------------------------------------------------- = */

View File

@ -1,5 +1,6 @@
<?php
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
/*
* [상점 결제결과처리(DB) 페이지]
@ -144,6 +145,19 @@ if ( $LGD_HASHDATA2 == $LGD_HASHDATA ) { //해쉬값 검증이 성공이면
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 --------------------------------------------------------
}
//if( 무통장 입금 성공 상점처리결과 성공 )

View File

@ -1,5 +1,6 @@
<?php
include_once('./_common.php');
include_once(G5_KAKAO5_PATH.'/kakao5.lib.php');
$NICEPAY_log_path = G5_DATA_PATH.'/log'; // 나이스페이 가상계좌 로그저장 경로
$NICEPAY_payLog = false; // 로그를 기록하려면 true 로 수정
@ -117,8 +118,20 @@ if (in_array($_SERVER['REMOTE_ADDR'], $pg_allow_ips)) {
where od_id = '$od_id' ";
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 --------------------------------------------------------
}
if($NICEPAY_payLog) {

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { min-width: 320px; max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -17,7 +17,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
@include_once(get_social_skin_path().'/social_register.skin.php');
?>
<section id="fregister_term">
<h2>회원가입약관</h2>
<h2>(필수) 회원가입약관</h2>
<textarea readonly><?php echo get_text($config['cf_stipulation']) ?></textarea>
<fieldset class="fregister_agree">
<input type="checkbox" name="agree" value="1" id="agree11" class="selec_chk">
@ -25,8 +25,8 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</fieldset>
</section>
<section id="fregister_private">
<h2>개인정보 수집 및 이용</h2>
<section id="fregister_private" class="fregister_terms">
<h2>(필수) 개인정보 수집 및 이용</h2>
<div>
<table>
<caption>개인정보 수집 및 이용</caption>

View File

@ -51,7 +51,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>개인정보 입력</h2>
<ul>
<li>
<?php
$desc_name = '';
$desc_phone = '';
@ -62,7 +61,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
if (!$config['cf_cert_simple'] && !$config['cf_cert_hp'] && $config['cf_cert_ipin']) {
$desc_phone = '';
}
?>
<li>
<?php
if ($config['cf_cert_simple']) {
echo '<button type="button" id="win_sa_kakao_cert" class="btn_frmline win_sa_cert" data-type="">간편인증</button>'.PHP_EOL;
}
@ -73,10 +74,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
echo '<span class="cert_req">(필수)</span>';
echo '<noscript>본인확인을 위해서는 자바스크립트 사용이 가능해야합니다.</noscript>'.PHP_EOL;
}
?>
<?php
if ($config['cf_cert_use'] && $member['mb_certify']) {
if ($member['mb_certify']) {
switch ($member['mb_certify']) {
case "simple":
$mb_cert = "간편인증";
@ -94,6 +94,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
<?php } ?>
</li>
<?php } ?>
<li>
<label for="reg_mb_name">이름 (필수)<?php echo $desc_name ?></label>
<input type="text" id="reg_mb_name" name="mb_name" value="<?php echo get_text($member['mb_name']) ?>" <?php echo $required ?> <?php echo $name_readonly; ?> class="frm_input full_input <?php echo $required ?> <?php echo $name_readonly ?>" size="10" placeholder="이름">
@ -135,13 +136,12 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</li>
<?php } ?>
<li>
<?php if ($config['cf_use_tel']) { ?>
<li>
<label for="reg_mb_tel">전화번호<?php if ($config['cf_req_tel']) { ?> (필수)<?php } ?></label>
<input type="text" name="mb_tel" value="<?php echo get_text($member['mb_tel']) ?>" id="reg_mb_tel" <?php echo $config['cf_req_tel']?"required":""; ?> class="frm_input full_input <?php echo $config['cf_req_tel']?"required":""; ?>" maxlength="20" placeholder="전화번호">
<?php } ?>
</li>
<?php } ?>
<li>
<?php if ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) { ?>
<label for="reg_mb_hp">휴대폰번호<?php if (!empty($hp_required)) { ?> (필수)<?php } ?><?php echo $desc_phone ?></label>
@ -228,26 +228,6 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
</li>
<?php } ?>
<li class="chk_box">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo ($w=='' || $member['mb_mailling'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_mailling">
<span></span>
<b class="sound_only">메일링서비스</b>
</label>
<span class="chk_li">정보 메일을 받겠습니다.</span>
</li>
<?php if ($config['cf_use_hp']) { ?>
<li class="chk_box">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo ($w=='' || $member['mb_sms'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_sms">
<span></span>
<b class="sound_only">SMS 수신여부</b>
</label>
<span class="chk_li">휴대폰 문자메세지를 받겠습니다.</span>
</li>
<?php } ?>
<?php if (isset($member['mb_open_date']) && $member['mb_open_date'] <= date("Y-m-d", G5_SERVER_TIME - ($config['cf_open_modify'] * 86400)) || empty($member['mb_open_date'])) { // 정보공개 수정일이 지났다면 수정가능 ?>
<li class="chk_box">
<input type="checkbox" name="mb_open" value="1" id="reg_mb_open" <?php echo ($w=='' || $member['mb_open'])?'checked':''; ?> class="selec_chk">
@ -288,7 +268,161 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
<input type="text" name="mb_recommend" id="reg_mb_recommend" class="frm_input" placeholder="추천인아이디">
</li>
<?php } ?>
</ul>
</div>
<?php if($config['cf_kakaotalk_use'] != "") { ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>
게시판 알림설정
<button type="button" class="tooltip_icon"><i class="fa fa-question-circle-o" aria-hidden="true"></i><span class="sound_only">설명보기</span></button>
<span class="tooltip">게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.<br>알림은 등록된 휴대폰 번호로 발송됩니다.</span>
</h2>
<ul>
<!-- 게시글 알림 -->
<li class="chk_box consent-group">
<label><b>게시글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_post" value="1" id="mb_board_post" <?php echo ($w=='' || $member['mb_board_post'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_post"><span></span><b class="sound_only">내 게시글 작성 완료 알림</b></label>
<span class="chk_li">내 게시글 작성 완료 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_reply" value="1" id="mb_board_reply" <?php echo ($w=='' || $member['mb_board_reply'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_reply"><span></span><b class="sound_only">내 게시글에 달린 답변 알림</b></label>
<span class="chk_li">내 게시글에 달린 답변 알림</span>
</li>
</ul>
</li>
<br>
<!-- 댓글 알림 -->
<li class="chk_box consent-group">
<label><b>댓글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_comment" value="1" id="mb_board_comment" <?php echo ($w=='' || $member['mb_board_comment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_comment"><span></span><b class="sound_only">내 게시글에 달린 댓글 알림</b></label>
<span class="chk_li">내 게시글에 달린 댓글 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_recomment" value="1" id="mb_board_recomment" <?php echo ($w=='' || $member['mb_board_recomment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_recomment"><span></span><b class="sound_only">댓글에 대댓글 알림</b></label>
<span class="chk_li">내 댓글에 달린 대댓글 알림</span>
</li>
</ul>
</li>
</ul>
</div>
<?php } ?>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_marketing_agree'] == 1 && $member['mb_marketing_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_marketing_date'].")"; ?></div>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_mailling'] == 1 && $member['mb_mailling_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_mailling_date'].")"; ?></div>
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_sms'] == 1 && $member['mb_sms_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_sms_date'].")"; ?></div>
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_thirdparty_agree'] == 1 && $member['mb_thirdparty_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_thirdparty_date'].")"; ?></div>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>자동등록방지</h2>
<ul>
<li class="is_captcha_use">
자동등록방지
<?php echo captcha_html(); ?>
@ -302,6 +436,9 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
</div>
</form>
</div>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
$("#reg_zip_find").css("display", "inline-block");
@ -509,6 +646,30 @@ jQuery(function($){
});
});
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
<!-- } 회원정보 입력/수정 끝 -->

View File

@ -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}

View File

@ -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}

View File

@ -340,11 +340,11 @@ add_stylesheet('<link rel="stylesheet" href="'.G5_SHOP_CSS_URL.'/style.css">', 0
}
}
// 재입고SMS 알림
// 재입고 알림
function popup_stocksms(it_id)
{
url = "<?php echo G5_SHOP_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);
}
</script>

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { min-width: 320px; max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -33,7 +33,7 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
</fieldset>
</section>
<section id="fregister_private">
<section id="fregister_private" class="fregister_terms">
<h2>개인정보 수집 및 이용</h2>
<div>
<table>
@ -144,6 +144,104 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
</li>
</ul>
</div>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
</div>
<div class="btn_confirm">
@ -197,6 +295,8 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
</div>
</div>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
// 모두선택
@ -336,6 +436,30 @@ $email_msg = $is_exists_email ? '등록할 이메일이 중복되었습니다.
return true;
}
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
<!-- } 회원정보 입력/수정 끝 -->

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; word-break:keep-all; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -22,7 +22,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</div>
<section id="fregister_term">
<h2>회원가입약관</h2>
<h2>(필수) 회원가입약관</h2>
<textarea readonly><?php echo get_text($config['cf_stipulation']) ?></textarea>
<fieldset class="fregister_agree">
<input type="checkbox" name="agree" value="1" id="agree11" class="selec_chk">
@ -31,7 +31,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</section>
<section id="fregister_private">
<h2>개인정보 수집 및 이용</h2>
<h2>(필수) 개인정보 수집 및 이용</h2>
<div class="tbl_head01 tbl_wrap">
<table>
<caption>개인정보 수집 및 이용</caption>

View File

@ -45,7 +45,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<div class="form_01">
<h2>개인정보 입력</h2>
<ul>
<li>
<?php
$desc_name = '';
$desc_phone = '';
@ -56,7 +55,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
if (!$config['cf_cert_simple'] && !$config['cf_cert_hp'] && $config['cf_cert_ipin']) {
$desc_phone = '';
}
?>
<li>
<?php
if ($config['cf_cert_simple']) {
echo '<button type="button" id="win_sa_kakao_cert" class="btn_frmline btn win_sa_cert" data-type="">간편인증</button>'.PHP_EOL;
}
@ -67,10 +68,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
echo '<span class="cert_req">(필수)</span>';
echo '<noscript>본인확인을 위해서는 자바스크립트 사용이 가능해야합니다.</noscript>'.PHP_EOL;
}
?>
<?php
if ($config['cf_cert_use'] && $member['mb_certify']) {
if ($member['mb_certify']) {
switch ($member['mb_certify']) {
case "simple":
$mb_cert = "간편인증";
@ -88,6 +88,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
<?php } ?>
</li>
<?php } ?>
<li class="rgs_name_li">
<label for="reg_mb_name" class="sound_only">이름 (필수)<?php echo $desc_name ?></label>
<input type="text" id="reg_mb_name" name="mb_name" value="<?php echo get_text($member['mb_name']) ?>" <?php echo $required ?> <?php echo $name_readonly; ?> class="frm_input full_input <?php echo $required ?> <?php echo $name_readonly ?>" placeholder="이름 (필수)<?php echo $desc_name ?>">
@ -217,26 +218,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</li>
<?php } ?>
<li class="chk_box">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo ($w=='' || $member['mb_mailling'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_mailling">
<span></span>
<b class="sound_only">메일링서비스</b>
</label>
<span class="chk_li">정보 메일을 받겠습니다.</span>
</li>
<?php if ($config['cf_use_hp']) { ?>
<li class="chk_box">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo ($w=='' || $member['mb_sms'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_sms">
<span></span>
<b class="sound_only">SMS 수신여부</b>
</label>
<span class="chk_li">휴대폰 문자메세지를 받겠습니다.</span>
</li>
<?php } ?>
<?php if (isset($member['mb_open_date']) && $member['mb_open_date'] <= date("Y-m-d", G5_SERVER_TIME - ($config['cf_open_modify'] * 86400)) || empty($member['mb_open_date'])) { // 정보공개 수정일이 지났다면 수정가능 ?>
<li class="chk_box">
<input type="checkbox" name="mb_open" value="1" id="reg_mb_open" <?php echo ($w=='' || $member['mb_open'])?'checked':''; ?> class="selec_chk">
@ -245,7 +226,7 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<b class="sound_only">정보공개</b>
</label>
<span class="chk_li">다른분들이 나의 정보를 볼 수 있도록 합니다.</span>
<span class="frm_info">
<span class="frm_info add_info">
정보공개를 바꾸시면 앞으로 <?php echo (int)$config['cf_open_modify'] ?>일 이내에는 변경이 안됩니다.
</span>
<input type="hidden" name="mb_open_default" value="<?php echo $member['mb_open'] ?>">
@ -275,7 +256,159 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<input type="text" name="mb_recommend" id="reg_mb_recommend" class="frm_input full_input" placeholder="추천인아이디">
</li>
<?php } ?>
</ul>
</div>
<?php if($config['cf_kakaotalk_use'] != "") { ?>
<div class="form_01">
<h2>게시판 알림설정</h2>
<span class="frm_info add_info">게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.<br>알림은 등록된 휴대폰 번호로 발송됩니다.</span>
<ul>
<!-- 게시글 알림 -->
<li class="chk_box consent-group">
<label><b>게시글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_post" value="1" id="mb_board_post" <?php echo ($w=='' || $member['mb_board_post'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_post"><span></span><b class="sound_only">내 게시글 작성 완료 알림</b></label>
<span class="chk_li">내 게시글 작성 완료 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_reply" value="1" id="mb_board_reply" <?php echo ($w=='' || $member['mb_board_reply'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_reply"><span></span><b class="sound_only">내 게시글에 달린 답변 알림</b></label>
<span class="chk_li">내 게시글에 달린 답변 알림</span>
</li>
</ul>
</li>
<br>
<!-- 댓글 알림 -->
<li class="chk_box consent-group">
<label><b>댓글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_comment" value="1" id="mb_board_comment" <?php echo ($w=='' || $member['mb_board_comment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_comment"><span></span><b class="sound_only">내 게시글에 달린 댓글 알림</b></label>
<span class="chk_li">내 게시글에 달린 댓글 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_recomment" value="1" id="mb_board_recomment" <?php echo ($w=='' || $member['mb_board_recomment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_recomment"><span></span><b class="sound_only">댓글에 대댓글 알림</b></label>
<span class="chk_li">내 댓글에 달린 대댓글 알림</span>
</li>
</ul>
</li>
</ul>
</div>
<?php } ?>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="form_01">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_marketing_agree'] == 1 && $member['mb_marketing_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_marketing_date'].")"; ?></div>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_mailling'] == 1 && $member['mb_mailling_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_mailling_date'].")"; ?></div>
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_sms'] == 1 && $member['mb_sms_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_sms_date'].")"; ?></div>
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_thirdparty_agree'] == 1 && $member['mb_thirdparty_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_thirdparty_date'].")"; ?></div>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
<div class="form_01">
<h2>자동등록방지</h2>
<ul>
<li class="is_captcha_use">
<span class="frm_label">자동등록방지</span>
<?php echo captcha_html(); ?>
@ -289,6 +422,8 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
</form>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
$("#reg_zip_find").css("display", "inline-block");
@ -512,5 +647,29 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
}
$(this).siblings('.fileName').val(filename);
});
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
</div>

View File

@ -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}

View File

@ -545,11 +545,11 @@ function popup_item_recommend(it_id)
}
}
// 재입고SMS 알림
// 재입고 알림
function popup_stocksms(it_id)
{
url = "<?php echo G5_SHOP_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);
}

View File

@ -0,0 +1,85 @@
<!-- HTML -->
<?php if (!defined('_GNUBOARD_')) exit; ?>
<dialog id="consentDialog" aria-labelledby="consentDialogTitle" aria-describedby="consentDialogBody">
<form method="dialog" class="cd-card">
<header class="cd-head">
<h3 id="consentDialogTitle" class="cd-title">안내</h3>
</header>
<div id="consentDialogBody" class="cd-body"></div>
<footer class="cd-actions">
<button type="button" class="cd-agree">동의합니다</button>
<button value="close" class="cd-close">닫기</button>
</footer>
</form>
</dialog>
<!-- 스타일 -->
<style>
#consentDialog { padding:0; border:none; border-radius:12px; }
#consentDialog::backdrop { background: rgba(0,0,0,.45); backdrop-filter: blur(5px);}
.cd-card { min-width: 320px; max-width: 560px; background:#fff; border-radius:12px; }
.cd-head { display:flex; align-items:center; justify-content:space-between; padding:16px; }
.cd-title { margin:0; font-size:18px; font-weight:bold; }
.cd-body { max-height:500px; overflow-y:auto; padding:16px; border-top:1px solid #e6e6e9; border-bottom:1px solid #e6e6e9; line-height:1.6; font-size:14px; color:#222; }
.cd-actions { display:flex; gap:8px; justify-content:flex-end; padding:12px 16px 16px; }
.cd-actions .cd-agree { padding:8px 14px; border:1px solid #3a8afd; background:#3a8afd; color:#fff; border-radius:8px; }
.cd-actions .cd-close { padding:8px 14px; border:1px solid #ccc; background:#fff; color:#111; border-radius:8px; }
</style>
<!-- JS -->
<script>
(function(){
const dlg = document.getElementById('consentDialog');
if (!dlg) return;
const body = document.getElementById('consentDialogBody');
const titleE = document.getElementById('consentDialogTitle');
let opener = null;
const openFrom = (btn) => {
opener = btn;
const tplSel = btn.getAttribute('data-template');
const title = btn.getAttribute('data-title') || '안내';
const tpl = tplSel ? document.querySelector(tplSel) : null;
titleE.textContent = title;
body.innerHTML = tpl ? tpl.innerHTML : '';
dlg.dataset.check = btn.getAttribute('data-check') || '';
dlg.dataset.checkGroup = btn.getAttribute('data-check-group') || '';
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute('open','');
};
const closeDialog = () => {
if (dlg.close) dlg.close(); else dlg.removeAttribute('open');
if (opener) opener.focus();
};
document.addEventListener('click', (e)=>{
const trigger = e.target.closest('.js-open-consent');
if (trigger) { openFrom(trigger); return; }
if (e.target.classList.contains('cd-agree')) {
const sel = dlg.dataset.check;
const groupSel = dlg.dataset.checkGroup;
if (groupSel) {
document.querySelectorAll(groupSel).forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles:true}));
});
}
if (sel) {
const cb = document.querySelector(sel);
if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', {bubbles:true})); }
}
closeDialog();
e.preventDefault();
return;
}
});
dlg.addEventListener('cancel', (e)=>{ e.preventDefault(); closeDialog(); });
})();
</script>

View File

@ -17,7 +17,7 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
@include_once(get_social_skin_path().'/social_register.skin.php');
?>
<section id="fregister_term">
<h2>회원가입약관</h2>
<h2>(필수) 회원가입약관</h2>
<textarea readonly><?php echo get_text($config['cf_stipulation']) ?></textarea>
<fieldset class="fregister_agree">
<input type="checkbox" name="agree" value="1" id="agree11" class="selec_chk">
@ -25,8 +25,8 @@ add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">',
</fieldset>
</section>
<section id="fregister_private">
<h2>개인정보 수집 및 이용</h2>
<section id="fregister_private" class="fregister_terms">
<h2>(필수) 개인정보 수집 및 이용</h2>
<div>
<table>
<caption>개인정보 수집 및 이용</caption>

View File

@ -51,7 +51,6 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>개인정보 입력</h2>
<ul>
<li>
<?php
$desc_name = '';
$desc_phone = '';
@ -62,7 +61,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
if (!$config['cf_cert_simple'] && !$config['cf_cert_hp'] && $config['cf_cert_ipin']) {
$desc_phone = '';
}
?>
<li>
<?php
if ($config['cf_cert_simple']) {
echo '<button type="button" id="win_sa_kakao_cert" class="btn_frmline win_sa_cert" data-type="">간편인증</button>'.PHP_EOL;
}
@ -73,10 +74,9 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
echo '<span class="cert_req">(필수)</span>';
echo '<noscript>본인확인을 위해서는 자바스크립트 사용이 가능해야합니다.</noscript>'.PHP_EOL;
}
?>
<?php
if ($config['cf_cert_use'] && $member['mb_certify']) {
if ($member['mb_certify']) {
switch ($member['mb_certify']) {
case "simple":
$mb_cert = "간편인증";
@ -94,9 +94,10 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</div>
<?php } ?>
</li>
<?php } ?>
<li>
<label for="reg_mb_name">이름 (필수)<?php echo $desc_name ?></label>
<input type="text" id="reg_mb_name" name="mb_name" value="<?php echo get_text($member['mb_name']) ?>" <?php echo $required ?> <?php echo $readonly; ?> class="frm_input full_input <?php echo $required ?> <?php echo $name_readonly ?>" size="10" placeholder="이름">
<input type="text" id="reg_mb_name" name="mb_name" value="<?php echo get_text($member['mb_name']) ?>" <?php echo $required ?> <?php echo $name_readonly; ?> class="frm_input full_input <?php echo $required ?> <?php echo $name_readonly ?>" size="10" placeholder="이름">
</li>
<?php if ($req_nick) { ?>
<li>
@ -135,13 +136,12 @@ if ($config['cf_cert_use'] && ($config['cf_cert_simple'] || $config['cf_cert_ipi
</li>
<?php } ?>
<li>
<?php if ($config['cf_use_tel']) { ?>
<li>
<label for="reg_mb_tel">전화번호<?php if ($config['cf_req_tel']) { ?> (필수)<?php } ?></label>
<input type="text" name="mb_tel" value="<?php echo get_text($member['mb_tel']) ?>" id="reg_mb_tel" <?php echo $config['cf_req_tel']?"required":""; ?> class="frm_input full_input <?php echo $config['cf_req_tel']?"required":""; ?>" maxlength="20" placeholder="전화번호">
<?php } ?>
</li>
<?php } ?>
<li>
<?php if ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) { ?>
<label for="reg_mb_hp">휴대폰번호<?php if (!empty($hp_required)) { ?> (필수)<?php } ?><?php echo $desc_phone ?></label>
@ -228,26 +228,6 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
</li>
<?php } ?>
<li class="chk_box">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo ($w=='' || $member['mb_mailling'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_mailling">
<span></span>
<b class="sound_only">메일링서비스</b>
</label>
<span class="chk_li">정보 메일을 받겠습니다.</span>
</li>
<?php if ($config['cf_use_hp']) { ?>
<li class="chk_box">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo ($w=='' || $member['mb_sms'])?'checked':''; ?> class="selec_chk">
<label for="reg_mb_sms">
<span></span>
<b class="sound_only">SMS 수신여부</b>
</label>
<span class="chk_li">휴대폰 문자메세지를 받겠습니다.</span>
</li>
<?php } ?>
<?php if (isset($member['mb_open_date']) && $member['mb_open_date'] <= date("Y-m-d", G5_SERVER_TIME - ($config['cf_open_modify'] * 86400)) || empty($member['mb_open_date'])) { // 정보공개 수정일이 지났다면 수정가능 ?>
<li class="chk_box">
<input type="checkbox" name="mb_open" value="1" id="reg_mb_open" <?php echo ($w=='' || $member['mb_open'])?'checked':''; ?> class="selec_chk">
@ -288,7 +268,161 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
<input type="text" name="mb_recommend" id="reg_mb_recommend" class="frm_input" placeholder="추천인아이디">
</li>
<?php } ?>
</ul>
</div>
<?php if($config['cf_kakaotalk_use'] != "") { ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>
게시판 알림설정
<button type="button" class="tooltip_icon"><i class="fa fa-question-circle-o" aria-hidden="true"></i><span class="sound_only">설명보기</span></button>
<span class="tooltip">게시판이나 댓글이 등록되면 알림톡으로 안내를 받을 수 있습니다.<br>알림은 등록된 휴대폰 번호로 발송됩니다.</span>
</h2>
<ul>
<!-- 게시글 알림 -->
<li class="chk_box consent-group">
<label><b>게시글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_post" value="1" id="mb_board_post" <?php echo ($w=='' || $member['mb_board_post'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_post"><span></span><b class="sound_only">내 게시글 작성 완료 알림</b></label>
<span class="chk_li">내 게시글 작성 완료 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_reply" value="1" id="mb_board_reply" <?php echo ($w=='' || $member['mb_board_reply'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_reply"><span></span><b class="sound_only">내 게시글에 달린 답변 알림</b></label>
<span class="chk_li">내 게시글에 달린 답변 알림</span>
</li>
</ul>
</li>
<br>
<!-- 댓글 알림 -->
<li class="chk_box consent-group">
<label><b>댓글 알림</b></label>
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_comment" value="1" id="mb_board_comment" <?php echo ($w=='' || $member['mb_board_comment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_comment"><span></span><b class="sound_only">내 게시글에 달린 댓글 알림</b></label>
<span class="chk_li">내 게시글에 달린 댓글 알림</span>
</li>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_board_recomment" value="1" id="mb_board_recomment" <?php echo ($w=='' || $member['mb_board_recomment'])?'checked':''; ?> class="selec_chk">
<label for="mb_board_recomment"><span></span><b class="sound_only">댓글에 대댓글 알림</b></label>
<span class="chk_li">내 댓글에 달린 대댓글 알림</span>
</li>
</ul>
</li>
</ul>
</div>
<?php } ?>
<!-- 회원가입 약관 동의에 광고성 정보 수신 동의 표시 여부가 사용시에만 -->
<?php if($config['cf_use_promotion'] == 1) { ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>수신설정</h2>
<!-- 수신설정만 팝업 및 체크박스 관련 class 적용 -->
<ul>
<!-- (선택) 마케팅 목적의 개인정보 수집 및 이용 -->
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_marketing_agree" value="1" id="reg_mb_marketing_agree" aria-describedby="desc_marketing" <?php echo $member['mb_marketing_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_marketing_agree"><span></span><b class="sound_only">(선택) 마케팅 목적의 개인정보 수집 및 이용</b></label>
<span class="chk_li">(선택) 마케팅 목적의 개인정보 수집 및 이용</span>
<button type="button" class="js-open-consent" data-title="마케팅 목적의 개인정보 수집 및 이용" data-template="#tpl_marketing" data-check="#reg_mb_marketing_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_marketing_agree_default" value="<?php echo $member['mb_marketing_agree'] ?>">
<div id="desc_marketing" class="sound_only">마케팅 목적의 개인정보 수집·이용에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_marketing_agree'] == 1 && $member['mb_marketing_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_marketing_date'].")"; ?></div>
<template id="tpl_marketing">
* 목적: 서비스 마케팅 및 프로모션<br>
* 항목: 이름, 이메일<?php echo ($config['cf_use_hp'] || ($config["cf_cert_use"] && ($config['cf_cert_hp'] || $config['cf_cert_simple']))) ? ", 휴대폰 번호" : "";?><br>
* 보유기간: 회원 탈퇴 시까지<br>
동의를 거부하셔도 서비스 기본 이용은 가능하나, 맞춤형 혜택 제공은 제한될 수 있습니다.
</template>
</li>
<!-- (선택) 광고성 정보 수신 동의 (상위) -->
<li class="chk_box consent-group">
<div class="consent-line">
<input type="checkbox" name="mb_promotion_agree" value="1" id="reg_mb_promotion_agree" aria-describedby="desc_promotion" class="selec_chk marketing-sync parent-promo">
<label for="reg_mb_promotion_agree"><span></span><b class="sound_only">(선택) 광고성 정보 수신 동의</b></label>
<span class="chk_li">(선택) 광고성 정보 수신 동의</span>
<button type="button" class="js-open-consent" data-title="광고성 정보 수신 동의" data-template="#tpl_promotion" data-check="#reg_mb_promotion_agree" data-check-group=".child-promo" aria-controls="consentDialog">자세히보기</button>
</div>
<div id="desc_promotion" class="sound_only">광고성 정보(이메일/SMS·카카오톡) 수신 동의의 상위 항목입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<!-- 하위 채널(이메일/SMS) -->
<ul class="sub-consents">
<li class="chk_box is-inline">
<input type="checkbox" name="mb_mailling" value="1" id="reg_mb_mailling" <?php echo $member['mb_mailling'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_mailling"><span></span><b class="sound_only">광고성 이메일 수신 동의</b></label>
<span class="chk_li">광고성 이메일 수신 동의</span>
<input type="hidden" name="mb_mailling_default" value="<?php echo $member['mb_mailling']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_mailling'] == 1 && $member['mb_mailling_date'] != "0000-00-00 00:00:00") echo " (동의일자: ".$member['mb_mailling_date'].")"; ?></div>
</li>
<!-- 휴대폰번호 입력 보이기 or 필수입력일 경우에만 -->
<?php if ($config['cf_use_hp'] || $config['cf_req_hp']) { ?>
<li class="chk_box is-inline">
<input type="checkbox" name="mb_sms" value="1" id="reg_mb_sms" <?php echo $member['mb_sms'] ? 'checked' : ''; ?> class="selec_chk child-promo">
<label for="reg_mb_sms"><span></span><b class="sound_only">광고성 SMS/카카오톡 수신 동의</b></label>
<span class="chk_li">광고성 SMS/카카오톡 수신 동의</span>
<input type="hidden" name="mb_sms_default" value="<?php echo $member['mb_sms']; ?>">
<div class="consent-date"><?php if ($w == 'u' && $member['mb_sms'] == 1 && $member['mb_sms_date'] != "0000-00-00 00:00:00") echo " (동의일자: ".$member['mb_sms_date'].")"; ?></div>
</li>
<?php } ?>
</ul>
<template id="tpl_promotion">
수집·이용에 동의한 개인정보를 이용하여 이메일/SMS/카카오톡 등으로 오전 8시~오후 9시에 광고성 정보를 전송할 수 있습니다.<br>
동의는 언제든지 마이페이지에서 철회할 수 있습니다.
</template>
</li>
<!-- (선택) 개인정보 제3자 제공 동의 -->
<!-- SMS 및 카카오톡 사용시에만 -->
<?php
$configKeys = ['cf_sms_use', 'cf_kakaotalk_use'];
$companies = ['icode' => '아이코드', 'popbill' => '팝빌'];
$usedCompanies = [];
foreach ($configKeys as $key) {
if (!empty($config[$key]) && isset($companies[$config[$key]])) {
$usedCompanies[] = $companies[$config[$key]];
}
}
?>
<?php if (!empty($usedCompanies)) { ?>
<li class="chk_box">
<div class="consent-line">
<input type="checkbox" name="mb_thirdparty_agree" value="1" id="reg_mb_thirdparty_agree" aria-describedby="desc_thirdparty" <?php echo $member['mb_thirdparty_agree'] ? 'checked' : ''; ?> class="selec_chk marketing-sync">
<label for="reg_mb_thirdparty_agree"><span></span><b class="sound_only">(선택) 개인정보 제3자 제공 동의</b></label>
<span class="chk_li">(선택) 개인정보 제3자 제공 동의</span>
<button type="button" class="js-open-consent" data-title="개인정보 제3자 제공 동의" data-template="#tpl_thirdparty" data-check="#reg_mb_thirdparty_agree" aria-controls="consentDialog">자세히보기</button>
</div>
<input type="hidden" name="mb_thirdparty_agree_default" value="<?php echo $member['mb_thirdparty_agree'] ?>">
<div id="desc_thirdparty" class="sound_only">개인정보 제3자 제공 동의에 대한 안내입니다. 자세히보기를 눌러 전문을 확인할 수 있습니다.</div>
<div class="consent-date"><?php if ($member['mb_thirdparty_agree'] == 1 && $member['mb_thirdparty_date'] != "0000-00-00 00:00:00") echo "(동의일자: ".$member['mb_thirdparty_date'].")"; ?></div>
<template id="tpl_thirdparty">
* 목적: 상품/서비스, 사은/판촉행사, 이벤트 등의 마케팅 안내(카카오톡 등)<br>
* 항목: 이름, 휴대폰 번호<br>
* 제공받는 자: <?php echo implode(', ', $usedCompanies);?><br>
* 보유기간: 제공 목적 서비스 기간 또는 동의 철회 시까지
</template>
</li>
<?php } ?>
</ul>
</div>
<?php } ?>
<div class="tbl_frm01 tbl_wrap register_form_inner">
<h2>자동등록방지</h2>
<ul>
<li class="is_captcha_use">
자동등록방지
<?php echo captcha_html(); ?>
@ -302,6 +436,9 @@ gif, jpg, png파일만 가능하며 용량 <?php echo number_format($config['cf_
</div>
</form>
</div>
<?php include_once(__DIR__ . '/consent_modal.inc.php'); ?>
<script>
$(function() {
$("#reg_zip_find").css("display", "inline-block");
@ -509,6 +646,31 @@ jQuery(function($){
});
});
document.addEventListener('DOMContentLoaded', function () {
const parentPromo = document.getElementById('reg_mb_promotion_agree');
const childPromo = Array.from(document.querySelectorAll('.child-promo'));
if (!parentPromo || childPromo.length === 0) return;
const syncParentFromChildren = () => {
const anyChecked = childPromo.some(cb => cb.checked);
parentPromo.checked = anyChecked; // 하나라도 체크되면 부모 체크
};
const syncChildrenFromParent = () => {
const isChecked = parentPromo.checked;
childPromo.forEach(cb => {
cb.checked = isChecked;
cb.dispatchEvent(new Event('change', { bubbles: true }));
});
};
syncParentFromChildren();
parentPromo.addEventListener('change', syncChildrenFromParent);
childPromo.forEach(cb => cb.addEventListener('change', syncParentFromChildren));
});
</script>
<!-- } 회원정보 입력/수정 끝 -->

View File

@ -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}

View File

@ -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}

View File

@ -340,11 +340,11 @@ add_stylesheet('<link rel="stylesheet" href="'.G5_SHOP_CSS_URL.'/style.css">', 0
}
}
// 재입고SMS 알림
// 재입고 알림
function popup_stocksms(it_id)
{
url = "<?php echo G5_SHOP_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);
}
</script>