Files
Gnuboard7/plugins/_bundled/sirsoft-pay_nhnkcp/dist/js/plugin.iife.js
T
HeuJung 1e89335258 fix(core,extensions): 동적 스크립트 주입 경로 전부에 출처 게이트 적용
레이아웃 scripts[] 에만 있던 원격 스크립트 차단 게이트가 다른 주입 경로에는
없어, 저장측(SafeLayoutExpressions·NoExternalUrls)이 외부 URL 저장을 422 로
막아도 런타임 디스패치 한 번으로 임의 원격 코드가 로드됐다. 저장 검증이
통째로 무의미해지는 상태였고 오류도 경고도 남지 않았다.

판정식을 support/scriptSrcPolicy.ts 로 분리해 런타임 SSoT 를 하나로 두고,
loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · 편집기 자산 매니페스트 ·
G7Core.asset.loadScript · PG 플러그인 SDK 주입이 모두 그것을 경유하게 했다.
사본을 두지 않은 이유는 차집합이 그대로 우회로가 되기 때문이다.

- 게이트는 캐시 검사보다 앞에 둔다 (뒤에 두면 이미 로드된 미신뢰 스크립트가
 캐시 히트로 통과한다)
- NoExternalUrls 순회를 9키로 확대 — 번들 레이아웃 601개 사전 스캔으로
 정당한 외부 URL 0건을 확인한 뒤 넓혔다 (저장 회귀 없음)
- callExternal 은 생성자를 참조 동일성으로 거부하고(별칭 전역 포함),
 프로토타입 경로 세그먼트를 읽기·매핑 키 양쪽에서 차단한다
- PG SDK 는 서비스 SDK 라 자체 호스팅이 불가능하므로 manifest 선언 +
 주입 직전 호스트 확인이 게이트다. tosspayments SDK URL 은 확장자가 없어
 정적 검사에 걸리지 않아 그 런타임 확인이 유일한 게이트다
- 함께 드러난 결함: 동시 loadScript 가 로드 전에 완료 처리되던 문제,
 확장 재로드에서 script 기존재가 CSS 까지 건너뛰던 문제, loadCSS 의
 in-flight 미공유
2026-09-02 21:43:13 +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 Be(e){return e.trim().slice(0,160)}function He(e){if(e)return e.replace(/\/payment\/close-report$/,"/payment/retry")}async function oe(e,t,n=!1){const o=window.G7Core?.api;if(typeof o?.post=="function"){await o.post(e,t);return}const r=await fetch(De(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),keepalive:n});if(!r.ok){let a=`NHN KCP payment request failed (${r.status})`;try{const s=await r.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 Fe(e){const t=He(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 I="g7:sirsoft-pay_nhnkcp:pendingClose";function M(){try{return window.sessionStorage??null}catch{return null}}function Ue(e){const t=M();if(t)try{t.setItem(I,JSON.stringify(e))}catch{}}function re(){const e=M();if(e)try{e.removeItem(I)}catch{}}function qe(){const e=M();if(!e)return null;try{const t=e.getItem(I);if(!t)return null;const n=JSON.parse(t);return n&&typeof n.oid=="string"&&n.oid!==""?n:null}catch{return null}}async function Ge(){const e=qe();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)){re();return}const o=t.get("error")??"",r=t.get("message")??"";o===""&&n===""||(re(),await ae(e,r!==""?r:o))}async function ae(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:Be(t)||"kcp-window-closed"};try{await oe(e.closeReportUrl,n,!0)}catch(o){console.warn("[sirsoft-pay_nhnkcp] failed to report payment window close",o)}}const Ye="nhnkcp_applepay";function T(e){return e===Ye}function x(){if(typeof navigator>"u")return!1;const e=navigator,t=(e.userAgent||"").toLowerCase(),n=((e.userAgentData?.platform??e.platform)||"").toLowerCase(),o=e.maxTouchPoints??0;return/iphone|ipad|ipod/.test(t)||/iphone|ipad|ipod|ios/.test(n)?!0:/macintosh|mac os x/.test(t)&&o>1}function R(){return((typeof document<"u"?document.documentElement.lang:"")||"").toLowerCase().startsWith("ko")?"애플페이는 IOS 기기에 모바일결제만 가능합니다.":"Apple Pay is available only for mobile payments on iOS devices."}function L(e){return(e??"KRW").trim().toUpperCase()||"KRW"}function We(e){return L(e)==="KRW"}const ze=["testpay.kcp.co.kr","pay.kcp.co.kr"];function Je(e,t){const n=window?.G7Core?.t;if(typeof n!="function")return t;const o=`sirsoft-pay_nhnkcp.${e}`,r=n(o);return typeof r=="string"&&r!==o?r:t}function Ve(e){let t=null;try{t=new URL(e)}catch{t=null}if(t===null||t.protocol!=="https:"||!ze.includes(t.hostname.toLowerCase()))throw new Error(Je("payment.error.sdk_url_untrusted","결제 모듈 주소가 올바르지 않아 결제를 진행할 수 없습니다."))}function Qe(e){const t=L(e);return((typeof document<"u"?document.documentElement.lang:"")||"").toLowerCase().startsWith("ko")?`NHN KCP는 KRW 결제만 지원합니다. (${t})`:`NHN KCP supports KRW payments only. (${t})`}function Xe(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 o=Math.max(0,Math.round(Number(e.tax_amount??0))),r=Math.max(0,Math.round(Number(e.vat_amount??0))),a=Math.max(0,t-n),s=a>0&&o>0&&r>0?Math.min(a,Math.round(a*(r/o))):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 Ze={card:"100000000000",bank_transfer:"010000000000",virtual_account:"001000000000",mobile:"000010000000",bank:"010000000000",vbank:"001000000000",phone:"000010000000"},se={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 et(e,t){const n=e.toLowerCase();return!t||!Object.prototype.hasOwnProperty.call(se,n)?{}:{param_opt_1:n,nhnkcp_easy_pay_method:n}}function tt(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 nt(e,t){let n=!1,o,r;const a=()=>{n=!0,r&&(r.disconnect(),r=void 0),o!==void 0&&(window.clearInterval(o),o=void 0)},s=()=>{n||!tt(e)||(a(),t())},i=document.body??document.documentElement;return i&&typeof MutationObserver<"u"&&(r=new MutationObserver(s),r.observe(i,{attributes:!0,attributeFilter:["class","hidden","style"],childList:!0,subtree:!0})),o=window.setInterval(s,300),a}function ot(){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:o}=e.params||{};if(!n){console.error("[sirsoft-pay_nhnkcp] pgPaymentData is required");return}const r=o??n.payment_method??n.pay_method??"card",a=typeof r=="string"&&r.startsWith("nhnkcp_"),s=window.G7Core;let i=null;try{if(!We(n.currency)){const u=Qe(n.currency);s?.state?.setLocal?.({paymentErrorMessage:u,isSubmittingOrder:!1,paymentMethod:r}),s?.modal?.open?.("nhnkcp_payment_error_modal");return}if(T(r)&&!x()){const u=R();s?.state?.setLocal?.({paymentErrorMessage:u,isSubmittingOrder:!1,paymentMethod:r}),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:r};const l=window.location.origin+d.callback_urls.callback;await Fe(i),Ue(i),ot()?await at(s,n,r,a,l):await st(d,n,r,a,l)}catch(c){if(c instanceof $){i&&await ae(i,c.message||"kcp-window-closed"),s?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:r});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:r}),s?.modal?.open?.("nhnkcp_payment_error_modal")}}async function at(e,t,n,o,r){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:r,currency:L(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 st(e,t,n,o,r){const a=o?"100000000000":Ze[t.pay_method??"card"]??"100000000000",i={site_cd:n==="nhnkcp_payco"?e.easy_pay_client_id??e.client_id:e.use_escrow&&!o?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:r};if(Object.assign(i,Xe(t)),Object.assign(i,et(n,o)),a==="001000000000"&&(i.vcnt_expire_term=String(e.vbank_expire_days??3),i.disp_tax_yn="N"),e.use_escrow&&!o&&(i.escw_used="Y",i.pay_mod="O"),o){i.payco_direct="",i.naverpay_direct="A",i.kakaopay_direct="A",i.applepay_direct="A";const d=se[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("");Ve(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 Z=!1,y,k;const _n=()=>{k?.(),k=void 0,y!==void 0&&(clearTimeout(y),y=void 0),p.remove(),u.remove()},N=m=>{Z||(Z=!0,_n(),l(m))},gn=()=>{Z=!0,k?.(),k=void 0,y!==void 0&&(clearTimeout(y),y=void 0)};k=nt(p,()=>{N(new $("kcp-payment-frame-closed"))}),f.m_Completepayment=function(m){const A=m.elements.res_cd?.value??"",te=m.elements.res_msg?.value??"";if(A==="0000"){gn();const h=f.document.forms.namedItem("order_info"),C=h&&h!==m?h:m;if(h&&h!==m)for(let ne=0;ne<m.elements.length;ne++){const S=m.elements[ne];if(!S.name)continue;let w=h.elements.namedItem(S.name);w||(w=f.document.createElement("input"),w.type="hidden",w.name=S.name,h.appendChild(w)),w.value=S.value}C.action=r,C.method="POST",C.target="_top",f.document.body.appendChild(C),C.submit()}else{const h=A===""||A==="7777"||te.includes("취소");N(h?new $(te):new Error(`KCP 오류 [${A}]: ${te}`))}},f.__kcpFail=m=>{N(m)};const ee=p.contentDocument||f.document;ee.open(),ee.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>`),ee.close(),y=window.setTimeout(()=>{N(new Error("KCP SDK load timeout"))},15e3),f.__kcpReady=()=>{y!==void 0&&(clearTimeout(y),y=void 0)}})}const it={nhnkcp_payco:"PAYCO",nhnkcp_naverpay:"네이버페이",nhnkcp_naverpay_point:"네이버페이 포인트",nhnkcp_kakaopay:"카카오페이",nhnkcp_applepay:"Apple Pay"},ct="0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)";function ie(){return document.querySelector(".nhnkcp-easy-pay-btns")??null}function ce(){const e=ie();e&&e.querySelectorAll("button").forEach(t=>{t.style.boxShadow="",t.style.outline=""})}function dt(e){const t=ie();if(!t)return;const n=it[e];t.querySelectorAll("button").forEach(o=>{o.textContent?.trim()===n?(o.style.boxShadow=ct,o.style.outline="none"):(o.style.boxShadow="",o.style.outline="")})}function lt(e){const t=e.params?.paymentMethod;if(!t)return;const n=window.G7Core;if(T(t)&&!x()){const r=R();n?.state?.setLocal?.({paymentErrorMessage:r,isSubmittingOrder:!1}),n?.modal?.open?.("nhnkcp_payment_error_modal"),ce();return}const o=typeof t=="string"&&t.startsWith("nhnkcp_");n?.state?.setLocal?.({paymentMethod:t}),o?dt(t):ce()}function ut(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 pt(e){const t=String(e.params?.text??"");if(t!==""){if(navigator.clipboard&&typeof navigator.clipboard.writeText=="function"){await navigator.clipboard.writeText(t);return}ut(t)}}const de={requestPayment:rt,setPaymentMethod:lt,copyToClipboard:pt},le="/api/modules/sirsoft-ecommerce/user/orders",O="sirsoft-pay_nhnkcp",ue={info:(...e)=>console.info(`[${O}]`,...e),warn:(...e)=>console.warn(`[${O}]`,...e),error:(...e)=>console.error(`[${O}]`,...e)};function mt(e){return typeof e=="string"?e:e instanceof URL?e.toString():typeof Request<"u"&&e instanceof Request?e.url:String(e)}function ft(e,t){return t?.method?t.method.toUpperCase():typeof Request<"u"&&e instanceof Request?e.method.toUpperCase():"GET"}function yt(e,t){if(t!=="POST")return!1;const n=e.split("?")[0].split("#")[0];return n===le||n.endsWith(le)}function ht(e){try{return JSON.parse(e).payment_method}catch{return}}function _t(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 gt(){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(r,a){const s=mt(r),i=ft(r,a);if(!yt(s,i))return n(r,a);let c;if(a?.body&&typeof a.body=="string"&&(c=ht(a.body)),T(c)&&!x()){const d=R();return ue.warn(d),_t(d)}return n(r,a)},ue.info("order request interceptor installed (Apple Pay device guard)")}const K="sirsoft-pay_nhnkcp",pe="__kcpOcReceiptInjectorInstalled",me="kcp-oc-receipt-btn",wt=/^\/shop\/orders\/([^/]+)\/complete$/;function fe(){return localStorage.getItem("auth_token")}async function bt(e){const t=fe();if(!t)return null;try{const n=await fetch(`/api/modules/sirsoft-ecommerce/user/orders/${e}`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?(await n.json())?.data?.payment??null:null}catch{return null}}async function j(e){const t=fe();if(!t)return null;try{const n=await fetch(`/api/plugins/${K}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?await n.json():null}catch{return null}}function ye(e,t,n){const o=document.createElement("button");return o.type="button",o.className=t,o.textContent=e,o.addEventListener("click",()=>n(o)),o}function kt(e){if(!e)return!1;const t=Array.from(document.querySelectorAll("div"));for(const n of t){const o=Array.from(n.children).filter(s=>s instanceof HTMLElement&&s.tagName==="SPAN");if(o.length<2)continue;const r=o[0].textContent?.trim();if(r!=="결제 방법"&&r!=="결제수단"&&r!=="결제 방식")continue;const a=o[o.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 Ct(e){if(document.getElementById(me))return;const t=await bt(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 o=await j(e);kt(o?.payment_method_display_label);const r=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=me;const s=r.lastElementChild;r.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,r.insertBefore(c,s)}console.info(`[${K}] receipt button injected on order complete page`)}function xt(){const t=location.pathname.match(wt);t&&Ct(t[1])}function vt(){if(typeof window>"u")return;const e=window;if(e[pe])return;e[pe]=!0,console.info(`[${K}] order complete receipt injector installed`);const t=(o=1200)=>setTimeout(xt,o);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...o)=>{n(...o),t()},window.addEventListener("popstate",()=>t(500))}const _="sirsoft-pay_nhnkcp",he="__kcpMpShowInjectorInstalled",D="kcp-mp-vbank-row",B="kcp-mp-receipt-row",v="kcp-mp-mock-deposit",Pt=/^\/mypage\/orders\/([^/]+)$/;function Et(e){try{const n=window.G7Core?.getState,a=n?.()?.currentDataContext?.order?.data;return!a||a.order_number!==e?null:a}catch{return null}}function _e(){return localStorage.getItem("auth_token")}async function ge(e){const t=_e();if(!t)return null;try{const n=await fetch(`/api/plugins/${_}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?await n.json():null}catch{return null}}async function we(e){const t=_e();if(!t)return null;try{const n=await fetch(`/api/plugins/${_}/user/orders/${e}/vbank-mock-deposit-info`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?await n.json():null}catch{return null}}function be(e){const t=document.createElement("div");t.id=v,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 o=document.createElement("span");o.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",o.textContent="TEST";const r=document.createElement("p");r.className="text-sm font-semibold text-orange-700 dark:text-orange-300",r.textContent="모의입금처리",n.appendChild(o),n.appendChild(r);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 ke(){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(r=>r.textContent?.includes("결제 정보"));if(!t)return null;const n=t.parentElement?.parentElement;return n?Array.from(n.children).find(r=>r.className?.includes("space-y"))??n:null}function b(e,t,n="font-medium text-gray-900 dark:text-white"){const o=document.createElement("div");o.className="flex items-center justify-between text-sm";const r=document.createElement("span");r.className="text-gray-500 dark:text-gray-400",r.textContent=e;const a=document.createElement("span");return a.className=n,a.textContent=t,o.appendChild(r),o.appendChild(a),o}function Nt(e,t){if(!t)return!1;const n=Array.from(e.querySelectorAll("div"));for(const o of n){const r=Array.from(o.children).filter(i=>i instanceof HTMLElement&&i.tagName==="SPAN");if(r.length<2)continue;const a=r[0].textContent?.trim();if(a!=="결제 방법"&&a!=="결제수단"&&a!=="Payment Method")continue;const s=r[r.length-1];return s.textContent?.trim()===t?(o.dataset.nhnkcpPaymentMethodRow="true",!0):(s.textContent=t,s.dataset.nhnkcpPaymentMethodPatched="true",o.dataset.nhnkcpPaymentMethodRow="true",!0)}return!1}function At(e,t){const n=document.createElement("div");n.id=D,n.className="pt-4 mt-2 border-t border-gray-200 dark:border-gray-700 space-y-3";const o=document.createElement("p");o.className="text-sm font-semibold text-gray-800 dark:text-gray-200",o.textContent="가상계좌 입금 안내";const r=document.createElement("div");r.className="space-y-1.5",r.appendChild(b("은행",e.vbank_name??"")),r.appendChild(b("계좌번호",e.vbank_number??"","font-medium text-gray-900 dark:text-white font-mono tracking-wide")),r.appendChild(b("예금주",e.vbank_holder??"")),r.appendChild(b("입금 금액",t)),e.due_date_formatted&&r.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(o),n.appendChild(r),n.appendChild(a),n}function St(e){const t=document.createElement("div");t.id=B,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 o=document.createElement("span");o.className="text-gray-500 dark:text-gray-400 text-sm",o.textContent="영수증";const r=document.createElement("button");return r.type="button",r.className="inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",r.textContent="영수증 조회",r.addEventListener("click",async()=>{r.disabled=!0,r.textContent="로딩 중...";const a=await ge(e);if(r.disabled=!1,r.textContent="영수증 조회",a){const s=window.KcpReceiptPopup;s&&new s({url:a.receipt_url,cash_url:a.cash_receipt_url})}}),n.appendChild(o),n.appendChild(r),t.appendChild(n),t}async function It(e){const t=Et(e);if(!t){if(!document.getElementById(v)){const p=ke();if(!p)return!1;const f=await we(e);f?.available&&(p.appendChild(be(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 o=n.payment_method==="vbank"&&!!n.vbank_name,r=!!n.transaction_id&&!!n.paid_at,a=n.payment_method==="vbank"&&!!n.vbank_number,s=!o||!!document.getElementById(D),i=!r||!!document.getElementById(B),c=!a||!!document.getElementById(v);if(s&&i&&c)return!0;const d=ke();if(!d)return!1;const l=r?await ge(e):null,u=Nt(d,l?.payment_method_display_label);if(!document.getElementById(D)&&o)return d.appendChild(At(n,t.total_amount_formatted??"")),console.info(`[${_}] vbank info injected on mypage order show`),!1;if(!document.getElementById(B)&&r)return d.appendChild(St(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(v)&&a){const p=await we(e);if(p?.available)d.appendChild(be(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++,It(e).then(o=>{(o||t>=30)&&clearInterval(n)})},400)}function Tt(){const e=location.pathname.match(Pt);e&&Mt(e[1])}function Rt(){if(typeof window>"u")return;const e=window;if(e[he])return;e[he]=!0,console.info(`[${_}] mypage order show injector installed`);const t=(o=1500)=>setTimeout(Tt,o);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...o)=>{n(...o),t(600)},window.addEventListener("popstate",()=>t(500))}const Lt="sirsoft-pay_nhnkcp",Ce="__kcpAdminApplePayNoticeInjectorInstalled",$t=/^\/admin\/ecommerce\/settings\/?$/,xe="__kcpAdminApplePayNoticeListenerAttached",Ot="애플페이는 IOS 기기에 모바일결제만 가능합니다.",Kt="Apple Pay is available only for mobile payments on iOS devices.";let ve=null,P=null,H=!1;function jt(){return window}function F(){if(!$t.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e==="order_settings"}function Pe(){return(document.documentElement.lang||navigator.language||"").toLowerCase().startsWith("ko")?Ot:Kt}function Ee(e){return(e??"").replace(/\s+/g," ").trim()}function Dt(e){const t=Ee(e.textContent);return t==="애플페이"||t==="Apple Pay"}function Bt(){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=Pe(),e}function U(e=document){if(typeof window>"u"||typeof document>"u"||!F()||Ee(document.body?.innerText??document.body?.textContent).includes(Pe()))return!1;const n=Array.from(e.querySelectorAll("span, p, div")).find(Dt);return n?(n.insertAdjacentElement("afterend",Bt()),!0):!1}function Ht(){H||(H=!0,window.setTimeout(()=>{H=!1,U()},0))}function Ne(){P!==null&&(window.clearInterval(P),P=null)}function q(){if(!F())return;Ne(),U();let e=0;P=window.setInterval(()=>{e+=1,(U()||e>=50)&&Ne()},200);const t=document.body;t[xe]||(t[xe]=!0,ve=new MutationObserver(()=>Ht()),ve.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function Ae(){F()&&q()}function Ft(){if(typeof window>"u"||typeof document>"u")return;const e=jt();if(e[Ce])return;e[Ce]=!0,console.info(`[${Lt}] 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(()=>Ae(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>Ae(),200))}const Ut="sirsoft-pay_nhnkcp",Se="__nhnkcpAdminPaymentMethodBrandInjectorInstalled",Ie="__nhnkcpAdminPaymentMethodBrandSyncListenerAttached",qt=/^\/admin\/ecommerce\/settings\/?$/,Me='[data-nhnkcp-admin-payment-brand-mark="true"]',Gt=200,Yt=120,Wt=[{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,E=null,G=!1;function zt(){return window}function Y(){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 Jt(e){return e instanceof HTMLElement?e.className:""}function Re(e){return(e??"").replace(/\u200B/g,"").replace(/\s+/g," ").trim()}function Vt(e){const t=Jt(e);return t.includes("excel-card")||t.includes("flex-center")&&t.includes("border")&&t.includes("gap-4")}function Le(e){return e.querySelector(".font-medium")}function Qt(e){const t=Re(Le(e)?.textContent),n=Re(e.textContent);return Wt.find(o=>o.shortLabels.some(r=>t===r)||o.labels.some(r=>n.includes(r)))??null}function Xt(e){return Array.from(e.querySelectorAll("div")).filter(Vt)}function Zt(e){return Array.from(e.querySelectorAll("svg, [data-icon], i")).find(n=>!n.closest("[data-drag-handle]")&&!n.closest(Me)&&!n.closest('.row-stack, .excel-card-body, button, select, [role="switch"]'))??null}function $e(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(o=>{const r=document.createElement("span");r.style.lineHeight="1",r.textContent=o,e.appendChild(r)})):(e.style.removeProperty("flex-direction"),e.textContent=t.markLines[0]??""))}function en(e){const t=document.createElement("span");return $e(t,e),t}function tn(e,t){const n=e.querySelector(Me);if(n instanceof HTMLSpanElement)return e.dataset.nhnkcpAdminPaymentMethod=t.id,$e(n,t),!0;const o=en(t),r=Zt(e);if(r&&r.parentElement)return e.dataset.nhnkcpAdminPaymentMethod=t.id,r.replaceWith(o),!0;const a=Le(e);return a&&a.parentElement?(e.dataset.nhnkcpAdminPaymentMethod=t.id,a.parentElement.insertBefore(o,a),!0):!1}function W(e=document){if(typeof window>"u"||typeof document>"u"||!Y())return!1;let t=!1;return Xt(e).forEach(n=>{const o=Qt(n);o&&tn(n,o)&&(t=!0)}),t}function nn(){G||(G=!0,window.setTimeout(()=>{G=!1,W()},0))}function z(){E!==null&&(window.clearInterval(E),E=null)}function J(){if(!Y())return;z(),W();let e=0;E=window.setInterval(()=>{e+=1,W(),e>=Yt&&z()},Gt);const t=document.body;t[Ie]||(t[Ie]=!0,Te=new MutationObserver(()=>nn()),Te.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function V(){if(Y()){J();return}z()}function on(){if(typeof window>"u"||typeof document>"u")return;const e=zt();if(e[Se])return;e[Se]=!0,console.info(`[${Ut}] admin payment method brand sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>J()):J();const t=history.pushState.bind(history);history.pushState=(...o)=>{t(...o),window.setTimeout(()=>V(),200)};const n=history.replaceState.bind(history);history.replaceState=(...o)=>{n(...o),window.setTimeout(()=>V(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>V(),200))}const Q="sirsoft-pay_nhnkcp",Oe="__nhnkcpAdminPaymentDisplayInjectorInstalled",rn=/^\/admin\/ecommerce\/orders\/([^/]+)$/;function an(){return localStorage.getItem("auth_token")}async function sn(e){const t=an();if(!t)return null;try{const n=await fetch(`/api/plugins/${Q}/admin/orders/${e}/transaction-status`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});if(!n.ok)return null;const o=await n.json();return("data"in o?o.data:o)??null}catch{return null}}function cn(e){return Array.from(e.children).filter(t=>t instanceof HTMLElement)}function dn(e,t){let n=!1;const o=Array.from(e.querySelectorAll("div"));for(const r of o){const a=cn(r),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"),r.dataset.nhnkcpPaymentMethodRow="admin-order",c&&d&&(c.textContent="",c.style.display="none",c.dataset.nhnkcpPaymentInstallmentHidden="true"),n=!0}return n}function ln(e,t,n){const r=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 r?(r.textContent=n,r.dataset.nhnkcpPaymentMethodPatched="true",r.dataset.nhnkcpPaymentMethodBadge="admin-order",!0):!1}function un(e,t){const n=t?._pay_method_label,o=t?._embedded_pg_provider_label,r=t?._base_pay_method_label;if(!n||!o)return!1;const a=dn(e,n),s=r?ln(e,r,o):!1;return a||s}async function pn(e){const t=document.getElementById("section_payment_info")??document.getElementById("payment_info_card");if(!t)return!1;const n=await sn(e);return n&&un(t,n)&&console.info(`[${Q}] payment method display patched on admin order detail`),!0}function mn(e){let t=0;const n=setInterval(()=>{t++,pn(e).then(o=>{(o||t>=30)&&clearInterval(n)})},400)}function fn(){const e=location.pathname.match(rn);e&&mn(e[1])}function yn(){if(typeof window>"u")return;const e=window;if(e[Oe])return;e[Oe]=!0,console.info(`[${Q}] admin order payment display injector installed`);const t=(o=1500)=>setTimeout(fn,o);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...o)=>{n(...o),t(600)},window.addEventListener("popstate",()=>t(500))}class hn{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=hn;const g="sirsoft-pay_nhnkcp",X={info:(...e)=>console.info(`[${g}]`,...e),warn:(...e)=>console.warn(`[${g}]`,...e),error:(...e)=>console.error(`[${g}]`,...e)};function Ke(){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 o=0;for(const[r,a]of Object.entries(de)){const s=`${g}.${r}`;n.registerHandler(s,a,{category:"plugin",source:g}),o++}return o}function je(){Ge();const e=()=>{const t=Ke();if(t>0){X.info(`${t} handler(s) registered`);return}let n=0;const o=50,r=setInterval(()=>{n++;const a=Ke();if(a>0){clearInterval(r),X.info(`${a} handler(s) registered (after ${n} retries)`);return}n>=o&&(clearInterval(r),X.warn("ActionDispatcher not available after timeout"))},100)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}gt(),vt(),Rt(),Ft(),on(),yn(),je(),window.__SirsoftNhnkcp={identifier:g,handlers:Object.keys(de),initPlugin:je}})();