Files
Gnuboard7/plugins/_bundled/sirsoft-pay_nhnkcp/dist/js/plugin.iife.js
T
HeuJung 9cf9c3ff8c fix(core,board,ecommerce,payments,basic): KVE-2026 보안 게이트 6묶음 + 자격증명 전송로 정합
KISA 제보 취약점(KVE-2026-1914/1919/2019/2029/2041/2042/2043/2044)과
그 수정 과정에서 드러난 자격증명 전송로 결함을 함께 해소한다.

globalHeaders 는 데이터소스와 apiCall 핸들러에만 적용되는데, 코어 ApiClient 를
직접 부르는 경로들이 그 사실을 모른 채 게이트된 엔드포인트를 호출하고 있었다.
서버는 정당한 사용자를 거부하고 화면은 이미 버튼을 내준 뒤라, 예외도 로그도 없이
그 자리만 비는 형태로만 드러났다. 전송로 10축을 전수 열거해 6건을 고치고,
같은 실수가 반복되지 않도록 규정과 coverage 에 등재했다.

아웃바운드 프록시가 사이트 자기 자신으로 가는 내부 요청까지 가로채 저장이 수십 초씩
걸리던 문제도 함께 고쳤다. 실패가 폴백으로 삼켜져 화면에는 지연으로만 나타났다.
2026-09-06 00:42:29 +09:00

18 lines
35 KiB
JavaScript

(function(){"use strict";function De(e){return/^https?:\/\//i.test(e)||e.startsWith("/api/")?e:e.startsWith("/plugins/")?`/api${e}`:e.startsWith("plugins/")?`/api/${e}`:e}function je(e){return e.trim().slice(0,160)}function Be(e){if(e)return e.replace(/\/payment\/close-report$/,"/payment/retry")}async function oe(e,t,n=!1){const r=window.G7Core?.api;if(typeof r?.post=="function"){await r.post(e,t);return}const o=await fetch(De(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),keepalive:n});if(!o.ok){let a=`NHN KCP payment request failed (${o.status})`;try{const s=await o.json(),i=Array.isArray(s?.errors?.message)?s.errors.message[0]:s?.error??s?.message;typeof i=="string"&&i.trim()!==""&&(a=i)}catch{}throw new Error(a)}}async function He(e){const t=Be(e.closeReportUrl);t&&await oe(t,{oid:e.oid,price:Number(e.price),buyer_email:e.buyer_email??"",buyer_phone:e.buyer_phone??"",payment_method:e.payment_method??""})}const T="g7:sirsoft-pay_nhnkcp:pendingClose";function M(){try{return window.sessionStorage??null}catch{return null}}function Fe(e){const t=M();if(t)try{t.setItem(T,JSON.stringify(e))}catch{}}function ae(){const e=M();if(e)try{e.removeItem(T)}catch{}}function Ge(){const e=M();if(!e)return null;try{const t=e.getItem(T);if(!t)return null;const n=JSON.parse(t);return n&&typeof n.oid=="string"&&n.oid!==""?n:null}catch{return null}}async function Ue(){const e=Ge();if(!e)return;let t;try{t=new URLSearchParams(window.location.search)}catch{return}const n=t.get("orderId")??"";if(n!==""&&n!==e.oid)return;if(/\/(complete|success)(\/|$|\?)/.test(window.location.pathname)){ae();return}const r=t.get("error")??"",o=t.get("message")??"";r===""&&n===""||(ae(),await se(e,o!==""?o:r))}async function se(e,t="kcp-window-closed"){if(!e.closeReportUrl)return;const n={oid:e.oid,price:Number(e.price),buyer_email:e.buyer_email??"",buyer_phone:e.buyer_phone??"",payment_method:e.payment_method??"",reason:je(t)||"kcp-window-closed"};try{await oe(e.closeReportUrl,n,!0)}catch(r){console.warn("[sirsoft-pay_nhnkcp] failed to report payment window close",r)}}const qe="nhnkcp_applepay";function R(e){return e===qe}function x(){if(typeof navigator>"u")return!1;const e=navigator,t=(e.userAgent||"").toLowerCase(),n=((e.userAgentData?.platform??e.platform)||"").toLowerCase(),r=e.maxTouchPoints??0;return/iphone|ipad|ipod/.test(t)||/iphone|ipad|ipod|ios/.test(n)?!0:/macintosh|mac os x/.test(t)&&r>1}function L(){return((typeof document<"u"?document.documentElement.lang:"")||"").toLowerCase().startsWith("ko")?"애플페이는 IOS 기기에 모바일결제만 가능합니다.":"Apple Pay is available only for mobile payments on iOS devices."}function O(e){return(e??"KRW").trim().toUpperCase()||"KRW"}function Ye(e){return O(e)==="KRW"}const We=["testpay.kcp.co.kr","pay.kcp.co.kr"];function ze(e,t){const n=window?.G7Core?.t;if(typeof n!="function")return t;const r=`sirsoft-pay_nhnkcp.${e}`,o=n(r);return typeof o=="string"&&o!==r?o:t}function Je(e){let t=null;try{t=new URL(e)}catch{t=null}if(t===null||t.protocol!=="https:"||!We.includes(t.hostname.toLowerCase()))throw new Error(ze("payment.error.sdk_url_untrusted","결제 모듈 주소가 올바르지 않아 결제를 진행할 수 없습니다."))}function Ve(e){const t=O(e);return((typeof document<"u"?document.documentElement.lang:"")||"").toLowerCase().startsWith("ko")?`NHN KCP는 KRW 결제만 지원합니다. (${t})`:`NHN KCP supports KRW payments only. (${t})`}function Qe(e){const t=Math.max(0,Math.round(Number(e.amount??0))),n=Math.min(Math.max(0,Math.round(Number(e.tax_free_amount??0))),t);if(n<=0)return{};const r=Math.max(0,Math.round(Number(e.tax_amount??0))),o=Math.max(0,Math.round(Number(e.vat_amount??0))),a=Math.max(0,t-n),s=a>0&&r>0&&o>0?Math.min(a,Math.round(a*(o/r))):0,i=a-s;return{tax_flag:"TG03",comm_tax_mny:String(i),comm_vat_mny:String(s),comm_free_mny:String(n)}}class $ extends Error{constructor(t){super(t??"결제가 취소되었습니다."),this.name="KcpCancelledError"}}const Xe={card:"100000000000",bank_transfer:"010000000000",virtual_account:"001000000000",mobile:"000010000000",bank:"010000000000",vbank:"001000000000",phone:"000010000000"},ie={nhnkcp_payco:{payco_direct:"Y"},nhnkcp_naverpay:{naverpay_direct:"Y"},nhnkcp_naverpay_point:{naverpay_direct:"Y",naverpay_point_direct:"Y"},nhnkcp_kakaopay:{kakaopay_direct:"Y"},nhnkcp_applepay:{applepay_direct:"Y"}};function Ze(e,t){const n=e.toLowerCase();return!t||!Object.prototype.hasOwnProperty.call(ie,n)?{}:{param_opt_1:n,nhnkcp_easy_pay_method:n}}function et(e){if(!e.isConnected)return!0;const t=window.getComputedStyle(e);if(t.display==="none"||t.visibility==="hidden")return!0;const n=e.getBoundingClientRect();return n.width<=0||n.height<=0}function tt(e,t){let n=!1,r,o;const a=()=>{n=!0,o&&(o.disconnect(),o=void 0),r!==void 0&&(window.clearInterval(r),r=void 0)},s=()=>{n||!et(e)||(a(),t())},i=document.body??document.documentElement;return i&&typeof MutationObserver<"u"&&(o=new MutationObserver(s),o.observe(i,{attributes:!0,attributeFilter:["class","hidden","style"],childList:!0,subtree:!0})),r=window.setInterval(s,300),a}function nt(){if(typeof navigator>"u")return!1;if(x())return!0;const e=navigator;if(e.userAgentData?.mobile!==void 0)return e.userAgentData.mobile;const t=(navigator.userAgent||"").toLowerCase();return!!(/android|iphone|ipod|windows phone|iemobile|blackberry|opera mini|mobile safari/.test(t)||(navigator.maxTouchPoints??0)>1&&!t.includes("windows")&&!t.includes("macintosh"))}async function rt(e,t){const{pgPaymentData:n,paymentMethod:r}=e.params||{};if(!n){console.error("[sirsoft-pay_nhnkcp] pgPaymentData is required");return}const o=r??n.payment_method??n.pay_method??"card",a=typeof o=="string"&&o.startsWith("nhnkcp_"),s=window.G7Core;let i=null;try{if(!Ye(n.currency)){const u=Ve(n.currency);s?.state?.setLocal?.({paymentErrorMessage:u,isSubmittingOrder:!1,paymentMethod:o}),s?.modal?.open?.("nhnkcp_payment_error_modal");return}if(R(o)&&!x()){const u=L();s?.state?.setLocal?.({paymentErrorMessage:u,isSubmittingOrder:!1,paymentMethod:o}),s?.modal?.open?.("nhnkcp_payment_error_modal");return}const c=await s.api.get("/modules/sirsoft-ecommerce/payments/client-config/nhnkcp");if(!c.data){console.error("[sirsoft-pay_nhnkcp] Failed to fetch client config",c);return}const d=c.data;i={closeReportUrl:d.callback_urls.close_report,oid:n.order_number,price:Number(n.amount),buyer_email:n.customer_email??"",buyer_phone:n.customer_phone??"",payment_method:o};const l=window.location.origin+d.callback_urls.callback;await He(i),Fe(i),nt()?await ot(s,n,o,a,l):await at(d,n,o,a,l)}catch(c){if(c instanceof $){i&&await se(i,c.message||"kcp-window-closed"),s?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:o});return}console.error("[sirsoft-pay_nhnkcp] requestPayment error",c);const l=c?.response?.data,u=l?.errors?Object.values(l.errors)[0]?.[0]:void 0,p=l?.error??l?.message??u??(c instanceof Error?c.message:"Unknown error");s?.state?.setLocal?.({paymentErrorMessage:p,isSubmittingOrder:!1,paymentMethod:o}),s?.modal?.open?.("nhnkcp_payment_error_modal")}}async function ot(e,t,n,r,o){const a=await e.api.post("/plugins/sirsoft-pay_nhnkcp/mobile/approval-key",{order_number:t.order_number,amount:t.amount,good_name:t.order_name,pay_method:n,buyr_name:t.customer_name??"",buyr_mail:t.customer_email??"",buyr_tel1:t.customer_phone??"",ret_url:o,currency:O(t.currency)});if(!a.success||!a.data){const d=a.errors,l=d?Object.values(d)[0]?.[0]:void 0;throw new Error(a.error??a.message??l??"KCP 모바일 승인키 획득 실패")}const{pay_url:s,fields:i}=a.data,c=document.createElement("form");c.method="POST",c.action=s,c.acceptCharset="euc-kr",c.style.display="none";for(const[d,l]of Object.entries(i)){const u=document.createElement("input");u.type="hidden",u.name=d,u.value=String(l),c.appendChild(u)}document.body.appendChild(c),c.submit(),await new Promise(()=>{})}async function at(e,t,n,r,o){const a=r?"100000000000":Xe[t.pay_method??"card"]??"100000000000",i={site_cd:n==="nhnkcp_payco"?e.easy_pay_client_id??e.client_id:e.use_escrow&&!r?e.escrow_client_id??e.client_id:e.client_id,ordr_idxx:t.order_number,good_name:t.order_name,good_mny:String(t.amount),buyr_name:t.customer_name??"",buyr_mail:t.customer_email??"",buyr_tel1:t.customer_phone??"",pay_method:a,Ret_URL:o};if(Object.assign(i,Qe(t)),Object.assign(i,Ze(n,r)),a==="001000000000"&&(i.vcnt_expire_term=String(e.vbank_expire_days??3),i.disp_tax_yn="N"),e.use_escrow&&!r&&(i.escw_used="Y",i.pay_mod="O"),r){i.payco_direct="",i.naverpay_direct="A",i.kakaopay_direct="A",i.applepay_direct="A";const d=ie[n];d&&Object.assign(i,d)}const c=Object.entries(i).map(([d,l])=>`<input type="hidden" name="${d}" value="${l.replace(/"/g,"&quot;")}">`).join("");Je(e.sdk_url),await new Promise((d,l)=>{document.getElementById("kcp-sdk-iframe")?.remove(),document.getElementById("kcp-dim-overlay")?.remove();const u=document.createElement("div");u.id="kcp-dim-overlay",u.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:99998;",document.body.appendChild(u);const p=document.createElement("iframe");p.id="kcp-sdk-iframe",p.setAttribute("allowtransparency","true"),p.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;border:0;z-index:99999;background:transparent;",document.body.appendChild(p);const f=p.contentWindow;let ee=!1,y,k;const gn=()=>{k?.(),k=void 0,y!==void 0&&(clearTimeout(y),y=void 0),p.remove(),u.remove()},A=m=>{ee||(ee=!0,gn(),l(m))},wn=()=>{ee=!0,k?.(),k=void 0,y!==void 0&&(clearTimeout(y),y=void 0)};k=tt(p,()=>{A(new $("kcp-payment-frame-closed"))}),f.m_Completepayment=function(m){const S=m.elements.res_cd?.value??"",ne=m.elements.res_msg?.value??"";if(S==="0000"){wn();const h=f.document.forms.namedItem("order_info"),C=h&&h!==m?h:m;if(h&&h!==m)for(let re=0;re<m.elements.length;re++){const I=m.elements[re];if(!I.name)continue;let w=h.elements.namedItem(I.name);w||(w=f.document.createElement("input"),w.type="hidden",w.name=I.name,h.appendChild(w)),w.value=I.value}C.action=o,C.method="POST",C.target="_top",f.document.body.appendChild(C),C.submit()}else{const h=S===""||S==="7777"||ne.includes("취소");A(h?new $(ne):new Error(`KCP 오류 [${S}]: ${ne}`))}},f.__kcpFail=m=>{A(m)};const te=p.contentDocument||f.document;te.open(),te.write(`<!DOCTYPE html><html><head>
<script src="${e.sdk_url}"><\/script>
</head><body style="margin:0;padding:0;background:transparent;">
<form name="order_info">${c}</form>
<script>
try {
if (typeof KCP_Pay_Execute === 'function') {
KCP_Pay_Execute(document.forms['order_info']);
window.__kcpReady && window.__kcpReady();
} else {
window.__kcpFail && window.__kcpFail(new Error('KCP_Pay_Execute not defined'));
}
} catch(e) {
window.__kcpFail && window.__kcpFail(e);
}
<\/script>
</body></html>`),te.close(),y=window.setTimeout(()=>{A(new Error("KCP SDK load timeout"))},15e3),f.__kcpReady=()=>{y!==void 0&&(clearTimeout(y),y=void 0)}})}const st={nhnkcp_payco:"PAYCO",nhnkcp_naverpay:"네이버페이",nhnkcp_naverpay_point:"네이버페이 포인트",nhnkcp_kakaopay:"카카오페이",nhnkcp_applepay:"Apple Pay"},it="0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)";function ce(){return document.querySelector(".nhnkcp-easy-pay-btns")??null}function de(){const e=ce();e&&e.querySelectorAll("button").forEach(t=>{t.style.boxShadow="",t.style.outline=""})}function ct(e){const t=ce();if(!t)return;const n=st[e];t.querySelectorAll("button").forEach(r=>{r.textContent?.trim()===n?(r.style.boxShadow=it,r.style.outline="none"):(r.style.boxShadow="",r.style.outline="")})}function dt(e){const t=e.params?.paymentMethod;if(!t)return;const n=window.G7Core;if(R(t)&&!x()){const o=L();n?.state?.setLocal?.({paymentErrorMessage:o,isSubmittingOrder:!1}),n?.modal?.open?.("nhnkcp_payment_error_modal"),de();return}const r=typeof t=="string"&&t.startsWith("nhnkcp_");n?.state?.setLocal?.({paymentMethod:t}),r?ct(t):de()}function lt(e){const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly","readonly"),t.style.position="fixed",t.style.left="-9999px",t.style.top="0",document.body.appendChild(t),t.select();try{document.execCommand("copy")}finally{t.remove()}}async function ut(e){const t=String(e.params?.text??"");if(t!==""){if(navigator.clipboard&&typeof navigator.clipboard.writeText=="function"){await navigator.clipboard.writeText(t);return}lt(t)}}const le={requestPayment:rt,setPaymentMethod:dt,copyToClipboard:ut},ue="/api/modules/sirsoft-ecommerce/user/orders",K="sirsoft-pay_nhnkcp",pe={info:(...e)=>console.info(`[${K}]`,...e),warn:(...e)=>console.warn(`[${K}]`,...e),error:(...e)=>console.error(`[${K}]`,...e)};function pt(e){return typeof e=="string"?e:e instanceof URL?e.toString():typeof Request<"u"&&e instanceof Request?e.url:String(e)}function mt(e,t){return t?.method?t.method.toUpperCase():typeof Request<"u"&&e instanceof Request?e.method.toUpperCase():"GET"}function ft(e,t){if(t!=="POST")return!1;const n=e.split("?")[0].split("#")[0];return n===ue||n.endsWith(ue)}function yt(e){try{return JSON.parse(e).payment_method}catch{return}}function ht(e){return new Response(JSON.stringify({success:!1,message:e,error:e,errors:{payment_method:[e]}}),{status:422,statusText:"Unprocessable Content",headers:{"Content-Type":"application/json"}})}function _t(){if(typeof window>"u"||typeof window.fetch!="function")return;const e="__sirsoftNhnkcpInterceptorInstalled",t=window;if(t[e])return;t[e]=!0;const n=window.fetch.bind(window);window.fetch=async function(o,a){const s=pt(o),i=mt(o,a);if(!ft(s,i))return n(o,a);let c;if(a?.body&&typeof a.body=="string"&&(c=yt(a.body)),R(c)&&!x()){const d=L();return pe.warn(d),ht(d)}return n(o,a)},pe.info("order request interceptor installed (Apple Pay device guard)")}function gt(){return localStorage.getItem("auth_token")}function wt(){try{const t=sessionStorage.getItem("g7_guest_order_token");if(t)return t}catch{}const e=window.G7Core?.state?.get?.("_global")?.guestOrderToken;return typeof e=="string"&&e!==""?e:null}function v(){const e=gt(),t=wt();if(!e&&!t)return null;const n={Accept:"application/json"};return e?n.Authorization=`Bearer ${e}`:t&&(n["X-Guest-Order-Token"]=t),n}const D="sirsoft-pay_nhnkcp",me="__kcpOcReceiptInjectorInstalled",fe="kcp-oc-receipt-btn",bt=/^\/shop\/orders\/([^/]+)\/complete$/;async function kt(e){const t=v();if(!t)return null;try{const n=await fetch(`/api/modules/sirsoft-ecommerce/user/orders/${e}`,{headers:t,credentials:"same-origin"});return n.ok?(await n.json())?.data?.payment??null:null}catch{return null}}async function j(e){const t=v();if(!t)return null;try{const n=await fetch(`/api/plugins/${D}/user/orders/${e}/receipt`,{headers:t,credentials:"same-origin"});return n.ok?await n.json():null}catch{return null}}function ye(e,t,n){const r=document.createElement("button");return r.type="button",r.className=t,r.textContent=e,r.addEventListener("click",()=>n(r)),r}function Ct(e){if(!e)return!1;const t=Array.from(document.querySelectorAll("div"));for(const n of t){const r=Array.from(n.children).filter(s=>s instanceof HTMLElement&&s.tagName==="SPAN");if(r.length<2)continue;const o=r[0].textContent?.trim();if(o!=="결제 방법"&&o!=="결제수단"&&o!=="결제 방식")continue;const a=r[r.length-1];return a.textContent?.trim()===e?(n.dataset.nhnkcpPaymentMethodRow="true",!0):(a.textContent=e,a.dataset.nhnkcpPaymentMethodPatched="true",n.dataset.nhnkcpPaymentMethodRow="true",!0)}return!1}async function xt(e){if(document.getElementById(fe))return;const t=await kt(e);if(!t||t.pg_provider!=="nhnkcp"||!t.transaction_id||!t.paid_at)return;const n=Array.from(document.querySelectorAll('button[type="button"]')).find(c=>c.className.includes("bg-blue-600"));if(!n?.parentElement)return;const r=await j(e);Ct(r?.payment_method_display_label);const o=n.parentElement,a=ye("영수증 조회",n.className.replace(/bg-blue-\d+/g,"bg-green-600").replace(/hover:bg-blue-\d+/g,"hover:bg-green-700"),async c=>{c.disabled=!0,c.textContent="로딩 중...";const d=await j(e);if(c.disabled=!1,c.textContent="영수증 조회",d){const l=window.KcpReceiptPopup;l&&new l({url:d.receipt_url??void 0})}});a.id=fe;const s=o.lastElementChild;o.insertBefore(a,s);const i="kcp-oc-cash-receipt-btn";if(!document.getElementById(i)&&(t.payment_method==="vbank"||t.payment_method==="bank_transfer")){const c=ye("현금영수증 조회",a.className.replace(/bg-green-\d+/g,"bg-teal-600").replace(/hover:bg-green-\d+/g,"hover:bg-teal-700"),async d=>{d.disabled=!0,d.textContent="로딩 중...";const l=await j(e);if(d.disabled=!1,d.textContent="현금영수증 조회",l){const u=window.KcpReceiptPopup;u&&new u({cash_url:l.cash_receipt_url??void 0})}});c.id=i,o.insertBefore(c,s)}console.info(`[${D}] receipt button injected on order complete page`)}function vt(){const t=location.pathname.match(bt);t&&xt(t[1])}function Pt(){if(typeof window>"u")return;const e=window;if(e[me])return;e[me]=!0,console.info(`[${D}] order complete receipt injector installed`);const t=(r=1200)=>setTimeout(vt,r);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...r)=>{n(...r),t()},window.addEventListener("popstate",()=>t(500))}const _="sirsoft-pay_nhnkcp",he="__kcpMpShowInjectorInstalled",B="kcp-mp-vbank-row",H="kcp-mp-receipt-row",P="kcp-mp-mock-deposit",Et=/^(?:\/mypage\/orders\/([^/]+)|\/shop\/guest\/orders\/([^/]+))$/;function Nt(e){try{const n=window.G7Core?.getState,a=n?.()?.currentDataContext?.order?.data;return!a||a.order_number!==e?null:a}catch{return null}}async function _e(e){const t=v();if(!t)return null;try{const n=await fetch(`/api/plugins/${_}/user/orders/${e}/receipt`,{headers:t,credentials:"same-origin"});return n.ok?await n.json():null}catch{return null}}async function ge(e){const t=v();if(!t)return null;try{const n=await fetch(`/api/plugins/${_}/user/orders/${e}/vbank-mock-deposit-info`,{headers:t,credentials:"same-origin"});return n.ok?await n.json():null}catch{return null}}function we(e){const t=document.createElement("div");t.id=P,t.className="pt-4 mt-2 border-t border-dashed border-orange-300 dark:border-orange-700";const n=document.createElement("div");n.className="flex items-center gap-2 mb-3";const r=document.createElement("span");r.className="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-300 border border-orange-300 dark:border-orange-700",r.textContent="TEST";const o=document.createElement("p");o.className="text-sm font-semibold text-orange-700 dark:text-orange-300",o.textContent="모의입금처리",n.appendChild(r),n.appendChild(o);const a=document.createElement("form");a.method="POST",a.action=e.mock_url??"",a.target="_blank",a.className="space-y-2";const s=[{label:"KCP 거래번호",name:"e_trade_no",value:e.trade_no??""},{label:"입금계좌",name:"deposit_no",value:e.account_no??""},{label:"입금자명",name:"req_name",value:"NHN KCP",editable:!0},{label:"입금통보 URL",name:"noti_url",value:e.notify_url??""}];for(const d of s){const l=document.createElement("div");l.className="flex items-center gap-2 text-sm";const u=document.createElement("span");u.className="w-28 flex-shrink-0 text-gray-500 dark:text-gray-400 text-xs",u.textContent=d.label;const p=document.createElement("input");p.type="text",p.name=d.name,p.value=d.value,p.readOnly=!d.editable,p.className=d.editable?"flex-1 px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono":"flex-1 px-2 py-1 text-xs border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 font-mono",l.appendChild(u),l.appendChild(p),a.appendChild(l)}const i=document.createElement("div");i.className="pt-1";const c=document.createElement("button");if(c.type="submit",c.className="w-full py-1.5 text-xs font-semibold rounded border border-orange-400 dark:border-orange-600 bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300 hover:bg-orange-100 dark:hover:bg-orange-900/50 transition-colors",c.textContent="입금통보 테스트",i.appendChild(c),a.appendChild(i),t.appendChild(n),t.appendChild(a),e.is_admin_view){const d=document.createElement("p");d.className="mt-2 text-xs text-center text-orange-500 dark:text-orange-400 font-medium",d.textContent="관리자에게만 출력되는 내용",t.appendChild(d)}return t}function be(){const e=document.getElementById("order_payment_info_panel");if(e)return Array.from(e.children).find(a=>a.className?.includes("space-y"))??e;const t=Array.from(document.querySelectorAll("h3")).find(o=>o.textContent?.includes("결제 정보"));if(!t)return null;const n=t.parentElement?.parentElement;return n?Array.from(n.children).find(o=>o.className?.includes("space-y"))??n:null}function b(e,t,n="font-medium text-gray-900 dark:text-white"){const r=document.createElement("div");r.className="flex items-center justify-between text-sm";const o=document.createElement("span");o.className="text-gray-500 dark:text-gray-400",o.textContent=e;const a=document.createElement("span");return a.className=n,a.textContent=t,r.appendChild(o),r.appendChild(a),r}function At(e,t){if(!t)return!1;const n=Array.from(e.querySelectorAll("div"));for(const r of n){const o=Array.from(r.children).filter(i=>i instanceof HTMLElement&&i.tagName==="SPAN");if(o.length<2)continue;const a=o[0].textContent?.trim();if(a!=="결제 방법"&&a!=="결제수단"&&a!=="Payment Method")continue;const s=o[o.length-1];return s.textContent?.trim()===t?(r.dataset.nhnkcpPaymentMethodRow="true",!0):(s.textContent=t,s.dataset.nhnkcpPaymentMethodPatched="true",r.dataset.nhnkcpPaymentMethodRow="true",!0)}return!1}function St(e,t){const n=document.createElement("div");n.id=B,n.className="pt-4 mt-2 border-t border-gray-200 dark:border-gray-700 space-y-3";const r=document.createElement("p");r.className="text-sm font-semibold text-gray-800 dark:text-gray-200",r.textContent="가상계좌 입금 안내";const o=document.createElement("div");o.className="space-y-1.5",o.appendChild(b("은행",e.vbank_name??"")),o.appendChild(b("계좌번호",e.vbank_number??"","font-medium text-gray-900 dark:text-white font-mono tracking-wide")),o.appendChild(b("예금주",e.vbank_holder??"")),o.appendChild(b("입금 금액",t)),e.due_date_formatted&&o.appendChild(b("입금 기한",e.due_date_formatted));const a=document.createElement("p");return a.className="text-xs text-amber-600 dark:text-amber-400",a.textContent="입금 기한 내에 입금하지 않으시면 주문이 자동 취소됩니다.",n.appendChild(r),n.appendChild(o),n.appendChild(a),n}function It(e){const t=document.createElement("div");t.id=H,t.className="pt-4 mt-2 border-t border-gray-200 dark:border-gray-700";const n=document.createElement("div");n.className="flex items-center justify-between";const r=document.createElement("span");r.className="text-gray-500 dark:text-gray-400 text-sm",r.textContent="영수증";const o=document.createElement("button");return o.type="button",o.className="inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",o.textContent="영수증 조회",o.addEventListener("click",async()=>{o.disabled=!0,o.textContent="로딩 중...";const a=await _e(e);if(o.disabled=!1,o.textContent="영수증 조회",a){const s=window.KcpReceiptPopup;s&&new s({url:a.receipt_url,cash_url:a.cash_receipt_url})}}),n.appendChild(r),n.appendChild(o),t.appendChild(n),t}async function Tt(e){const t=Nt(e);if(!t){if(!document.getElementById(P)){const p=be();if(!p)return!1;const f=await ge(e);f?.available&&(p.appendChild(we(f)),console.info(`[${_}] mock deposit form injected on mypage order show`))}return!0}const{payment:n}=t;if(!n||n.pg_provider!=="nhnkcp")return!0;const r=n.payment_method==="vbank"&&!!n.vbank_name,o=!!n.transaction_id&&!!n.paid_at,a=n.payment_method==="vbank"&&!!n.vbank_number,s=!r||!!document.getElementById(B),i=!o||!!document.getElementById(H),c=!a||!!document.getElementById(P);if(s&&i&&c)return!0;const d=be();if(!d)return!1;const l=o?await _e(e):null,u=At(d,l?.payment_method_display_label);if(!document.getElementById(B)&&r)return d.appendChild(St(n,t.total_amount_formatted??"")),console.info(`[${_}] vbank info injected on mypage order show`),!1;if(!document.getElementById(H)&&o)return d.appendChild(It(e)),console.info(`[${_}] receipt button injected on mypage order show`),!1;if(u&&console.info(`[${_}] payment method display patched on mypage order show`),!document.getElementById(P)&&a){const p=await ge(e);if(p?.available)d.appendChild(we(p)),console.info(`[${_}] mock deposit form injected on mypage order show`);else return!0}return!0}function Mt(e){let t=0;const n=setInterval(()=>{t++,Tt(e).then(r=>{(r||t>=30)&&clearInterval(n)})},400)}function Rt(){const e=location.pathname.match(Et),t=e?.[1]??e?.[2];t&&Mt(t)}function Lt(){if(typeof window>"u")return;const e=window;if(e[he])return;e[he]=!0,console.info(`[${_}] mypage order show injector installed`);const t=(r=1500)=>setTimeout(Rt,r);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...r)=>{n(...r),t(600)},window.addEventListener("popstate",()=>t(500))}const Ot="sirsoft-pay_nhnkcp",ke="__kcpAdminApplePayNoticeInjectorInstalled",$t=/^\/admin\/ecommerce\/settings\/?$/,Ce="__kcpAdminApplePayNoticeListenerAttached",Kt="애플페이는 IOS 기기에 모바일결제만 가능합니다.",Dt="Apple Pay is available only for mobile payments on iOS devices.";let xe=null,E=null,F=!1;function jt(){return window}function G(){if(!$t.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e==="order_settings"}function ve(){return(document.documentElement.lang||navigator.language||"").toLowerCase().startsWith("ko")?Kt:Dt}function Pe(e){return(e??"").replace(/\s+/g," ").trim()}function Bt(e){const t=Pe(e.textContent);return t==="애플페이"||t==="Apple Pay"}function Ht(){const e=document.createElement("span");return e.dataset.nhnkcpAdminApplePayNotice="true",e.className="text-label-subtle",e.style.display="block",e.style.width="100%",e.style.flexBasis="100%",e.style.marginTop="2px",e.style.fontSize="12px",e.style.lineHeight="1rem",e.textContent=ve(),e}function U(e=document){if(typeof window>"u"||typeof document>"u"||!G()||Pe(document.body?.innerText??document.body?.textContent).includes(ve()))return!1;const n=Array.from(e.querySelectorAll("span, p, div")).find(Bt);return n?(n.insertAdjacentElement("afterend",Ht()),!0):!1}function Ft(){F||(F=!0,window.setTimeout(()=>{F=!1,U()},0))}function Ee(){E!==null&&(window.clearInterval(E),E=null)}function q(){if(!G())return;Ee(),U();let e=0;E=window.setInterval(()=>{e+=1,(U()||e>=50)&&Ee()},200);const t=document.body;t[Ce]||(t[Ce]=!0,xe=new MutationObserver(()=>Ft()),xe.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function Ne(){G()&&q()}function Gt(){if(typeof window>"u"||typeof document>"u")return;const e=jt();if(e[ke])return;e[ke]=!0,console.info(`[${Ot}] admin Apple Pay notice injector installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>q()):q();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>Ne(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>Ne(),200))}const Ut="sirsoft-pay_nhnkcp",Ae="__nhnkcpAdminPaymentMethodBrandInjectorInstalled",Se="__nhnkcpAdminPaymentMethodBrandSyncListenerAttached",qt=/^\/admin\/ecommerce\/settings\/?$/,Ie='[data-nhnkcp-admin-payment-brand-mark="true"]',Yt=200,Wt=120,zt=[{id:"nhnkcp_payco",labels:["PAYCO (NHN KCP)","PAYCO로 결제 (NHN KCP)","Pay with PAYCO (NHN KCP)"],shortLabels:["PAYCO"],markLines:["P"],markClassName:"bg-red-500 text-white"},{id:"nhnkcp_naverpay",labels:["네이버페이 (NHN KCP)","네이버페이 신용카드로 결제 (NHN KCP)","Naver Pay (NHN KCP)","Pay by Naver Pay credit card (NHN KCP)"],shortLabels:["네이버페이 (카드)","Naver Pay (Card)"],markLines:["N"],markClassName:"bg-green-500 text-white"},{id:"nhnkcp_naverpay_point",labels:["네이버페이 포인트 (NHN KCP)","네이버페이 머니/포인트로 결제 (NHN KCP)","Naver Pay Point (NHN KCP)","Pay with Naver Pay Money/Points (NHN KCP)"],shortLabels:["네이버페이 (포인트)","Naver Pay (Point)"],markLines:["NP"],markClassName:"bg-green-600 text-white"},{id:"nhnkcp_kakaopay",labels:["카카오페이 (NHN KCP)","카카오페이로 결제 (NHN KCP)","Kakao Pay (NHN KCP)","Pay with Kakao Pay (NHN KCP)"],shortLabels:["카카오페이","Kakao Pay"],markLines:["K"],markClassName:"bg-yellow-400 text-gray-950"},{id:"nhnkcp_applepay",labels:["애플페이 (NHN KCP)","애플페이로 결제 (NHN KCP)","Apple Pay (NHN KCP)","Pay with Apple Pay (NHN KCP)"],shortLabels:["애플페이","Apple Pay"],markLines:["A"],markClassName:"bg-gray-900 text-white"}];let Te=null,N=null,Y=!1;function Jt(){return window}function W(){if(!qt.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e===""||e==="order_settings"}function Vt(e){return e instanceof HTMLElement?e.className:""}function Me(e){return(e??"").replace(/\u200B/g,"").replace(/\s+/g," ").trim()}function Qt(e){const t=Vt(e);return t.includes("excel-card")||t.includes("flex-center")&&t.includes("border")&&t.includes("gap-4")}function Re(e){return e.querySelector(".font-medium")}function Xt(e){const t=Me(Re(e)?.textContent),n=Me(e.textContent);return zt.find(r=>r.shortLabels.some(o=>t===o)||r.labels.some(o=>n.includes(o)))??null}function Zt(e){return Array.from(e.querySelectorAll("div")).filter(Qt)}function en(e){return Array.from(e.querySelectorAll("svg, [data-icon], i")).find(n=>!n.closest("[data-drag-handle]")&&!n.closest(Ie)&&!n.closest('.row-stack, .excel-card-body, button, select, [role="switch"]'))??null}function Le(e,t){e.dataset.nhnkcpAdminPaymentBrandMark="true",e.dataset.nhnkcpAdminPaymentMethod=t.id,e.setAttribute("aria-hidden","true"),e.className=`inline-flex items-center justify-center rounded-lg font-bold ${t.markClassName}`,e.style.width="32px",e.style.height="32px",e.style.flex="0 0 32px",e.style.lineHeight="1",e.style.fontSize=t.markLines.join("").length>2?"9px":"12px";const n=t.markLines.join("");e.textContent!==n&&(e.replaceChildren(),t.markLines.length>1?(e.style.flexDirection="column",t.markLines.forEach(r=>{const o=document.createElement("span");o.style.lineHeight="1",o.textContent=r,e.appendChild(o)})):(e.style.removeProperty("flex-direction"),e.textContent=t.markLines[0]??""))}function tn(e){const t=document.createElement("span");return Le(t,e),t}function nn(e,t){const n=e.querySelector(Ie);if(n instanceof HTMLSpanElement)return e.dataset.nhnkcpAdminPaymentMethod=t.id,Le(n,t),!0;const r=tn(t),o=en(e);if(o&&o.parentElement)return e.dataset.nhnkcpAdminPaymentMethod=t.id,o.replaceWith(r),!0;const a=Re(e);return a&&a.parentElement?(e.dataset.nhnkcpAdminPaymentMethod=t.id,a.parentElement.insertBefore(r,a),!0):!1}function z(e=document){if(typeof window>"u"||typeof document>"u"||!W())return!1;let t=!1;return Zt(e).forEach(n=>{const r=Xt(n);r&&nn(n,r)&&(t=!0)}),t}function rn(){Y||(Y=!0,window.setTimeout(()=>{Y=!1,z()},0))}function J(){N!==null&&(window.clearInterval(N),N=null)}function V(){if(!W())return;J(),z();let e=0;N=window.setInterval(()=>{e+=1,z(),e>=Wt&&J()},Yt);const t=document.body;t[Se]||(t[Se]=!0,Te=new MutationObserver(()=>rn()),Te.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function Q(){if(W()){V();return}J()}function on(){if(typeof window>"u"||typeof document>"u")return;const e=Jt();if(e[Ae])return;e[Ae]=!0,console.info(`[${Ut}] admin payment method brand sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>V()):V();const t=history.pushState.bind(history);history.pushState=(...r)=>{t(...r),window.setTimeout(()=>Q(),200)};const n=history.replaceState.bind(history);history.replaceState=(...r)=>{n(...r),window.setTimeout(()=>Q(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>Q(),200))}const X="sirsoft-pay_nhnkcp",Oe="__nhnkcpAdminPaymentDisplayInjectorInstalled",an=/^\/admin\/ecommerce\/orders\/([^/]+)$/;function sn(){return localStorage.getItem("auth_token")}async function cn(e){const t=sn();if(!t)return null;try{const n=await fetch(`/api/plugins/${X}/admin/orders/${e}/transaction-status`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});if(!n.ok)return null;const r=await n.json();return("data"in r?r.data:r)??null}catch{return null}}function dn(e){return Array.from(e.children).filter(t=>t instanceof HTMLElement)}function ln(e,t){let n=!1;const r=Array.from(e.querySelectorAll("div"));for(const o of r){const a=dn(o),s=a.findIndex(u=>u.textContent?.trim()==="결제수단");if(s<0)continue;const i=a.slice(s+1).find(u=>u.textContent?.trim()&&u.textContent.trim()!=="결제수단");if(!i)continue;const c=a.slice(s+2).find(u=>/^\(.+\)$/.test(u.textContent?.trim()??"")),d=c?.textContent?.trim().replace(/^\(|\)$/g,"")??"",l=d&&t.endsWith(")")?t.replace(/\)$/,`, ${d})`):t;i.textContent?.trim()!==l&&(i.textContent=l,i.dataset.nhnkcpPaymentMethodPatched="true"),o.dataset.nhnkcpPaymentMethodRow="admin-order",c&&d&&(c.textContent="",c.style.display="none",c.dataset.nhnkcpPaymentInstallmentHidden="true"),n=!0}return n}function un(e,t,n){const o=Array.from(e.querySelectorAll("span")).filter(a=>a.textContent?.trim()===t).find(a=>{const s=typeof a.className=="string"?a.className:"";return s.includes("rounded-full")||s.includes("font-medium")});return o?(o.textContent=n,o.dataset.nhnkcpPaymentMethodPatched="true",o.dataset.nhnkcpPaymentMethodBadge="admin-order",!0):!1}function pn(e,t){const n=t?._pay_method_label,r=t?._embedded_pg_provider_label,o=t?._base_pay_method_label;if(!n||!r)return!1;const a=ln(e,n),s=o?un(e,o,r):!1;return a||s}async function mn(e){const t=document.getElementById("section_payment_info")??document.getElementById("payment_info_card");if(!t)return!1;const n=await cn(e);return n&&pn(t,n)&&console.info(`[${X}] payment method display patched on admin order detail`),!0}function fn(e){let t=0;const n=setInterval(()=>{t++,mn(e).then(r=>{(r||t>=30)&&clearInterval(n)})},400)}function yn(){const e=location.pathname.match(an);e&&fn(e[1])}function hn(){if(typeof window>"u")return;const e=window;if(e[Oe])return;e[Oe]=!0,console.info(`[${X}] admin order payment display injector installed`);const t=(r=1500)=>setTimeout(yn,r);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...r)=>{n(...r),t(600)},window.addEventListener("popstate",()=>t(500))}class _n{constructor(t){const n="width=800,height=600,scrollbars=yes,resizable=yes,toolbar=no,menubar=no";t.url&&window.open(t.url,"kcp_receipt",n),t.cash_url&&window.open(t.cash_url,"kcp_cash_receipt",n)}}window.KcpReceiptPopup=_n;const g="sirsoft-pay_nhnkcp",Z={info:(...e)=>console.info(`[${g}]`,...e),warn:(...e)=>console.warn(`[${g}]`,...e),error:(...e)=>console.error(`[${g}]`,...e)};function $e(){const e=window.G7Core;if(!e)return 0;const t=e.getActionDispatcher;if(typeof t!="function")return 0;const n=t();if(!n||typeof n.registerHandler!="function")return 0;let r=0;for(const[o,a]of Object.entries(le)){const s=`${g}.${o}`;n.registerHandler(s,a,{category:"plugin",source:g}),r++}return r}function Ke(){Ue();const e=()=>{const t=$e();if(t>0){Z.info(`${t} handler(s) registered`);return}let n=0;const r=50,o=setInterval(()=>{n++;const a=$e();if(a>0){clearInterval(o),Z.info(`${a} handler(s) registered (after ${n} retries)`);return}n>=r&&(clearInterval(o),Z.warn("ActionDispatcher not available after timeout"))},100)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}_t(),Pt(),Lt(),Gt(),on(),hn(),Ke(),window.__SirsoftNhnkcp={identifier:g,handlers:Object.keys(le),initPlugin:Ke}})();