From 5a9ad9732785ed8a39e0c51139da2249804d2c93 Mon Sep 17 00:00:00 2001 From: thisgun Date: Sat, 4 Jul 2026 10:05:24 +0900 Subject: [PATCH] =?UTF-8?q?fix(payment):=20=EB=82=98=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=20=EA=B0=84=ED=8E=B8=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EB=9D=BC=EB=B2=A8=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 나이스페이 간편결제 선택값을 콜백까지 보존하고 주문완료, 마이페이지, 관리자 주문목록/상세에서 실제 간편결제명을 표시한다. --- .../sirsoft-pay_nicepayments/CHANGELOG.md | 1 + .../dist/js/plugin.iife.js | 2 +- .../dist/js/plugin.iife.js.map | 2 +- .../__tests__/handlers/requestPayment.test.ts | 4 + .../orderPaymentDisplayInjectors.test.ts | 47 ++++ .../js/adminOrderPaymentDisplayInjector.ts | 163 +++++++++++++ .../resources/js/handlers/requestPayment.ts | 4 + .../resources/js/index.ts | 2 + .../resources/js/mypageOrderShowInjector.ts | 40 ++- .../js/orderCompleteReceiptInjector.ts | 41 +++- .../src/Concerns/ResolvesEasyPayDisplay.php | 227 ++++++++++++++++++ .../Controllers/AdminOrderListController.php | 43 ++++ .../AdminTransactionController.php | 16 +- .../Controllers/PaymentCallbackController.php | 19 +- .../src/Controllers/UserReceiptController.php | 17 +- .../src/Http/Requests/AuthCallbackRequest.php | 4 + ...eAdminOrderListTestBadgeLayoutListener.php | 68 +++++- .../src/routes/api.php | 5 + .../EasyPayDisplayControllerTest.php | 147 ++++++++++++ .../PaymentCallbackControllerTest.php | 38 +++ .../PaymentCloseReportControllerTest.php | 5 + .../tests/PluginTestCase.php | 18 ++ ...AdminOrderLayoutExtensionsListenerTest.php | 5 + 23 files changed, 891 insertions(+), 27 deletions(-) create mode 100644 plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/orderPaymentDisplayInjectors.test.ts create mode 100644 plugins/_bundled/sirsoft-pay_nicepayments/resources/js/adminOrderPaymentDisplayInjector.ts create mode 100644 plugins/_bundled/sirsoft-pay_nicepayments/src/Concerns/ResolvesEasyPayDisplay.php create mode 100644 plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/EasyPayDisplayControllerTest.php diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/CHANGELOG.md b/plugins/_bundled/sirsoft-pay_nicepayments/CHANGELOG.md index 9c086bf2..df3f3879 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/CHANGELOG.md +++ b/plugins/_bundled/sirsoft-pay_nicepayments/CHANGELOG.md @@ -17,6 +17,7 @@ ### Fixed +- 나이스페이먼츠 간편결제 주문이 주문완료, 마이페이지 주문상세, 관리자 주문목록/상세에서 실제 간편결제 수단 대신 `신용카드`로만 표시되던 문제를 수정했습니다. - 관리자 주문목록과 주문상세 레이아웃이 리뉴얼되거나 플러그인 업데이트 과정에서 나이스페이먼츠 테스트결제 배지, 테스트 모드 안내, 결제 조회/결제 정보 패널이 누락될 수 있던 문제를 수정했습니다. - 운영 모드에서 live MID가 비어 있으면 `SR` 같은 잘못된 MID로 결제창을 만들지 않고 서명 요청 단계에서 실패하도록 보강했습니다. - 환불 요청 시 현재 설정값이 아니라 결제 당시 MID와 테스트/운영 모드로 나이스페이먼츠 API 서명 컨텍스트를 복원하도록 수정했습니다. diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js b/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js index 6087371a..d2eb55e1 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js +++ b/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js @@ -1,2 +1,2 @@ -(function(){"use strict";function Ie(e){return/^https?:\/\//i.test(e)||e.startsWith("/api/")?e:e.startsWith("/plugins/")?`/api${e}`:e.startsWith("plugins/")?`/api/${e}`:e}function Re(e){return e.trim().slice(0,160)}async function Oe(e,t="nicepay-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:Re(t)||"nicepay-window-closed"};try{const a=window.G7Core?.api;if(typeof a?.post=="function"){await a.post(e.closeReportUrl,n);return}await fetch(Ie(e.closeReportUrl),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),keepalive:!0})}catch(a){console.warn("[sirsoft-pay_nicepayments] failed to report payment window close",a)}}function De(e){return new Promise((t,n)=>{if(document.querySelector(`script[src="${e}"]`)){t();return}const a=document.createElement("script");a.src=e,a.onload=()=>t(),a.onerror=()=>n(new Error(`Failed to load script: ${e}`)),document.head.appendChild(a)})}function Me(e,t){const n=document.createElement("form");n.id="nicepayForm",n.method="post",n.action=e,n.acceptCharset="utf-8";for(const[a,r]of Object.entries(t)){const o=document.createElement("input");o.type="hidden",o.name=a,o.value=r,n.appendChild(o)}return document.body.appendChild(n),n}const $e="/plugins/sirsoft-pay_nicepayments/payment/callback",Ge="https://web.nicepay.co.kr/v3/v3Payment.jsp";function Be(){if(typeof navigator>"u")return!1;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 z(e,t){const{pgPaymentData:n,paymentMethod:a}=e.params??{},r=window.__templateApp?.globalState?._local,o=a??r?.paymentMethod??"card";if(!n){console.error("[sirsoft-pay_nicepayments] pgPaymentData is required");return}const i=window.G7Core;try{const s=await i.api.get("/modules/sirsoft-ecommerce/payments/client-config/nicepayments");if(!s.data){console.error("[sirsoft-pay_nicepayments] Failed to fetch client config",s);return}const p=s.data,u=Be();if(!u&&(await De(p.sdk_url),typeof window.goPay!="function")){i?.toast?.error?.("나이스페이먼츠 SDK를 불러올 수 없습니다. 잠시 후 다시 시도해주세요."),i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:o});return}const v=window.location.origin+p.sign_data_url,K=typeof localStorage<"u"?localStorage.getItem("auth_token"):null,y={"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",Accept:"application/json"};K&&(y.Authorization=`Bearer ${K}`);const g=await fetch(v,{method:"POST",credentials:"include",headers:y,body:JSON.stringify({amt:n.amount,moid:n.order_number})});if(!g.ok)throw new Error("SignData 생성에 실패했습니다.");const h=await g.json(),L=window.location.origin+$e,m=typeof o=="string"&&o.startsWith("nicepay_"),b=m?"CARD":{card:"CARD",vbank:"VBANK",bank:"BANK",phone:"CELLPHONE"}[o??"card"]??"CARD",l={PayMethod:b,GoodsName:n.order_name,Amt:String(n.amount),MID:h.mid,Moid:n.order_number,BuyerName:n.customer_name??"",BuyerEmail:n.customer_email??"",BuyerTel:n.customer_phone??"",ReturnURL:L,EdiDate:h.ediDate,SignData:h.signData,CharSet:"utf-8",GoodsCl:"1",DirectShowOpt:"",DirectEasyPay:"",NicepayReserved:"",EasyPayMethod:""};b==="CELLPHONE"&&(l.GoodsCl=n.goods_cl??"1");const c=p.useEscrow&&!m;if(l.TransType=c?"1":"0",m)switch(l.DirectShowOpt="CARD",o){case"nicepay_naverpay":l.DirectEasyPay="E020",l.EasyPayMethod="E020=CARD";break;case"nicepay_kakaopay":l.NicepayReserved="DirectKakao=Y";break;case"nicepay_samsungpay":l.DirectEasyPay="E021";break;case"nicepay_applepay":l.DirectEasyPay="E025";break;case"nicepay_payco":l.NicepayReserved="DirectPayco=Y";break;case"nicepay_skpay":l.NicepayReserved="DirectPay11=Y";break;case"nicepay_ssgpay":l.DirectEasyPay="E007";break;case"nicepay_lpay":l.DirectEasyPay="E018";break}try{const w=(await i.api.get(`/modules/sirsoft-ecommerce/user/orders/${n.order_number}`))?.data;if(w){const T=Number(w.total_tax_amount??0),S=Number(w.total_vat_amount??0),Te=Number(w.total_tax_free_amount??0);(T>0||S>0||Te>0)&&(l.TaxAmt=String(T),l.VatAmt=String(S),l.TaxFreeAmt=String(Te))}}catch{}const d=Me(L,l);if(u){d.action=Ge,d.acceptCharset="euc-kr";const k=d.querySelector('input[name="CharSet"]');k&&(k.value="euc-kr"),d.submit();return}window.nicepaySubmit=()=>{d.submit()};let N=!1;const V=new Set(document.body.children),ve=(k,w)=>{if(N)return;N=!0;const T=[k,w].filter(Boolean).join(": ")||"nicepay-window-closed";Oe({closeReportUrl:p.close_report_url,oid:n.order_number,price:Number(n.amount),buyer_email:n.customer_email??"",buyer_phone:n.customer_phone??"",payment_method:o},T),window.removeEventListener("popstate",Le),Array.from(document.body.children).forEach(S=>{V.has(S)||S.remove()}),d.parentNode&&d.parentNode.removeChild(d),window.__sirsoftNicepayPaymentInProgress=!1,i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:o}),w&&i?.toast?.error?.(w)};window.nicepayClose=ve;const Le=()=>ve("","");window.history.pushState({nicepayOpen:!0},""),window.addEventListener("popstate",Le),window.goPay(d)}catch(s){console.error("[sirsoft-pay_nicepayments] requestPayment error",s),window.__sirsoftNicepayPaymentInProgress=!1,i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:o}),i?.toast?.error?.("결제 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.")}}const je={nicepay_naverpay:"네이버페이",nicepay_kakaopay:"카카오페이",nicepay_samsungpay:"삼성페이",nicepay_applepay:"애플페이",nicepay_payco:"PAYCO",nicepay_skpay:"11pay",nicepay_ssgpay:"SSG페이",nicepay_lpay:"L.pay"},Fe="0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)";function J(){const e=document.getElementById("nicepay_checkout_payment_section");if(!e)return null;const t=e.querySelectorAll("p");for(const n of t)if(n.textContent?.includes("간편결제"))return n.nextElementSibling;return null}function X(){const e=J();e&&e.querySelectorAll("button").forEach(t=>{t.style.boxShadow="",t.style.outline=""})}function Ye(e){const t=J();if(!t)return;const n=je[e];t.querySelectorAll("button").forEach(a=>{a.textContent?.trim()===n?(a.style.boxShadow=Fe,a.style.outline="none"):(a.style.boxShadow="",a.style.outline="")})}function qe(e){const t=e.params?.paymentMethod;if(!t)return;const n=typeof t=="string"&&t.indexOf("nicepay_")===0;window.G7Core?.state?.setLocal?.({paymentMethod:t,serverPaymentMethod:n?"card":t}),n?Ye(t):X()}function Q(){const e=window.__templateApp;typeof e?.onGlobalStateChange=="function"&&e.onGlobalStateChange(t=>{const n=t?._local?.paymentMethod;typeof n=="string"&&(n.startsWith("nicepay_")||X())})}const Z={requestPayment:z,setPaymentMethod:qe},ee="/user/orders",He="sirsoft-nicepayments",I="sirsoft-pay_nicepayments",te="__sirsoftNicepayFetchInterceptorInstalled",ne="/api/modules/sirsoft-ecommerce/checkout",R="__sirsoftNicepayPaymentInProgress",P="__sirsoftNicepayCheckoutCache",f={info:(...e)=>console.info(`[${I}]`,...e),warn:(...e)=>console.warn(`[${I}]`,...e),error:(...e)=>console.error(`[${I}]`,...e)};function Ue(){return window.location.pathname+window.location.search+window.location.hash}function Ke(e,t){if(t.toUpperCase()!=="POST")return!1;const n=(e??"").split("?")[0].split("#")[0];return n===ee||n.endsWith(ee)}function O(e,t){if(t.toUpperCase()!=="GET")return!1;const n=(e??"").split("?")[0].split("#")[0];return n===ne||n.endsWith(ne)}function We(e){if(!e)return;let t=null;if(typeof e=="string")try{t=JSON.parse(e)}catch{return}else typeof e=="object"&&(t=e);if(!t)return;const n=t.payment_method;return typeof n=="string"&&n.length>0?n:void 0}function Ve(e,t){try{const n=JSON.parse(e);if("payment_method"in n)return JSON.stringify({...n,payment_method:t})}catch{}return e}function ze(e){const t=[e.total_due_amount,e.total_amount];for(const n of t){const a=Number(n??0);if(Number.isFinite(a)&&a>0)return Math.floor(a)}return 0}function Je(){const e=window;if(e[te])return;e[te]=!0;const t="__sirsoftPgOriginalFetch";e[t]||(e[t]=window.fetch.bind(window));const n=e[t],a=window.fetch.bind(window);window.fetch=async(r,o)=>{const i=typeof r=="string"?r:r instanceof URL?r.href:r.url,s=o?.method??(r instanceof Request?r.method:"GET");if(O(i,s)){if(e[R]&&e[P])return f.info("payment in progress — serving cached checkout response"),new Response(e[P],{status:200,headers:{"Content-Type":"application/json"}});const c=await a(r,o);if(c.ok){const d=await c.text();return e[P]=d,new Response(d,{status:c.status,statusText:c.statusText,headers:c.headers})}return c}if(!Ke(i,s))return a(r,o);let p,u=!1,v=o;if(o?.body&&typeof o.body=="string"){const c=We(o.body);typeof c=="string"&&c.length>0&&(p=c,c.startsWith("nicepay_")&&(u=!0,v={...o,body:Ve(o.body,"card")},f.info(`easy pay detected: replacing payment_method '${c}' → 'card'`)))}const y=await(u?n:a)(r,v);if(!y.ok)return y;let g=null;try{g=await y.clone().json()}catch{return y}const h=g?.data??g;if(!h?.requires_pg_payment&&!u)return y;const L=h.pg_provider===He;if(!u&&!L)return y;let m=h.pg_payment_data;if(!m&&u){const c=h.order;if(c){const d=c.options,N=d?.[0]?.product_name??String(c.order_number??""),V=(d?.length??0)>1?`${N} 외 ${(d?.length??0)-1}건`:N;m={order_number:c.order_number,order_name:V,amount:ze(c),currency:"KRW",customer_name:c.orderer_name??null,customer_email:c.orderer_email??null,customer_phone:String(c.orderer_phone??"").replace(/[^0-9]/g,""),customer_key:c.user_id??null},f.info("pg_payment_data constructed from order (기본 PG 미설정)",{order_number:m.order_number,amount:m.amount})}}if(!m)return f.warn("nicepayments order detected but pg_payment_data missing"),y;const W=p??"card";f.info("intercepted order create response — opening PG popup",{paymentMethod:W}),z({params:{pgPaymentData:m,paymentMethod:W}}),e[R]=!0;const b={...h,requires_pg_payment:!1,redirect_url:Ue()},l=g?.data?{...g,data:b}:b;return new Response(JSON.stringify(l),{status:y.status,statusText:y.statusText,headers:y.headers})},f.info("fetch order interceptor installed"),re(e,15)}const ae="__sirsoftNicepayAxiosCheckoutInterceptorInstalled";function re(e,t){const n=window?.G7Core?.api?.client;if(!n?.interceptors){t>0&&setTimeout(()=>re(e,t-1),300);return}e[ae]||(e[ae]=!0,n.interceptors.response.use(a=>{const r=(a?.config?.method??"").toLowerCase(),o=String((a?.config?.baseURL??"")+(a?.config?.url??""));return r==="get"&&O(o,"GET")&&a?.status===200&&(e[P]=JSON.stringify(a.data),f.info("checkout response cached (axios)")),a}),n.interceptors.request.use(a=>{const r=(a?.method??"").toLowerCase(),o=String((a?.baseURL??"")+(a?.url??""));return r==="get"&&O(o,"GET")&&e[R]&&e[P]&&(f.info("payment in progress — serving cached checkout response (axios)"),a.adapter=()=>Promise.resolve({data:JSON.parse(e[P]),status:200,statusText:"OK (cached)",headers:{"content-type":"application/json"},config:a,request:{}})),a}),f.info("axios checkout interceptor installed"),e[P]||(async()=>{try{await window?.G7Core?.api?.get?.("/modules/sirsoft-ecommerce/checkout")}catch{}})())}const Xe="sirsoft-pay_nicepayments",oe="__nicepayCheckoutEasyPayInjectorInstalled",Qe=/^\/shop\/checkout\/?$/,ie="__nicepayCheckoutEasyPaySyncListenerAttached",Ze=200,et=120,tt="nicepay_checkout_payment_section",se=[{id:"nicepay_naverpay",labels:["네이버페이 (나이스페이먼츠)","네이버페이로 결제 (나이스페이먼츠)","Naver Pay (NicePayments)","Pay with Naver Pay (NicePayments)"],ko:{heading:"네이버페이 (나이스페이먼츠)",description:"네이버페이로 결제 (나이스페이먼츠)",title:"네이버페이로 결제 (나이스페이먼츠)"},en:{heading:"Naver Pay (NicePayments)",description:"Pay with Naver Pay (NicePayments)",title:"Pay with Naver Pay (NicePayments)"},markText:"N",markClassName:"bg-green-500 text-white"},{id:"nicepay_kakaopay",labels:["카카오페이 (나이스페이먼츠)","카카오페이로 결제 (나이스페이먼츠)","Kakao Pay (NicePayments)","Pay with Kakao Pay (NicePayments)"],ko:{heading:"카카오페이 (나이스페이먼츠)",description:"카카오페이로 결제 (나이스페이먼츠)",title:"카카오페이로 결제 (나이스페이먼츠)"},en:{heading:"Kakao Pay (NicePayments)",description:"Pay with Kakao Pay (NicePayments)",title:"Pay with Kakao Pay (NicePayments)"},markText:"K",markClassName:"bg-yellow-400 text-gray-950"},{id:"nicepay_samsungpay",labels:["삼성페이 (나이스페이먼츠)","삼성페이로 결제 (나이스페이먼츠)","Samsung Pay (NicePayments)","Pay with Samsung Pay (NicePayments)"],ko:{heading:"삼성페이 (나이스페이먼츠)",description:"삼성페이로 결제 (나이스페이먼츠)",title:"삼성페이로 결제 (나이스페이먼츠)"},en:{heading:"Samsung Pay (NicePayments)",description:"Pay with Samsung Pay (NicePayments)",title:"Pay with Samsung Pay (NicePayments)"},markText:"S",markClassName:"bg-blue-600 text-white"},{id:"nicepay_applepay",labels:["애플페이 (나이스페이먼츠)","애플페이로 결제 (나이스페이먼츠)","Apple Pay (NicePayments)","Pay with Apple Pay (NicePayments)"],ko:{heading:"애플페이 (나이스페이먼츠)",description:"애플페이로 결제 (나이스페이먼츠)",title:"애플페이로 결제 (나이스페이먼츠)"},en:{heading:"Apple Pay (NicePayments)",description:"Pay with Apple Pay (NicePayments)",title:"Pay with Apple Pay (NicePayments)"},markText:"A",markClassName:"bg-gray-900 text-white"},{id:"nicepay_payco",labels:["PAYCO (나이스페이먼츠)","PAYCO로 결제 (나이스페이먼츠)","PAYCO (NicePayments)","Pay with PAYCO (NicePayments)"],ko:{heading:"PAYCO (나이스페이먼츠)",description:"PAYCO로 결제 (나이스페이먼츠)",title:"PAYCO로 결제 (나이스페이먼츠)"},en:{heading:"PAYCO (NicePayments)",description:"Pay with PAYCO (NicePayments)",title:"Pay with PAYCO (NicePayments)"},markText:"P",markClassName:"bg-red-500 text-white"},{id:"nicepay_skpay",labels:["11pay (나이스페이먼츠)","11pay로 결제 (나이스페이먼츠)","11pay (NicePayments)","Pay with 11pay (NicePayments)"],ko:{heading:"11pay (나이스페이먼츠)",description:"11pay로 결제 (나이스페이먼츠)",title:"11pay로 결제 (나이스페이먼츠)"},en:{heading:"11pay (NicePayments)",description:"Pay with 11pay (NicePayments)",title:"Pay with 11pay (NicePayments)"},markText:"11",markClassName:"bg-orange-500 text-white"},{id:"nicepay_ssgpay",labels:["SSG페이 (나이스페이먼츠)","SSG페이로 결제 (나이스페이먼츠)","SSG Pay (NicePayments)","Pay with SSG Pay (NicePayments)"],ko:{heading:"SSG페이 (나이스페이먼츠)",description:"SSG페이로 결제 (나이스페이먼츠)",title:"SSG페이로 결제 (나이스페이먼츠)"},en:{heading:"SSG Pay (NicePayments)",description:"Pay with SSG Pay (NicePayments)",title:"Pay with SSG Pay (NicePayments)"},markText:"SSG",markClassName:"bg-red-700 text-white"},{id:"nicepay_lpay",labels:["L.pay (나이스페이먼츠)","L.pay로 결제 (나이스페이먼츠)","L.pay (NicePayments)","Pay with L.pay (NicePayments)"],ko:{heading:"L.pay (나이스페이먼츠)",description:"L.pay로 결제 (나이스페이먼츠)",title:"L.pay로 결제 (나이스페이먼츠)"},en:{heading:"L.pay (NicePayments)",description:"Pay with L.pay (NicePayments)",title:"Pay with L.pay (NicePayments)"},markText:"L",markClassName:"bg-purple-600 text-white"}];let ce=null,C=null,D=!1;function nt(){return window}function M(){return Qe.test(window.location.pathname)}function at(e){return(e??"").replace(/\s+/g," ").trim()}function rt(e){return at(e).replace(/\u200B/g,"")}function ot(){return(document.documentElement.lang||navigator.language||"").toLowerCase().startsWith("ko")}function it(e){return ot()?e.ko:e.en}function le(e){return e.replaceAll("네이버페이","네이​버페이").replaceAll("카카오페이","카카​오페이").replaceAll("삼성페이","삼성​페이").replaceAll("Naver Pay","Naver​ Pay").replaceAll("Kakao Pay","Kakao​ Pay").replaceAll("Samsung Pay","Samsung​ Pay").replaceAll("L.pay","L.​pay")}function st(e){return se.find(t=>t.id===e)??null}function ct(e){const t=st(e.dataset.nicepayEasyPayMethod);if(t)return t;const n=rt(e.textContent);return se.find(a=>a.labels.some(r=>n.includes(r)))??null}function de(e){return e.querySelector(".flex.items-center.gap-2, .flex.items-center.gap-3")??e.querySelector(".flex.items-center")}function lt(e,t,n){e.style.textAlign="left";const a=de(e);a&&(a.style.justifyContent="flex-start",a.style.textAlign="left",a.style.width="100%");const r=t?.parentElement??n?.parentElement;r instanceof HTMLElement&&(r.style.textAlign="left",r.style.minWidth="0")}function dt(e){const t=document.createElement("span");return t.dataset.nicepayEasyPayMark="true",t.dataset.nicepayEasyPayMethod=e.id,t.setAttribute("aria-hidden","true"),t.className=`inline-flex items-center justify-center rounded-lg text-xs font-bold ${e.markClassName}`,t.style.width="32px",t.style.height="32px",t.style.flex="0 0 32px",t.textContent=e.markText,t}function yt(e){delete e.dataset.kginicisBrandPaymentButton,delete e.dataset.kginicisBrandPaymentMethod,delete e.dataset.kginicisNaverpayBrandButton,delete e.dataset.nhnkcpEasyPayMethod,delete e.dataset.nhnkcpEasyPayVisible,delete e.dataset.nhnkcpEasyPayHidden,e.querySelectorAll('[data-kginicis-brand-payment-mark="true"], [data-kginicis-naverpay-mark="true"], [data-nhnkcp-easy-pay-mark="true"]').forEach(t=>{t.remove()})}function ut(e){return e.querySelector('[data-nicepay-easy-pay-mark="true"]')??e.querySelector('[data-kginicis-brand-payment-mark="true"], [data-kginicis-naverpay-mark="true"], [data-nhnkcp-easy-pay-mark="true"]')??e.querySelector("svg")??e.querySelector('i[class*="fa-"], i[role="img"], i')}function pt(e,t){const n=e.querySelector('[data-nicepay-easy-pay-mark="true"]');if(n){n.dataset.nicepayEasyPayMethod=t.id,n.className=`inline-flex items-center justify-center rounded-lg text-xs font-bold ${t.markClassName}`,n.textContent=t.markText;return}const a=dt(t),r=ut(e);if(r&&r.parentElement){r.replaceWith(a);return}de(e)?.prepend(a)}function mt(e,t){const n=Array.from(e.querySelectorAll("p")),a=it(t),r=le(a.heading),o=le(a.description),i=n[0],s=n[1];i&&i.textContent!==r&&(i.textContent=r),i&&(i.setAttribute("aria-label",a.heading),i.style.textAlign="left",i.style.whiteSpace="normal",i.style.wordBreak="keep-all",i.style.overflowWrap="anywhere"),s&&s.textContent!==o&&(s.textContent=o),s&&(s.style.textAlign="left",s.style.fontSize="12px",s.style.lineHeight="1rem",s.style.whiteSpace="normal",s.style.wordBreak="keep-all",s.style.overflowWrap="anywhere"),lt(e,i,s),e.title=a.title}function ft(e,t){e.hidden&&(e.hidden=!1),e.disabled&&(e.disabled=!1),e.style.display==="none"&&e.style.removeProperty("display"),e.removeAttribute("aria-hidden"),e.dataset.nicepayEasyPayMethod=t.id,e.dataset.nicepayEasyPayVisible="true",yt(e),pt(e,t),mt(e,t)}function ht(){const e=document.getElementById(tt);return e?(e.remove(),!0):!1}async function $(e=document){if(typeof window>"u"||typeof document>"u"||!M())return!1;let t=ht();return e.querySelectorAll("button").forEach(n=>{const a=ct(n);a&&(ft(n,a),t=!0)}),t}function gt(){D||(D=!0,window.setTimeout(()=>{D=!1,$()},0))}function ye(){C!==null&&(window.clearInterval(C),C=null)}function G(){if(!M())return;ye(),$();let e=0;C=window.setInterval(()=>{e+=1,$(),e>=et&&ye()},Ze);const t=document.body;t[ie]||(t[ie]=!0,ce=new MutationObserver(()=>gt()),ce.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function ue(){M()&&G()}function wt(){if(typeof window>"u"||typeof document>"u")return;const e=nt();if(e[oe])return;e[oe]=!0,console.info(`[${Xe}] checkout easy pay method sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>G()):G();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>ue(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>ue(),200))}const B="sirsoft-pay_nicepayments",pe="__nicepayOcReceiptInjectorInstalled",me="nicepay-oc-receipt-btn",Pt=/^\/shop\/orders\/([^/]+)\/complete$/;function fe(){return localStorage.getItem("auth_token")}async function _t(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 bt(e){const t=fe();if(!t)return null;try{const n=await fetch(`/api/plugins/${B}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?(await n.json()).receipt_url??null:null}catch{return null}}async function Nt(e){if(document.getElementById(me))return;const t=await _t(e);if(!t||t.pg_provider!=="nicepayments"||!t.transaction_id||!t.paid_at)return;const n=Array.from(document.querySelectorAll('button[type="button"]')).find(i=>i.className.includes("bg-blue-600"));if(!n?.parentElement)return;const a=n.parentElement,r=document.createElement("button");r.id=me,r.type="button",r.className=n.className.replace(/bg-blue-\d+/g,"bg-green-600").replace(/hover:bg-blue-\d+/g,"hover:bg-green-700"),r.textContent="영수증 조회",r.addEventListener("click",async()=>{r.disabled=!0,r.textContent="로딩 중...";const i=await bt(e);r.disabled=!1,r.textContent="영수증 조회",i&&window.open(i,"nicepay_receipt","width=800,height=600,scrollbars=yes,resizable=yes")});const o=a.lastElementChild;a.insertBefore(r,o),console.info(`[${B}] receipt button injected on order complete page`)}function kt(){const e=location.pathname.match(Pt);e&&Nt(e[1])}function St(){if(typeof window>"u")return;const e=window;if(e[pe])return;e[pe]=!0,console.info(`[${B}] order complete receipt injector installed`);const t=(a=1200)=>setTimeout(kt,a);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...a)=>{n(...a),t()},window.addEventListener("popstate",()=>t(500))}const E="sirsoft-pay_nicepayments",he="__nicepayOrderShowInjectorInstalled",ge="nicepay-mp-receipt-row",we="nicepay-mp-vbank-info",Ct=/^\/mypage\/orders\/([^/]+)$/;function Et(e){try{const n=window.G7Core?.getState,o=n?.()?.currentDataContext?.order?.data;return!o||o.order_number!==e?null:o}catch{return null}}function xt(){return localStorage.getItem("auth_token")}async function At(e){const t=xt();if(!t)return null;try{const n=await fetch(`/api/plugins/${E}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?(await n.json()).receipt_url??null:null}catch{return null}}function vt(){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(a=>a.textContent?.includes("결제 정보"));if(!t)return null;const n=t.parentElement?.parentElement;return n?Array.from(n.children).find(a=>a.className?.includes("space-y"))??n:null}function Lt(e){const t=document.createElement("div");t.id=ge,t.className="flex items-center justify-between";const n=document.createElement("span");n.className="text-gray-500 dark:text-gray-400 text-sm",n.textContent="영수증";const a=document.createElement("button");return a.type="button",a.className="inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",a.textContent="영수증 조회",a.addEventListener("click",async()=>{a.disabled=!0,a.textContent="로딩 중...";const r=await At(e);a.disabled=!1,a.textContent="영수증 조회",r&&window.open(r,"nicepay_receipt","width=800,height=600,scrollbars=yes,resizable=yes")}),t.appendChild(n),t.appendChild(a),t}function Tt(e){if(!e)return"";try{const t=new Date(e);if(Number.isNaN(t.getTime()))return e;const n=a=>String(a).padStart(2,"0");return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}catch{return e}}function x(e,t,n="font-medium text-gray-900 dark:text-white"){const a=document.createElement("div");a.className="flex justify-between text-sm";const r=document.createElement("span");r.className="text-gray-600 dark:text-gray-400",r.textContent=e;const o=document.createElement("span");return o.className=n,o.textContent=t,a.appendChild(r),a.appendChild(o),a}function It(e){const t=e.payment??{},n=document.createElement("div");n.id=we,n.className="pt-4 mt-2 border-t border-gray-200 dark:border-gray-700";const a=document.createElement("div");a.className="bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-700 rounded-lg p-4";const r=document.createElement("h3");r.className="text-sm font-semibold text-blue-800 dark:text-blue-300 mb-3",r.textContent="가상계좌 입금 안내";const o=document.createElement("div");o.className="space-y-2",o.appendChild(x("은행",t.vbank_name??"")),o.appendChild(x("계좌번호",t.vbank_number??"","font-medium font-mono text-gray-900 dark:text-white")),o.appendChild(x("예금주",t.vbank_holder??"")),o.appendChild(x("입금 금액",e.total_amount_formatted??"","font-bold text-blue-600 dark:text-blue-400"));const i=t.vbank_due_at;if(i){const s=document.createElement("div");s.className="flex justify-between text-sm pt-2 border-t border-blue-200 dark:border-blue-700 mt-2";const p=document.createElement("span");p.className="text-gray-600 dark:text-gray-400",p.textContent="입금 기한";const u=document.createElement("span");u.className="font-medium text-red-600 dark:text-red-400",u.textContent=Tt(i),s.appendChild(p),s.appendChild(u),o.appendChild(s)}if(a.appendChild(r),a.appendChild(o),!t.paid_at){const s=document.createElement("p");s.className="text-xs text-blue-700 dark:text-blue-300 mt-3",s.textContent="입금 기한 내에 입금이 완료되지 않으면 주문이 자동 취소됩니다.",a.appendChild(s)}return n.appendChild(a),n}async function Rt(e){const t=Et(e);if(!t)return!1;const{payment:n}=t;if(!n||n.pg_provider!=="nicepayments")return!0;const a=vt();return a?(n.payment_method==="vbank"&&n.vbank_number&&!document.getElementById(we)&&(a.appendChild(It(t)),console.info(`[${E}] vbank info injected on mypage order show`)),!n.transaction_id||!n.paid_at||document.getElementById(ge)||(a.appendChild(Lt(e)),console.info(`[${E}] receipt button injected on mypage order show`)),!0):!1}function Ot(e){let t=0;const n=setInterval(()=>{t++,Rt(e).then(a=>{(a||t>=30)&&clearInterval(n)})},400)}function Dt(){const e=location.pathname.match(Ct);e&&Ot(e[1])}function Mt(){if(typeof window>"u")return;const e=window;if(e[he])return;e[he]=!0,console.info(`[${E}] mypage order show injector installed`);const t=(a=1500)=>setTimeout(Dt,a);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...a)=>{n(...a),t(600)},window.addEventListener("popstate",()=>t(500))}const $t="sirsoft-pay_nicepayments",Pe="__nicepayAdminPaymentMethodBrandInjectorInstalled",_e="__nicepayAdminPaymentMethodBrandSyncListenerAttached",Gt=/^\/admin\/ecommerce\/settings\/?$/,be='[data-nicepay-admin-payment-brand-mark="true"]',Bt=200,jt=120,Ft=[{id:"nicepay_naverpay",labels:["네이버페이 (나이스페이먼츠)","네이버페이로 결제 (나이스페이먼츠)","Naver Pay (NicePayments)","Pay with Naver Pay (NicePayments)"],shortLabels:[],markLines:["N"],markClassName:"bg-green-500 text-white"},{id:"nicepay_kakaopay",labels:["카카오페이 (나이스페이먼츠)","카카오페이로 결제 (나이스페이먼츠)","Kakao Pay (NicePayments)","Pay with Kakao Pay (NicePayments)"],shortLabels:[],markLines:["K"],markClassName:"bg-yellow-400 text-gray-950"},{id:"nicepay_samsungpay",labels:["삼성페이 (나이스페이먼츠)","삼성페이로 결제 (나이스페이먼츠)","Samsung Pay (NicePayments)","Pay with Samsung Pay (NicePayments)"],shortLabels:[],markLines:["S"],markClassName:"bg-blue-600 text-white"},{id:"nicepay_applepay",labels:["애플페이 (나이스페이먼츠)","애플페이로 결제 (나이스페이먼츠)","Apple Pay (NicePayments)","Pay with Apple Pay (NicePayments)"],shortLabels:[],markLines:["A"],markClassName:"bg-gray-900 text-white"},{id:"nicepay_payco",labels:["PAYCO (나이스페이먼츠)","PAYCO로 결제 (나이스페이먼츠)","PAYCO (NicePayments)","Pay with PAYCO (NicePayments)"],shortLabels:[],markLines:["P"],markClassName:"bg-red-500 text-white"},{id:"nicepay_skpay",labels:["11pay (나이스페이먼츠)","11pay로 결제 (나이스페이먼츠)","11pay (NicePayments)","Pay with 11pay (NicePayments)"],shortLabels:[],markLines:["11"],markClassName:"bg-orange-500 text-white"},{id:"nicepay_ssgpay",labels:["SSG페이 (나이스페이먼츠)","SSG페이로 결제 (나이스페이먼츠)","SSG Pay (NicePayments)","Pay with SSG Pay (NicePayments)"],shortLabels:[],markLines:["SSG"],markClassName:"bg-red-700 text-white"},{id:"nicepay_lpay",labels:["L.pay (나이스페이먼츠)","L.pay로 결제 (나이스페이먼츠)","L.pay (NicePayments)","Pay with L.pay (NicePayments)"],shortLabels:[],markLines:["L"],markClassName:"bg-purple-600 text-white"}];let Ne=null,A=null,j=!1;function Yt(){return window}function F(){if(!Gt.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e===""||e==="order_settings"}function qt(e){return e instanceof HTMLElement?e.className:""}function ke(e){return(e??"").replace(/\u200B/g,"").replace(/\s+/g," ").trim()}function Ht(e){const t=qt(e);return t.includes("excel-card")||t.includes("flex-center")&&t.includes("border")&&t.includes("gap-4")}function Se(e){return e.querySelector(".font-medium")}function Ut(e){const t=ke(Se(e)?.textContent),n=ke(e.textContent);return Ft.find(a=>a.shortLabels.some(r=>t===r)||a.labels.some(r=>n.includes(r)))??null}function Kt(e){return Array.from(e.querySelectorAll("div")).filter(Ht)}function Wt(e){return Array.from(e.querySelectorAll("svg, [data-icon], i")).find(n=>!n.closest("[data-drag-handle]")&&!n.closest(be)&&!n.closest('.row-stack, .excel-card-body, button, select, [role="switch"]'))??null}function Ce(e,t){e.dataset.nicepayAdminPaymentBrandMark="true",e.dataset.nicepayAdminPaymentMethod=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.textContent=t.markLines[0]??"")}function Vt(e){const t=document.createElement("span");return Ce(t,e),t}function zt(e,t){const n=e.querySelector(be);if(n instanceof HTMLSpanElement)return e.dataset.nicepayAdminPaymentMethod=t.id,Ce(n,t),!0;const a=Vt(t),r=Wt(e);if(r&&r.parentElement)return e.dataset.nicepayAdminPaymentMethod=t.id,r.replaceWith(a),!0;const o=Se(e);return o&&o.parentElement?(e.dataset.nicepayAdminPaymentMethod=t.id,o.parentElement.insertBefore(a,o),!0):!1}function Y(e=document){if(typeof window>"u"||typeof document>"u"||!F())return!1;let t=!1;return Kt(e).forEach(n=>{const a=Ut(n);a&&zt(n,a)&&(t=!0)}),t}function Jt(){j||(j=!0,window.setTimeout(()=>{j=!1,Y()},0))}function q(){A!==null&&(window.clearInterval(A),A=null)}function H(){if(!F())return;q(),Y();let e=0;A=window.setInterval(()=>{e+=1,Y(),e>=jt&&q()},Bt);const t=document.body;t[_e]||(t[_e]=!0,Ne=new MutationObserver(()=>Jt()),Ne.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function Ee(){if(F()){H();return}q()}function Xt(){if(typeof window>"u"||typeof document>"u")return;const e=Yt();if(e[Pe])return;e[Pe]=!0,console.info(`[${$t}] admin payment method brand sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>H()):H();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>Ee(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>Ee(),200))}const _="sirsoft-pay_nicepayments",U={info:(...e)=>console.info(`[${_}]`,...e),warn:(...e)=>console.warn(`[${_}]`,...e),error:(...e)=>console.error(`[${_}]`,...e)};function xe(){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 a=0;for(const[r,o]of Object.entries(Z)){const i=`${_}.${r}`;n.registerHandler(i,o,{category:"plugin",source:_}),a++}return a}function Ae(){Je(),wt(),St(),Mt(),Xt();const e=()=>{const t=xe();if(t>0){U.info(`${t} handler(s) registered`),Q();return}let n=0;const a=50,r=setInterval(()=>{n++;const o=xe();if(o>0){clearInterval(r),U.info(`${o} handler(s) registered (after ${n} retries)`),Q();return}n>=a&&(clearInterval(r),U.warn("ActionDispatcher not available after timeout"))},100)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}Ae(),window.__SirsoftNicepayments={identifier:_,handlers:Object.keys(Z),initPlugin:Ae}})(); +(function(){"use strict";function Oe(e){return/^https?:\/\//i.test(e)||e.startsWith("/api/")?e:e.startsWith("/plugins/")?`/api${e}`:e.startsWith("plugins/")?`/api/${e}`:e}function De(e){return e.trim().slice(0,160)}async function Be(e,t="nicepay-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:De(t)||"nicepay-window-closed"};try{const a=window.G7Core?.api;if(typeof a?.post=="function"){await a.post(e.closeReportUrl,n);return}await fetch(Oe(e.closeReportUrl),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),keepalive:!0})}catch(a){console.warn("[sirsoft-pay_nicepayments] failed to report payment window close",a)}}function Ge(e){return new Promise((t,n)=>{if(document.querySelector(`script[src="${e}"]`)){t();return}const a=document.createElement("script");a.src=e,a.onload=()=>t(),a.onerror=()=>n(new Error(`Failed to load script: ${e}`)),document.head.appendChild(a)})}function je(e,t){const n=document.createElement("form");n.id="nicepayForm",n.method="post",n.action=e,n.acceptCharset="utf-8";for(const[a,o]of Object.entries(t)){const r=document.createElement("input");r.type="hidden",r.name=a,r.value=o,n.appendChild(r)}return document.body.appendChild(n),n}const qe="/plugins/sirsoft-pay_nicepayments/payment/callback",Fe="https://web.nicepay.co.kr/v3/v3Payment.jsp";function Ye(){if(typeof navigator>"u")return!1;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 J(e,t){const{pgPaymentData:n,paymentMethod:a}=e.params??{},o=window.__templateApp?.globalState?._local,r=a??o?.paymentMethod??"card";if(!n){console.error("[sirsoft-pay_nicepayments] pgPaymentData is required");return}const i=window.G7Core;try{const s=await i.api.get("/modules/sirsoft-ecommerce/payments/client-config/nicepayments");if(!s.data){console.error("[sirsoft-pay_nicepayments] Failed to fetch client config",s);return}const l=s.data,u=Ye();if(!u&&(await Ge(l.sdk_url),typeof window.goPay!="function")){i?.toast?.error?.("나이스페이먼츠 SDK를 불러올 수 없습니다. 잠시 후 다시 시도해주세요."),i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:r});return}const b=window.location.origin+l.sign_data_url,m=typeof localStorage<"u"?localStorage.getItem("auth_token"):null,p={"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",Accept:"application/json"};m&&(p.Authorization=`Bearer ${m}`);const P=await fetch(b,{method:"POST",credentials:"include",headers:p,body:JSON.stringify({amt:n.amount,moid:n.order_number})});if(!P.ok)throw new Error("SignData 생성에 실패했습니다.");const g=await P.json(),I=window.location.origin+qe,f=typeof r=="string"&&r.startsWith("nicepay_"),C=f?"CARD":{card:"CARD",vbank:"VBANK",bank:"BANK",phone:"CELLPHONE"}[r??"card"]??"CARD",d={PayMethod:C,GoodsName:n.order_name,Amt:String(n.amount),MID:g.mid,Moid:n.order_number,BuyerName:n.customer_name??"",BuyerEmail:n.customer_email??"",BuyerTel:n.customer_phone??"",ReturnURL:I,EdiDate:g.ediDate,SignData:g.signData,CharSet:"utf-8",GoodsCl:"1",DirectShowOpt:"",DirectEasyPay:"",NicepayReserved:"",EasyPayMethod:"",MallReserved:"",MallReserved1:""};C==="CELLPHONE"&&(d.GoodsCl=n.goods_cl??"1");const c=l.useEscrow&&!f;if(d.TransType=c?"1":"0",f)switch(d.DirectShowOpt="CARD",d.MallReserved=`nicepay_easy_pay_method=${encodeURIComponent(r)}`,d.MallReserved1=r,r){case"nicepay_naverpay":d.DirectEasyPay="E020",d.EasyPayMethod="E020=CARD";break;case"nicepay_kakaopay":d.NicepayReserved="DirectKakao=Y";break;case"nicepay_samsungpay":d.DirectEasyPay="E021";break;case"nicepay_applepay":d.DirectEasyPay="E025";break;case"nicepay_payco":d.NicepayReserved="DirectPayco=Y";break;case"nicepay_skpay":d.NicepayReserved="DirectPay11=Y";break;case"nicepay_ssgpay":d.DirectEasyPay="E007";break;case"nicepay_lpay":d.DirectEasyPay="E018";break}try{const w=(await i.api.get(`/modules/sirsoft-ecommerce/user/orders/${n.order_number}`))?.data;if(w){const T=Number(w.total_tax_amount??0),E=Number(w.total_vat_amount??0),$e=Number(w.total_tax_free_amount??0);(T>0||E>0||$e>0)&&(d.TaxAmt=String(T),d.VatAmt=String(E),d.TaxFreeAmt=String($e))}}catch{}const y=je(I,d);if(u){y.action=Fe,y.acceptCharset="euc-kr";const S=y.querySelector('input[name="CharSet"]');S&&(S.value="euc-kr"),y.submit();return}window.nicepaySubmit=()=>{y.submit()};let k=!1;const z=new Set(document.body.children),Re=(S,w)=>{if(k)return;k=!0;const T=[S,w].filter(Boolean).join(": ")||"nicepay-window-closed";Be({closeReportUrl:l.close_report_url,oid:n.order_number,price:Number(n.amount),buyer_email:n.customer_email??"",buyer_phone:n.customer_phone??"",payment_method:r},T),window.removeEventListener("popstate",Me),Array.from(document.body.children).forEach(E=>{z.has(E)||E.remove()}),y.parentNode&&y.parentNode.removeChild(y),window.__sirsoftNicepayPaymentInProgress=!1,i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:r}),w&&i?.toast?.error?.(w)};window.nicepayClose=Re;const Me=()=>Re("","");window.history.pushState({nicepayOpen:!0},""),window.addEventListener("popstate",Me),window.goPay(y)}catch(s){console.error("[sirsoft-pay_nicepayments] requestPayment error",s),window.__sirsoftNicepayPaymentInProgress=!1,i?.state?.setLocal?.({isSubmittingOrder:!1,paymentMethod:r}),i?.toast?.error?.("결제 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.")}}const He={nicepay_naverpay:"네이버페이",nicepay_kakaopay:"카카오페이",nicepay_samsungpay:"삼성페이",nicepay_applepay:"애플페이",nicepay_payco:"PAYCO",nicepay_skpay:"11pay",nicepay_ssgpay:"SSG페이",nicepay_lpay:"L.pay"},Ue="0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)";function X(){const e=document.getElementById("nicepay_checkout_payment_section");if(!e)return null;const t=e.querySelectorAll("p");for(const n of t)if(n.textContent?.includes("간편결제"))return n.nextElementSibling;return null}function Q(){const e=X();e&&e.querySelectorAll("button").forEach(t=>{t.style.boxShadow="",t.style.outline=""})}function Ke(e){const t=X();if(!t)return;const n=He[e];t.querySelectorAll("button").forEach(a=>{a.textContent?.trim()===n?(a.style.boxShadow=Ue,a.style.outline="none"):(a.style.boxShadow="",a.style.outline="")})}function We(e){const t=e.params?.paymentMethod;if(!t)return;const n=typeof t=="string"&&t.indexOf("nicepay_")===0;window.G7Core?.state?.setLocal?.({paymentMethod:t,serverPaymentMethod:n?"card":t}),n?Ke(t):Q()}function Z(){const e=window.__templateApp;typeof e?.onGlobalStateChange=="function"&&e.onGlobalStateChange(t=>{const n=t?._local?.paymentMethod;typeof n=="string"&&(n.startsWith("nicepay_")||Q())})}const ee={requestPayment:J,setPaymentMethod:We},te="/user/orders",Ve="sirsoft-nicepayments",R="sirsoft-pay_nicepayments",ne="__sirsoftNicepayFetchInterceptorInstalled",ae="/api/modules/sirsoft-ecommerce/checkout",M="__sirsoftNicepayPaymentInProgress",_="__sirsoftNicepayCheckoutCache",h={info:(...e)=>console.info(`[${R}]`,...e),warn:(...e)=>console.warn(`[${R}]`,...e),error:(...e)=>console.error(`[${R}]`,...e)};function ze(){return window.location.pathname+window.location.search+window.location.hash}function Je(e,t){if(t.toUpperCase()!=="POST")return!1;const n=(e??"").split("?")[0].split("#")[0];return n===te||n.endsWith(te)}function $(e,t){if(t.toUpperCase()!=="GET")return!1;const n=(e??"").split("?")[0].split("#")[0];return n===ae||n.endsWith(ae)}function Xe(e){if(!e)return;let t=null;if(typeof e=="string")try{t=JSON.parse(e)}catch{return}else typeof e=="object"&&(t=e);if(!t)return;const n=t.payment_method;return typeof n=="string"&&n.length>0?n:void 0}function Qe(e,t){try{const n=JSON.parse(e);if("payment_method"in n)return JSON.stringify({...n,payment_method:t})}catch{}return e}function Ze(e){const t=[e.total_due_amount,e.total_amount];for(const n of t){const a=Number(n??0);if(Number.isFinite(a)&&a>0)return Math.floor(a)}return 0}function et(){const e=window;if(e[ne])return;e[ne]=!0;const t="__sirsoftPgOriginalFetch";e[t]||(e[t]=window.fetch.bind(window));const n=e[t],a=window.fetch.bind(window);window.fetch=async(o,r)=>{const i=typeof o=="string"?o:o instanceof URL?o.href:o.url,s=r?.method??(o instanceof Request?o.method:"GET");if($(i,s)){if(e[M]&&e[_])return h.info("payment in progress — serving cached checkout response"),new Response(e[_],{status:200,headers:{"Content-Type":"application/json"}});const c=await a(o,r);if(c.ok){const y=await c.text();return e[_]=y,new Response(y,{status:c.status,statusText:c.statusText,headers:c.headers})}return c}if(!Je(i,s))return a(o,r);let l,u=!1,b=r;if(r?.body&&typeof r.body=="string"){const c=Xe(r.body);typeof c=="string"&&c.length>0&&(l=c,c.startsWith("nicepay_")&&(u=!0,b={...r,body:Qe(r.body,"card")},h.info(`easy pay detected: replacing payment_method '${c}' → 'card'`)))}const p=await(u?n:a)(o,b);if(!p.ok)return p;let P=null;try{P=await p.clone().json()}catch{return p}const g=P?.data??P;if(!g?.requires_pg_payment&&!u)return p;const I=g.pg_provider===Ve;if(!u&&!I)return p;let f=g.pg_payment_data;if(!f&&u){const c=g.order;if(c){const y=c.options,k=y?.[0]?.product_name??String(c.order_number??""),z=(y?.length??0)>1?`${k} 외 ${(y?.length??0)-1}건`:k;f={order_number:c.order_number,order_name:z,amount:Ze(c),currency:"KRW",customer_name:c.orderer_name??null,customer_email:c.orderer_email??null,customer_phone:String(c.orderer_phone??"").replace(/[^0-9]/g,""),customer_key:c.user_id??null},h.info("pg_payment_data constructed from order (기본 PG 미설정)",{order_number:f.order_number,amount:f.amount})}}if(!f)return h.warn("nicepayments order detected but pg_payment_data missing"),p;const V=l??"card";h.info("intercepted order create response — opening PG popup",{paymentMethod:V}),J({params:{pgPaymentData:f,paymentMethod:V}}),e[M]=!0;const C={...g,requires_pg_payment:!1,redirect_url:ze()},d=P?.data?{...P,data:C}:C;return new Response(JSON.stringify(d),{status:p.status,statusText:p.statusText,headers:p.headers})},h.info("fetch order interceptor installed"),oe(e,15)}const re="__sirsoftNicepayAxiosCheckoutInterceptorInstalled";function oe(e,t){const n=window?.G7Core?.api?.client;if(!n?.interceptors){t>0&&setTimeout(()=>oe(e,t-1),300);return}e[re]||(e[re]=!0,n.interceptors.response.use(a=>{const o=(a?.config?.method??"").toLowerCase(),r=String((a?.config?.baseURL??"")+(a?.config?.url??""));return o==="get"&&$(r,"GET")&&a?.status===200&&(e[_]=JSON.stringify(a.data),h.info("checkout response cached (axios)")),a}),n.interceptors.request.use(a=>{const o=(a?.method??"").toLowerCase(),r=String((a?.baseURL??"")+(a?.url??""));return o==="get"&&$(r,"GET")&&e[M]&&e[_]&&(h.info("payment in progress — serving cached checkout response (axios)"),a.adapter=()=>Promise.resolve({data:JSON.parse(e[_]),status:200,statusText:"OK (cached)",headers:{"content-type":"application/json"},config:a,request:{}})),a}),h.info("axios checkout interceptor installed"),e[_]||(async()=>{try{await window?.G7Core?.api?.get?.("/modules/sirsoft-ecommerce/checkout")}catch{}})())}const tt="sirsoft-pay_nicepayments",ie="__nicepayCheckoutEasyPayInjectorInstalled",nt=/^\/shop\/checkout\/?$/,se="__nicepayCheckoutEasyPaySyncListenerAttached",at=200,rt=120,ot="nicepay_checkout_payment_section",ce=[{id:"nicepay_naverpay",labels:["네이버페이 (나이스페이먼츠)","네이버페이로 결제 (나이스페이먼츠)","Naver Pay (NicePayments)","Pay with Naver Pay (NicePayments)"],ko:{heading:"네이버페이 (나이스페이먼츠)",description:"네이버페이로 결제 (나이스페이먼츠)",title:"네이버페이로 결제 (나이스페이먼츠)"},en:{heading:"Naver Pay (NicePayments)",description:"Pay with Naver Pay (NicePayments)",title:"Pay with Naver Pay (NicePayments)"},markText:"N",markClassName:"bg-green-500 text-white"},{id:"nicepay_kakaopay",labels:["카카오페이 (나이스페이먼츠)","카카오페이로 결제 (나이스페이먼츠)","Kakao Pay (NicePayments)","Pay with Kakao Pay (NicePayments)"],ko:{heading:"카카오페이 (나이스페이먼츠)",description:"카카오페이로 결제 (나이스페이먼츠)",title:"카카오페이로 결제 (나이스페이먼츠)"},en:{heading:"Kakao Pay (NicePayments)",description:"Pay with Kakao Pay (NicePayments)",title:"Pay with Kakao Pay (NicePayments)"},markText:"K",markClassName:"bg-yellow-400 text-gray-950"},{id:"nicepay_samsungpay",labels:["삼성페이 (나이스페이먼츠)","삼성페이로 결제 (나이스페이먼츠)","Samsung Pay (NicePayments)","Pay with Samsung Pay (NicePayments)"],ko:{heading:"삼성페이 (나이스페이먼츠)",description:"삼성페이로 결제 (나이스페이먼츠)",title:"삼성페이로 결제 (나이스페이먼츠)"},en:{heading:"Samsung Pay (NicePayments)",description:"Pay with Samsung Pay (NicePayments)",title:"Pay with Samsung Pay (NicePayments)"},markText:"S",markClassName:"bg-blue-600 text-white"},{id:"nicepay_applepay",labels:["애플페이 (나이스페이먼츠)","애플페이로 결제 (나이스페이먼츠)","Apple Pay (NicePayments)","Pay with Apple Pay (NicePayments)"],ko:{heading:"애플페이 (나이스페이먼츠)",description:"애플페이로 결제 (나이스페이먼츠)",title:"애플페이로 결제 (나이스페이먼츠)"},en:{heading:"Apple Pay (NicePayments)",description:"Pay with Apple Pay (NicePayments)",title:"Pay with Apple Pay (NicePayments)"},markText:"A",markClassName:"bg-gray-900 text-white"},{id:"nicepay_payco",labels:["PAYCO (나이스페이먼츠)","PAYCO로 결제 (나이스페이먼츠)","PAYCO (NicePayments)","Pay with PAYCO (NicePayments)"],ko:{heading:"PAYCO (나이스페이먼츠)",description:"PAYCO로 결제 (나이스페이먼츠)",title:"PAYCO로 결제 (나이스페이먼츠)"},en:{heading:"PAYCO (NicePayments)",description:"Pay with PAYCO (NicePayments)",title:"Pay with PAYCO (NicePayments)"},markText:"P",markClassName:"bg-red-500 text-white"},{id:"nicepay_skpay",labels:["11pay (나이스페이먼츠)","11pay로 결제 (나이스페이먼츠)","11pay (NicePayments)","Pay with 11pay (NicePayments)"],ko:{heading:"11pay (나이스페이먼츠)",description:"11pay로 결제 (나이스페이먼츠)",title:"11pay로 결제 (나이스페이먼츠)"},en:{heading:"11pay (NicePayments)",description:"Pay with 11pay (NicePayments)",title:"Pay with 11pay (NicePayments)"},markText:"11",markClassName:"bg-orange-500 text-white"},{id:"nicepay_ssgpay",labels:["SSG페이 (나이스페이먼츠)","SSG페이로 결제 (나이스페이먼츠)","SSG Pay (NicePayments)","Pay with SSG Pay (NicePayments)"],ko:{heading:"SSG페이 (나이스페이먼츠)",description:"SSG페이로 결제 (나이스페이먼츠)",title:"SSG페이로 결제 (나이스페이먼츠)"},en:{heading:"SSG Pay (NicePayments)",description:"Pay with SSG Pay (NicePayments)",title:"Pay with SSG Pay (NicePayments)"},markText:"SSG",markClassName:"bg-red-700 text-white"},{id:"nicepay_lpay",labels:["L.pay (나이스페이먼츠)","L.pay로 결제 (나이스페이먼츠)","L.pay (NicePayments)","Pay with L.pay (NicePayments)"],ko:{heading:"L.pay (나이스페이먼츠)",description:"L.pay로 결제 (나이스페이먼츠)",title:"L.pay로 결제 (나이스페이먼츠)"},en:{heading:"L.pay (NicePayments)",description:"Pay with L.pay (NicePayments)",title:"Pay with L.pay (NicePayments)"},markText:"L",markClassName:"bg-purple-600 text-white"}];let le=null,x=null,O=!1;function it(){return window}function D(){return nt.test(window.location.pathname)}function st(e){return(e??"").replace(/\s+/g," ").trim()}function ct(e){return st(e).replace(/\u200B/g,"")}function lt(){return(document.documentElement.lang||navigator.language||"").toLowerCase().startsWith("ko")}function dt(e){return lt()?e.ko:e.en}function de(e){return e.replaceAll("네이버페이","네이​버페이").replaceAll("카카오페이","카카​오페이").replaceAll("삼성페이","삼성​페이").replaceAll("Naver Pay","Naver​ Pay").replaceAll("Kakao Pay","Kakao​ Pay").replaceAll("Samsung Pay","Samsung​ Pay").replaceAll("L.pay","L.​pay")}function ut(e){return ce.find(t=>t.id===e)??null}function yt(e){const t=ut(e.dataset.nicepayEasyPayMethod);if(t)return t;const n=ct(e.textContent);return ce.find(a=>a.labels.some(o=>n.includes(o)))??null}function ue(e){return e.querySelector(".flex.items-center.gap-2, .flex.items-center.gap-3")??e.querySelector(".flex.items-center")}function pt(e,t,n){e.style.textAlign="left";const a=ue(e);a&&(a.style.justifyContent="flex-start",a.style.textAlign="left",a.style.width="100%");const o=t?.parentElement??n?.parentElement;o instanceof HTMLElement&&(o.style.textAlign="left",o.style.minWidth="0")}function mt(e){const t=document.createElement("span");return t.dataset.nicepayEasyPayMark="true",t.dataset.nicepayEasyPayMethod=e.id,t.setAttribute("aria-hidden","true"),t.className=`inline-flex items-center justify-center rounded-lg text-xs font-bold ${e.markClassName}`,t.style.width="32px",t.style.height="32px",t.style.flex="0 0 32px",t.textContent=e.markText,t}function ft(e){delete e.dataset.kginicisBrandPaymentButton,delete e.dataset.kginicisBrandPaymentMethod,delete e.dataset.kginicisNaverpayBrandButton,delete e.dataset.nhnkcpEasyPayMethod,delete e.dataset.nhnkcpEasyPayVisible,delete e.dataset.nhnkcpEasyPayHidden,e.querySelectorAll('[data-kginicis-brand-payment-mark="true"], [data-kginicis-naverpay-mark="true"], [data-nhnkcp-easy-pay-mark="true"]').forEach(t=>{t.remove()})}function ht(e){return e.querySelector('[data-nicepay-easy-pay-mark="true"]')??e.querySelector('[data-kginicis-brand-payment-mark="true"], [data-kginicis-naverpay-mark="true"], [data-nhnkcp-easy-pay-mark="true"]')??e.querySelector("svg")??e.querySelector('i[class*="fa-"], i[role="img"], i')}function gt(e,t){const n=e.querySelector('[data-nicepay-easy-pay-mark="true"]');if(n){n.dataset.nicepayEasyPayMethod=t.id,n.className=`inline-flex items-center justify-center rounded-lg text-xs font-bold ${t.markClassName}`,n.textContent=t.markText;return}const a=mt(t),o=ht(e);if(o&&o.parentElement){o.replaceWith(a);return}ue(e)?.prepend(a)}function Pt(e,t){const n=Array.from(e.querySelectorAll("p")),a=dt(t),o=de(a.heading),r=de(a.description),i=n[0],s=n[1];i&&i.textContent!==o&&(i.textContent=o),i&&(i.setAttribute("aria-label",a.heading),i.style.textAlign="left",i.style.whiteSpace="normal",i.style.wordBreak="keep-all",i.style.overflowWrap="anywhere"),s&&s.textContent!==r&&(s.textContent=r),s&&(s.style.textAlign="left",s.style.fontSize="12px",s.style.lineHeight="1rem",s.style.whiteSpace="normal",s.style.wordBreak="keep-all",s.style.overflowWrap="anywhere"),pt(e,i,s),e.title=a.title}function wt(e,t){e.hidden&&(e.hidden=!1),e.disabled&&(e.disabled=!1),e.style.display==="none"&&e.style.removeProperty("display"),e.removeAttribute("aria-hidden"),e.dataset.nicepayEasyPayMethod=t.id,e.dataset.nicepayEasyPayVisible="true",ft(e),gt(e,t),Pt(e,t)}function _t(){const e=document.getElementById(ot);return e?(e.remove(),!0):!1}async function B(e=document){if(typeof window>"u"||typeof document>"u"||!D())return!1;let t=_t();return e.querySelectorAll("button").forEach(n=>{const a=yt(n);a&&(wt(n,a),t=!0)}),t}function bt(){O||(O=!0,window.setTimeout(()=>{O=!1,B()},0))}function ye(){x!==null&&(window.clearInterval(x),x=null)}function G(){if(!D())return;ye(),B();let e=0;x=window.setInterval(()=>{e+=1,B(),e>=rt&&ye()},at);const t=document.body;t[se]||(t[se]=!0,le=new MutationObserver(()=>bt()),le.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function pe(){D()&&G()}function Nt(){if(typeof window>"u"||typeof document>"u")return;const e=it();if(e[ie])return;e[ie]=!0,console.info(`[${tt}] checkout easy pay method sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>G()):G();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>pe(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>pe(),200))}const j="sirsoft-pay_nicepayments",me="__nicepayOcReceiptInjectorInstalled",fe="nicepay-oc-receipt-btn",Ct=/^\/shop\/orders\/([^/]+)\/complete$/;function he(){return localStorage.getItem("auth_token")}async function kt(e){const t=he();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 ge(e){const t=he();if(!t)return null;try{const n=await fetch(`/api/plugins/${j}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?await n.json():null}catch{return null}}function St(e){if(!e)return!1;const t=Array.from(document.querySelectorAll("div"));for(const n of t){const a=Array.from(n.children).filter(i=>i instanceof HTMLElement&&i.tagName==="SPAN");if(a.length<2)continue;const o=a[0].textContent?.trim();if(o!=="결제 방법"&&o!=="결제수단"&&o!=="결제 방식")continue;const r=a[a.length-1];return r.textContent?.trim()!==e&&(r.textContent=e,r.dataset.nicepayPaymentMethodPatched="true"),n.dataset.nicepayPaymentMethodRow="true",!0}return!1}async function Et(e){if(document.getElementById(fe))return;const t=await kt(e);if(!t||t.pg_provider!=="nicepayments"||!t.transaction_id||!t.paid_at)return;const n=Array.from(document.querySelectorAll('button[type="button"]')).find(s=>s.className.includes("bg-blue-600"));if(!n?.parentElement)return;const a=await ge(e);St(a?.payment_method_display_label);const o=n.parentElement,r=document.createElement("button");r.id=fe,r.type="button",r.className=n.className.replace(/bg-blue-\d+/g,"bg-green-600").replace(/hover:bg-blue-\d+/g,"hover:bg-green-700"),r.textContent="영수증 조회",r.addEventListener("click",async()=>{r.disabled=!0,r.textContent="로딩 중...";const s=await ge(e);r.disabled=!1,r.textContent="영수증 조회";const l=s?.receipt_url??null;l&&window.open(l,"nicepay_receipt","width=800,height=600,scrollbars=yes,resizable=yes")});const i=o.lastElementChild;o.insertBefore(r,i),console.info(`[${j}] receipt button injected on order complete page`)}function xt(){const e=location.pathname.match(Ct);e&&Et(e[1])}function At(){if(typeof window>"u")return;const e=window;if(e[me])return;e[me]=!0,console.info(`[${j}] order complete receipt injector installed`);const t=(a=1200)=>setTimeout(xt,a);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...a)=>{n(...a),t()},window.addEventListener("popstate",()=>t(500))}const A="sirsoft-pay_nicepayments",Pe="__nicepayOrderShowInjectorInstalled",we="nicepay-mp-receipt-row",_e="nicepay-mp-vbank-info",vt=/^\/mypage\/orders\/([^/]+)$/;function Lt(e){try{const n=window.G7Core?.getState,r=n?.()?.currentDataContext?.order?.data;return!r||r.order_number!==e?null:r}catch{return null}}function It(){return localStorage.getItem("auth_token")}async function be(e){const t=It();if(!t)return null;try{const n=await fetch(`/api/plugins/${A}/user/orders/${e}/receipt`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});return n.ok?await n.json():null}catch{return null}}function Tt(e,t){if(!t)return!1;const n=Array.from(e.querySelectorAll("div"));for(const a of n){const o=Array.from(a.children).filter(s=>s instanceof HTMLElement&&s.tagName==="SPAN");if(o.length<2)continue;const r=o[0].textContent?.trim();if(r!=="결제 방법"&&r!=="결제수단"&&r!=="Payment Method")continue;const i=o[o.length-1];return i.textContent?.trim()!==t&&(i.textContent=t,i.dataset.nicepayPaymentMethodPatched="true"),a.dataset.nicepayPaymentMethodRow="true",!0}return!1}function Rt(){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(a=>a.textContent?.includes("결제 정보"));if(!t)return null;const n=t.parentElement?.parentElement;return n?Array.from(n.children).find(a=>a.className?.includes("space-y"))??n:null}function Mt(e){const t=document.createElement("div");t.id=we,t.className="flex items-center justify-between";const n=document.createElement("span");n.className="text-gray-500 dark:text-gray-400 text-sm",n.textContent="영수증";const a=document.createElement("button");return a.type="button",a.className="inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",a.textContent="영수증 조회",a.addEventListener("click",async()=>{a.disabled=!0,a.textContent="로딩 중...";const o=await be(e);a.disabled=!1,a.textContent="영수증 조회";const r=o?.receipt_url??null;r&&window.open(r,"nicepay_receipt","width=800,height=600,scrollbars=yes,resizable=yes")}),t.appendChild(n),t.appendChild(a),t}function $t(e){if(!e)return"";try{const t=new Date(e);if(Number.isNaN(t.getTime()))return e;const n=a=>String(a).padStart(2,"0");return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}catch{return e}}function v(e,t,n="font-medium text-gray-900 dark:text-white"){const a=document.createElement("div");a.className="flex justify-between text-sm";const o=document.createElement("span");o.className="text-gray-600 dark:text-gray-400",o.textContent=e;const r=document.createElement("span");return r.className=n,r.textContent=t,a.appendChild(o),a.appendChild(r),a}function Ot(e){const t=e.payment??{},n=document.createElement("div");n.id=_e,n.className="pt-4 mt-2 border-t border-gray-200 dark:border-gray-700";const a=document.createElement("div");a.className="bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-700 rounded-lg p-4";const o=document.createElement("h3");o.className="text-sm font-semibold text-blue-800 dark:text-blue-300 mb-3",o.textContent="가상계좌 입금 안내";const r=document.createElement("div");r.className="space-y-2",r.appendChild(v("은행",t.vbank_name??"")),r.appendChild(v("계좌번호",t.vbank_number??"","font-medium font-mono text-gray-900 dark:text-white")),r.appendChild(v("예금주",t.vbank_holder??"")),r.appendChild(v("입금 금액",e.total_amount_formatted??"","font-bold text-blue-600 dark:text-blue-400"));const i=t.vbank_due_at;if(i){const s=document.createElement("div");s.className="flex justify-between text-sm pt-2 border-t border-blue-200 dark:border-blue-700 mt-2";const l=document.createElement("span");l.className="text-gray-600 dark:text-gray-400",l.textContent="입금 기한";const u=document.createElement("span");u.className="font-medium text-red-600 dark:text-red-400",u.textContent=$t(i),s.appendChild(l),s.appendChild(u),r.appendChild(s)}if(a.appendChild(o),a.appendChild(r),!t.paid_at){const s=document.createElement("p");s.className="text-xs text-blue-700 dark:text-blue-300 mt-3",s.textContent="입금 기한 내에 입금이 완료되지 않으면 주문이 자동 취소됩니다.",a.appendChild(s)}return n.appendChild(a),n}async function Dt(e){const t=Lt(e);if(!t)return!1;const{payment:n}=t;if(!n||n.pg_provider!=="nicepayments")return!0;const a=Rt();if(!a)return!1;const o=await be(e);return Tt(a,o?.payment_method_display_label),n.payment_method==="vbank"&&n.vbank_number&&!document.getElementById(_e)&&(a.appendChild(Ot(t)),console.info(`[${A}] vbank info injected on mypage order show`)),!n.transaction_id||!n.paid_at||document.getElementById(we)||(a.appendChild(Mt(e)),console.info(`[${A}] receipt button injected on mypage order show`)),!0}function Bt(e){let t=0;const n=setInterval(()=>{t++,Dt(e).then(a=>{(a||t>=30)&&clearInterval(n)})},400)}function Gt(){const e=location.pathname.match(vt);e&&Bt(e[1])}function jt(){if(typeof window>"u")return;const e=window;if(e[Pe])return;e[Pe]=!0,console.info(`[${A}] mypage order show injector installed`);const t=(a=1500)=>setTimeout(Gt,a);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...a)=>{n(...a),t(600)},window.addEventListener("popstate",()=>t(500))}const qt="sirsoft-pay_nicepayments",Ne="__nicepayAdminPaymentMethodBrandInjectorInstalled",Ce="__nicepayAdminPaymentMethodBrandSyncListenerAttached",Ft=/^\/admin\/ecommerce\/settings\/?$/,ke='[data-nicepay-admin-payment-brand-mark="true"]',Yt=200,Ht=120,Ut=[{id:"nicepay_naverpay",labels:["네이버페이 (나이스페이먼츠)","네이버페이로 결제 (나이스페이먼츠)","Naver Pay (NicePayments)","Pay with Naver Pay (NicePayments)"],shortLabels:[],markLines:["N"],markClassName:"bg-green-500 text-white"},{id:"nicepay_kakaopay",labels:["카카오페이 (나이스페이먼츠)","카카오페이로 결제 (나이스페이먼츠)","Kakao Pay (NicePayments)","Pay with Kakao Pay (NicePayments)"],shortLabels:[],markLines:["K"],markClassName:"bg-yellow-400 text-gray-950"},{id:"nicepay_samsungpay",labels:["삼성페이 (나이스페이먼츠)","삼성페이로 결제 (나이스페이먼츠)","Samsung Pay (NicePayments)","Pay with Samsung Pay (NicePayments)"],shortLabels:[],markLines:["S"],markClassName:"bg-blue-600 text-white"},{id:"nicepay_applepay",labels:["애플페이 (나이스페이먼츠)","애플페이로 결제 (나이스페이먼츠)","Apple Pay (NicePayments)","Pay with Apple Pay (NicePayments)"],shortLabels:[],markLines:["A"],markClassName:"bg-gray-900 text-white"},{id:"nicepay_payco",labels:["PAYCO (나이스페이먼츠)","PAYCO로 결제 (나이스페이먼츠)","PAYCO (NicePayments)","Pay with PAYCO (NicePayments)"],shortLabels:[],markLines:["P"],markClassName:"bg-red-500 text-white"},{id:"nicepay_skpay",labels:["11pay (나이스페이먼츠)","11pay로 결제 (나이스페이먼츠)","11pay (NicePayments)","Pay with 11pay (NicePayments)"],shortLabels:[],markLines:["11"],markClassName:"bg-orange-500 text-white"},{id:"nicepay_ssgpay",labels:["SSG페이 (나이스페이먼츠)","SSG페이로 결제 (나이스페이먼츠)","SSG Pay (NicePayments)","Pay with SSG Pay (NicePayments)"],shortLabels:[],markLines:["SSG"],markClassName:"bg-red-700 text-white"},{id:"nicepay_lpay",labels:["L.pay (나이스페이먼츠)","L.pay로 결제 (나이스페이먼츠)","L.pay (NicePayments)","Pay with L.pay (NicePayments)"],shortLabels:[],markLines:["L"],markClassName:"bg-purple-600 text-white"}];let Se=null,L=null,q=!1;function Kt(){return window}function F(){if(!Ft.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e===""||e==="order_settings"}function Wt(e){return e instanceof HTMLElement?e.className:""}function Ee(e){return(e??"").replace(/\u200B/g,"").replace(/\s+/g," ").trim()}function Vt(e){const t=Wt(e);return t.includes("excel-card")||t.includes("flex-center")&&t.includes("border")&&t.includes("gap-4")}function xe(e){return e.querySelector(".font-medium")}function zt(e){const t=Ee(xe(e)?.textContent),n=Ee(e.textContent);return Ut.find(a=>a.shortLabels.some(o=>t===o)||a.labels.some(o=>n.includes(o)))??null}function Jt(e){return Array.from(e.querySelectorAll("div")).filter(Vt)}function Xt(e){return Array.from(e.querySelectorAll("svg, [data-icon], i")).find(n=>!n.closest("[data-drag-handle]")&&!n.closest(ke)&&!n.closest('.row-stack, .excel-card-body, button, select, [role="switch"]'))??null}function Ae(e,t){e.dataset.nicepayAdminPaymentBrandMark="true",e.dataset.nicepayAdminPaymentMethod=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.textContent=t.markLines[0]??"")}function Qt(e){const t=document.createElement("span");return Ae(t,e),t}function Zt(e,t){const n=e.querySelector(ke);if(n instanceof HTMLSpanElement)return e.dataset.nicepayAdminPaymentMethod=t.id,Ae(n,t),!0;const a=Qt(t),o=Xt(e);if(o&&o.parentElement)return e.dataset.nicepayAdminPaymentMethod=t.id,o.replaceWith(a),!0;const r=xe(e);return r&&r.parentElement?(e.dataset.nicepayAdminPaymentMethod=t.id,r.parentElement.insertBefore(a,r),!0):!1}function Y(e=document){if(typeof window>"u"||typeof document>"u"||!F())return!1;let t=!1;return Jt(e).forEach(n=>{const a=zt(n);a&&Zt(n,a)&&(t=!0)}),t}function en(){q||(q=!0,window.setTimeout(()=>{q=!1,Y()},0))}function H(){L!==null&&(window.clearInterval(L),L=null)}function U(){if(!F())return;H(),Y();let e=0;L=window.setInterval(()=>{e+=1,Y(),e>=Ht&&H()},Yt);const t=document.body;t[Ce]||(t[Ce]=!0,Se=new MutationObserver(()=>en()),Se.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function ve(){if(F()){U();return}H()}function tn(){if(typeof window>"u"||typeof document>"u")return;const e=Kt();if(e[Ne])return;e[Ne]=!0,console.info(`[${qt}] admin payment method brand sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>U()):U();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>ve(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>ve(),200))}const K="sirsoft-pay_nicepayments",Le="__nicepayAdminPaymentDisplayInjectorInstalled",nn=/^\/admin\/ecommerce\/orders\/([^/]+)$/;function an(){return localStorage.getItem("auth_token")}async function rn(e){const t=an();if(!t)return null;try{const n=await fetch(`/api/plugins/${K}/admin/orders/${e}/transaction-status`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/json"}});if(!n.ok)return null;const a=await n.json();return("data"in a?a.data:a)??null}catch{return null}}function on(e){return Array.from(e.children).filter(t=>t instanceof HTMLElement)}function sn(e,t){let n=!1;const a=Array.from(e.querySelectorAll("div"));for(const o of a){const r=on(o),i=r.findIndex(m=>m.textContent?.trim()==="결제수단");if(i<0)continue;const s=r.slice(i+1).find(m=>m.textContent?.trim()&&m.textContent.trim()!=="결제수단");if(!s)continue;const l=r.slice(i+2).find(m=>/^\(.+\)$/.test(m.textContent?.trim()??"")),u=l?.textContent?.trim().replace(/^\(|\)$/g,"")??"",b=u&&t.endsWith(")")?t.replace(/\)$/,`, ${u})`):t;s.textContent?.trim()!==b&&(s.textContent=b,s.dataset.nicepayPaymentMethodPatched="true"),o.dataset.nicepayPaymentMethodRow="admin-order",l&&u&&(l.textContent="",l.style.display="none",l.dataset.nicepayPaymentInstallmentHidden="true"),n=!0}return n}function cn(e,t,n){const o=Array.from(e.querySelectorAll("span")).filter(r=>r.textContent?.trim()===t).find(r=>{const i=typeof r.className=="string"?r.className:"";return i.includes("rounded-full")||i.includes("font-medium")});return o?(o.textContent=n,o.dataset.nicepayPaymentMethodPatched="true",o.dataset.nicepayPaymentMethodBadge="admin-order",!0):!1}function ln(e,t){const n=t?._pay_method_label,a=t?._embedded_pg_provider_label,o=t?._base_pay_method_label;if(!n||!a)return!1;const r=sn(e,n),i=o?cn(e,o,a):!1;return r||i}async function dn(e){const t=document.getElementById("section_payment_info")??document.getElementById("payment_info_card");if(!t)return!1;const n=await rn(e);return n&&ln(t,n)&&console.info(`[${K}] payment method display patched on admin order detail`),!0}function un(e){let t=0;const n=setInterval(()=>{t++,dn(e).then(a=>{(a||t>=30)&&clearInterval(n)})},400)}function yn(){const e=location.pathname.match(nn);e&&un(e[1])}function pn(){if(typeof window>"u")return;const e=window;if(e[Le])return;e[Le]=!0,console.info(`[${K}] admin order payment display injector installed`);const t=(a=1500)=>setTimeout(yn,a);document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>t()):t();const n=history.pushState.bind(history);history.pushState=(...a)=>{n(...a),t(600)},window.addEventListener("popstate",()=>t(500))}const N="sirsoft-pay_nicepayments",W={info:(...e)=>console.info(`[${N}]`,...e),warn:(...e)=>console.warn(`[${N}]`,...e),error:(...e)=>console.error(`[${N}]`,...e)};function Ie(){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 a=0;for(const[o,r]of Object.entries(ee)){const i=`${N}.${o}`;n.registerHandler(i,r,{category:"plugin",source:N}),a++}return a}function Te(){et(),Nt(),At(),jt(),tn(),pn();const e=()=>{const t=Ie();if(t>0){W.info(`${t} handler(s) registered`),Z();return}let n=0;const a=50,o=setInterval(()=>{n++;const r=Ie();if(r>0){clearInterval(o),W.info(`${r} handler(s) registered (after ${n} retries)`),Z();return}n>=a&&(clearInterval(o),W.warn("ActionDispatcher not available after timeout"))},100)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}Te(),window.__SirsoftNicepayments={identifier:N,handlers:Object.keys(ee),initPlugin:Te}})(); //# sourceMappingURL=plugin.iife.js.map diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js.map b/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js.map index 4386bc6a..dd9499b6 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js.map +++ b/plugins/_bundled/sirsoft-pay_nicepayments/dist/js/plugin.iife.js.map @@ -1 +1 @@ -{"version":3,"file":"plugin.iife.js","sources":["../../resources/js/paymentCloseReport.ts","../../resources/js/handlers/requestPayment.ts","../../resources/js/handlers/setPaymentMethod.ts","../../resources/js/handlers/index.ts","../../resources/js/orderResponseInterceptor.ts","../../resources/js/checkoutEasyPayInjector.ts","../../resources/js/orderCompleteReceiptInjector.ts","../../resources/js/mypageOrderShowInjector.ts","../../resources/js/adminPaymentMethodBrandInjector.ts","../../resources/js/index.ts"],"sourcesContent":["export interface PaymentCloseReportContext {\n closeReportUrl?: string;\n oid: string;\n price: number;\n buyer_email?: string;\n buyer_phone?: string;\n payment_method?: string;\n}\n\nfunction resolveApiUrl(url: string): string {\n if (/^https?:\\/\\//i.test(url) || url.startsWith('/api/')) {\n return url;\n }\n\n if (url.startsWith('/plugins/')) {\n return `/api${url}`;\n }\n\n if (url.startsWith('plugins/')) {\n return `/api/${url}`;\n }\n\n return url;\n}\n\nfunction trimReason(reason: string): string {\n return reason.trim().slice(0, 160);\n}\n\nexport async function reportPaymentWindowClosed(\n context: PaymentCloseReportContext,\n reason = 'nicepay-window-closed',\n): Promise {\n if (!context.closeReportUrl) {\n return;\n }\n\n const payload = {\n oid: context.oid,\n price: Number(context.price),\n buyer_email: context.buyer_email ?? '',\n buyer_phone: context.buyer_phone ?? '',\n payment_method: context.payment_method ?? '',\n reason: trimReason(reason) || 'nicepay-window-closed',\n };\n\n try {\n const apiClient = ((window as any).G7Core)?.api;\n if (typeof apiClient?.post === 'function') {\n await apiClient.post(context.closeReportUrl, payload);\n return;\n }\n\n await fetch(resolveApiUrl(context.closeReportUrl), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n keepalive: true,\n });\n } catch (error) {\n console.warn('[sirsoft-pay_nicepayments] failed to report payment window close', error);\n }\n}\n","import { reportPaymentWindowClosed } from '../paymentCloseReport';\n\ninterface G7ApiResponse {\n data?: unknown;\n}\n\ninterface G7CoreApi {\n get: (url: string) => Promise;\n post?: (url: string, data?: Record) => Promise;\n}\n\ninterface G7CoreToast {\n error: (message: string) => void;\n}\n\ninterface G7CoreStateApi {\n setLocal: (state: Record) => void;\n}\n\ninterface G7Core {\n api: G7CoreApi;\n toast?: G7CoreToast;\n state?: G7CoreStateApi;\n}\n\ninterface TemplateLocalState {\n paymentMethod?: string;\n}\n\ninterface TemplateApp {\n globalState?: {\n _local?: TemplateLocalState;\n };\n}\n\ninterface PgPaymentData {\n order_number: string;\n order_name: string;\n amount: number;\n currency?: string;\n customer_name?: string;\n customer_email?: string;\n customer_phone?: string;\n goods_cl?: string; // 휴대폰결제 상품 유형: '0'=디지털컨텐츠, '1'=실물\n}\n\ninterface RequestPaymentParams {\n pgPaymentData: PgPaymentData;\n paymentMethod?: string;\n}\n\ninterface ClientConfig {\n mid: string;\n sdk_url: string;\n sign_data_url: string;\n close_report_url?: string;\n useEscrow?: boolean;\n}\n\ninterface SignDataResponse {\n ediDate: string;\n signData: string;\n mid: string;\n}\n\ninterface PaymentAction {\n params?: RequestPaymentParams;\n}\n\ndeclare global {\n interface Window {\n nicepaySubmit: () => void;\n nicepayClose: (resultCode: string, resultMsg: string) => void;\n goPay: (form: HTMLFormElement) => void;\n G7Core: G7Core;\n __templateApp?: TemplateApp;\n }\n}\n\nfunction loadScript(src: string): Promise {\n return new Promise((resolve, reject) => {\n if (document.querySelector(`script[src=\"${src}\"]`)) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.src = src;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load script: ${src}`));\n document.head.appendChild(script);\n });\n}\n\nfunction createPaymentForm(action: string, fields: Record): HTMLFormElement {\n const form = document.createElement('form');\n form.id = 'nicepayForm';\n form.method = 'post';\n form.action = action;\n // CharSet 필드와 일치 — 모바일은 form.acceptCharset 으로 charset 결정\n form.acceptCharset = 'utf-8';\n\n for (const [name, value] of Object.entries(fields)) {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = value;\n form.appendChild(input);\n }\n\n document.body.appendChild(form);\n return form;\n}\n\nconst CALLBACK_PATH = '/plugins/sirsoft-pay_nicepayments/payment/callback';\n\n// 나이스페이 v3 모바일 결제창 endpoint — 폼을 직접 POST 하면 NicePay 모바일 페이지로 전체 redirect.\n// PC 와 달리 SDK 불필요. 결제 완료 후 ReturnURL 로 redirect.\nconst NICEPAY_MOBILE_ENDPOINT = 'https://web.nicepay.co.kr/v3/v3Payment.jsp';\n\n/**\n * 모바일 환경 판별.\n *\n * 판별 우선순위:\n * 1. navigator.userAgentData.mobile — 브라우저가 직접 판단하는 표준 API (Chrome/Edge 90+)\n * 2. UA 문자열 정규식 — iOS/Android/Windows Phone 및 주요 인앱 브라우저\n * 3. maxTouchPoints 보조 판단 — iPadOS처럼 데스크탑 UA를 보내는 터치 기기 처리\n * (Windows/Mac 데스크탑 터치스크린 오탐 방지를 위해 플랫폼 확인 병행)\n */\nfunction isMobileDevice(): boolean {\n if (typeof navigator === 'undefined') return false;\n\n // 1단계: User Agent Client Hints (가장 정확, 브라우저가 직접 판단)\n const nav = navigator as Navigator & { userAgentData?: { mobile: boolean } };\n if (nav.userAgentData?.mobile !== undefined) {\n return nav.userAgentData.mobile;\n }\n\n // 2단계: UA 문자열 파싱\n const ua = (navigator.userAgent || '').toLowerCase();\n const mobileUA = /android|iphone|ipod|windows phone|iemobile|blackberry|opera mini|mobile safari/;\n if (mobileUA.test(ua)) return true;\n\n // 3단계: iPadOS 등 데스크탑 UA를 보내는 터치 기기 판별\n // maxTouchPoints > 1 이면 실제 멀티터치 기기 (터치스크린 노트북은 보통 1)\n // Windows/Mac 플랫폼이면 데스크탑으로 간주하여 오탐 방지\n const touchPoints = (navigator as Navigator & { maxTouchPoints?: number }).maxTouchPoints ?? 0;\n if (touchPoints > 1 && !ua.includes('windows') && !ua.includes('macintosh')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * 나이스페이먼츠 결제창 호출 핸들러 (나이스페이 구형 API, goPay 방식)\n *\n * 체크아웃 레이아웃에서 주문 생성 API 성공 후 호출됩니다:\n * handler: \"sirsoft-pay_nicepayments.requestPayment\"\n * params: { pgPaymentData: response.data.pg_payment_data }\n *\n * 호출 순서:\n * 1. Client Config API 호출 → MID, SDK URL, SignData URL 획득\n * 2. 환경 분기: PC 면 SDK 동적 로드 / 모바일 은 SDK 생략\n * 3. 서버에서 EdiDate + SignData 생성\n * 4. 결제 폼 생성:\n * - PC: 폼 action=ReturnURL → goPay(form) 으로 iframe 팝업\n * - 모바일: 폼 action=https://web.nicepay.co.kr/v3/v3Payment.jsp → 직접 submit (전체 페이지 redirect)\n * 5. 결제 완료 시 나이스페이먼츠가 ReturnURL(POST)로 인증값 전달\n *\n * NicePay 의 PC SDK 는 모바일 자동 감지를 하지 않으므로 UA 기반 분기가 필수.\n * 잘못 분기하면 모바일에서도 PC 팝업이 떠 사용성이 매우 나빠짐.\n */\nexport async function requestPaymentHandler(action: PaymentAction, _context?: unknown): Promise {\n const { pgPaymentData, paymentMethod: paramPaymentMethod } = action.params ?? {};\n\n const localState = window.__templateApp?.globalState?._local;\n const paymentMethod = paramPaymentMethod ?? localState?.paymentMethod ?? 'card';\n\n if (!pgPaymentData) {\n console.error('[sirsoft-pay_nicepayments] pgPaymentData is required');\n return;\n }\n\n const G7Core = window.G7Core;\n\n try {\n // 1. Client Config 가져오기\n const configJson = await G7Core.api.get('/modules/sirsoft-ecommerce/payments/client-config/nicepayments');\n\n if (!configJson.data) {\n console.error('[sirsoft-pay_nicepayments] Failed to fetch client config', configJson);\n return;\n }\n\n const config = configJson.data as ClientConfig;\n const isMobile = isMobileDevice();\n\n // 2. PC 인 경우에만 SDK 로드 (모바일 은 직접 form submit 으로 NicePay 모바일 페이지로 이동)\n if (!isMobile) {\n await loadScript(config.sdk_url);\n\n if (typeof window.goPay !== 'function') {\n G7Core?.toast?.error?.('나이스페이먼츠 SDK를 불러올 수 없습니다. 잠시 후 다시 시도해주세요.');\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n return;\n }\n }\n\n // 3. 서버에서 EdiDate + SignData 생성\n // sign-data 엔드포인트는 'auth' 미들웨어가 걸려있어 Sanctum Bearer 토큰\n // 또는 세션 쿠키 중 하나가 필요. SPA 모드에서 토큰만 있는 경우를 대비해\n // localStorage 의 auth_token 을 Authorization 헤더로 명시 전달하고,\n // credentials:include 로 세션 쿠키도 함께 전송 (둘 중 하나만 있어도 통과).\n const signDataUrl = window.location.origin + config.sign_data_url;\n const authToken = (typeof localStorage !== 'undefined') ? localStorage.getItem('auth_token') : null;\n const signDataHeaders: Record = {\n 'Content-Type': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'Accept': 'application/json',\n };\n if (authToken) {\n signDataHeaders['Authorization'] = `Bearer ${authToken}`;\n }\n const signDataRes = await fetch(signDataUrl, {\n method: 'POST',\n credentials: 'include',\n headers: signDataHeaders,\n body: JSON.stringify({ amt: pgPaymentData.amount, moid: pgPaymentData.order_number }),\n });\n\n if (!signDataRes.ok) {\n throw new Error('SignData 생성에 실패했습니다.');\n }\n\n const signData: SignDataResponse = await signDataRes.json();\n const callbackUrl = window.location.origin + CALLBACK_PATH;\n\n // 4. 결제 폼 생성\n // 간편결제는 PayMethod='CARD' + DirectShowOpt='CARD' + 방식별 directive 필드 조합.\n // PayMethod에 'KAKAOPAY' 등을 직접 넣으면 [W004] 발생 — gnu5 orderform.js 동일 방식.\n const isEasyPay = typeof paymentMethod === 'string' && paymentMethod.startsWith('nicepay_');\n\n const payMethodMap: Record = {\n card: 'CARD',\n vbank: 'VBANK',\n bank: 'BANK',\n phone: 'CELLPHONE',\n };\n const payMethod = isEasyPay ? 'CARD' : (payMethodMap[paymentMethod ?? 'card'] ?? 'CARD');\n\n const formFields: Record = {\n PayMethod: payMethod,\n GoodsName: pgPaymentData.order_name,\n Amt: String(pgPaymentData.amount),\n MID: signData.mid,\n Moid: pgPaymentData.order_number,\n BuyerName: pgPaymentData.customer_name ?? '',\n BuyerEmail: pgPaymentData.customer_email ?? '',\n BuyerTel: pgPaymentData.customer_phone ?? '',\n ReturnURL: callbackUrl,\n EdiDate: signData.ediDate,\n SignData: signData.signData,\n CharSet: 'utf-8',\n GoodsCl: '1',\n DirectShowOpt: '',\n DirectEasyPay: '',\n NicepayReserved: '',\n EasyPayMethod: '',\n };\n\n // 휴대폰결제: 상품 유형 덮어쓰기 (0:디지털컨텐츠, 1:실물)\n if (payMethod === 'CELLPHONE') {\n formFields.GoodsCl = pgPaymentData.goods_cl ?? '1';\n }\n\n // TransType: 에스크로 활성 + 일반결제 → '1', 그 외(간편결제 포함) → '0'\n const useEscrow = config.useEscrow && !isEasyPay;\n formFields.TransType = useEscrow ? '1' : '0';\n\n // 간편결제 directive 필드 설정 — gnu5 orderform.js switch(settle_method==='간편결제') 동일\n if (isEasyPay) {\n formFields.DirectShowOpt = 'CARD';\n switch (paymentMethod) {\n case 'nicepay_naverpay':\n formFields.DirectEasyPay = 'E020';\n formFields.EasyPayMethod = 'E020=CARD';\n break;\n case 'nicepay_kakaopay':\n formFields.NicepayReserved = 'DirectKakao=Y';\n break;\n case 'nicepay_samsungpay':\n formFields.DirectEasyPay = 'E021';\n break;\n case 'nicepay_applepay':\n formFields.DirectEasyPay = 'E025';\n break;\n case 'nicepay_payco':\n formFields.NicepayReserved = 'DirectPayco=Y';\n break;\n case 'nicepay_skpay':\n formFields.NicepayReserved = 'DirectPay11=Y';\n break;\n case 'nicepay_ssgpay':\n formFields.DirectEasyPay = 'E007';\n break;\n case 'nicepay_lpay':\n formFields.DirectEasyPay = 'E018';\n break;\n }\n }\n\n // 4-2. 과세/비과세 금액 조회 (optional — 실패해도 결제 진행)\n try {\n const orderRes = await G7Core.api.get(`/modules/sirsoft-ecommerce/user/orders/${pgPaymentData.order_number}`);\n const od = orderRes?.data as Record | null | undefined;\n if (od) {\n const taxAmt = Number(od['total_tax_amount'] ?? 0);\n const vatAmt = Number(od['total_vat_amount'] ?? 0);\n const taxFreeAmt = Number(od['total_tax_free_amount'] ?? 0);\n if (taxAmt > 0 || vatAmt > 0 || taxFreeAmt > 0) {\n formFields.TaxAmt = String(taxAmt);\n formFields.VatAmt = String(vatAmt);\n formFields.TaxFreeAmt = String(taxFreeAmt);\n }\n }\n } catch {\n // 과세 필드는 선택 사항 — 조회 실패 시 미포함 상태로 진행\n }\n\n const form = createPaymentForm(callbackUrl, formFields);\n\n if (isMobile) {\n // 모바일 결제창 호출:\n // - form.action = NicePay 모바일 endpoint\n // - acceptCharset = 'euc-kr' : NicePay v3 mobile 은 EUC-KR 로 form 데이터를 받음\n // (NicePay 공식 샘플과 동일). 브라우저가 자동으로 UTF-8 → EUC-KR 변환.\n // - CharSet 입력값도 'euc-kr' 로 일치 — NicePay 가 같은 인코딩으로 디코딩하도록.\n // (utf-8 그대로 두면 GoodsName/BuyerName 한글이 mojibake \"遺?瑜?\" 로 깨짐)\n // 결제 완료 후 NicePay 가 ReturnURL 로 redirect.\n form.action = NICEPAY_MOBILE_ENDPOINT;\n form.acceptCharset = 'euc-kr';\n const charsetInput = form.querySelector('input[name=\"CharSet\"]') as HTMLInputElement | null;\n if (charsetInput) {\n charsetInput.value = 'euc-kr';\n }\n form.submit();\n // 페이지 자체가 redirect 되므로 정리 로직 불필요. submit 후 이 함수의 후속 코드는 실행되지 않음.\n return;\n }\n\n // 이하 PC 전용 — iframe 팝업 + nicepaySubmit / nicepayClose 콜백 처리\n\n // 5. 나이스페이 전역 콜백 정의\n window.nicepaySubmit = () => {\n form.submit();\n };\n\n let paymentClosed = false;\n\n // goPay() 전 body 자식 스냅샷 — SDK가 추가하는 오버레이/iframe 식별용\n const bodySnapshot = new Set(document.body.children);\n\n const closePayment = (_resultCode: string, resultMsg: string) => {\n if (paymentClosed) return;\n paymentClosed = true;\n const closeReason = [_resultCode, resultMsg].filter(Boolean).join(': ') || 'nicepay-window-closed';\n void reportPaymentWindowClosed({\n closeReportUrl: config.close_report_url,\n oid: pgPaymentData.order_number,\n price: Number(pgPaymentData.amount),\n buyer_email: pgPaymentData.customer_email ?? '',\n buyer_phone: pgPaymentData.customer_phone ?? '',\n payment_method: paymentMethod,\n }, closeReason);\n window.removeEventListener('popstate', handlePopState);\n // SDK가 body에 추가한 오버레이/iframe 등 제거\n Array.from(document.body.children).forEach(el => {\n if (!bodySnapshot.has(el)) el.remove();\n });\n if (form.parentNode) form.parentNode.removeChild(form);\n (window as unknown as Record)['__sirsoftNicepayPaymentInProgress'] = false;\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n if (resultMsg) G7Core?.toast?.error?.(resultMsg);\n };\n\n window.nicepayClose = closePayment;\n\n // 뒤로가기(popstate) 감지 → 결제창 정리\n const handlePopState = () => closePayment('', '');\n\n // 결제창 열기 전 history state 추가 → 뒤로가기 시 popstate 발생\n window.history.pushState({ nicepayOpen: true }, '');\n window.addEventListener('popstate', handlePopState);\n\n // 6. PC 결제창 호출 (iframe 팝업)\n window.goPay(form);\n\n } catch (error: unknown) {\n console.error('[sirsoft-pay_nicepayments] requestPayment error', error);\n (window as unknown as Record)['__sirsoftNicepayPaymentInProgress'] = false;\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n G7Core?.toast?.error?.('결제 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.');\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nconst METHOD_TO_TEXT: Record = {\n nicepay_naverpay: '네이버페이',\n nicepay_kakaopay: '카카오페이',\n nicepay_samsungpay: '삼성페이',\n nicepay_applepay: '애플페이',\n nicepay_payco: 'PAYCO',\n nicepay_skpay: '11pay',\n nicepay_ssgpay: 'SSG페이',\n nicepay_lpay: 'L.pay',\n};\n\n// 흰색 내부 ring + 어두운 외부 ring으로 어떤 버튼 배경색에도 선택 상태가 확실히 보임\nconst SELECTED_SHADOW = '0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)';\n\nfunction getEasyPayButtonContainer(): Element | null {\n const section = document.getElementById('nicepay_checkout_payment_section');\n if (!section) return null;\n const paras = section.querySelectorAll('p');\n for (const p of paras) {\n if (p.textContent?.includes('간편결제')) {\n return p.nextElementSibling;\n }\n }\n return null;\n}\n\nfunction clearEasyPayButtonStyles(): void {\n const container = getEasyPayButtonContainer();\n if (!container) return;\n container.querySelectorAll('button').forEach(btn => {\n btn.style.boxShadow = '';\n btn.style.outline = '';\n });\n}\n\nfunction updateEasyPayButtonStyles(selectedMethod: string): void {\n const container = getEasyPayButtonContainer();\n if (!container) return;\n\n const selectedText = METHOD_TO_TEXT[selectedMethod];\n container.querySelectorAll('button').forEach(btn => {\n if (btn.textContent?.trim() === selectedText) {\n btn.style.boxShadow = SELECTED_SHADOW;\n btn.style.outline = 'none';\n } else {\n btn.style.boxShadow = '';\n btn.style.outline = '';\n }\n });\n}\n\nexport function setPaymentMethodHandler(action: any): void {\n const paymentMethod = action.params?.paymentMethod;\n if (!paymentMethod) return;\n\n const isEasyPay = typeof paymentMethod === 'string' && paymentMethod.indexOf('nicepay_') === 0;\n (window as any).G7Core?.state?.setLocal?.({\n paymentMethod,\n serverPaymentMethod: isEasyPay ? 'card' : paymentMethod,\n });\n\n if (isEasyPay) {\n updateEasyPayButtonStyles(paymentMethod);\n } else {\n clearEasyPayButtonStyles();\n }\n}\n\n// 코어 결제 버튼(신용카드 등) 클릭 시에도 간편결제 선택 표시 해제\nexport function initEasyPayWatcher(): void {\n const templateApp = (window as any).__templateApp;\n if (typeof templateApp?.onGlobalStateChange !== 'function') return;\n\n templateApp.onGlobalStateChange((state: any) => {\n const paymentMethod = state?._local?.paymentMethod;\n if (typeof paymentMethod !== 'string') return;\n if (!paymentMethod.startsWith('nicepay_')) {\n clearEasyPayButtonStyles();\n }\n });\n}\n","import { requestPaymentHandler } from './requestPayment';\nimport { setPaymentMethodHandler } from './setPaymentMethod';\n\nexport const handlerMap: Record unknown> = {\n requestPayment: requestPaymentHandler,\n setPaymentMethod: setPaymentMethodHandler,\n} as const;\n","/**\n * 주문 생성 API 요청/응답 인터셉터\n *\n * G7Core 템플릿 엔진의 apiCall 핸들러는 window.fetch 를 직접 사용하므로\n * window.fetch 를 래핑하는 방식으로 동작한다.\n *\n * 1. POST /api/modules/sirsoft-ecommerce/user/orders 요청을 가로챈다\n * 2. payment_method 가 'nicepay_*' 이면 'card' 로 교체해 서버에 전송\n * (서버의 PaymentMethodEnum 에 nicepay_* 가 없어서 422 발생 방지)\n * 3. 응답에서 pg_provider === 'sirsoft-nicepayments' 이면 requestPayment 핸들러를 직접 호출\n * 4. 원래 nicepay 방식(paymentMethod)을 requestPayment 에 전달해 올바른 결제창 호출\n * 5. requires_pg_payment → false, redirect_url → 현재 URL 로 교체\n * → 템플릿 조건 분기가 !requires_pg_payment 쪽으로 떨어져 navigate-to-self 가 됨\n */\n\nimport { requestPaymentHandler } from './handlers/requestPayment';\n\nconst ORDER_CREATE_PATH = '/user/orders';\nconst TARGET_PG_PROVIDER = 'sirsoft-nicepayments';\nconst PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments';\nconst FETCH_FLAG = '__sirsoftNicepayFetchInterceptorInstalled';\nconst CHECKOUT_ENDPOINT_PATH = '/api/modules/sirsoft-ecommerce/checkout';\nconst PAYMENT_IN_PROGRESS_FLAG = '__sirsoftNicepayPaymentInProgress';\nconst CHECKOUT_CACHE_KEY = '__sirsoftNicepayCheckoutCache';\n\nconst logger = {\n info: (...args: unknown[]) => console.info(`[${PLUGIN_IDENTIFIER}]`, ...args),\n warn: (...args: unknown[]) => console.warn(`[${PLUGIN_IDENTIFIER}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${PLUGIN_IDENTIFIER}]`, ...args),\n};\n\nfunction buildNoOpRedirectUrl(): string {\n return window.location.pathname + window.location.search + window.location.hash;\n}\n\nfunction isTargetOrderEndpoint(url: string, method: string): boolean {\n if (method.toUpperCase() !== 'POST') return false;\n const path = (url ?? '').split('?')[0].split('#')[0];\n return path === ORDER_CREATE_PATH || path.endsWith(ORDER_CREATE_PATH);\n}\n\nfunction isCheckoutEndpoint(url: string, method: string): boolean {\n if (method.toUpperCase() !== 'GET') return false;\n const path = (url ?? '').split('?')[0].split('#')[0];\n return path === CHECKOUT_ENDPOINT_PATH || path.endsWith(CHECKOUT_ENDPOINT_PATH);\n}\n\nfunction extractPaymentMethodFromBody(body: unknown): string | undefined {\n if (!body) return undefined;\n let obj: Record | null = null;\n if (typeof body === 'string') {\n try { obj = JSON.parse(body) as Record; } catch { return undefined; }\n } else if (typeof body === 'object') {\n obj = body as Record;\n }\n if (!obj) return undefined;\n const v = obj['payment_method'];\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction replacePaymentMethodInBody(body: string, newMethod: string): string {\n try {\n const parsed = JSON.parse(body) as Record;\n if ('payment_method' in parsed) {\n return JSON.stringify({ ...parsed, payment_method: newMethod });\n }\n } catch { /* fall through */ }\n return body;\n}\n\nfunction resolveOrderPaymentAmount(orderData: Record): number {\n const candidates = [orderData.total_due_amount, orderData.total_amount];\n for (const candidate of candidates) {\n const amount = Number(candidate ?? 0);\n if (Number.isFinite(amount) && amount > 0) {\n return Math.floor(amount);\n }\n }\n\n return 0;\n}\n\nexport function installOrderResponseInterceptor(): void {\n const w = window as unknown as Record;\n if (w[FETCH_FLAG]) return;\n w[FETCH_FLAG] = true;\n\n // 최초 설치 플러그인이 원본 브라우저 fetch를 보존 (다른 PG 인터셉터가 쌓이기 전)\n // easy pay 요청 시 이 fetch를 사용해 다른 PG 인터셉터(NHN KCP 등)의 간섭을 차단한다\n const ORIGINAL_FETCH_KEY = '__sirsoftPgOriginalFetch';\n if (!w[ORIGINAL_FETCH_KEY]) {\n w[ORIGINAL_FETCH_KEY] = window.fetch.bind(window);\n }\n const browserFetch = w[ORIGINAL_FETCH_KEY] as typeof fetch;\n\n const originalFetch = window.fetch.bind(window);\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : (input as Request).url;\n const method = init?.method ?? (input instanceof Request ? input.method : 'GET');\n\n // GET checkout 캐싱 — 결제 진행 중 페이지 재초기화 시 \"주문서 없음\" 다이얼로그 억제\n if (isCheckoutEndpoint(url, method)) {\n if (w[PAYMENT_IN_PROGRESS_FLAG] && w[CHECKOUT_CACHE_KEY]) {\n logger.info('payment in progress — serving cached checkout response');\n return new Response(w[CHECKOUT_CACHE_KEY] as string, {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n const checkoutResp = await originalFetch(input, init);\n if (checkoutResp.ok) {\n const text = await checkoutResp.text();\n w[CHECKOUT_CACHE_KEY] = text;\n return new Response(text, {\n status: checkoutResp.status,\n statusText: checkoutResp.statusText,\n headers: checkoutResp.headers,\n });\n }\n return checkoutResp;\n }\n\n if (!isTargetOrderEndpoint(url, method)) {\n return originalFetch(input, init);\n }\n\n // 요청 body에서 payment_method 추출. nicepay_* (간편결제) 만 'card' 로 교체.\n // 일반 결제수단(card / vbank / bank / phone)도 그대로 보존해 requestPaymentHandler\n // 호출 시 정확한 결제수단으로 결제창을 연다 (이전엔 nicepay_* 만 추적해서\n // 가상계좌/계좌이체/휴대폰 선택 시 'card' 로 폴백되는 회귀가 있었음).\n let originalPaymentMethod: string | undefined;\n let isEasyPay = false;\n let modifiedInit = init;\n\n if (init?.body && typeof init.body === 'string') {\n const pm = extractPaymentMethodFromBody(init.body);\n if (typeof pm === 'string' && pm.length > 0) {\n originalPaymentMethod = pm;\n if (pm.startsWith('nicepay_')) {\n isEasyPay = true;\n modifiedInit = { ...init, body: replacePaymentMethodInBody(init.body, 'card') };\n logger.info(`easy pay detected: replacing payment_method '${pm}' → 'card'`);\n }\n }\n }\n\n // easy pay일 때는 browserFetch(원본)를 사용해 NHN KCP 등 다른 PG 인터셉터를 우회한다.\n // \"타 PG와 사용가능함\"이 ON이고 기본 PG가 NHN KCP일 때 NHN KCP 결제창이 먼저 열리는 것을 방지.\n const fetchFn = isEasyPay ? browserFetch : originalFetch;\n const response = await fetchFn(input, modifiedInit);\n\n // 2xx 응답만 처리 (4xx/5xx 오류 응답은 그대로 통과)\n if (!response.ok) {\n return response;\n }\n\n // 응답 파싱 (clone으로 원본 스트림 보호)\n // 서버 응답 구조: { success, message, data: { order, requires_pg_payment, pg_provider, pg_payment_data, redirect_url } }\n let envelope: Record | null = null;\n try {\n envelope = (await response.clone().json()) as Record;\n } catch {\n return response;\n }\n\n const responseData = (envelope?.data ?? envelope) as Record | null;\n\n // 일반 결제: PG 결제가 필요 없으면 통과\n // 간편결제(nicepay_*): requires_pg_payment=false여도 계속 처리\n // → 기본 PG가 미설정이면 백엔드가 requires_pg_payment:false를 반환하지만\n // 간편결제는 나이스페이먼츠 결제창을 열어야 하므로 통과시킴 (취약점 방어)\n if (!responseData?.requires_pg_payment && !isEasyPay) {\n return response;\n }\n\n // easy pay: pg_provider 무관하게 나이스페이 처리\n // 일반 결제: pg_provider가 나이스페이인 경우에만 처리 (나이스페이가 기본 PG일 때)\n const isNicepayPg = responseData.pg_provider === TARGET_PG_PROVIDER;\n\n if (!isEasyPay && !isNicepayPg) {\n return response;\n }\n\n // pg_payment_data: 백엔드 응답에 포함되거나,\n // 간편결제 + 기본 PG 미설정 시 order 데이터에서 직접 구성\n let pgPaymentData = responseData.pg_payment_data as Record | undefined;\n if (!pgPaymentData && isEasyPay) {\n const orderData = responseData.order as Record | undefined;\n if (orderData) {\n const options = orderData.options as Array> | undefined;\n const firstName = (options?.[0]?.product_name as string | undefined) ?? String(orderData.order_number ?? '');\n const orderName = (options?.length ?? 0) > 1\n ? `${firstName} 외 ${(options?.length ?? 0) - 1}건`\n : firstName;\n pgPaymentData = {\n order_number: orderData.order_number,\n order_name: orderName,\n // PG 청구액 SSoT는 total_due_amount. 구형 응답 호환만 total_amount로 fallback.\n amount: resolveOrderPaymentAmount(orderData),\n currency: 'KRW',\n customer_name: orderData.orderer_name ?? null,\n customer_email: orderData.orderer_email ?? null,\n customer_phone: String(orderData.orderer_phone ?? '').replace(/[^0-9]/g, ''),\n customer_key: orderData.user_id ?? null,\n };\n logger.info('pg_payment_data constructed from order (기본 PG 미설정)', {\n order_number: pgPaymentData.order_number,\n amount: pgPaymentData.amount,\n });\n }\n }\n\n if (!pgPaymentData) {\n logger.warn('nicepayments order detected but pg_payment_data missing');\n return response;\n }\n\n const paymentMethod = originalPaymentMethod ?? 'card';\n\n logger.info('intercepted order create response — opening PG popup', { paymentMethod });\n\n void requestPaymentHandler({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n params: { pgPaymentData: pgPaymentData as any, paymentMethod },\n });\n\n // 결제 진행 중 플래그 설정 — GET checkout 캐시 서빙으로 \"주문서 없음\" 다이얼로그 억제\n w[PAYMENT_IN_PROGRESS_FLAG] = true;\n\n // 결제창(팝업)이 열려 있는 동안 checkout 페이지에 머물도록\n // redirect_url을 항상 현재 URL(navigate-to-self)로 교체한다.\n // → 템플릿 fallback navigate가 무력화되어 \"결제 완료\" 페이지로 이동하지 않음\n // → 실제 결제 완료 후 requestPaymentHandler 콜백이 complete 페이지로 이동시킴\n // navigate-to-self로 checkout 재초기화 시 GET checkout 404 → \"주문서 없음\" 다이얼로그가\n // 뜰 수 있으나, PAYMENT_IN_PROGRESS_FLAG + 캐시 서빙으로 억제된다.\n\n // envelope.data 안에 있는 경우와 최상위에 있는 경우 모두 처리\n const modifiedData = { ...responseData, requires_pg_payment: false, redirect_url: buildNoOpRedirectUrl() };\n const modifiedBody = envelope?.data\n ? { ...envelope, data: modifiedData }\n : modifiedData;\n\n return new Response(JSON.stringify(modifiedBody), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n };\n\n logger.info('fetch order interceptor installed');\n\n // Axios 인터셉터 설치 — GET checkout 캐싱 및 결제 중 캐시 서빙\n // G7Core.api.client(Axios)가 초기화된 후 설치해야 하므로 비동기로 재시도\n tryInstallAxiosCheckoutInterceptor(w, 15);\n}\n\nconst AXIOS_CHECKOUT_FLAG = '__sirsoftNicepayAxiosCheckoutInterceptorInstalled';\n\nfunction tryInstallAxiosCheckoutInterceptor(w: Record, retries: number): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const axiosClient = ((window as any)?.G7Core?.api?.client) as any;\n if (!axiosClient?.interceptors) {\n if (retries > 0) {\n setTimeout(() => tryInstallAxiosCheckoutInterceptor(w, retries - 1), 300);\n }\n return;\n }\n\n if (w[AXIOS_CHECKOUT_FLAG]) return;\n w[AXIOS_CHECKOUT_FLAG] = true;\n\n // 응답 인터셉터: GET checkout 성공 시 캐시 저장\n // Axios config.url은 baseURL을 포함하지 않으므로 (config.baseURL ?? '') + config.url 로 완성\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n axiosClient.interceptors.response.use((response: any) => {\n const method = (response?.config?.method ?? '').toLowerCase();\n const url = String((response?.config?.baseURL ?? '') + (response?.config?.url ?? ''));\n if (method === 'get' && isCheckoutEndpoint(url, 'GET') && response?.status === 200) {\n w[CHECKOUT_CACHE_KEY] = JSON.stringify(response.data);\n logger.info('checkout response cached (axios)');\n }\n return response;\n });\n\n // 요청 인터셉터: 결제 진행 중 GET checkout을 캐시에서 반환\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n axiosClient.interceptors.request.use((config: any) => {\n const method = (config?.method ?? '').toLowerCase();\n const url = String((config?.baseURL ?? '') + (config?.url ?? ''));\n if (method === 'get' && isCheckoutEndpoint(url, 'GET') && w[PAYMENT_IN_PROGRESS_FLAG] && w[CHECKOUT_CACHE_KEY]) {\n logger.info('payment in progress — serving cached checkout response (axios)');\n // adapter를 Promise.resolve로 교체해 실제 요청 없이 캐시 반환\n config.adapter = () => Promise.resolve({\n data: JSON.parse(w[CHECKOUT_CACHE_KEY] as string),\n status: 200,\n statusText: 'OK (cached)',\n headers: { 'content-type': 'application/json' },\n config,\n request: {},\n });\n }\n return config;\n });\n\n logger.info('axios checkout interceptor installed');\n\n // 인터셉터 설치 직후 GET checkout 즉시 캐싱 — navigate-to-self 재초기화 대비\n if (!w[CHECKOUT_CACHE_KEY]) {\n void (async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coreApi = ((window as any)?.G7Core?.api) as { get?: (url: string) => Promise } | undefined;\n await coreApi?.get?.('/modules/sirsoft-ecommerce/checkout');\n // 응답은 Axios 응답 인터셉터가 자동으로 캐싱함\n } catch { /* 체크아웃 세션 없으면 실패 — 무시 */ }\n })();\n }\n}\n\n/** @deprecated Axios 인터셉터 방식 — apiCall 핸들러가 window.fetch 를 사용하므로 동작하지 않음. installOrderResponseInterceptor 를 사용할 것. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function installAxiosOrderInterceptor(_axiosClient: any): void {\n // no-op\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayCheckoutEasyPayInjectorInstalled';\nconst CHECKOUT_RE = /^\\/shop\\/checkout\\/?$/;\nconst LISTENER_FLAG = '__nicepayCheckoutEasyPaySyncListenerAttached';\nconst SYNC_RETRY_INTERVAL_MS = 200;\nconst SYNC_RETRY_ATTEMPTS = 120;\nconst LEGACY_CONTAINER_ID = 'nicepay_checkout_payment_section';\n\ninterface EasyPayCopy {\n heading: string;\n description: string;\n title: string;\n}\n\ninterface EasyPayDefinition {\n id: string;\n labels: string[];\n ko: EasyPayCopy;\n en: EasyPayCopy;\n markText: string;\n markClassName: string;\n}\n\nconst EASY_PAY_DEFINITIONS: EasyPayDefinition[] = [\n {\n id: 'nicepay_naverpay',\n labels: [\n '네이버페이 (나이스페이먼츠)',\n '네이버페이로 결제 (나이스페이먼츠)',\n 'Naver Pay (NicePayments)',\n 'Pay with Naver Pay (NicePayments)',\n ],\n ko: {\n heading: '네이버페이 (나이스페이먼츠)',\n description: '네이버페이로 결제 (나이스페이먼츠)',\n title: '네이버페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Naver Pay (NicePayments)',\n description: 'Pay with Naver Pay (NicePayments)',\n title: 'Pay with Naver Pay (NicePayments)',\n },\n markText: 'N',\n markClassName: 'bg-green-500 text-white',\n },\n {\n id: 'nicepay_kakaopay',\n labels: [\n '카카오페이 (나이스페이먼츠)',\n '카카오페이로 결제 (나이스페이먼츠)',\n 'Kakao Pay (NicePayments)',\n 'Pay with Kakao Pay (NicePayments)',\n ],\n ko: {\n heading: '카카오페이 (나이스페이먼츠)',\n description: '카카오페이로 결제 (나이스페이먼츠)',\n title: '카카오페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Kakao Pay (NicePayments)',\n description: 'Pay with Kakao Pay (NicePayments)',\n title: 'Pay with Kakao Pay (NicePayments)',\n },\n markText: 'K',\n markClassName: 'bg-yellow-400 text-gray-950',\n },\n {\n id: 'nicepay_samsungpay',\n labels: [\n '삼성페이 (나이스페이먼츠)',\n '삼성페이로 결제 (나이스페이먼츠)',\n 'Samsung Pay (NicePayments)',\n 'Pay with Samsung Pay (NicePayments)',\n ],\n ko: {\n heading: '삼성페이 (나이스페이먼츠)',\n description: '삼성페이로 결제 (나이스페이먼츠)',\n title: '삼성페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Samsung Pay (NicePayments)',\n description: 'Pay with Samsung Pay (NicePayments)',\n title: 'Pay with Samsung Pay (NicePayments)',\n },\n markText: 'S',\n markClassName: 'bg-blue-600 text-white',\n },\n {\n id: 'nicepay_applepay',\n labels: [\n '애플페이 (나이스페이먼츠)',\n '애플페이로 결제 (나이스페이먼츠)',\n 'Apple Pay (NicePayments)',\n 'Pay with Apple Pay (NicePayments)',\n ],\n ko: {\n heading: '애플페이 (나이스페이먼츠)',\n description: '애플페이로 결제 (나이스페이먼츠)',\n title: '애플페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Apple Pay (NicePayments)',\n description: 'Pay with Apple Pay (NicePayments)',\n title: 'Pay with Apple Pay (NicePayments)',\n },\n markText: 'A',\n markClassName: 'bg-gray-900 text-white',\n },\n {\n id: 'nicepay_payco',\n labels: [\n 'PAYCO (나이스페이먼츠)',\n 'PAYCO로 결제 (나이스페이먼츠)',\n 'PAYCO (NicePayments)',\n 'Pay with PAYCO (NicePayments)',\n ],\n ko: {\n heading: 'PAYCO (나이스페이먼츠)',\n description: 'PAYCO로 결제 (나이스페이먼츠)',\n title: 'PAYCO로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'PAYCO (NicePayments)',\n description: 'Pay with PAYCO (NicePayments)',\n title: 'Pay with PAYCO (NicePayments)',\n },\n markText: 'P',\n markClassName: 'bg-red-500 text-white',\n },\n {\n id: 'nicepay_skpay',\n labels: [\n '11pay (나이스페이먼츠)',\n '11pay로 결제 (나이스페이먼츠)',\n '11pay (NicePayments)',\n 'Pay with 11pay (NicePayments)',\n ],\n ko: {\n heading: '11pay (나이스페이먼츠)',\n description: '11pay로 결제 (나이스페이먼츠)',\n title: '11pay로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: '11pay (NicePayments)',\n description: 'Pay with 11pay (NicePayments)',\n title: 'Pay with 11pay (NicePayments)',\n },\n markText: '11',\n markClassName: 'bg-orange-500 text-white',\n },\n {\n id: 'nicepay_ssgpay',\n labels: [\n 'SSG페이 (나이스페이먼츠)',\n 'SSG페이로 결제 (나이스페이먼츠)',\n 'SSG Pay (NicePayments)',\n 'Pay with SSG Pay (NicePayments)',\n ],\n ko: {\n heading: 'SSG페이 (나이스페이먼츠)',\n description: 'SSG페이로 결제 (나이스페이먼츠)',\n title: 'SSG페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'SSG Pay (NicePayments)',\n description: 'Pay with SSG Pay (NicePayments)',\n title: 'Pay with SSG Pay (NicePayments)',\n },\n markText: 'SSG',\n markClassName: 'bg-red-700 text-white',\n },\n {\n id: 'nicepay_lpay',\n labels: [\n 'L.pay (나이스페이먼츠)',\n 'L.pay로 결제 (나이스페이먼츠)',\n 'L.pay (NicePayments)',\n 'Pay with L.pay (NicePayments)',\n ],\n ko: {\n heading: 'L.pay (나이스페이먼츠)',\n description: 'L.pay로 결제 (나이스페이먼츠)',\n title: 'L.pay로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'L.pay (NicePayments)',\n description: 'Pay with L.pay (NicePayments)',\n title: 'Pay with L.pay (NicePayments)',\n },\n markText: 'L',\n markClassName: 'bg-purple-600 text-white',\n },\n];\n\nlet observer: MutationObserver | null = null;\nlet retryTimer: number | null = null;\nlet syncQueued = false;\n\nfunction windowRecord(): Record {\n return window as unknown as Record;\n}\n\nfunction isCheckoutPage(): boolean {\n return CHECKOUT_RE.test(window.location.pathname);\n}\n\nfunction normalizedText(value: string | null | undefined): string {\n return (value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction comparableText(value: string | null | undefined): string {\n return normalizedText(value).replace(/\\u200B/g, '');\n}\n\nfunction isKoreanPage(): boolean {\n const lang = document.documentElement.lang || navigator.language || '';\n\n return lang.toLowerCase().startsWith('ko');\n}\n\nfunction copyFor(definition: EasyPayDefinition): EasyPayCopy {\n return isKoreanPage() ? definition.ko : definition.en;\n}\n\nfunction displayText(value: string): string {\n return value\n .replaceAll('네이버페이', '네이\\u200B버페이')\n .replaceAll('카카오페이', '카카\\u200B오페이')\n .replaceAll('삼성페이', '삼성\\u200B페이')\n .replaceAll('Naver Pay', 'Naver\\u200B Pay')\n .replaceAll('Kakao Pay', 'Kakao\\u200B Pay')\n .replaceAll('Samsung Pay', 'Samsung\\u200B Pay')\n .replaceAll('L.pay', 'L.\\u200Bpay');\n}\n\nfunction findDefinitionById(id: string | undefined): EasyPayDefinition | null {\n return EASY_PAY_DEFINITIONS.find((definition) => definition.id === id) ?? null;\n}\n\nfunction findDefinitionForButton(button: HTMLButtonElement): EasyPayDefinition | null {\n const markedDefinition = findDefinitionById(button.dataset.nicepayEasyPayMethod);\n if (markedDefinition) return markedDefinition;\n\n const text = comparableText(button.textContent);\n\n return EASY_PAY_DEFINITIONS.find((definition) => (\n definition.labels.some((label) => text.includes(label))\n )) ?? null;\n}\n\nfunction findPaymentRow(button: HTMLButtonElement): HTMLElement | null {\n return button.querySelector('.flex.items-center.gap-2, .flex.items-center.gap-3')\n ?? button.querySelector('.flex.items-center');\n}\n\nfunction alignPaymentTextLeft(button: HTMLButtonElement, heading?: HTMLElement, description?: HTMLElement): void {\n button.style.textAlign = 'left';\n\n const row = findPaymentRow(button);\n if (row) {\n row.style.justifyContent = 'flex-start';\n row.style.textAlign = 'left';\n row.style.width = '100%';\n }\n\n const textContainer = heading?.parentElement ?? description?.parentElement;\n if (textContainer instanceof HTMLElement) {\n textContainer.style.textAlign = 'left';\n textContainer.style.minWidth = '0';\n }\n}\n\nfunction createMark(definition: EasyPayDefinition): HTMLSpanElement {\n const mark = document.createElement('span');\n mark.dataset.nicepayEasyPayMark = 'true';\n mark.dataset.nicepayEasyPayMethod = definition.id;\n mark.setAttribute('aria-hidden', 'true');\n mark.className = `inline-flex items-center justify-center rounded-lg text-xs font-bold ${definition.markClassName}`;\n mark.style.width = '32px';\n mark.style.height = '32px';\n mark.style.flex = '0 0 32px';\n mark.textContent = definition.markText;\n\n return mark;\n}\n\nfunction removeOtherPgArtifacts(button: HTMLButtonElement): void {\n delete button.dataset.kginicisBrandPaymentButton;\n delete button.dataset.kginicisBrandPaymentMethod;\n delete button.dataset.kginicisNaverpayBrandButton;\n delete button.dataset.nhnkcpEasyPayMethod;\n delete button.dataset.nhnkcpEasyPayVisible;\n delete button.dataset.nhnkcpEasyPayHidden;\n\n button.querySelectorAll('[data-kginicis-brand-payment-mark=\"true\"], [data-kginicis-naverpay-mark=\"true\"], [data-nhnkcp-easy-pay-mark=\"true\"]').forEach((element) => {\n element.remove();\n });\n}\n\nfunction findPaymentIcon(button: HTMLButtonElement): Element | null {\n return button.querySelector('[data-nicepay-easy-pay-mark=\"true\"]')\n ?? button.querySelector('[data-kginicis-brand-payment-mark=\"true\"], [data-kginicis-naverpay-mark=\"true\"], [data-nhnkcp-easy-pay-mark=\"true\"]')\n ?? button.querySelector('svg')\n ?? button.querySelector('i[class*=\"fa-\"], i[role=\"img\"], i');\n}\n\nfunction ensureMark(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n const existingMark = button.querySelector('[data-nicepay-easy-pay-mark=\"true\"]');\n if (existingMark) {\n existingMark.dataset.nicepayEasyPayMethod = definition.id;\n existingMark.className = `inline-flex items-center justify-center rounded-lg text-xs font-bold ${definition.markClassName}`;\n existingMark.textContent = definition.markText;\n return;\n }\n\n const mark = createMark(definition);\n const icon = findPaymentIcon(button);\n if (icon && icon.parentElement) {\n icon.replaceWith(mark);\n return;\n }\n\n const row = findPaymentRow(button);\n row?.prepend(mark);\n}\n\nfunction updatePaymentText(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n const paragraphs = Array.from(button.querySelectorAll('p'));\n const copy = copyFor(definition);\n const headingText = displayText(copy.heading);\n const descriptionText = displayText(copy.description);\n\n const heading = paragraphs[0];\n const description = paragraphs[1];\n\n if (heading && heading.textContent !== headingText) {\n heading.textContent = headingText;\n }\n if (heading) {\n heading.setAttribute('aria-label', copy.heading);\n heading.style.textAlign = 'left';\n heading.style.whiteSpace = 'normal';\n heading.style.wordBreak = 'keep-all';\n heading.style.overflowWrap = 'anywhere';\n }\n\n if (description && description.textContent !== descriptionText) {\n description.textContent = descriptionText;\n }\n if (description) {\n description.style.textAlign = 'left';\n description.style.fontSize = '12px';\n description.style.lineHeight = '1rem';\n description.style.whiteSpace = 'normal';\n description.style.wordBreak = 'keep-all';\n description.style.overflowWrap = 'anywhere';\n }\n\n alignPaymentTextLeft(button, heading, description);\n button.title = copy.title;\n}\n\nfunction showButton(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n if (button.hidden) button.hidden = false;\n if (button.disabled) button.disabled = false;\n if (button.style.display === 'none') button.style.removeProperty('display');\n button.removeAttribute('aria-hidden');\n button.dataset.nicepayEasyPayMethod = definition.id;\n button.dataset.nicepayEasyPayVisible = 'true';\n\n removeOtherPgArtifacts(button);\n ensureMark(button, definition);\n updatePaymentText(button, definition);\n}\n\nfunction removeLegacyInjectedSection(): boolean {\n const legacySection = document.getElementById(LEGACY_CONTAINER_ID);\n if (!legacySection) return false;\n\n legacySection.remove();\n\n return true;\n}\n\nexport async function syncRenderedCheckoutEasyPayMethods(\n root: ParentNode = document,\n): Promise {\n if (typeof window === 'undefined' || typeof document === 'undefined') return false;\n if (!isCheckoutPage()) return false;\n\n let changed = removeLegacyInjectedSection();\n\n root.querySelectorAll('button').forEach((button) => {\n const definition = findDefinitionForButton(button);\n if (!definition) return;\n\n showButton(button, definition);\n changed = true;\n });\n\n return changed;\n}\n\nfunction queueSync(): void {\n if (syncQueued) return;\n syncQueued = true;\n\n window.setTimeout(() => {\n syncQueued = false;\n void syncRenderedCheckoutEasyPayMethods();\n }, 0);\n}\n\nfunction stopRetries(): void {\n if (retryTimer === null) return;\n\n window.clearInterval(retryTimer);\n retryTimer = null;\n}\n\nfunction startSync(): void {\n if (!isCheckoutPage()) return;\n\n stopRetries();\n void syncRenderedCheckoutEasyPayMethods();\n\n let attempts = 0;\n retryTimer = window.setInterval(() => {\n attempts += 1;\n void syncRenderedCheckoutEasyPayMethods();\n\n if (attempts >= SYNC_RETRY_ATTEMPTS) {\n stopRetries();\n }\n }, SYNC_RETRY_INTERVAL_MS);\n\n const body = document.body as HTMLElement & Record;\n if (body[LISTENER_FLAG]) return;\n body[LISTENER_FLAG] = true;\n\n observer = new MutationObserver(() => queueSync());\n observer.observe(document.body, { childList: true, subtree: true, characterData: true });\n}\n\nfunction onRouteChange(): void {\n if (isCheckoutPage()) startSync();\n}\n\nexport function installCheckoutEasyPayInjector(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const w = windowRecord();\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] checkout easy pay method sync installed`);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => startSync());\n } else {\n startSync();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n window.setTimeout(() => onRouteChange(), 200);\n };\n window.addEventListener('popstate', () => window.setTimeout(() => onRouteChange(), 200));\n}\n\nexport function resetCheckoutEasyPayInjectorForTests(): void {\n observer?.disconnect();\n observer = null;\n stopRetries();\n syncQueued = false;\n delete windowRecord()[FLAG];\n if (document.body) {\n delete ((document.body as HTMLElement & Record)[LISTENER_FLAG]);\n }\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayOcReceiptInjectorInstalled';\nconst BTN_ID = 'nicepay-oc-receipt-btn';\n\nconst ORDER_COMPLETE_RE = /^\\/shop\\/orders\\/([^/]+)\\/complete$/;\n\ntype Payment = {\n pg_provider: string;\n transaction_id: string | null;\n paid_at: string | null;\n [key: string]: unknown;\n};\n\nfunction getToken(): string | null {\n return localStorage.getItem('auth_token');\n}\n\nasync function fetchPayment(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n\n try {\n const res = await fetch(`/api/modules/sirsoft-ecommerce/user/orders/${orderNumber}`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { data?: { payment?: Payment } };\n return data?.data?.payment ?? null;\n } catch {\n return null;\n }\n}\n\nasync function fetchReceiptUrl(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n\n try {\n const res = await fetch(`/api/plugins/${PLUGIN_ID}/user/orders/${orderNumber}/receipt`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { receipt_url?: string };\n return data.receipt_url ?? null;\n } catch {\n return null;\n }\n}\n\nasync function injectOnOrderComplete(orderNumber: string): Promise {\n if (document.getElementById(BTN_ID)) return;\n\n const payment = await fetchPayment(orderNumber);\n // 입금완료(paid_at 채워짐) 시점에만 영수증 버튼 표시 — 가상계좌 입금대기 차단\n if (!payment || payment.pg_provider !== 'nicepayments' || !payment.transaction_id || !payment.paid_at) return;\n\n const blueBtn = Array.from(document.querySelectorAll('button[type=\"button\"]'))\n .find(b => b.className.includes('bg-blue-600'));\n\n if (!blueBtn?.parentElement) return;\n\n const container = blueBtn.parentElement;\n\n const receiptBtn = document.createElement('button');\n receiptBtn.id = BTN_ID;\n receiptBtn.type = 'button';\n receiptBtn.className = blueBtn.className\n .replace(/bg-blue-\\d+/g, 'bg-green-600')\n .replace(/hover:bg-blue-\\d+/g, 'hover:bg-green-700');\n receiptBtn.textContent = '영수증 조회';\n\n receiptBtn.addEventListener('click', async () => {\n receiptBtn.disabled = true;\n receiptBtn.textContent = '로딩 중...';\n const url = await fetchReceiptUrl(orderNumber);\n receiptBtn.disabled = false;\n receiptBtn.textContent = '영수증 조회';\n if (url) {\n window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes');\n }\n });\n\n const lastBtn = container.lastElementChild;\n container.insertBefore(receiptBtn, lastBtn);\n\n console.info(`[${PLUGIN_ID}] receipt button injected on order complete page`);\n}\n\nfunction tryInject(): void {\n const match = location.pathname.match(ORDER_COMPLETE_RE);\n if (match) {\n void injectOnOrderComplete(match[1]);\n }\n}\n\nexport function installOrderCompleteReceiptInjector(): void {\n if (typeof window === 'undefined') return;\n const w = window as Record;\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] order complete receipt injector installed`);\n\n const schedule = (delay = 1200) => setTimeout(tryInject, delay);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => schedule());\n } else {\n schedule();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n schedule();\n };\n window.addEventListener('popstate', () => schedule(500));\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayOrderShowInjectorInstalled';\nconst ROW_ID = 'nicepay-mp-receipt-row';\nconst VBANK_BLOCK_ID = 'nicepay-mp-vbank-info';\n\nconst ORDER_SHOW_RE = /^\\/mypage\\/orders\\/([^/]+)$/;\n\ninterface Payment {\n pg_provider?: string;\n payment_method?: string;\n transaction_id?: string | null;\n paid_at?: string | null;\n vbank_name?: string | null;\n vbank_number?: string | null;\n vbank_holder?: string | null;\n vbank_due_at?: string | null;\n [key: string]: unknown;\n}\n\ninterface OrderData {\n order_number?: string;\n total_amount_formatted?: string;\n payment?: Payment;\n}\n\nfunction getOrderFromState(orderNumber: string): OrderData | null {\n try {\n const g7 = (window as Record).G7Core as Record | undefined;\n const getState = g7?.getState as (() => Record) | undefined;\n const ctx = getState?.()?.currentDataContext as Record | undefined;\n const order = ctx?.order as { data?: OrderData } | undefined;\n const data = order?.data;\n if (!data || data.order_number !== orderNumber) return null;\n return data;\n } catch {\n return null;\n }\n}\n\nfunction getToken(): string | null {\n return localStorage.getItem('auth_token');\n}\n\nasync function fetchReceiptUrl(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n try {\n const res = await fetch(`/api/plugins/${PLUGIN_ID}/user/orders/${orderNumber}/receipt`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { receipt_url?: string };\n return data.receipt_url ?? null;\n } catch {\n return null;\n }\n}\n\nfunction findPaymentContainer(): Element | null {\n const panel = document.getElementById('order_payment_info_panel');\n if (panel) {\n return Array.from(panel.children).find(el => el.className?.includes('space-y')) ?? panel;\n }\n\n const h3 = Array.from(document.querySelectorAll('h3')).find(\n el => el.textContent?.includes('결제 정보'),\n );\n if (!h3) return null;\n\n const panelDiv = h3.parentElement?.parentElement;\n if (!panelDiv) return null;\n\n return Array.from(panelDiv.children).find(el => el.className?.includes('space-y')) ?? panelDiv;\n}\n\nfunction buildReceiptRow(orderNumber: string): HTMLElement {\n const row = document.createElement('div');\n row.id = ROW_ID;\n row.className = 'flex items-center justify-between';\n\n const label = document.createElement('span');\n label.className = 'text-gray-500 dark:text-gray-400 text-sm';\n label.textContent = '영수증';\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className =\n 'inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50';\n btn.textContent = '영수증 조회';\n\n btn.addEventListener('click', async () => {\n btn.disabled = true;\n btn.textContent = '로딩 중...';\n const url = await fetchReceiptUrl(orderNumber);\n btn.disabled = false;\n btn.textContent = '영수증 조회';\n if (url) {\n window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes');\n }\n });\n\n row.appendChild(label);\n row.appendChild(btn);\n return row;\n}\n\nfunction formatVbankDueAt(raw: string | null | undefined): string {\n if (!raw) return '';\n try {\n const d = new Date(raw);\n if (Number.isNaN(d.getTime())) return raw;\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;\n } catch {\n return raw;\n }\n}\n\nfunction buildVbankRow(label: string, value: string, valueClass = 'font-medium text-gray-900 dark:text-white'): HTMLElement {\n const row = document.createElement('div');\n row.className = 'flex justify-between text-sm';\n const l = document.createElement('span');\n l.className = 'text-gray-600 dark:text-gray-400';\n l.textContent = label;\n const v = document.createElement('span');\n v.className = valueClass;\n v.textContent = value;\n row.appendChild(l);\n row.appendChild(v);\n return row;\n}\n\nfunction buildVbankBlock(orderData: OrderData): HTMLElement {\n const p = orderData.payment ?? {};\n const wrap = document.createElement('div');\n wrap.id = VBANK_BLOCK_ID;\n wrap.className = 'pt-4 mt-2 border-t border-gray-200 dark:border-gray-700';\n\n const inner = document.createElement('div');\n inner.className = 'bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-700 rounded-lg p-4';\n\n const title = document.createElement('h3');\n title.className = 'text-sm font-semibold text-blue-800 dark:text-blue-300 mb-3';\n title.textContent = '가상계좌 입금 안내';\n\n const rows = document.createElement('div');\n rows.className = 'space-y-2';\n rows.appendChild(buildVbankRow('은행', p.vbank_name ?? ''));\n rows.appendChild(buildVbankRow('계좌번호', p.vbank_number ?? '', 'font-medium font-mono text-gray-900 dark:text-white'));\n rows.appendChild(buildVbankRow('예금주', p.vbank_holder ?? ''));\n rows.appendChild(buildVbankRow('입금 금액', orderData.total_amount_formatted ?? '', 'font-bold text-blue-600 dark:text-blue-400'));\n\n const dueRaw = p.vbank_due_at;\n if (dueRaw) {\n const dueRow = document.createElement('div');\n dueRow.className = 'flex justify-between text-sm pt-2 border-t border-blue-200 dark:border-blue-700 mt-2';\n const dueLabel = document.createElement('span');\n dueLabel.className = 'text-gray-600 dark:text-gray-400';\n dueLabel.textContent = '입금 기한';\n const dueValue = document.createElement('span');\n dueValue.className = 'font-medium text-red-600 dark:text-red-400';\n dueValue.textContent = formatVbankDueAt(dueRaw);\n dueRow.appendChild(dueLabel);\n dueRow.appendChild(dueValue);\n rows.appendChild(dueRow);\n }\n\n inner.appendChild(title);\n inner.appendChild(rows);\n\n if (!p.paid_at) {\n const notice = document.createElement('p');\n notice.className = 'text-xs text-blue-700 dark:text-blue-300 mt-3';\n notice.textContent = '입금 기한 내에 입금이 완료되지 않으면 주문이 자동 취소됩니다.';\n inner.appendChild(notice);\n }\n\n wrap.appendChild(inner);\n return wrap;\n}\n\nasync function tryInject(orderNumber: string): Promise {\n const orderData = getOrderFromState(orderNumber);\n if (!orderData) return false;\n\n const { payment } = orderData;\n if (!payment || payment.pg_provider !== 'nicepayments') return true;\n\n const container = findPaymentContainer();\n if (!container) return false;\n\n // 가상계좌 입금 안내 — 결제수단이 vbank 이고 가상계좌 번호 발급된 경우 표시 (paid_at 무관: 입금 전후 모두 표시).\n if (payment.payment_method === 'vbank' && payment.vbank_number && !document.getElementById(VBANK_BLOCK_ID)) {\n container.appendChild(buildVbankBlock(orderData));\n console.info(`[${PLUGIN_ID}] vbank info injected on mypage order show`);\n }\n\n if (!payment.transaction_id) return true;\n // 영수증 버튼은 결제완료(paid_at 채워짐) 시점에만 표시 — 가상계좌 입금대기 차단\n if (!payment.paid_at) return true;\n\n if (document.getElementById(ROW_ID)) return true;\n\n container.appendChild(buildReceiptRow(orderNumber));\n console.info(`[${PLUGIN_ID}] receipt button injected on mypage order show`);\n return true;\n}\n\nfunction startPolling(orderNumber: string): void {\n let attempts = 0;\n const id = setInterval(() => {\n attempts++;\n void tryInject(orderNumber).then(done => {\n if (done || attempts >= 30) clearInterval(id);\n });\n }, 400);\n}\n\nfunction onRouteChange(): void {\n const match = location.pathname.match(ORDER_SHOW_RE);\n if (match) startPolling(match[1]);\n}\n\nexport function installMypageOrderShowInjector(): void {\n if (typeof window === 'undefined') return;\n const w = window as unknown as Record;\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] mypage order show injector installed`);\n\n const schedule = (delay = 1500) => setTimeout(onRouteChange, delay);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => schedule());\n } else {\n schedule();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n schedule(600);\n };\n window.addEventListener('popstate', () => schedule(500));\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayAdminPaymentMethodBrandInjectorInstalled';\nconst LISTENER_FLAG = '__nicepayAdminPaymentMethodBrandSyncListenerAttached';\nconst ADMIN_SETTINGS_RE = /^\\/admin\\/ecommerce\\/settings\\/?$/;\nconst MARK_SELECTOR = '[data-nicepay-admin-payment-brand-mark=\"true\"]';\nconst SYNC_RETRY_INTERVAL_MS = 200;\nconst SYNC_RETRY_ATTEMPTS = 120;\n\ninterface AdminPaymentBrandDefinition {\n id: string;\n labels: string[];\n shortLabels: string[];\n markLines: string[];\n markClassName: string;\n}\n\nconst ADMIN_PAYMENT_BRAND_DEFINITIONS: AdminPaymentBrandDefinition[] = [\n {\n id: 'nicepay_naverpay',\n labels: ['네이버페이 (나이스페이먼츠)', '네이버페이로 결제 (나이스페이먼츠)', 'Naver Pay (NicePayments)', 'Pay with Naver Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['N'],\n markClassName: 'bg-green-500 text-white',\n },\n {\n id: 'nicepay_kakaopay',\n labels: ['카카오페이 (나이스페이먼츠)', '카카오페이로 결제 (나이스페이먼츠)', 'Kakao Pay (NicePayments)', 'Pay with Kakao Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['K'],\n markClassName: 'bg-yellow-400 text-gray-950',\n },\n {\n id: 'nicepay_samsungpay',\n labels: ['삼성페이 (나이스페이먼츠)', '삼성페이로 결제 (나이스페이먼츠)', 'Samsung Pay (NicePayments)', 'Pay with Samsung Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['S'],\n markClassName: 'bg-blue-600 text-white',\n },\n {\n id: 'nicepay_applepay',\n labels: ['애플페이 (나이스페이먼츠)', '애플페이로 결제 (나이스페이먼츠)', 'Apple Pay (NicePayments)', 'Pay with Apple Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['A'],\n markClassName: 'bg-gray-900 text-white',\n },\n {\n id: 'nicepay_payco',\n labels: ['PAYCO (나이스페이먼츠)', 'PAYCO로 결제 (나이스페이먼츠)', 'PAYCO (NicePayments)', 'Pay with PAYCO (NicePayments)'],\n shortLabels: [],\n markLines: ['P'],\n markClassName: 'bg-red-500 text-white',\n },\n {\n id: 'nicepay_skpay',\n labels: ['11pay (나이스페이먼츠)', '11pay로 결제 (나이스페이먼츠)', '11pay (NicePayments)', 'Pay with 11pay (NicePayments)'],\n shortLabels: [],\n markLines: ['11'],\n markClassName: 'bg-orange-500 text-white',\n },\n {\n id: 'nicepay_ssgpay',\n labels: ['SSG페이 (나이스페이먼츠)', 'SSG페이로 결제 (나이스페이먼츠)', 'SSG Pay (NicePayments)', 'Pay with SSG Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['SSG'],\n markClassName: 'bg-red-700 text-white',\n },\n {\n id: 'nicepay_lpay',\n labels: ['L.pay (나이스페이먼츠)', 'L.pay로 결제 (나이스페이먼츠)', 'L.pay (NicePayments)', 'Pay with L.pay (NicePayments)'],\n shortLabels: [],\n markLines: ['L'],\n markClassName: 'bg-purple-600 text-white',\n },\n];\n\nlet observer: MutationObserver | null = null;\nlet retryTimer: number | null = null;\nlet syncQueued = false;\n\nfunction windowRecord(): Record {\n return window as unknown as Record;\n}\n\nfunction isOrderSettingsPage(): boolean {\n if (!ADMIN_SETTINGS_RE.test(window.location.pathname)) return false;\n\n const tab = new URLSearchParams(window.location.search).get('tab');\n return tab === null || tab === '' || tab === 'order_settings';\n}\n\nfunction classNameOf(element: Element): string {\n return element instanceof HTMLElement ? element.className : '';\n}\n\nfunction comparableText(value: string | null | undefined): string {\n return (value ?? '')\n .replace(/\\u200B/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction isPaymentMethodItem(element: HTMLElement): boolean {\n const className = classNameOf(element);\n\n return className.includes('excel-card')\n || (className.includes('flex-center') && className.includes('border') && className.includes('gap-4'));\n}\n\nfunction findTitleElement(item: HTMLElement): HTMLElement | null {\n return item.querySelector('.font-medium');\n}\n\nfunction findDefinitionForItem(item: HTMLElement): AdminPaymentBrandDefinition | null {\n const title = comparableText(findTitleElement(item)?.textContent);\n const text = comparableText(item.textContent);\n\n return ADMIN_PAYMENT_BRAND_DEFINITIONS.find((definition) => (\n definition.shortLabels.some((label) => title === label)\n || definition.labels.some((label) => text.includes(label))\n )) ?? null;\n}\n\nfunction findPaymentMethodItems(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll('div'))\n .filter(isPaymentMethodItem);\n}\n\nfunction findPaymentIcon(item: HTMLElement): Element | null {\n const icons = Array.from(item.querySelectorAll('svg, [data-icon], i'));\n\n return icons.find((element) => (\n !element.closest('[data-drag-handle]')\n && !element.closest(MARK_SELECTOR)\n && !element.closest('.row-stack, .excel-card-body, button, select, [role=\"switch\"]')\n )) ?? null;\n}\n\nfunction applyBrandMarkContent(mark: HTMLSpanElement, definition: AdminPaymentBrandDefinition): void {\n mark.dataset.nicepayAdminPaymentBrandMark = 'true';\n mark.dataset.nicepayAdminPaymentMethod = definition.id;\n mark.setAttribute('aria-hidden', 'true');\n mark.className = `inline-flex items-center justify-center rounded-lg font-bold ${definition.markClassName}`;\n mark.style.width = '32px';\n mark.style.height = '32px';\n mark.style.flex = '0 0 32px';\n mark.style.lineHeight = '1';\n mark.style.fontSize = definition.markLines.join('').length > 2 ? '9px' : '12px';\n\n const expectedText = definition.markLines.join('');\n if (mark.textContent !== expectedText) {\n mark.textContent = definition.markLines[0] ?? '';\n }\n}\n\nfunction createBrandMark(definition: AdminPaymentBrandDefinition): HTMLSpanElement {\n const mark = document.createElement('span');\n applyBrandMarkContent(mark, definition);\n\n return mark;\n}\n\nfunction syncBrandMark(item: HTMLElement, definition: AdminPaymentBrandDefinition): boolean {\n const existing = item.querySelector(MARK_SELECTOR);\n if (existing instanceof HTMLSpanElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n applyBrandMarkContent(existing, definition);\n return true;\n }\n\n const mark = createBrandMark(definition);\n const icon = findPaymentIcon(item);\n if (icon && icon.parentElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n icon.replaceWith(mark);\n return true;\n }\n\n const title = findTitleElement(item);\n if (title && title.parentElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n title.parentElement.insertBefore(mark, title);\n return true;\n }\n\n return false;\n}\n\nexport function syncRenderedAdminPaymentMethodBrands(root: ParentNode = document): boolean {\n if (typeof window === 'undefined' || typeof document === 'undefined') return false;\n if (!isOrderSettingsPage()) return false;\n\n let matched = false;\n\n findPaymentMethodItems(root).forEach((item) => {\n const definition = findDefinitionForItem(item);\n if (!definition) return;\n\n if (syncBrandMark(item, definition)) {\n matched = true;\n }\n });\n\n return matched;\n}\n\nfunction queueSync(): void {\n if (syncQueued) return;\n syncQueued = true;\n\n window.setTimeout(() => {\n syncQueued = false;\n syncRenderedAdminPaymentMethodBrands();\n }, 0);\n}\n\nfunction stopRetries(): void {\n if (retryTimer === null) return;\n\n window.clearInterval(retryTimer);\n retryTimer = null;\n}\n\nfunction startSync(): void {\n if (!isOrderSettingsPage()) return;\n\n stopRetries();\n syncRenderedAdminPaymentMethodBrands();\n\n let attempts = 0;\n retryTimer = window.setInterval(() => {\n attempts += 1;\n syncRenderedAdminPaymentMethodBrands();\n\n if (attempts >= SYNC_RETRY_ATTEMPTS) {\n stopRetries();\n }\n }, SYNC_RETRY_INTERVAL_MS);\n\n const body = document.body as HTMLElement & Record;\n if (body[LISTENER_FLAG]) return;\n body[LISTENER_FLAG] = true;\n\n observer = new MutationObserver(() => queueSync());\n observer.observe(document.body, { childList: true, subtree: true, characterData: true });\n}\n\nfunction onRouteChange(): void {\n if (isOrderSettingsPage()) {\n startSync();\n return;\n }\n\n stopRetries();\n}\n\nexport function installAdminPaymentMethodBrandInjector(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const w = windowRecord();\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] admin payment method brand sync installed`);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => startSync());\n } else {\n startSync();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n window.setTimeout(() => onRouteChange(), 200);\n };\n window.addEventListener('popstate', () => window.setTimeout(() => onRouteChange(), 200));\n}\n\nexport function resetAdminPaymentMethodBrandInjectorForTests(): void {\n observer?.disconnect();\n observer = null;\n stopRetries();\n syncQueued = false;\n delete windowRecord()[FLAG];\n if (document.body) {\n delete ((document.body as HTMLElement & Record)[LISTENER_FLAG]);\n }\n}\n","import { handlerMap } from './handlers';\nimport { initEasyPayWatcher } from './handlers/setPaymentMethod';\nimport { installOrderResponseInterceptor } from './orderResponseInterceptor';\nimport { installCheckoutEasyPayInjector } from './checkoutEasyPayInjector';\nimport { installOrderCompleteReceiptInjector } from './orderCompleteReceiptInjector';\nimport { installMypageOrderShowInjector } from './mypageOrderShowInjector';\nimport { installAdminPaymentMethodBrandInjector } from './adminPaymentMethodBrandInjector';\n\nconst PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments';\n\nconst logger = {\n info: (...args: unknown[]) => console.info(`[${PLUGIN_IDENTIFIER}]`, ...args),\n warn: (...args: unknown[]) => console.warn(`[${PLUGIN_IDENTIFIER}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${PLUGIN_IDENTIFIER}]`, ...args),\n};\n\nfunction registerHandlers(): number {\n const g7Core = (window as Record).G7Core as Record | undefined;\n\n if (!g7Core) {\n return 0;\n }\n\n const getDispatcher = g7Core.getActionDispatcher as (() => Record) | undefined;\n\n if (typeof getDispatcher !== 'function') {\n return 0;\n }\n\n const dispatcher = getDispatcher() as Record | undefined;\n\n if (!dispatcher || typeof dispatcher.registerHandler !== 'function') {\n return 0;\n }\n\n let count = 0;\n for (const [name, handler] of Object.entries(handlerMap)) {\n const fullName = `${PLUGIN_IDENTIFIER}.${name}`;\n dispatcher.registerHandler(fullName, handler, {\n category: 'plugin',\n source: PLUGIN_IDENTIFIER,\n });\n count++;\n }\n\n return count;\n}\n\nfunction initPlugin(): void {\n // fetch 인터셉터는 G7Core 초기화와 무관하게 즉시 설치\n installOrderResponseInterceptor();\n installCheckoutEasyPayInjector();\n installOrderCompleteReceiptInjector();\n installMypageOrderShowInjector();\n installAdminPaymentMethodBrandInjector();\n\n const doInit = () => {\n const count = registerHandlers();\n\n if (count > 0) {\n logger.info(`${count} handler(s) registered`);\n initEasyPayWatcher();\n return;\n }\n\n let retries = 0;\n const maxRetries = 50;\n const interval = setInterval(() => {\n retries++;\n const result = registerHandlers();\n\n if (result > 0) {\n clearInterval(interval);\n logger.info(`${result} handler(s) registered (after ${retries} retries)`);\n initEasyPayWatcher();\n return;\n }\n\n if (retries >= maxRetries) {\n clearInterval(interval);\n logger.warn('ActionDispatcher not available after timeout');\n }\n }, 100);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', doInit);\n } else {\n doInit();\n }\n}\n\ninitPlugin();\n\n(window as Record).__SirsoftNicepayments = {\n identifier: PLUGIN_IDENTIFIER,\n handlers: Object.keys(handlerMap),\n initPlugin,\n};\n"],"names":["resolveApiUrl","url","trimReason","reason","reportPaymentWindowClosed","context","payload","apiClient","error","loadScript","src","resolve","reject","script","createPaymentForm","action","fields","form","name","value","input","CALLBACK_PATH","NICEPAY_MOBILE_ENDPOINT","isMobileDevice","nav","ua","requestPaymentHandler","_context","pgPaymentData","paramPaymentMethod","localState","paymentMethod","G7Core","configJson","config","isMobile","signDataUrl","authToken","signDataHeaders","signDataRes","signData","callbackUrl","isEasyPay","payMethod","formFields","useEscrow","od","taxAmt","vatAmt","taxFreeAmt","charsetInput","paymentClosed","bodySnapshot","closePayment","_resultCode","resultMsg","closeReason","handlePopState","el","METHOD_TO_TEXT","SELECTED_SHADOW","getEasyPayButtonContainer","section","paras","p","clearEasyPayButtonStyles","container","btn","updateEasyPayButtonStyles","selectedMethod","selectedText","setPaymentMethodHandler","initEasyPayWatcher","templateApp","state","handlerMap","ORDER_CREATE_PATH","TARGET_PG_PROVIDER","PLUGIN_IDENTIFIER","FETCH_FLAG","CHECKOUT_ENDPOINT_PATH","PAYMENT_IN_PROGRESS_FLAG","CHECKOUT_CACHE_KEY","logger","args","buildNoOpRedirectUrl","isTargetOrderEndpoint","method","path","isCheckoutEndpoint","extractPaymentMethodFromBody","body","obj","v","replacePaymentMethodInBody","newMethod","parsed","resolveOrderPaymentAmount","orderData","candidates","candidate","amount","installOrderResponseInterceptor","w","ORIGINAL_FETCH_KEY","browserFetch","originalFetch","init","checkoutResp","text","originalPaymentMethod","modifiedInit","pm","response","envelope","responseData","isNicepayPg","options","firstName","orderName","modifiedData","modifiedBody","tryInstallAxiosCheckoutInterceptor","AXIOS_CHECKOUT_FLAG","retries","axiosClient","PLUGIN_ID","FLAG","CHECKOUT_RE","LISTENER_FLAG","SYNC_RETRY_INTERVAL_MS","SYNC_RETRY_ATTEMPTS","LEGACY_CONTAINER_ID","EASY_PAY_DEFINITIONS","observer","retryTimer","syncQueued","windowRecord","isCheckoutPage","normalizedText","comparableText","isKoreanPage","copyFor","definition","displayText","findDefinitionById","id","findDefinitionForButton","button","markedDefinition","label","findPaymentRow","alignPaymentTextLeft","heading","description","row","textContainer","createMark","mark","removeOtherPgArtifacts","element","findPaymentIcon","ensureMark","existingMark","icon","updatePaymentText","paragraphs","copy","headingText","descriptionText","showButton","removeLegacyInjectedSection","legacySection","syncRenderedCheckoutEasyPayMethods","root","changed","queueSync","stopRetries","startSync","attempts","onRouteChange","installCheckoutEasyPayInjector","origPush","BTN_ID","ORDER_COMPLETE_RE","getToken","fetchPayment","orderNumber","token","res","fetchReceiptUrl","injectOnOrderComplete","payment","blueBtn","b","receiptBtn","lastBtn","tryInject","match","installOrderCompleteReceiptInjector","schedule","delay","ROW_ID","VBANK_BLOCK_ID","ORDER_SHOW_RE","getOrderFromState","getState","data","findPaymentContainer","panel","h3","panelDiv","buildReceiptRow","formatVbankDueAt","raw","d","pad","n","buildVbankRow","valueClass","l","buildVbankBlock","wrap","inner","title","rows","dueRaw","dueRow","dueLabel","dueValue","notice","startPolling","done","installMypageOrderShowInjector","ADMIN_SETTINGS_RE","MARK_SELECTOR","ADMIN_PAYMENT_BRAND_DEFINITIONS","isOrderSettingsPage","tab","classNameOf","isPaymentMethodItem","className","findTitleElement","item","findDefinitionForItem","findPaymentMethodItems","applyBrandMarkContent","expectedText","createBrandMark","syncBrandMark","existing","syncRenderedAdminPaymentMethodBrands","matched","installAdminPaymentMethodBrandInjector","registerHandlers","g7Core","getDispatcher","dispatcher","count","handler","fullName","initPlugin","doInit","maxRetries","interval","result"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"plugin.iife.js","sources":["../../resources/js/paymentCloseReport.ts","../../resources/js/handlers/requestPayment.ts","../../resources/js/handlers/setPaymentMethod.ts","../../resources/js/handlers/index.ts","../../resources/js/orderResponseInterceptor.ts","../../resources/js/checkoutEasyPayInjector.ts","../../resources/js/orderCompleteReceiptInjector.ts","../../resources/js/mypageOrderShowInjector.ts","../../resources/js/adminPaymentMethodBrandInjector.ts","../../resources/js/adminOrderPaymentDisplayInjector.ts","../../resources/js/index.ts"],"sourcesContent":["export interface PaymentCloseReportContext {\n closeReportUrl?: string;\n oid: string;\n price: number;\n buyer_email?: string;\n buyer_phone?: string;\n payment_method?: string;\n}\n\nfunction resolveApiUrl(url: string): string {\n if (/^https?:\\/\\//i.test(url) || url.startsWith('/api/')) {\n return url;\n }\n\n if (url.startsWith('/plugins/')) {\n return `/api${url}`;\n }\n\n if (url.startsWith('plugins/')) {\n return `/api/${url}`;\n }\n\n return url;\n}\n\nfunction trimReason(reason: string): string {\n return reason.trim().slice(0, 160);\n}\n\nexport async function reportPaymentWindowClosed(\n context: PaymentCloseReportContext,\n reason = 'nicepay-window-closed',\n): Promise {\n if (!context.closeReportUrl) {\n return;\n }\n\n const payload = {\n oid: context.oid,\n price: Number(context.price),\n buyer_email: context.buyer_email ?? '',\n buyer_phone: context.buyer_phone ?? '',\n payment_method: context.payment_method ?? '',\n reason: trimReason(reason) || 'nicepay-window-closed',\n };\n\n try {\n const apiClient = ((window as any).G7Core)?.api;\n if (typeof apiClient?.post === 'function') {\n await apiClient.post(context.closeReportUrl, payload);\n return;\n }\n\n await fetch(resolveApiUrl(context.closeReportUrl), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n keepalive: true,\n });\n } catch (error) {\n console.warn('[sirsoft-pay_nicepayments] failed to report payment window close', error);\n }\n}\n","import { reportPaymentWindowClosed } from '../paymentCloseReport';\n\ninterface G7ApiResponse {\n data?: unknown;\n}\n\ninterface G7CoreApi {\n get: (url: string) => Promise;\n post?: (url: string, data?: Record) => Promise;\n}\n\ninterface G7CoreToast {\n error: (message: string) => void;\n}\n\ninterface G7CoreStateApi {\n setLocal: (state: Record) => void;\n}\n\ninterface G7Core {\n api: G7CoreApi;\n toast?: G7CoreToast;\n state?: G7CoreStateApi;\n}\n\ninterface TemplateLocalState {\n paymentMethod?: string;\n}\n\ninterface TemplateApp {\n globalState?: {\n _local?: TemplateLocalState;\n };\n}\n\ninterface PgPaymentData {\n order_number: string;\n order_name: string;\n amount: number;\n currency?: string;\n customer_name?: string;\n customer_email?: string;\n customer_phone?: string;\n goods_cl?: string; // 휴대폰결제 상품 유형: '0'=디지털컨텐츠, '1'=실물\n}\n\ninterface RequestPaymentParams {\n pgPaymentData: PgPaymentData;\n paymentMethod?: string;\n}\n\ninterface ClientConfig {\n mid: string;\n sdk_url: string;\n sign_data_url: string;\n close_report_url?: string;\n useEscrow?: boolean;\n}\n\ninterface SignDataResponse {\n ediDate: string;\n signData: string;\n mid: string;\n}\n\ninterface PaymentAction {\n params?: RequestPaymentParams;\n}\n\ndeclare global {\n interface Window {\n nicepaySubmit: () => void;\n nicepayClose: (resultCode: string, resultMsg: string) => void;\n goPay: (form: HTMLFormElement) => void;\n G7Core: G7Core;\n __templateApp?: TemplateApp;\n }\n}\n\nfunction loadScript(src: string): Promise {\n return new Promise((resolve, reject) => {\n if (document.querySelector(`script[src=\"${src}\"]`)) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.src = src;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load script: ${src}`));\n document.head.appendChild(script);\n });\n}\n\nfunction createPaymentForm(action: string, fields: Record): HTMLFormElement {\n const form = document.createElement('form');\n form.id = 'nicepayForm';\n form.method = 'post';\n form.action = action;\n // CharSet 필드와 일치 — 모바일은 form.acceptCharset 으로 charset 결정\n form.acceptCharset = 'utf-8';\n\n for (const [name, value] of Object.entries(fields)) {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = value;\n form.appendChild(input);\n }\n\n document.body.appendChild(form);\n return form;\n}\n\nconst CALLBACK_PATH = '/plugins/sirsoft-pay_nicepayments/payment/callback';\n\n// 나이스페이 v3 모바일 결제창 endpoint — 폼을 직접 POST 하면 NicePay 모바일 페이지로 전체 redirect.\n// PC 와 달리 SDK 불필요. 결제 완료 후 ReturnURL 로 redirect.\nconst NICEPAY_MOBILE_ENDPOINT = 'https://web.nicepay.co.kr/v3/v3Payment.jsp';\n\n/**\n * 모바일 환경 판별.\n *\n * 판별 우선순위:\n * 1. navigator.userAgentData.mobile — 브라우저가 직접 판단하는 표준 API (Chrome/Edge 90+)\n * 2. UA 문자열 정규식 — iOS/Android/Windows Phone 및 주요 인앱 브라우저\n * 3. maxTouchPoints 보조 판단 — iPadOS처럼 데스크탑 UA를 보내는 터치 기기 처리\n * (Windows/Mac 데스크탑 터치스크린 오탐 방지를 위해 플랫폼 확인 병행)\n */\nfunction isMobileDevice(): boolean {\n if (typeof navigator === 'undefined') return false;\n\n // 1단계: User Agent Client Hints (가장 정확, 브라우저가 직접 판단)\n const nav = navigator as Navigator & { userAgentData?: { mobile: boolean } };\n if (nav.userAgentData?.mobile !== undefined) {\n return nav.userAgentData.mobile;\n }\n\n // 2단계: UA 문자열 파싱\n const ua = (navigator.userAgent || '').toLowerCase();\n const mobileUA = /android|iphone|ipod|windows phone|iemobile|blackberry|opera mini|mobile safari/;\n if (mobileUA.test(ua)) return true;\n\n // 3단계: iPadOS 등 데스크탑 UA를 보내는 터치 기기 판별\n // maxTouchPoints > 1 이면 실제 멀티터치 기기 (터치스크린 노트북은 보통 1)\n // Windows/Mac 플랫폼이면 데스크탑으로 간주하여 오탐 방지\n const touchPoints = (navigator as Navigator & { maxTouchPoints?: number }).maxTouchPoints ?? 0;\n if (touchPoints > 1 && !ua.includes('windows') && !ua.includes('macintosh')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * 나이스페이먼츠 결제창 호출 핸들러 (나이스페이 구형 API, goPay 방식)\n *\n * 체크아웃 레이아웃에서 주문 생성 API 성공 후 호출됩니다:\n * handler: \"sirsoft-pay_nicepayments.requestPayment\"\n * params: { pgPaymentData: response.data.pg_payment_data }\n *\n * 호출 순서:\n * 1. Client Config API 호출 → MID, SDK URL, SignData URL 획득\n * 2. 환경 분기: PC 면 SDK 동적 로드 / 모바일 은 SDK 생략\n * 3. 서버에서 EdiDate + SignData 생성\n * 4. 결제 폼 생성:\n * - PC: 폼 action=ReturnURL → goPay(form) 으로 iframe 팝업\n * - 모바일: 폼 action=https://web.nicepay.co.kr/v3/v3Payment.jsp → 직접 submit (전체 페이지 redirect)\n * 5. 결제 완료 시 나이스페이먼츠가 ReturnURL(POST)로 인증값 전달\n *\n * NicePay 의 PC SDK 는 모바일 자동 감지를 하지 않으므로 UA 기반 분기가 필수.\n * 잘못 분기하면 모바일에서도 PC 팝업이 떠 사용성이 매우 나빠짐.\n */\nexport async function requestPaymentHandler(action: PaymentAction, _context?: unknown): Promise {\n const { pgPaymentData, paymentMethod: paramPaymentMethod } = action.params ?? {};\n\n const localState = window.__templateApp?.globalState?._local;\n const paymentMethod = paramPaymentMethod ?? localState?.paymentMethod ?? 'card';\n\n if (!pgPaymentData) {\n console.error('[sirsoft-pay_nicepayments] pgPaymentData is required');\n return;\n }\n\n const G7Core = window.G7Core;\n\n try {\n // 1. Client Config 가져오기\n const configJson = await G7Core.api.get('/modules/sirsoft-ecommerce/payments/client-config/nicepayments');\n\n if (!configJson.data) {\n console.error('[sirsoft-pay_nicepayments] Failed to fetch client config', configJson);\n return;\n }\n\n const config = configJson.data as ClientConfig;\n const isMobile = isMobileDevice();\n\n // 2. PC 인 경우에만 SDK 로드 (모바일 은 직접 form submit 으로 NicePay 모바일 페이지로 이동)\n if (!isMobile) {\n await loadScript(config.sdk_url);\n\n if (typeof window.goPay !== 'function') {\n G7Core?.toast?.error?.('나이스페이먼츠 SDK를 불러올 수 없습니다. 잠시 후 다시 시도해주세요.');\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n return;\n }\n }\n\n // 3. 서버에서 EdiDate + SignData 생성\n // sign-data 엔드포인트는 'auth' 미들웨어가 걸려있어 Sanctum Bearer 토큰\n // 또는 세션 쿠키 중 하나가 필요. SPA 모드에서 토큰만 있는 경우를 대비해\n // localStorage 의 auth_token 을 Authorization 헤더로 명시 전달하고,\n // credentials:include 로 세션 쿠키도 함께 전송 (둘 중 하나만 있어도 통과).\n const signDataUrl = window.location.origin + config.sign_data_url;\n const authToken = (typeof localStorage !== 'undefined') ? localStorage.getItem('auth_token') : null;\n const signDataHeaders: Record = {\n 'Content-Type': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'Accept': 'application/json',\n };\n if (authToken) {\n signDataHeaders['Authorization'] = `Bearer ${authToken}`;\n }\n const signDataRes = await fetch(signDataUrl, {\n method: 'POST',\n credentials: 'include',\n headers: signDataHeaders,\n body: JSON.stringify({ amt: pgPaymentData.amount, moid: pgPaymentData.order_number }),\n });\n\n if (!signDataRes.ok) {\n throw new Error('SignData 생성에 실패했습니다.');\n }\n\n const signData: SignDataResponse = await signDataRes.json();\n const callbackUrl = window.location.origin + CALLBACK_PATH;\n\n // 4. 결제 폼 생성\n // 간편결제는 PayMethod='CARD' + DirectShowOpt='CARD' + 방식별 directive 필드 조합.\n // PayMethod에 'KAKAOPAY' 등을 직접 넣으면 [W004] 발생 — gnu5 orderform.js 동일 방식.\n const isEasyPay = typeof paymentMethod === 'string' && paymentMethod.startsWith('nicepay_');\n\n const payMethodMap: Record = {\n card: 'CARD',\n vbank: 'VBANK',\n bank: 'BANK',\n phone: 'CELLPHONE',\n };\n const payMethod = isEasyPay ? 'CARD' : (payMethodMap[paymentMethod ?? 'card'] ?? 'CARD');\n\n const formFields: Record = {\n PayMethod: payMethod,\n GoodsName: pgPaymentData.order_name,\n Amt: String(pgPaymentData.amount),\n MID: signData.mid,\n Moid: pgPaymentData.order_number,\n BuyerName: pgPaymentData.customer_name ?? '',\n BuyerEmail: pgPaymentData.customer_email ?? '',\n BuyerTel: pgPaymentData.customer_phone ?? '',\n ReturnURL: callbackUrl,\n EdiDate: signData.ediDate,\n SignData: signData.signData,\n CharSet: 'utf-8',\n GoodsCl: '1',\n DirectShowOpt: '',\n DirectEasyPay: '',\n NicepayReserved: '',\n EasyPayMethod: '',\n MallReserved: '',\n MallReserved1: '',\n };\n\n // 휴대폰결제: 상품 유형 덮어쓰기 (0:디지털컨텐츠, 1:실물)\n if (payMethod === 'CELLPHONE') {\n formFields.GoodsCl = pgPaymentData.goods_cl ?? '1';\n }\n\n // TransType: 에스크로 활성 + 일반결제 → '1', 그 외(간편결제 포함) → '0'\n const useEscrow = config.useEscrow && !isEasyPay;\n formFields.TransType = useEscrow ? '1' : '0';\n\n // 간편결제 directive 필드 설정 — gnu5 orderform.js switch(settle_method==='간편결제') 동일\n if (isEasyPay) {\n formFields.DirectShowOpt = 'CARD';\n formFields.MallReserved = `nicepay_easy_pay_method=${encodeURIComponent(paymentMethod)}`;\n formFields.MallReserved1 = paymentMethod;\n switch (paymentMethod) {\n case 'nicepay_naverpay':\n formFields.DirectEasyPay = 'E020';\n formFields.EasyPayMethod = 'E020=CARD';\n break;\n case 'nicepay_kakaopay':\n formFields.NicepayReserved = 'DirectKakao=Y';\n break;\n case 'nicepay_samsungpay':\n formFields.DirectEasyPay = 'E021';\n break;\n case 'nicepay_applepay':\n formFields.DirectEasyPay = 'E025';\n break;\n case 'nicepay_payco':\n formFields.NicepayReserved = 'DirectPayco=Y';\n break;\n case 'nicepay_skpay':\n formFields.NicepayReserved = 'DirectPay11=Y';\n break;\n case 'nicepay_ssgpay':\n formFields.DirectEasyPay = 'E007';\n break;\n case 'nicepay_lpay':\n formFields.DirectEasyPay = 'E018';\n break;\n }\n }\n\n // 4-2. 과세/비과세 금액 조회 (optional — 실패해도 결제 진행)\n try {\n const orderRes = await G7Core.api.get(`/modules/sirsoft-ecommerce/user/orders/${pgPaymentData.order_number}`);\n const od = orderRes?.data as Record | null | undefined;\n if (od) {\n const taxAmt = Number(od['total_tax_amount'] ?? 0);\n const vatAmt = Number(od['total_vat_amount'] ?? 0);\n const taxFreeAmt = Number(od['total_tax_free_amount'] ?? 0);\n if (taxAmt > 0 || vatAmt > 0 || taxFreeAmt > 0) {\n formFields.TaxAmt = String(taxAmt);\n formFields.VatAmt = String(vatAmt);\n formFields.TaxFreeAmt = String(taxFreeAmt);\n }\n }\n } catch {\n // 과세 필드는 선택 사항 — 조회 실패 시 미포함 상태로 진행\n }\n\n const form = createPaymentForm(callbackUrl, formFields);\n\n if (isMobile) {\n // 모바일 결제창 호출:\n // - form.action = NicePay 모바일 endpoint\n // - acceptCharset = 'euc-kr' : NicePay v3 mobile 은 EUC-KR 로 form 데이터를 받음\n // (NicePay 공식 샘플과 동일). 브라우저가 자동으로 UTF-8 → EUC-KR 변환.\n // - CharSet 입력값도 'euc-kr' 로 일치 — NicePay 가 같은 인코딩으로 디코딩하도록.\n // (utf-8 그대로 두면 GoodsName/BuyerName 한글이 mojibake \"遺?瑜?\" 로 깨짐)\n // 결제 완료 후 NicePay 가 ReturnURL 로 redirect.\n form.action = NICEPAY_MOBILE_ENDPOINT;\n form.acceptCharset = 'euc-kr';\n const charsetInput = form.querySelector('input[name=\"CharSet\"]') as HTMLInputElement | null;\n if (charsetInput) {\n charsetInput.value = 'euc-kr';\n }\n form.submit();\n // 페이지 자체가 redirect 되므로 정리 로직 불필요. submit 후 이 함수의 후속 코드는 실행되지 않음.\n return;\n }\n\n // 이하 PC 전용 — iframe 팝업 + nicepaySubmit / nicepayClose 콜백 처리\n\n // 5. 나이스페이 전역 콜백 정의\n window.nicepaySubmit = () => {\n form.submit();\n };\n\n let paymentClosed = false;\n\n // goPay() 전 body 자식 스냅샷 — SDK가 추가하는 오버레이/iframe 식별용\n const bodySnapshot = new Set(document.body.children);\n\n const closePayment = (_resultCode: string, resultMsg: string) => {\n if (paymentClosed) return;\n paymentClosed = true;\n const closeReason = [_resultCode, resultMsg].filter(Boolean).join(': ') || 'nicepay-window-closed';\n void reportPaymentWindowClosed({\n closeReportUrl: config.close_report_url,\n oid: pgPaymentData.order_number,\n price: Number(pgPaymentData.amount),\n buyer_email: pgPaymentData.customer_email ?? '',\n buyer_phone: pgPaymentData.customer_phone ?? '',\n payment_method: paymentMethod,\n }, closeReason);\n window.removeEventListener('popstate', handlePopState);\n // SDK가 body에 추가한 오버레이/iframe 등 제거\n Array.from(document.body.children).forEach(el => {\n if (!bodySnapshot.has(el)) el.remove();\n });\n if (form.parentNode) form.parentNode.removeChild(form);\n (window as unknown as Record)['__sirsoftNicepayPaymentInProgress'] = false;\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n if (resultMsg) G7Core?.toast?.error?.(resultMsg);\n };\n\n window.nicepayClose = closePayment;\n\n // 뒤로가기(popstate) 감지 → 결제창 정리\n const handlePopState = () => closePayment('', '');\n\n // 결제창 열기 전 history state 추가 → 뒤로가기 시 popstate 발생\n window.history.pushState({ nicepayOpen: true }, '');\n window.addEventListener('popstate', handlePopState);\n\n // 6. PC 결제창 호출 (iframe 팝업)\n window.goPay(form);\n\n } catch (error: unknown) {\n console.error('[sirsoft-pay_nicepayments] requestPayment error', error);\n (window as unknown as Record)['__sirsoftNicepayPaymentInProgress'] = false;\n G7Core?.state?.setLocal?.({ isSubmittingOrder: false, paymentMethod });\n G7Core?.toast?.error?.('결제 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.');\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n\nconst METHOD_TO_TEXT: Record = {\n nicepay_naverpay: '네이버페이',\n nicepay_kakaopay: '카카오페이',\n nicepay_samsungpay: '삼성페이',\n nicepay_applepay: '애플페이',\n nicepay_payco: 'PAYCO',\n nicepay_skpay: '11pay',\n nicepay_ssgpay: 'SSG페이',\n nicepay_lpay: 'L.pay',\n};\n\n// 흰색 내부 ring + 어두운 외부 ring으로 어떤 버튼 배경색에도 선택 상태가 확실히 보임\nconst SELECTED_SHADOW = '0 0 0 2px #ffffff, 0 0 0 5px rgba(0,0,0,0.55)';\n\nfunction getEasyPayButtonContainer(): Element | null {\n const section = document.getElementById('nicepay_checkout_payment_section');\n if (!section) return null;\n const paras = section.querySelectorAll('p');\n for (const p of paras) {\n if (p.textContent?.includes('간편결제')) {\n return p.nextElementSibling;\n }\n }\n return null;\n}\n\nfunction clearEasyPayButtonStyles(): void {\n const container = getEasyPayButtonContainer();\n if (!container) return;\n container.querySelectorAll('button').forEach(btn => {\n btn.style.boxShadow = '';\n btn.style.outline = '';\n });\n}\n\nfunction updateEasyPayButtonStyles(selectedMethod: string): void {\n const container = getEasyPayButtonContainer();\n if (!container) return;\n\n const selectedText = METHOD_TO_TEXT[selectedMethod];\n container.querySelectorAll('button').forEach(btn => {\n if (btn.textContent?.trim() === selectedText) {\n btn.style.boxShadow = SELECTED_SHADOW;\n btn.style.outline = 'none';\n } else {\n btn.style.boxShadow = '';\n btn.style.outline = '';\n }\n });\n}\n\nexport function setPaymentMethodHandler(action: any): void {\n const paymentMethod = action.params?.paymentMethod;\n if (!paymentMethod) return;\n\n const isEasyPay = typeof paymentMethod === 'string' && paymentMethod.indexOf('nicepay_') === 0;\n (window as any).G7Core?.state?.setLocal?.({\n paymentMethod,\n serverPaymentMethod: isEasyPay ? 'card' : paymentMethod,\n });\n\n if (isEasyPay) {\n updateEasyPayButtonStyles(paymentMethod);\n } else {\n clearEasyPayButtonStyles();\n }\n}\n\n// 코어 결제 버튼(신용카드 등) 클릭 시에도 간편결제 선택 표시 해제\nexport function initEasyPayWatcher(): void {\n const templateApp = (window as any).__templateApp;\n if (typeof templateApp?.onGlobalStateChange !== 'function') return;\n\n templateApp.onGlobalStateChange((state: any) => {\n const paymentMethod = state?._local?.paymentMethod;\n if (typeof paymentMethod !== 'string') return;\n if (!paymentMethod.startsWith('nicepay_')) {\n clearEasyPayButtonStyles();\n }\n });\n}\n","import { requestPaymentHandler } from './requestPayment';\nimport { setPaymentMethodHandler } from './setPaymentMethod';\n\nexport const handlerMap: Record unknown> = {\n requestPayment: requestPaymentHandler,\n setPaymentMethod: setPaymentMethodHandler,\n} as const;\n","/**\n * 주문 생성 API 요청/응답 인터셉터\n *\n * G7Core 템플릿 엔진의 apiCall 핸들러는 window.fetch 를 직접 사용하므로\n * window.fetch 를 래핑하는 방식으로 동작한다.\n *\n * 1. POST /api/modules/sirsoft-ecommerce/user/orders 요청을 가로챈다\n * 2. payment_method 가 'nicepay_*' 이면 'card' 로 교체해 서버에 전송\n * (서버의 PaymentMethodEnum 에 nicepay_* 가 없어서 422 발생 방지)\n * 3. 응답에서 pg_provider === 'sirsoft-nicepayments' 이면 requestPayment 핸들러를 직접 호출\n * 4. 원래 nicepay 방식(paymentMethod)을 requestPayment 에 전달해 올바른 결제창 호출\n * 5. requires_pg_payment → false, redirect_url → 현재 URL 로 교체\n * → 템플릿 조건 분기가 !requires_pg_payment 쪽으로 떨어져 navigate-to-self 가 됨\n */\n\nimport { requestPaymentHandler } from './handlers/requestPayment';\n\nconst ORDER_CREATE_PATH = '/user/orders';\nconst TARGET_PG_PROVIDER = 'sirsoft-nicepayments';\nconst PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments';\nconst FETCH_FLAG = '__sirsoftNicepayFetchInterceptorInstalled';\nconst CHECKOUT_ENDPOINT_PATH = '/api/modules/sirsoft-ecommerce/checkout';\nconst PAYMENT_IN_PROGRESS_FLAG = '__sirsoftNicepayPaymentInProgress';\nconst CHECKOUT_CACHE_KEY = '__sirsoftNicepayCheckoutCache';\n\nconst logger = {\n info: (...args: unknown[]) => console.info(`[${PLUGIN_IDENTIFIER}]`, ...args),\n warn: (...args: unknown[]) => console.warn(`[${PLUGIN_IDENTIFIER}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${PLUGIN_IDENTIFIER}]`, ...args),\n};\n\nfunction buildNoOpRedirectUrl(): string {\n return window.location.pathname + window.location.search + window.location.hash;\n}\n\nfunction isTargetOrderEndpoint(url: string, method: string): boolean {\n if (method.toUpperCase() !== 'POST') return false;\n const path = (url ?? '').split('?')[0].split('#')[0];\n return path === ORDER_CREATE_PATH || path.endsWith(ORDER_CREATE_PATH);\n}\n\nfunction isCheckoutEndpoint(url: string, method: string): boolean {\n if (method.toUpperCase() !== 'GET') return false;\n const path = (url ?? '').split('?')[0].split('#')[0];\n return path === CHECKOUT_ENDPOINT_PATH || path.endsWith(CHECKOUT_ENDPOINT_PATH);\n}\n\nfunction extractPaymentMethodFromBody(body: unknown): string | undefined {\n if (!body) return undefined;\n let obj: Record | null = null;\n if (typeof body === 'string') {\n try { obj = JSON.parse(body) as Record; } catch { return undefined; }\n } else if (typeof body === 'object') {\n obj = body as Record;\n }\n if (!obj) return undefined;\n const v = obj['payment_method'];\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction replacePaymentMethodInBody(body: string, newMethod: string): string {\n try {\n const parsed = JSON.parse(body) as Record;\n if ('payment_method' in parsed) {\n return JSON.stringify({ ...parsed, payment_method: newMethod });\n }\n } catch { /* fall through */ }\n return body;\n}\n\nfunction resolveOrderPaymentAmount(orderData: Record): number {\n const candidates = [orderData.total_due_amount, orderData.total_amount];\n for (const candidate of candidates) {\n const amount = Number(candidate ?? 0);\n if (Number.isFinite(amount) && amount > 0) {\n return Math.floor(amount);\n }\n }\n\n return 0;\n}\n\nexport function installOrderResponseInterceptor(): void {\n const w = window as unknown as Record;\n if (w[FETCH_FLAG]) return;\n w[FETCH_FLAG] = true;\n\n // 최초 설치 플러그인이 원본 브라우저 fetch를 보존 (다른 PG 인터셉터가 쌓이기 전)\n // easy pay 요청 시 이 fetch를 사용해 다른 PG 인터셉터(NHN KCP 등)의 간섭을 차단한다\n const ORIGINAL_FETCH_KEY = '__sirsoftPgOriginalFetch';\n if (!w[ORIGINAL_FETCH_KEY]) {\n w[ORIGINAL_FETCH_KEY] = window.fetch.bind(window);\n }\n const browserFetch = w[ORIGINAL_FETCH_KEY] as typeof fetch;\n\n const originalFetch = window.fetch.bind(window);\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : (input as Request).url;\n const method = init?.method ?? (input instanceof Request ? input.method : 'GET');\n\n // GET checkout 캐싱 — 결제 진행 중 페이지 재초기화 시 \"주문서 없음\" 다이얼로그 억제\n if (isCheckoutEndpoint(url, method)) {\n if (w[PAYMENT_IN_PROGRESS_FLAG] && w[CHECKOUT_CACHE_KEY]) {\n logger.info('payment in progress — serving cached checkout response');\n return new Response(w[CHECKOUT_CACHE_KEY] as string, {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n const checkoutResp = await originalFetch(input, init);\n if (checkoutResp.ok) {\n const text = await checkoutResp.text();\n w[CHECKOUT_CACHE_KEY] = text;\n return new Response(text, {\n status: checkoutResp.status,\n statusText: checkoutResp.statusText,\n headers: checkoutResp.headers,\n });\n }\n return checkoutResp;\n }\n\n if (!isTargetOrderEndpoint(url, method)) {\n return originalFetch(input, init);\n }\n\n // 요청 body에서 payment_method 추출. nicepay_* (간편결제) 만 'card' 로 교체.\n // 일반 결제수단(card / vbank / bank / phone)도 그대로 보존해 requestPaymentHandler\n // 호출 시 정확한 결제수단으로 결제창을 연다 (이전엔 nicepay_* 만 추적해서\n // 가상계좌/계좌이체/휴대폰 선택 시 'card' 로 폴백되는 회귀가 있었음).\n let originalPaymentMethod: string | undefined;\n let isEasyPay = false;\n let modifiedInit = init;\n\n if (init?.body && typeof init.body === 'string') {\n const pm = extractPaymentMethodFromBody(init.body);\n if (typeof pm === 'string' && pm.length > 0) {\n originalPaymentMethod = pm;\n if (pm.startsWith('nicepay_')) {\n isEasyPay = true;\n modifiedInit = { ...init, body: replacePaymentMethodInBody(init.body, 'card') };\n logger.info(`easy pay detected: replacing payment_method '${pm}' → 'card'`);\n }\n }\n }\n\n // easy pay일 때는 browserFetch(원본)를 사용해 NHN KCP 등 다른 PG 인터셉터를 우회한다.\n // \"타 PG와 사용가능함\"이 ON이고 기본 PG가 NHN KCP일 때 NHN KCP 결제창이 먼저 열리는 것을 방지.\n const fetchFn = isEasyPay ? browserFetch : originalFetch;\n const response = await fetchFn(input, modifiedInit);\n\n // 2xx 응답만 처리 (4xx/5xx 오류 응답은 그대로 통과)\n if (!response.ok) {\n return response;\n }\n\n // 응답 파싱 (clone으로 원본 스트림 보호)\n // 서버 응답 구조: { success, message, data: { order, requires_pg_payment, pg_provider, pg_payment_data, redirect_url } }\n let envelope: Record | null = null;\n try {\n envelope = (await response.clone().json()) as Record;\n } catch {\n return response;\n }\n\n const responseData = (envelope?.data ?? envelope) as Record | null;\n\n // 일반 결제: PG 결제가 필요 없으면 통과\n // 간편결제(nicepay_*): requires_pg_payment=false여도 계속 처리\n // → 기본 PG가 미설정이면 백엔드가 requires_pg_payment:false를 반환하지만\n // 간편결제는 나이스페이먼츠 결제창을 열어야 하므로 통과시킴 (취약점 방어)\n if (!responseData?.requires_pg_payment && !isEasyPay) {\n return response;\n }\n\n // easy pay: pg_provider 무관하게 나이스페이 처리\n // 일반 결제: pg_provider가 나이스페이인 경우에만 처리 (나이스페이가 기본 PG일 때)\n const isNicepayPg = responseData.pg_provider === TARGET_PG_PROVIDER;\n\n if (!isEasyPay && !isNicepayPg) {\n return response;\n }\n\n // pg_payment_data: 백엔드 응답에 포함되거나,\n // 간편결제 + 기본 PG 미설정 시 order 데이터에서 직접 구성\n let pgPaymentData = responseData.pg_payment_data as Record | undefined;\n if (!pgPaymentData && isEasyPay) {\n const orderData = responseData.order as Record | undefined;\n if (orderData) {\n const options = orderData.options as Array> | undefined;\n const firstName = (options?.[0]?.product_name as string | undefined) ?? String(orderData.order_number ?? '');\n const orderName = (options?.length ?? 0) > 1\n ? `${firstName} 외 ${(options?.length ?? 0) - 1}건`\n : firstName;\n pgPaymentData = {\n order_number: orderData.order_number,\n order_name: orderName,\n // PG 청구액 SSoT는 total_due_amount. 구형 응답 호환만 total_amount로 fallback.\n amount: resolveOrderPaymentAmount(orderData),\n currency: 'KRW',\n customer_name: orderData.orderer_name ?? null,\n customer_email: orderData.orderer_email ?? null,\n customer_phone: String(orderData.orderer_phone ?? '').replace(/[^0-9]/g, ''),\n customer_key: orderData.user_id ?? null,\n };\n logger.info('pg_payment_data constructed from order (기본 PG 미설정)', {\n order_number: pgPaymentData.order_number,\n amount: pgPaymentData.amount,\n });\n }\n }\n\n if (!pgPaymentData) {\n logger.warn('nicepayments order detected but pg_payment_data missing');\n return response;\n }\n\n const paymentMethod = originalPaymentMethod ?? 'card';\n\n logger.info('intercepted order create response — opening PG popup', { paymentMethod });\n\n void requestPaymentHandler({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n params: { pgPaymentData: pgPaymentData as any, paymentMethod },\n });\n\n // 결제 진행 중 플래그 설정 — GET checkout 캐시 서빙으로 \"주문서 없음\" 다이얼로그 억제\n w[PAYMENT_IN_PROGRESS_FLAG] = true;\n\n // 결제창(팝업)이 열려 있는 동안 checkout 페이지에 머물도록\n // redirect_url을 항상 현재 URL(navigate-to-self)로 교체한다.\n // → 템플릿 fallback navigate가 무력화되어 \"결제 완료\" 페이지로 이동하지 않음\n // → 실제 결제 완료 후 requestPaymentHandler 콜백이 complete 페이지로 이동시킴\n // navigate-to-self로 checkout 재초기화 시 GET checkout 404 → \"주문서 없음\" 다이얼로그가\n // 뜰 수 있으나, PAYMENT_IN_PROGRESS_FLAG + 캐시 서빙으로 억제된다.\n\n // envelope.data 안에 있는 경우와 최상위에 있는 경우 모두 처리\n const modifiedData = { ...responseData, requires_pg_payment: false, redirect_url: buildNoOpRedirectUrl() };\n const modifiedBody = envelope?.data\n ? { ...envelope, data: modifiedData }\n : modifiedData;\n\n return new Response(JSON.stringify(modifiedBody), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n };\n\n logger.info('fetch order interceptor installed');\n\n // Axios 인터셉터 설치 — GET checkout 캐싱 및 결제 중 캐시 서빙\n // G7Core.api.client(Axios)가 초기화된 후 설치해야 하므로 비동기로 재시도\n tryInstallAxiosCheckoutInterceptor(w, 15);\n}\n\nconst AXIOS_CHECKOUT_FLAG = '__sirsoftNicepayAxiosCheckoutInterceptorInstalled';\n\nfunction tryInstallAxiosCheckoutInterceptor(w: Record, retries: number): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const axiosClient = ((window as any)?.G7Core?.api?.client) as any;\n if (!axiosClient?.interceptors) {\n if (retries > 0) {\n setTimeout(() => tryInstallAxiosCheckoutInterceptor(w, retries - 1), 300);\n }\n return;\n }\n\n if (w[AXIOS_CHECKOUT_FLAG]) return;\n w[AXIOS_CHECKOUT_FLAG] = true;\n\n // 응답 인터셉터: GET checkout 성공 시 캐시 저장\n // Axios config.url은 baseURL을 포함하지 않으므로 (config.baseURL ?? '') + config.url 로 완성\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n axiosClient.interceptors.response.use((response: any) => {\n const method = (response?.config?.method ?? '').toLowerCase();\n const url = String((response?.config?.baseURL ?? '') + (response?.config?.url ?? ''));\n if (method === 'get' && isCheckoutEndpoint(url, 'GET') && response?.status === 200) {\n w[CHECKOUT_CACHE_KEY] = JSON.stringify(response.data);\n logger.info('checkout response cached (axios)');\n }\n return response;\n });\n\n // 요청 인터셉터: 결제 진행 중 GET checkout을 캐시에서 반환\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n axiosClient.interceptors.request.use((config: any) => {\n const method = (config?.method ?? '').toLowerCase();\n const url = String((config?.baseURL ?? '') + (config?.url ?? ''));\n if (method === 'get' && isCheckoutEndpoint(url, 'GET') && w[PAYMENT_IN_PROGRESS_FLAG] && w[CHECKOUT_CACHE_KEY]) {\n logger.info('payment in progress — serving cached checkout response (axios)');\n // adapter를 Promise.resolve로 교체해 실제 요청 없이 캐시 반환\n config.adapter = () => Promise.resolve({\n data: JSON.parse(w[CHECKOUT_CACHE_KEY] as string),\n status: 200,\n statusText: 'OK (cached)',\n headers: { 'content-type': 'application/json' },\n config,\n request: {},\n });\n }\n return config;\n });\n\n logger.info('axios checkout interceptor installed');\n\n // 인터셉터 설치 직후 GET checkout 즉시 캐싱 — navigate-to-self 재초기화 대비\n if (!w[CHECKOUT_CACHE_KEY]) {\n void (async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coreApi = ((window as any)?.G7Core?.api) as { get?: (url: string) => Promise } | undefined;\n await coreApi?.get?.('/modules/sirsoft-ecommerce/checkout');\n // 응답은 Axios 응답 인터셉터가 자동으로 캐싱함\n } catch { /* 체크아웃 세션 없으면 실패 — 무시 */ }\n })();\n }\n}\n\n/** @deprecated Axios 인터셉터 방식 — apiCall 핸들러가 window.fetch 를 사용하므로 동작하지 않음. installOrderResponseInterceptor 를 사용할 것. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function installAxiosOrderInterceptor(_axiosClient: any): void {\n // no-op\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayCheckoutEasyPayInjectorInstalled';\nconst CHECKOUT_RE = /^\\/shop\\/checkout\\/?$/;\nconst LISTENER_FLAG = '__nicepayCheckoutEasyPaySyncListenerAttached';\nconst SYNC_RETRY_INTERVAL_MS = 200;\nconst SYNC_RETRY_ATTEMPTS = 120;\nconst LEGACY_CONTAINER_ID = 'nicepay_checkout_payment_section';\n\ninterface EasyPayCopy {\n heading: string;\n description: string;\n title: string;\n}\n\ninterface EasyPayDefinition {\n id: string;\n labels: string[];\n ko: EasyPayCopy;\n en: EasyPayCopy;\n markText: string;\n markClassName: string;\n}\n\nconst EASY_PAY_DEFINITIONS: EasyPayDefinition[] = [\n {\n id: 'nicepay_naverpay',\n labels: [\n '네이버페이 (나이스페이먼츠)',\n '네이버페이로 결제 (나이스페이먼츠)',\n 'Naver Pay (NicePayments)',\n 'Pay with Naver Pay (NicePayments)',\n ],\n ko: {\n heading: '네이버페이 (나이스페이먼츠)',\n description: '네이버페이로 결제 (나이스페이먼츠)',\n title: '네이버페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Naver Pay (NicePayments)',\n description: 'Pay with Naver Pay (NicePayments)',\n title: 'Pay with Naver Pay (NicePayments)',\n },\n markText: 'N',\n markClassName: 'bg-green-500 text-white',\n },\n {\n id: 'nicepay_kakaopay',\n labels: [\n '카카오페이 (나이스페이먼츠)',\n '카카오페이로 결제 (나이스페이먼츠)',\n 'Kakao Pay (NicePayments)',\n 'Pay with Kakao Pay (NicePayments)',\n ],\n ko: {\n heading: '카카오페이 (나이스페이먼츠)',\n description: '카카오페이로 결제 (나이스페이먼츠)',\n title: '카카오페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Kakao Pay (NicePayments)',\n description: 'Pay with Kakao Pay (NicePayments)',\n title: 'Pay with Kakao Pay (NicePayments)',\n },\n markText: 'K',\n markClassName: 'bg-yellow-400 text-gray-950',\n },\n {\n id: 'nicepay_samsungpay',\n labels: [\n '삼성페이 (나이스페이먼츠)',\n '삼성페이로 결제 (나이스페이먼츠)',\n 'Samsung Pay (NicePayments)',\n 'Pay with Samsung Pay (NicePayments)',\n ],\n ko: {\n heading: '삼성페이 (나이스페이먼츠)',\n description: '삼성페이로 결제 (나이스페이먼츠)',\n title: '삼성페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Samsung Pay (NicePayments)',\n description: 'Pay with Samsung Pay (NicePayments)',\n title: 'Pay with Samsung Pay (NicePayments)',\n },\n markText: 'S',\n markClassName: 'bg-blue-600 text-white',\n },\n {\n id: 'nicepay_applepay',\n labels: [\n '애플페이 (나이스페이먼츠)',\n '애플페이로 결제 (나이스페이먼츠)',\n 'Apple Pay (NicePayments)',\n 'Pay with Apple Pay (NicePayments)',\n ],\n ko: {\n heading: '애플페이 (나이스페이먼츠)',\n description: '애플페이로 결제 (나이스페이먼츠)',\n title: '애플페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'Apple Pay (NicePayments)',\n description: 'Pay with Apple Pay (NicePayments)',\n title: 'Pay with Apple Pay (NicePayments)',\n },\n markText: 'A',\n markClassName: 'bg-gray-900 text-white',\n },\n {\n id: 'nicepay_payco',\n labels: [\n 'PAYCO (나이스페이먼츠)',\n 'PAYCO로 결제 (나이스페이먼츠)',\n 'PAYCO (NicePayments)',\n 'Pay with PAYCO (NicePayments)',\n ],\n ko: {\n heading: 'PAYCO (나이스페이먼츠)',\n description: 'PAYCO로 결제 (나이스페이먼츠)',\n title: 'PAYCO로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'PAYCO (NicePayments)',\n description: 'Pay with PAYCO (NicePayments)',\n title: 'Pay with PAYCO (NicePayments)',\n },\n markText: 'P',\n markClassName: 'bg-red-500 text-white',\n },\n {\n id: 'nicepay_skpay',\n labels: [\n '11pay (나이스페이먼츠)',\n '11pay로 결제 (나이스페이먼츠)',\n '11pay (NicePayments)',\n 'Pay with 11pay (NicePayments)',\n ],\n ko: {\n heading: '11pay (나이스페이먼츠)',\n description: '11pay로 결제 (나이스페이먼츠)',\n title: '11pay로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: '11pay (NicePayments)',\n description: 'Pay with 11pay (NicePayments)',\n title: 'Pay with 11pay (NicePayments)',\n },\n markText: '11',\n markClassName: 'bg-orange-500 text-white',\n },\n {\n id: 'nicepay_ssgpay',\n labels: [\n 'SSG페이 (나이스페이먼츠)',\n 'SSG페이로 결제 (나이스페이먼츠)',\n 'SSG Pay (NicePayments)',\n 'Pay with SSG Pay (NicePayments)',\n ],\n ko: {\n heading: 'SSG페이 (나이스페이먼츠)',\n description: 'SSG페이로 결제 (나이스페이먼츠)',\n title: 'SSG페이로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'SSG Pay (NicePayments)',\n description: 'Pay with SSG Pay (NicePayments)',\n title: 'Pay with SSG Pay (NicePayments)',\n },\n markText: 'SSG',\n markClassName: 'bg-red-700 text-white',\n },\n {\n id: 'nicepay_lpay',\n labels: [\n 'L.pay (나이스페이먼츠)',\n 'L.pay로 결제 (나이스페이먼츠)',\n 'L.pay (NicePayments)',\n 'Pay with L.pay (NicePayments)',\n ],\n ko: {\n heading: 'L.pay (나이스페이먼츠)',\n description: 'L.pay로 결제 (나이스페이먼츠)',\n title: 'L.pay로 결제 (나이스페이먼츠)',\n },\n en: {\n heading: 'L.pay (NicePayments)',\n description: 'Pay with L.pay (NicePayments)',\n title: 'Pay with L.pay (NicePayments)',\n },\n markText: 'L',\n markClassName: 'bg-purple-600 text-white',\n },\n];\n\nlet observer: MutationObserver | null = null;\nlet retryTimer: number | null = null;\nlet syncQueued = false;\n\nfunction windowRecord(): Record {\n return window as unknown as Record;\n}\n\nfunction isCheckoutPage(): boolean {\n return CHECKOUT_RE.test(window.location.pathname);\n}\n\nfunction normalizedText(value: string | null | undefined): string {\n return (value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction comparableText(value: string | null | undefined): string {\n return normalizedText(value).replace(/\\u200B/g, '');\n}\n\nfunction isKoreanPage(): boolean {\n const lang = document.documentElement.lang || navigator.language || '';\n\n return lang.toLowerCase().startsWith('ko');\n}\n\nfunction copyFor(definition: EasyPayDefinition): EasyPayCopy {\n return isKoreanPage() ? definition.ko : definition.en;\n}\n\nfunction displayText(value: string): string {\n return value\n .replaceAll('네이버페이', '네이\\u200B버페이')\n .replaceAll('카카오페이', '카카\\u200B오페이')\n .replaceAll('삼성페이', '삼성\\u200B페이')\n .replaceAll('Naver Pay', 'Naver\\u200B Pay')\n .replaceAll('Kakao Pay', 'Kakao\\u200B Pay')\n .replaceAll('Samsung Pay', 'Samsung\\u200B Pay')\n .replaceAll('L.pay', 'L.\\u200Bpay');\n}\n\nfunction findDefinitionById(id: string | undefined): EasyPayDefinition | null {\n return EASY_PAY_DEFINITIONS.find((definition) => definition.id === id) ?? null;\n}\n\nfunction findDefinitionForButton(button: HTMLButtonElement): EasyPayDefinition | null {\n const markedDefinition = findDefinitionById(button.dataset.nicepayEasyPayMethod);\n if (markedDefinition) return markedDefinition;\n\n const text = comparableText(button.textContent);\n\n return EASY_PAY_DEFINITIONS.find((definition) => (\n definition.labels.some((label) => text.includes(label))\n )) ?? null;\n}\n\nfunction findPaymentRow(button: HTMLButtonElement): HTMLElement | null {\n return button.querySelector('.flex.items-center.gap-2, .flex.items-center.gap-3')\n ?? button.querySelector('.flex.items-center');\n}\n\nfunction alignPaymentTextLeft(button: HTMLButtonElement, heading?: HTMLElement, description?: HTMLElement): void {\n button.style.textAlign = 'left';\n\n const row = findPaymentRow(button);\n if (row) {\n row.style.justifyContent = 'flex-start';\n row.style.textAlign = 'left';\n row.style.width = '100%';\n }\n\n const textContainer = heading?.parentElement ?? description?.parentElement;\n if (textContainer instanceof HTMLElement) {\n textContainer.style.textAlign = 'left';\n textContainer.style.minWidth = '0';\n }\n}\n\nfunction createMark(definition: EasyPayDefinition): HTMLSpanElement {\n const mark = document.createElement('span');\n mark.dataset.nicepayEasyPayMark = 'true';\n mark.dataset.nicepayEasyPayMethod = definition.id;\n mark.setAttribute('aria-hidden', 'true');\n mark.className = `inline-flex items-center justify-center rounded-lg text-xs font-bold ${definition.markClassName}`;\n mark.style.width = '32px';\n mark.style.height = '32px';\n mark.style.flex = '0 0 32px';\n mark.textContent = definition.markText;\n\n return mark;\n}\n\nfunction removeOtherPgArtifacts(button: HTMLButtonElement): void {\n delete button.dataset.kginicisBrandPaymentButton;\n delete button.dataset.kginicisBrandPaymentMethod;\n delete button.dataset.kginicisNaverpayBrandButton;\n delete button.dataset.nhnkcpEasyPayMethod;\n delete button.dataset.nhnkcpEasyPayVisible;\n delete button.dataset.nhnkcpEasyPayHidden;\n\n button.querySelectorAll('[data-kginicis-brand-payment-mark=\"true\"], [data-kginicis-naverpay-mark=\"true\"], [data-nhnkcp-easy-pay-mark=\"true\"]').forEach((element) => {\n element.remove();\n });\n}\n\nfunction findPaymentIcon(button: HTMLButtonElement): Element | null {\n return button.querySelector('[data-nicepay-easy-pay-mark=\"true\"]')\n ?? button.querySelector('[data-kginicis-brand-payment-mark=\"true\"], [data-kginicis-naverpay-mark=\"true\"], [data-nhnkcp-easy-pay-mark=\"true\"]')\n ?? button.querySelector('svg')\n ?? button.querySelector('i[class*=\"fa-\"], i[role=\"img\"], i');\n}\n\nfunction ensureMark(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n const existingMark = button.querySelector('[data-nicepay-easy-pay-mark=\"true\"]');\n if (existingMark) {\n existingMark.dataset.nicepayEasyPayMethod = definition.id;\n existingMark.className = `inline-flex items-center justify-center rounded-lg text-xs font-bold ${definition.markClassName}`;\n existingMark.textContent = definition.markText;\n return;\n }\n\n const mark = createMark(definition);\n const icon = findPaymentIcon(button);\n if (icon && icon.parentElement) {\n icon.replaceWith(mark);\n return;\n }\n\n const row = findPaymentRow(button);\n row?.prepend(mark);\n}\n\nfunction updatePaymentText(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n const paragraphs = Array.from(button.querySelectorAll('p'));\n const copy = copyFor(definition);\n const headingText = displayText(copy.heading);\n const descriptionText = displayText(copy.description);\n\n const heading = paragraphs[0];\n const description = paragraphs[1];\n\n if (heading && heading.textContent !== headingText) {\n heading.textContent = headingText;\n }\n if (heading) {\n heading.setAttribute('aria-label', copy.heading);\n heading.style.textAlign = 'left';\n heading.style.whiteSpace = 'normal';\n heading.style.wordBreak = 'keep-all';\n heading.style.overflowWrap = 'anywhere';\n }\n\n if (description && description.textContent !== descriptionText) {\n description.textContent = descriptionText;\n }\n if (description) {\n description.style.textAlign = 'left';\n description.style.fontSize = '12px';\n description.style.lineHeight = '1rem';\n description.style.whiteSpace = 'normal';\n description.style.wordBreak = 'keep-all';\n description.style.overflowWrap = 'anywhere';\n }\n\n alignPaymentTextLeft(button, heading, description);\n button.title = copy.title;\n}\n\nfunction showButton(button: HTMLButtonElement, definition: EasyPayDefinition): void {\n if (button.hidden) button.hidden = false;\n if (button.disabled) button.disabled = false;\n if (button.style.display === 'none') button.style.removeProperty('display');\n button.removeAttribute('aria-hidden');\n button.dataset.nicepayEasyPayMethod = definition.id;\n button.dataset.nicepayEasyPayVisible = 'true';\n\n removeOtherPgArtifacts(button);\n ensureMark(button, definition);\n updatePaymentText(button, definition);\n}\n\nfunction removeLegacyInjectedSection(): boolean {\n const legacySection = document.getElementById(LEGACY_CONTAINER_ID);\n if (!legacySection) return false;\n\n legacySection.remove();\n\n return true;\n}\n\nexport async function syncRenderedCheckoutEasyPayMethods(\n root: ParentNode = document,\n): Promise {\n if (typeof window === 'undefined' || typeof document === 'undefined') return false;\n if (!isCheckoutPage()) return false;\n\n let changed = removeLegacyInjectedSection();\n\n root.querySelectorAll('button').forEach((button) => {\n const definition = findDefinitionForButton(button);\n if (!definition) return;\n\n showButton(button, definition);\n changed = true;\n });\n\n return changed;\n}\n\nfunction queueSync(): void {\n if (syncQueued) return;\n syncQueued = true;\n\n window.setTimeout(() => {\n syncQueued = false;\n void syncRenderedCheckoutEasyPayMethods();\n }, 0);\n}\n\nfunction stopRetries(): void {\n if (retryTimer === null) return;\n\n window.clearInterval(retryTimer);\n retryTimer = null;\n}\n\nfunction startSync(): void {\n if (!isCheckoutPage()) return;\n\n stopRetries();\n void syncRenderedCheckoutEasyPayMethods();\n\n let attempts = 0;\n retryTimer = window.setInterval(() => {\n attempts += 1;\n void syncRenderedCheckoutEasyPayMethods();\n\n if (attempts >= SYNC_RETRY_ATTEMPTS) {\n stopRetries();\n }\n }, SYNC_RETRY_INTERVAL_MS);\n\n const body = document.body as HTMLElement & Record;\n if (body[LISTENER_FLAG]) return;\n body[LISTENER_FLAG] = true;\n\n observer = new MutationObserver(() => queueSync());\n observer.observe(document.body, { childList: true, subtree: true, characterData: true });\n}\n\nfunction onRouteChange(): void {\n if (isCheckoutPage()) startSync();\n}\n\nexport function installCheckoutEasyPayInjector(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const w = windowRecord();\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] checkout easy pay method sync installed`);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => startSync());\n } else {\n startSync();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n window.setTimeout(() => onRouteChange(), 200);\n };\n window.addEventListener('popstate', () => window.setTimeout(() => onRouteChange(), 200));\n}\n\nexport function resetCheckoutEasyPayInjectorForTests(): void {\n observer?.disconnect();\n observer = null;\n stopRetries();\n syncQueued = false;\n delete windowRecord()[FLAG];\n if (document.body) {\n delete ((document.body as HTMLElement & Record)[LISTENER_FLAG]);\n }\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayOcReceiptInjectorInstalled';\nconst BTN_ID = 'nicepay-oc-receipt-btn';\n\nconst ORDER_COMPLETE_RE = /^\\/shop\\/orders\\/([^/]+)\\/complete$/;\n\ntype Payment = {\n pg_provider: string;\n transaction_id: string | null;\n paid_at: string | null;\n [key: string]: unknown;\n};\n\ninterface ReceiptInfo {\n receipt_url?: string | null;\n payment_method_display_label?: string | null;\n}\n\nfunction getToken(): string | null {\n return localStorage.getItem('auth_token');\n}\n\nasync function fetchPayment(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n\n try {\n const res = await fetch(`/api/modules/sirsoft-ecommerce/user/orders/${orderNumber}`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { data?: { payment?: Payment } };\n return data?.data?.payment ?? null;\n } catch {\n return null;\n }\n}\n\nasync function fetchReceiptInfo(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n\n try {\n const res = await fetch(`/api/plugins/${PLUGIN_ID}/user/orders/${orderNumber}/receipt`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n return (await res.json()) as ReceiptInfo;\n } catch {\n return null;\n }\n}\n\nfunction patchPaymentMethodDisplay(displayLabel: string | null | undefined): boolean {\n if (!displayLabel) return false;\n\n const rows = Array.from(document.querySelectorAll('div'));\n for (const row of rows) {\n const spans = Array.from(row.children).filter(\n (child): child is HTMLElement => child instanceof HTMLElement && child.tagName === 'SPAN',\n );\n if (spans.length < 2) continue;\n\n const label = spans[0].textContent?.trim();\n if (label !== '결제 방법' && label !== '결제수단' && label !== '결제 방식') continue;\n\n const value = spans[spans.length - 1];\n if (value.textContent?.trim() !== displayLabel) {\n value.textContent = displayLabel;\n value.dataset.nicepayPaymentMethodPatched = 'true';\n }\n row.dataset.nicepayPaymentMethodRow = 'true';\n return true;\n }\n\n return false;\n}\n\nasync function injectOnOrderComplete(orderNumber: string): Promise {\n if (document.getElementById(BTN_ID)) return;\n\n const payment = await fetchPayment(orderNumber);\n // 입금완료(paid_at 채워짐) 시점에만 영수증 버튼 표시 — 가상계좌 입금대기 차단\n if (!payment || payment.pg_provider !== 'nicepayments' || !payment.transaction_id || !payment.paid_at) return;\n\n const blueBtn = Array.from(document.querySelectorAll('button[type=\"button\"]'))\n .find(b => b.className.includes('bg-blue-600'));\n\n if (!blueBtn?.parentElement) return;\n\n const receiptInfo = await fetchReceiptInfo(orderNumber);\n patchPaymentMethodDisplay(receiptInfo?.payment_method_display_label);\n\n const container = blueBtn.parentElement;\n\n const receiptBtn = document.createElement('button');\n receiptBtn.id = BTN_ID;\n receiptBtn.type = 'button';\n receiptBtn.className = blueBtn.className\n .replace(/bg-blue-\\d+/g, 'bg-green-600')\n .replace(/hover:bg-blue-\\d+/g, 'hover:bg-green-700');\n receiptBtn.textContent = '영수증 조회';\n\n receiptBtn.addEventListener('click', async () => {\n receiptBtn.disabled = true;\n receiptBtn.textContent = '로딩 중...';\n const data = await fetchReceiptInfo(orderNumber);\n receiptBtn.disabled = false;\n receiptBtn.textContent = '영수증 조회';\n const url = data?.receipt_url ?? null;\n if (url) {\n window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes');\n }\n });\n\n const lastBtn = container.lastElementChild;\n container.insertBefore(receiptBtn, lastBtn);\n\n console.info(`[${PLUGIN_ID}] receipt button injected on order complete page`);\n}\n\nfunction tryInject(): void {\n const match = location.pathname.match(ORDER_COMPLETE_RE);\n if (match) {\n void injectOnOrderComplete(match[1]);\n }\n}\n\nexport function installOrderCompleteReceiptInjector(): void {\n if (typeof window === 'undefined') return;\n const w = window as Record;\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] order complete receipt injector installed`);\n\n const schedule = (delay = 1200) => setTimeout(tryInject, delay);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => schedule());\n } else {\n schedule();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n schedule();\n };\n window.addEventListener('popstate', () => schedule(500));\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayOrderShowInjectorInstalled';\nconst ROW_ID = 'nicepay-mp-receipt-row';\nconst VBANK_BLOCK_ID = 'nicepay-mp-vbank-info';\n\nconst ORDER_SHOW_RE = /^\\/mypage\\/orders\\/([^/]+)$/;\n\ninterface Payment {\n pg_provider?: string;\n payment_method?: string;\n transaction_id?: string | null;\n paid_at?: string | null;\n vbank_name?: string | null;\n vbank_number?: string | null;\n vbank_holder?: string | null;\n vbank_due_at?: string | null;\n [key: string]: unknown;\n}\n\ninterface OrderData {\n order_number?: string;\n total_amount_formatted?: string;\n payment?: Payment;\n}\n\ninterface ReceiptInfo {\n receipt_url?: string | null;\n payment_method_display_label?: string | null;\n}\n\nfunction getOrderFromState(orderNumber: string): OrderData | null {\n try {\n const g7 = (window as Record).G7Core as Record | undefined;\n const getState = g7?.getState as (() => Record) | undefined;\n const ctx = getState?.()?.currentDataContext as Record | undefined;\n const order = ctx?.order as { data?: OrderData } | undefined;\n const data = order?.data;\n if (!data || data.order_number !== orderNumber) return null;\n return data;\n } catch {\n return null;\n }\n}\n\nfunction getToken(): string | null {\n return localStorage.getItem('auth_token');\n}\n\nasync function fetchReceiptInfo(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n try {\n const res = await fetch(`/api/plugins/${PLUGIN_ID}/user/orders/${orderNumber}/receipt`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n return (await res.json()) as ReceiptInfo;\n } catch {\n return null;\n }\n}\n\nfunction patchPaymentMethodDisplay(container: Element, displayLabel: string | null | undefined): boolean {\n if (!displayLabel) return false;\n\n const rows = Array.from(container.querySelectorAll('div'));\n for (const row of rows) {\n const spans = Array.from(row.children).filter(\n (child): child is HTMLElement => child instanceof HTMLElement && child.tagName === 'SPAN',\n );\n if (spans.length < 2) continue;\n\n const label = spans[0].textContent?.trim();\n if (label !== '결제 방법' && label !== '결제수단' && label !== 'Payment Method') continue;\n\n const value = spans[spans.length - 1];\n if (value.textContent?.trim() !== displayLabel) {\n value.textContent = displayLabel;\n value.dataset.nicepayPaymentMethodPatched = 'true';\n }\n row.dataset.nicepayPaymentMethodRow = 'true';\n return true;\n }\n\n return false;\n}\n\nfunction findPaymentContainer(): Element | null {\n const panel = document.getElementById('order_payment_info_panel');\n if (panel) {\n return Array.from(panel.children).find(el => el.className?.includes('space-y')) ?? panel;\n }\n\n const h3 = Array.from(document.querySelectorAll('h3')).find(\n el => el.textContent?.includes('결제 정보'),\n );\n if (!h3) return null;\n\n const panelDiv = h3.parentElement?.parentElement;\n if (!panelDiv) return null;\n\n return Array.from(panelDiv.children).find(el => el.className?.includes('space-y')) ?? panelDiv;\n}\n\nfunction buildReceiptRow(orderNumber: string): HTMLElement {\n const row = document.createElement('div');\n row.id = ROW_ID;\n row.className = 'flex items-center justify-between';\n\n const label = document.createElement('span');\n label.className = 'text-gray-500 dark:text-gray-400 text-sm';\n label.textContent = '영수증';\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className =\n 'inline-flex items-center gap-1 text-sm text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50';\n btn.textContent = '영수증 조회';\n\n btn.addEventListener('click', async () => {\n btn.disabled = true;\n btn.textContent = '로딩 중...';\n const data = await fetchReceiptInfo(orderNumber);\n btn.disabled = false;\n btn.textContent = '영수증 조회';\n const url = data?.receipt_url ?? null;\n if (url) {\n window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes');\n }\n });\n\n row.appendChild(label);\n row.appendChild(btn);\n return row;\n}\n\nfunction formatVbankDueAt(raw: string | null | undefined): string {\n if (!raw) return '';\n try {\n const d = new Date(raw);\n if (Number.isNaN(d.getTime())) return raw;\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;\n } catch {\n return raw;\n }\n}\n\nfunction buildVbankRow(label: string, value: string, valueClass = 'font-medium text-gray-900 dark:text-white'): HTMLElement {\n const row = document.createElement('div');\n row.className = 'flex justify-between text-sm';\n const l = document.createElement('span');\n l.className = 'text-gray-600 dark:text-gray-400';\n l.textContent = label;\n const v = document.createElement('span');\n v.className = valueClass;\n v.textContent = value;\n row.appendChild(l);\n row.appendChild(v);\n return row;\n}\n\nfunction buildVbankBlock(orderData: OrderData): HTMLElement {\n const p = orderData.payment ?? {};\n const wrap = document.createElement('div');\n wrap.id = VBANK_BLOCK_ID;\n wrap.className = 'pt-4 mt-2 border-t border-gray-200 dark:border-gray-700';\n\n const inner = document.createElement('div');\n inner.className = 'bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-700 rounded-lg p-4';\n\n const title = document.createElement('h3');\n title.className = 'text-sm font-semibold text-blue-800 dark:text-blue-300 mb-3';\n title.textContent = '가상계좌 입금 안내';\n\n const rows = document.createElement('div');\n rows.className = 'space-y-2';\n rows.appendChild(buildVbankRow('은행', p.vbank_name ?? ''));\n rows.appendChild(buildVbankRow('계좌번호', p.vbank_number ?? '', 'font-medium font-mono text-gray-900 dark:text-white'));\n rows.appendChild(buildVbankRow('예금주', p.vbank_holder ?? ''));\n rows.appendChild(buildVbankRow('입금 금액', orderData.total_amount_formatted ?? '', 'font-bold text-blue-600 dark:text-blue-400'));\n\n const dueRaw = p.vbank_due_at;\n if (dueRaw) {\n const dueRow = document.createElement('div');\n dueRow.className = 'flex justify-between text-sm pt-2 border-t border-blue-200 dark:border-blue-700 mt-2';\n const dueLabel = document.createElement('span');\n dueLabel.className = 'text-gray-600 dark:text-gray-400';\n dueLabel.textContent = '입금 기한';\n const dueValue = document.createElement('span');\n dueValue.className = 'font-medium text-red-600 dark:text-red-400';\n dueValue.textContent = formatVbankDueAt(dueRaw);\n dueRow.appendChild(dueLabel);\n dueRow.appendChild(dueValue);\n rows.appendChild(dueRow);\n }\n\n inner.appendChild(title);\n inner.appendChild(rows);\n\n if (!p.paid_at) {\n const notice = document.createElement('p');\n notice.className = 'text-xs text-blue-700 dark:text-blue-300 mt-3';\n notice.textContent = '입금 기한 내에 입금이 완료되지 않으면 주문이 자동 취소됩니다.';\n inner.appendChild(notice);\n }\n\n wrap.appendChild(inner);\n return wrap;\n}\n\nasync function tryInject(orderNumber: string): Promise {\n const orderData = getOrderFromState(orderNumber);\n if (!orderData) return false;\n\n const { payment } = orderData;\n if (!payment || payment.pg_provider !== 'nicepayments') return true;\n\n const container = findPaymentContainer();\n if (!container) return false;\n const receiptInfo = await fetchReceiptInfo(orderNumber);\n patchPaymentMethodDisplay(container, receiptInfo?.payment_method_display_label);\n\n // 가상계좌 입금 안내 — 결제수단이 vbank 이고 가상계좌 번호 발급된 경우 표시 (paid_at 무관: 입금 전후 모두 표시).\n if (payment.payment_method === 'vbank' && payment.vbank_number && !document.getElementById(VBANK_BLOCK_ID)) {\n container.appendChild(buildVbankBlock(orderData));\n console.info(`[${PLUGIN_ID}] vbank info injected on mypage order show`);\n }\n\n if (!payment.transaction_id) return true;\n // 영수증 버튼은 결제완료(paid_at 채워짐) 시점에만 표시 — 가상계좌 입금대기 차단\n if (!payment.paid_at) return true;\n\n if (document.getElementById(ROW_ID)) return true;\n\n container.appendChild(buildReceiptRow(orderNumber));\n console.info(`[${PLUGIN_ID}] receipt button injected on mypage order show`);\n return true;\n}\n\nfunction startPolling(orderNumber: string): void {\n let attempts = 0;\n const id = setInterval(() => {\n attempts++;\n void tryInject(orderNumber).then(done => {\n if (done || attempts >= 30) clearInterval(id);\n });\n }, 400);\n}\n\nfunction onRouteChange(): void {\n const match = location.pathname.match(ORDER_SHOW_RE);\n if (match) startPolling(match[1]);\n}\n\nexport function installMypageOrderShowInjector(): void {\n if (typeof window === 'undefined') return;\n const w = window as unknown as Record;\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] mypage order show injector installed`);\n\n const schedule = (delay = 1500) => setTimeout(onRouteChange, delay);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => schedule());\n } else {\n schedule();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n schedule(600);\n };\n window.addEventListener('popstate', () => schedule(500));\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayAdminPaymentMethodBrandInjectorInstalled';\nconst LISTENER_FLAG = '__nicepayAdminPaymentMethodBrandSyncListenerAttached';\nconst ADMIN_SETTINGS_RE = /^\\/admin\\/ecommerce\\/settings\\/?$/;\nconst MARK_SELECTOR = '[data-nicepay-admin-payment-brand-mark=\"true\"]';\nconst SYNC_RETRY_INTERVAL_MS = 200;\nconst SYNC_RETRY_ATTEMPTS = 120;\n\ninterface AdminPaymentBrandDefinition {\n id: string;\n labels: string[];\n shortLabels: string[];\n markLines: string[];\n markClassName: string;\n}\n\nconst ADMIN_PAYMENT_BRAND_DEFINITIONS: AdminPaymentBrandDefinition[] = [\n {\n id: 'nicepay_naverpay',\n labels: ['네이버페이 (나이스페이먼츠)', '네이버페이로 결제 (나이스페이먼츠)', 'Naver Pay (NicePayments)', 'Pay with Naver Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['N'],\n markClassName: 'bg-green-500 text-white',\n },\n {\n id: 'nicepay_kakaopay',\n labels: ['카카오페이 (나이스페이먼츠)', '카카오페이로 결제 (나이스페이먼츠)', 'Kakao Pay (NicePayments)', 'Pay with Kakao Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['K'],\n markClassName: 'bg-yellow-400 text-gray-950',\n },\n {\n id: 'nicepay_samsungpay',\n labels: ['삼성페이 (나이스페이먼츠)', '삼성페이로 결제 (나이스페이먼츠)', 'Samsung Pay (NicePayments)', 'Pay with Samsung Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['S'],\n markClassName: 'bg-blue-600 text-white',\n },\n {\n id: 'nicepay_applepay',\n labels: ['애플페이 (나이스페이먼츠)', '애플페이로 결제 (나이스페이먼츠)', 'Apple Pay (NicePayments)', 'Pay with Apple Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['A'],\n markClassName: 'bg-gray-900 text-white',\n },\n {\n id: 'nicepay_payco',\n labels: ['PAYCO (나이스페이먼츠)', 'PAYCO로 결제 (나이스페이먼츠)', 'PAYCO (NicePayments)', 'Pay with PAYCO (NicePayments)'],\n shortLabels: [],\n markLines: ['P'],\n markClassName: 'bg-red-500 text-white',\n },\n {\n id: 'nicepay_skpay',\n labels: ['11pay (나이스페이먼츠)', '11pay로 결제 (나이스페이먼츠)', '11pay (NicePayments)', 'Pay with 11pay (NicePayments)'],\n shortLabels: [],\n markLines: ['11'],\n markClassName: 'bg-orange-500 text-white',\n },\n {\n id: 'nicepay_ssgpay',\n labels: ['SSG페이 (나이스페이먼츠)', 'SSG페이로 결제 (나이스페이먼츠)', 'SSG Pay (NicePayments)', 'Pay with SSG Pay (NicePayments)'],\n shortLabels: [],\n markLines: ['SSG'],\n markClassName: 'bg-red-700 text-white',\n },\n {\n id: 'nicepay_lpay',\n labels: ['L.pay (나이스페이먼츠)', 'L.pay로 결제 (나이스페이먼츠)', 'L.pay (NicePayments)', 'Pay with L.pay (NicePayments)'],\n shortLabels: [],\n markLines: ['L'],\n markClassName: 'bg-purple-600 text-white',\n },\n];\n\nlet observer: MutationObserver | null = null;\nlet retryTimer: number | null = null;\nlet syncQueued = false;\n\nfunction windowRecord(): Record {\n return window as unknown as Record;\n}\n\nfunction isOrderSettingsPage(): boolean {\n if (!ADMIN_SETTINGS_RE.test(window.location.pathname)) return false;\n\n const tab = new URLSearchParams(window.location.search).get('tab');\n return tab === null || tab === '' || tab === 'order_settings';\n}\n\nfunction classNameOf(element: Element): string {\n return element instanceof HTMLElement ? element.className : '';\n}\n\nfunction comparableText(value: string | null | undefined): string {\n return (value ?? '')\n .replace(/\\u200B/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction isPaymentMethodItem(element: HTMLElement): boolean {\n const className = classNameOf(element);\n\n return className.includes('excel-card')\n || (className.includes('flex-center') && className.includes('border') && className.includes('gap-4'));\n}\n\nfunction findTitleElement(item: HTMLElement): HTMLElement | null {\n return item.querySelector('.font-medium');\n}\n\nfunction findDefinitionForItem(item: HTMLElement): AdminPaymentBrandDefinition | null {\n const title = comparableText(findTitleElement(item)?.textContent);\n const text = comparableText(item.textContent);\n\n return ADMIN_PAYMENT_BRAND_DEFINITIONS.find((definition) => (\n definition.shortLabels.some((label) => title === label)\n || definition.labels.some((label) => text.includes(label))\n )) ?? null;\n}\n\nfunction findPaymentMethodItems(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll('div'))\n .filter(isPaymentMethodItem);\n}\n\nfunction findPaymentIcon(item: HTMLElement): Element | null {\n const icons = Array.from(item.querySelectorAll('svg, [data-icon], i'));\n\n return icons.find((element) => (\n !element.closest('[data-drag-handle]')\n && !element.closest(MARK_SELECTOR)\n && !element.closest('.row-stack, .excel-card-body, button, select, [role=\"switch\"]')\n )) ?? null;\n}\n\nfunction applyBrandMarkContent(mark: HTMLSpanElement, definition: AdminPaymentBrandDefinition): void {\n mark.dataset.nicepayAdminPaymentBrandMark = 'true';\n mark.dataset.nicepayAdminPaymentMethod = definition.id;\n mark.setAttribute('aria-hidden', 'true');\n mark.className = `inline-flex items-center justify-center rounded-lg font-bold ${definition.markClassName}`;\n mark.style.width = '32px';\n mark.style.height = '32px';\n mark.style.flex = '0 0 32px';\n mark.style.lineHeight = '1';\n mark.style.fontSize = definition.markLines.join('').length > 2 ? '9px' : '12px';\n\n const expectedText = definition.markLines.join('');\n if (mark.textContent !== expectedText) {\n mark.textContent = definition.markLines[0] ?? '';\n }\n}\n\nfunction createBrandMark(definition: AdminPaymentBrandDefinition): HTMLSpanElement {\n const mark = document.createElement('span');\n applyBrandMarkContent(mark, definition);\n\n return mark;\n}\n\nfunction syncBrandMark(item: HTMLElement, definition: AdminPaymentBrandDefinition): boolean {\n const existing = item.querySelector(MARK_SELECTOR);\n if (existing instanceof HTMLSpanElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n applyBrandMarkContent(existing, definition);\n return true;\n }\n\n const mark = createBrandMark(definition);\n const icon = findPaymentIcon(item);\n if (icon && icon.parentElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n icon.replaceWith(mark);\n return true;\n }\n\n const title = findTitleElement(item);\n if (title && title.parentElement) {\n item.dataset.nicepayAdminPaymentMethod = definition.id;\n title.parentElement.insertBefore(mark, title);\n return true;\n }\n\n return false;\n}\n\nexport function syncRenderedAdminPaymentMethodBrands(root: ParentNode = document): boolean {\n if (typeof window === 'undefined' || typeof document === 'undefined') return false;\n if (!isOrderSettingsPage()) return false;\n\n let matched = false;\n\n findPaymentMethodItems(root).forEach((item) => {\n const definition = findDefinitionForItem(item);\n if (!definition) return;\n\n if (syncBrandMark(item, definition)) {\n matched = true;\n }\n });\n\n return matched;\n}\n\nfunction queueSync(): void {\n if (syncQueued) return;\n syncQueued = true;\n\n window.setTimeout(() => {\n syncQueued = false;\n syncRenderedAdminPaymentMethodBrands();\n }, 0);\n}\n\nfunction stopRetries(): void {\n if (retryTimer === null) return;\n\n window.clearInterval(retryTimer);\n retryTimer = null;\n}\n\nfunction startSync(): void {\n if (!isOrderSettingsPage()) return;\n\n stopRetries();\n syncRenderedAdminPaymentMethodBrands();\n\n let attempts = 0;\n retryTimer = window.setInterval(() => {\n attempts += 1;\n syncRenderedAdminPaymentMethodBrands();\n\n if (attempts >= SYNC_RETRY_ATTEMPTS) {\n stopRetries();\n }\n }, SYNC_RETRY_INTERVAL_MS);\n\n const body = document.body as HTMLElement & Record;\n if (body[LISTENER_FLAG]) return;\n body[LISTENER_FLAG] = true;\n\n observer = new MutationObserver(() => queueSync());\n observer.observe(document.body, { childList: true, subtree: true, characterData: true });\n}\n\nfunction onRouteChange(): void {\n if (isOrderSettingsPage()) {\n startSync();\n return;\n }\n\n stopRetries();\n}\n\nexport function installAdminPaymentMethodBrandInjector(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n const w = windowRecord();\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] admin payment method brand sync installed`);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => startSync());\n } else {\n startSync();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n window.setTimeout(() => onRouteChange(), 200);\n };\n window.addEventListener('popstate', () => window.setTimeout(() => onRouteChange(), 200));\n}\n\nexport function resetAdminPaymentMethodBrandInjectorForTests(): void {\n observer?.disconnect();\n observer = null;\n stopRetries();\n syncQueued = false;\n delete windowRecord()[FLAG];\n if (document.body) {\n delete ((document.body as HTMLElement & Record)[LISTENER_FLAG]);\n }\n}\n","const PLUGIN_ID = 'sirsoft-pay_nicepayments';\nconst FLAG = '__nicepayAdminPaymentDisplayInjectorInstalled';\n\nconst ADMIN_ORDER_SHOW_RE = /^\\/admin\\/ecommerce\\/orders\\/([^/]+)$/;\n\ninterface AdminTransactionStatus {\n _pay_method_label?: string | null;\n _base_pay_method_label?: string | null;\n _embedded_pg_provider_label?: string | null;\n}\n\nfunction getToken(): string | null {\n return localStorage.getItem('auth_token');\n}\n\nasync function fetchAdminTransactionStatus(orderNumber: string): Promise {\n const token = getToken();\n if (!token) return null;\n\n try {\n const res = await fetch(`/api/plugins/${PLUGIN_ID}/admin/orders/${orderNumber}/transaction-status`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n });\n if (!res.ok) return null;\n\n const json = (await res.json()) as { data?: AdminTransactionStatus | null } | AdminTransactionStatus;\n return ('data' in json ? json.data : json) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction directElementChildren(el: Element): HTMLElement[] {\n return Array.from(el.children).filter(\n (child): child is HTMLElement => child instanceof HTMLElement,\n );\n}\n\nfunction patchLabeledPaymentMethod(root: Element, displayLabel: string): boolean {\n let patched = false;\n\n const rows = Array.from(root.querySelectorAll('div'));\n for (const row of rows) {\n const children = directElementChildren(row);\n const labelIndex = children.findIndex(child => child.textContent?.trim() === '결제수단');\n if (labelIndex < 0) continue;\n\n const value = children\n .slice(labelIndex + 1)\n .find(child => child.textContent?.trim() && child.textContent.trim() !== '결제수단');\n if (!value) continue;\n\n const installment = children\n .slice(labelIndex + 2)\n .find(child => /^\\(.+\\)$/.test(child.textContent?.trim() ?? ''));\n const installmentText = installment?.textContent?.trim().replace(/^\\(|\\)$/g, '') ?? '';\n const nextDisplayLabel = installmentText && displayLabel.endsWith(')')\n ? displayLabel.replace(/\\)$/, `, ${installmentText})`)\n : displayLabel;\n\n if (value.textContent?.trim() !== nextDisplayLabel) {\n value.textContent = nextDisplayLabel;\n value.dataset.nicepayPaymentMethodPatched = 'true';\n }\n row.dataset.nicepayPaymentMethodRow = 'admin-order';\n if (installment && installmentText) {\n installment.textContent = '';\n installment.style.display = 'none';\n installment.dataset.nicepayPaymentInstallmentHidden = 'true';\n }\n patched = true;\n }\n\n return patched;\n}\n\nfunction patchPaymentMethodBadge(root: Element, baseLabel: string, embeddedLabel: string): boolean {\n const candidates = Array.from(root.querySelectorAll('span')).filter(\n el => el.textContent?.trim() === baseLabel,\n );\n\n const badge = candidates.find(el => {\n const className = typeof el.className === 'string' ? el.className : '';\n return className.includes('rounded-full') || className.includes('font-medium');\n });\n\n if (!badge) return false;\n\n badge.textContent = embeddedLabel;\n badge.dataset.nicepayPaymentMethodPatched = 'true';\n badge.dataset.nicepayPaymentMethodBadge = 'admin-order';\n return true;\n}\n\nexport function patchAdminPaymentMethodDisplay(\n root: Element,\n status: AdminTransactionStatus | null | undefined,\n): boolean {\n const displayLabel = status?._pay_method_label;\n const embeddedLabel = status?._embedded_pg_provider_label;\n const baseLabel = status?._base_pay_method_label;\n\n if (!displayLabel || !embeddedLabel) return false;\n\n const labeled = patchLabeledPaymentMethod(root, displayLabel);\n const badge = baseLabel ? patchPaymentMethodBadge(root, baseLabel, embeddedLabel) : false;\n\n return labeled || badge;\n}\n\nasync function tryInject(orderNumber: string): Promise {\n const root = document.getElementById('section_payment_info') ?? document.getElementById('payment_info_card');\n if (!root) return false;\n\n const status = await fetchAdminTransactionStatus(orderNumber);\n if (!status) return true;\n\n const patched = patchAdminPaymentMethodDisplay(root, status);\n if (patched) {\n console.info(`[${PLUGIN_ID}] payment method display patched on admin order detail`);\n }\n\n return true;\n}\n\nfunction startPolling(orderNumber: string): void {\n let attempts = 0;\n const id = setInterval(() => {\n attempts++;\n void tryInject(orderNumber).then(done => {\n if (done || attempts >= 30) clearInterval(id);\n });\n }, 400);\n}\n\nfunction onRouteChange(): void {\n const match = location.pathname.match(ADMIN_ORDER_SHOW_RE);\n if (match) startPolling(match[1]);\n}\n\nexport function installAdminOrderPaymentDisplayInjector(): void {\n if (typeof window === 'undefined') return;\n const w = window as unknown as Record;\n if (w[FLAG]) return;\n w[FLAG] = true;\n\n console.info(`[${PLUGIN_ID}] admin order payment display injector installed`);\n\n const schedule = (delay = 1500) => setTimeout(onRouteChange, delay);\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => schedule());\n } else {\n schedule();\n }\n\n const origPush = history.pushState.bind(history);\n history.pushState = (...args: Parameters) => {\n origPush(...args);\n schedule(600);\n };\n window.addEventListener('popstate', () => schedule(500));\n}\n","import { handlerMap } from './handlers';\nimport { initEasyPayWatcher } from './handlers/setPaymentMethod';\nimport { installOrderResponseInterceptor } from './orderResponseInterceptor';\nimport { installCheckoutEasyPayInjector } from './checkoutEasyPayInjector';\nimport { installOrderCompleteReceiptInjector } from './orderCompleteReceiptInjector';\nimport { installMypageOrderShowInjector } from './mypageOrderShowInjector';\nimport { installAdminPaymentMethodBrandInjector } from './adminPaymentMethodBrandInjector';\nimport { installAdminOrderPaymentDisplayInjector } from './adminOrderPaymentDisplayInjector';\n\nconst PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments';\n\nconst logger = {\n info: (...args: unknown[]) => console.info(`[${PLUGIN_IDENTIFIER}]`, ...args),\n warn: (...args: unknown[]) => console.warn(`[${PLUGIN_IDENTIFIER}]`, ...args),\n error: (...args: unknown[]) => console.error(`[${PLUGIN_IDENTIFIER}]`, ...args),\n};\n\nfunction registerHandlers(): number {\n const g7Core = (window as Record).G7Core as Record | undefined;\n\n if (!g7Core) {\n return 0;\n }\n\n const getDispatcher = g7Core.getActionDispatcher as (() => Record) | undefined;\n\n if (typeof getDispatcher !== 'function') {\n return 0;\n }\n\n const dispatcher = getDispatcher() as Record | undefined;\n\n if (!dispatcher || typeof dispatcher.registerHandler !== 'function') {\n return 0;\n }\n\n let count = 0;\n for (const [name, handler] of Object.entries(handlerMap)) {\n const fullName = `${PLUGIN_IDENTIFIER}.${name}`;\n dispatcher.registerHandler(fullName, handler, {\n category: 'plugin',\n source: PLUGIN_IDENTIFIER,\n });\n count++;\n }\n\n return count;\n}\n\nfunction initPlugin(): void {\n // fetch 인터셉터는 G7Core 초기화와 무관하게 즉시 설치\n installOrderResponseInterceptor();\n installCheckoutEasyPayInjector();\n installOrderCompleteReceiptInjector();\n installMypageOrderShowInjector();\n installAdminPaymentMethodBrandInjector();\n installAdminOrderPaymentDisplayInjector();\n\n const doInit = () => {\n const count = registerHandlers();\n\n if (count > 0) {\n logger.info(`${count} handler(s) registered`);\n initEasyPayWatcher();\n return;\n }\n\n let retries = 0;\n const maxRetries = 50;\n const interval = setInterval(() => {\n retries++;\n const result = registerHandlers();\n\n if (result > 0) {\n clearInterval(interval);\n logger.info(`${result} handler(s) registered (after ${retries} retries)`);\n initEasyPayWatcher();\n return;\n }\n\n if (retries >= maxRetries) {\n clearInterval(interval);\n logger.warn('ActionDispatcher not available after timeout');\n }\n }, 100);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', doInit);\n } else {\n doInit();\n }\n}\n\ninitPlugin();\n\n(window as Record).__SirsoftNicepayments = {\n identifier: PLUGIN_IDENTIFIER,\n handlers: Object.keys(handlerMap),\n initPlugin,\n};\n"],"names":["resolveApiUrl","url","trimReason","reason","reportPaymentWindowClosed","context","payload","apiClient","error","loadScript","src","resolve","reject","script","createPaymentForm","action","fields","form","name","value","input","CALLBACK_PATH","NICEPAY_MOBILE_ENDPOINT","isMobileDevice","nav","ua","requestPaymentHandler","_context","pgPaymentData","paramPaymentMethod","localState","paymentMethod","G7Core","configJson","config","isMobile","signDataUrl","authToken","signDataHeaders","signDataRes","signData","callbackUrl","isEasyPay","payMethod","formFields","useEscrow","od","taxAmt","vatAmt","taxFreeAmt","charsetInput","paymentClosed","bodySnapshot","closePayment","_resultCode","resultMsg","closeReason","handlePopState","el","METHOD_TO_TEXT","SELECTED_SHADOW","getEasyPayButtonContainer","section","paras","p","clearEasyPayButtonStyles","container","btn","updateEasyPayButtonStyles","selectedMethod","selectedText","setPaymentMethodHandler","initEasyPayWatcher","templateApp","state","handlerMap","ORDER_CREATE_PATH","TARGET_PG_PROVIDER","PLUGIN_IDENTIFIER","FETCH_FLAG","CHECKOUT_ENDPOINT_PATH","PAYMENT_IN_PROGRESS_FLAG","CHECKOUT_CACHE_KEY","logger","args","buildNoOpRedirectUrl","isTargetOrderEndpoint","method","path","isCheckoutEndpoint","extractPaymentMethodFromBody","body","obj","v","replacePaymentMethodInBody","newMethod","parsed","resolveOrderPaymentAmount","orderData","candidates","candidate","amount","installOrderResponseInterceptor","w","ORIGINAL_FETCH_KEY","browserFetch","originalFetch","init","checkoutResp","text","originalPaymentMethod","modifiedInit","pm","response","envelope","responseData","isNicepayPg","options","firstName","orderName","modifiedData","modifiedBody","tryInstallAxiosCheckoutInterceptor","AXIOS_CHECKOUT_FLAG","retries","axiosClient","PLUGIN_ID","FLAG","CHECKOUT_RE","LISTENER_FLAG","SYNC_RETRY_INTERVAL_MS","SYNC_RETRY_ATTEMPTS","LEGACY_CONTAINER_ID","EASY_PAY_DEFINITIONS","observer","retryTimer","syncQueued","windowRecord","isCheckoutPage","normalizedText","comparableText","isKoreanPage","copyFor","definition","displayText","findDefinitionById","id","findDefinitionForButton","button","markedDefinition","label","findPaymentRow","alignPaymentTextLeft","heading","description","row","textContainer","createMark","mark","removeOtherPgArtifacts","element","findPaymentIcon","ensureMark","existingMark","icon","updatePaymentText","paragraphs","copy","headingText","descriptionText","showButton","removeLegacyInjectedSection","legacySection","syncRenderedCheckoutEasyPayMethods","root","changed","queueSync","stopRetries","startSync","attempts","onRouteChange","installCheckoutEasyPayInjector","origPush","BTN_ID","ORDER_COMPLETE_RE","getToken","fetchPayment","orderNumber","token","res","fetchReceiptInfo","patchPaymentMethodDisplay","displayLabel","rows","spans","child","injectOnOrderComplete","payment","blueBtn","b","receiptInfo","receiptBtn","data","lastBtn","tryInject","match","installOrderCompleteReceiptInjector","schedule","delay","ROW_ID","VBANK_BLOCK_ID","ORDER_SHOW_RE","getOrderFromState","getState","findPaymentContainer","panel","h3","panelDiv","buildReceiptRow","formatVbankDueAt","raw","d","pad","n","buildVbankRow","valueClass","l","buildVbankBlock","wrap","inner","title","dueRaw","dueRow","dueLabel","dueValue","notice","startPolling","done","installMypageOrderShowInjector","ADMIN_SETTINGS_RE","MARK_SELECTOR","ADMIN_PAYMENT_BRAND_DEFINITIONS","isOrderSettingsPage","tab","classNameOf","isPaymentMethodItem","className","findTitleElement","item","findDefinitionForItem","findPaymentMethodItems","applyBrandMarkContent","expectedText","createBrandMark","syncBrandMark","existing","syncRenderedAdminPaymentMethodBrands","matched","installAdminPaymentMethodBrandInjector","ADMIN_ORDER_SHOW_RE","fetchAdminTransactionStatus","json","directElementChildren","patchLabeledPaymentMethod","patched","children","labelIndex","installment","installmentText","nextDisplayLabel","patchPaymentMethodBadge","baseLabel","embeddedLabel","badge","patchAdminPaymentMethodDisplay","status","labeled","installAdminOrderPaymentDisplayInjector","registerHandlers","g7Core","getDispatcher","dispatcher","count","handler","fullName","initPlugin","doInit","maxRetries","interval","result"],"mappings":"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"} \ No newline at end of file diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/handlers/requestPayment.test.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/handlers/requestPayment.test.ts index 1d30b05d..5f36b907 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/handlers/requestPayment.test.ts +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/handlers/requestPayment.test.ts @@ -247,9 +247,13 @@ describe('requestPaymentHandler', () => { const form = submitSpy.mock.instances[0] as HTMLFormElement; const reservedInput = form.querySelector('input[name="NicepayReserved"]') as HTMLInputElement | null; + const mallReservedInput = form.querySelector('input[name="MallReserved"]') as HTMLInputElement | null; + const mallReserved1Input = form.querySelector('input[name="MallReserved1"]') as HTMLInputElement | null; expect(getSubmittedPayMethod()).toBe('CARD'); expect(reservedInput?.value).toBe('DirectKakao=Y'); + expect(mallReservedInput?.value).toBe('nicepay_easy_pay_method=nicepay_kakaopay'); + expect(mallReserved1Input?.value).toBe('nicepay_kakaopay'); }); it('모바일 form 은 acceptCharset=euc-kr', async () => { diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/orderPaymentDisplayInjectors.test.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/orderPaymentDisplayInjectors.test.ts new file mode 100644 index 00000000..7873c069 --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/__tests__/orderPaymentDisplayInjectors.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from 'vitest'; +import { patchAdminPaymentMethodDisplay } from '../adminOrderPaymentDisplayInjector'; + +describe('NicePayments order payment display injectors', () => { + it('관리자 주문상세 결제수단 행을 간편결제 표시명으로 교체한다', () => { + document.body.innerHTML = ` +
+
+
결제수단
+
신용카드
+
(일시불)
+
+
+ `; + + const root = document.getElementById('section_payment_info'); + + const patched = patchAdminPaymentMethodDisplay(root!, { + _pay_method_label: '카카오페이 (신용카드)', + _base_pay_method_label: '신용카드', + _embedded_pg_provider_label: '카카오페이', + }); + + expect(patched).toBe(true); + expect(root?.textContent).toContain('카카오페이 (신용카드, 일시불)'); + expect(root?.textContent).not.toContain('(일시불)'); + }); + + it('관리자 주문상세 결제수단 배지를 간편결제 라벨로 교체한다', () => { + document.body.innerHTML = ` +
+ 신용카드 +
+ `; + + const root = document.getElementById('section_payment_info'); + + const patched = patchAdminPaymentMethodDisplay(root!, { + _pay_method_label: '네이버페이 (신용카드)', + _base_pay_method_label: '신용카드', + _embedded_pg_provider_label: '네이버페이', + }); + + expect(patched).toBe(true); + expect(root?.querySelector('span')?.textContent).toBe('네이버페이'); + }); +}); diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/adminOrderPaymentDisplayInjector.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/adminOrderPaymentDisplayInjector.ts new file mode 100644 index 00000000..dbc08a54 --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/adminOrderPaymentDisplayInjector.ts @@ -0,0 +1,163 @@ +const PLUGIN_ID = 'sirsoft-pay_nicepayments'; +const FLAG = '__nicepayAdminPaymentDisplayInjectorInstalled'; + +const ADMIN_ORDER_SHOW_RE = /^\/admin\/ecommerce\/orders\/([^/]+)$/; + +interface AdminTransactionStatus { + _pay_method_label?: string | null; + _base_pay_method_label?: string | null; + _embedded_pg_provider_label?: string | null; +} + +function getToken(): string | null { + return localStorage.getItem('auth_token'); +} + +async function fetchAdminTransactionStatus(orderNumber: string): Promise { + const token = getToken(); + if (!token) return null; + + try { + const res = await fetch(`/api/plugins/${PLUGIN_ID}/admin/orders/${orderNumber}/transaction-status`, { + headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, + }); + if (!res.ok) return null; + + const json = (await res.json()) as { data?: AdminTransactionStatus | null } | AdminTransactionStatus; + return ('data' in json ? json.data : json) ?? null; + } catch { + return null; + } +} + +function directElementChildren(el: Element): HTMLElement[] { + return Array.from(el.children).filter( + (child): child is HTMLElement => child instanceof HTMLElement, + ); +} + +function patchLabeledPaymentMethod(root: Element, displayLabel: string): boolean { + let patched = false; + + const rows = Array.from(root.querySelectorAll('div')); + for (const row of rows) { + const children = directElementChildren(row); + const labelIndex = children.findIndex(child => child.textContent?.trim() === '결제수단'); + if (labelIndex < 0) continue; + + const value = children + .slice(labelIndex + 1) + .find(child => child.textContent?.trim() && child.textContent.trim() !== '결제수단'); + if (!value) continue; + + const installment = children + .slice(labelIndex + 2) + .find(child => /^\(.+\)$/.test(child.textContent?.trim() ?? '')); + const installmentText = installment?.textContent?.trim().replace(/^\(|\)$/g, '') ?? ''; + const nextDisplayLabel = installmentText && displayLabel.endsWith(')') + ? displayLabel.replace(/\)$/, `, ${installmentText})`) + : displayLabel; + + if (value.textContent?.trim() !== nextDisplayLabel) { + value.textContent = nextDisplayLabel; + value.dataset.nicepayPaymentMethodPatched = 'true'; + } + row.dataset.nicepayPaymentMethodRow = 'admin-order'; + if (installment && installmentText) { + installment.textContent = ''; + installment.style.display = 'none'; + installment.dataset.nicepayPaymentInstallmentHidden = 'true'; + } + patched = true; + } + + return patched; +} + +function patchPaymentMethodBadge(root: Element, baseLabel: string, embeddedLabel: string): boolean { + const candidates = Array.from(root.querySelectorAll('span')).filter( + el => el.textContent?.trim() === baseLabel, + ); + + const badge = candidates.find(el => { + const className = typeof el.className === 'string' ? el.className : ''; + return className.includes('rounded-full') || className.includes('font-medium'); + }); + + if (!badge) return false; + + badge.textContent = embeddedLabel; + badge.dataset.nicepayPaymentMethodPatched = 'true'; + badge.dataset.nicepayPaymentMethodBadge = 'admin-order'; + return true; +} + +export function patchAdminPaymentMethodDisplay( + root: Element, + status: AdminTransactionStatus | null | undefined, +): boolean { + const displayLabel = status?._pay_method_label; + const embeddedLabel = status?._embedded_pg_provider_label; + const baseLabel = status?._base_pay_method_label; + + if (!displayLabel || !embeddedLabel) return false; + + const labeled = patchLabeledPaymentMethod(root, displayLabel); + const badge = baseLabel ? patchPaymentMethodBadge(root, baseLabel, embeddedLabel) : false; + + return labeled || badge; +} + +async function tryInject(orderNumber: string): Promise { + const root = document.getElementById('section_payment_info') ?? document.getElementById('payment_info_card'); + if (!root) return false; + + const status = await fetchAdminTransactionStatus(orderNumber); + if (!status) return true; + + const patched = patchAdminPaymentMethodDisplay(root, status); + if (patched) { + console.info(`[${PLUGIN_ID}] payment method display patched on admin order detail`); + } + + return true; +} + +function startPolling(orderNumber: string): void { + let attempts = 0; + const id = setInterval(() => { + attempts++; + void tryInject(orderNumber).then(done => { + if (done || attempts >= 30) clearInterval(id); + }); + }, 400); +} + +function onRouteChange(): void { + const match = location.pathname.match(ADMIN_ORDER_SHOW_RE); + if (match) startPolling(match[1]); +} + +export function installAdminOrderPaymentDisplayInjector(): void { + if (typeof window === 'undefined') return; + const w = window as unknown as Record; + if (w[FLAG]) return; + w[FLAG] = true; + + console.info(`[${PLUGIN_ID}] admin order payment display injector installed`); + + const schedule = (delay = 1500) => setTimeout(onRouteChange, delay); + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => schedule()); + } else { + schedule(); + } + + const origPush = history.pushState.bind(history); + history.pushState = (...args: Parameters) => { + origPush(...args); + schedule(600); + }; + window.addEventListener('popstate', () => schedule(500)); +} diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/handlers/requestPayment.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/handlers/requestPayment.ts index 2ef36c87..1d464090 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/handlers/requestPayment.ts +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/handlers/requestPayment.ts @@ -267,6 +267,8 @@ export async function requestPaymentHandler(action: PaymentAction, _context?: un DirectEasyPay: '', NicepayReserved: '', EasyPayMethod: '', + MallReserved: '', + MallReserved1: '', }; // 휴대폰결제: 상품 유형 덮어쓰기 (0:디지털컨텐츠, 1:실물) @@ -281,6 +283,8 @@ export async function requestPaymentHandler(action: PaymentAction, _context?: un // 간편결제 directive 필드 설정 — gnu5 orderform.js switch(settle_method==='간편결제') 동일 if (isEasyPay) { formFields.DirectShowOpt = 'CARD'; + formFields.MallReserved = `nicepay_easy_pay_method=${encodeURIComponent(paymentMethod)}`; + formFields.MallReserved1 = paymentMethod; switch (paymentMethod) { case 'nicepay_naverpay': formFields.DirectEasyPay = 'E020'; diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/index.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/index.ts index 582f2d3c..6f2075e0 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/index.ts +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/index.ts @@ -5,6 +5,7 @@ import { installCheckoutEasyPayInjector } from './checkoutEasyPayInjector'; import { installOrderCompleteReceiptInjector } from './orderCompleteReceiptInjector'; import { installMypageOrderShowInjector } from './mypageOrderShowInjector'; import { installAdminPaymentMethodBrandInjector } from './adminPaymentMethodBrandInjector'; +import { installAdminOrderPaymentDisplayInjector } from './adminOrderPaymentDisplayInjector'; const PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments'; @@ -53,6 +54,7 @@ function initPlugin(): void { installOrderCompleteReceiptInjector(); installMypageOrderShowInjector(); installAdminPaymentMethodBrandInjector(); + installAdminOrderPaymentDisplayInjector(); const doInit = () => { const count = registerHandlers(); diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/mypageOrderShowInjector.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/mypageOrderShowInjector.ts index bada6460..9d161951 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/mypageOrderShowInjector.ts +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/mypageOrderShowInjector.ts @@ -23,6 +23,11 @@ interface OrderData { payment?: Payment; } +interface ReceiptInfo { + receipt_url?: string | null; + payment_method_display_label?: string | null; +} + function getOrderFromState(orderNumber: string): OrderData | null { try { const g7 = (window as Record).G7Core as Record | undefined; @@ -41,7 +46,7 @@ function getToken(): string | null { return localStorage.getItem('auth_token'); } -async function fetchReceiptUrl(orderNumber: string): Promise { +async function fetchReceiptInfo(orderNumber: string): Promise { const token = getToken(); if (!token) return null; try { @@ -49,13 +54,37 @@ async function fetchReceiptUrl(orderNumber: string): Promise { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, }); if (!res.ok) return null; - const data = (await res.json()) as { receipt_url?: string }; - return data.receipt_url ?? null; + return (await res.json()) as ReceiptInfo; } catch { return null; } } +function patchPaymentMethodDisplay(container: Element, displayLabel: string | null | undefined): boolean { + if (!displayLabel) return false; + + const rows = Array.from(container.querySelectorAll('div')); + for (const row of rows) { + const spans = Array.from(row.children).filter( + (child): child is HTMLElement => child instanceof HTMLElement && child.tagName === 'SPAN', + ); + if (spans.length < 2) continue; + + const label = spans[0].textContent?.trim(); + if (label !== '결제 방법' && label !== '결제수단' && label !== 'Payment Method') continue; + + const value = spans[spans.length - 1]; + if (value.textContent?.trim() !== displayLabel) { + value.textContent = displayLabel; + value.dataset.nicepayPaymentMethodPatched = 'true'; + } + row.dataset.nicepayPaymentMethodRow = 'true'; + return true; + } + + return false; +} + function findPaymentContainer(): Element | null { const panel = document.getElementById('order_payment_info_panel'); if (panel) { @@ -91,9 +120,10 @@ function buildReceiptRow(orderNumber: string): HTMLElement { btn.addEventListener('click', async () => { btn.disabled = true; btn.textContent = '로딩 중...'; - const url = await fetchReceiptUrl(orderNumber); + const data = await fetchReceiptInfo(orderNumber); btn.disabled = false; btn.textContent = '영수증 조회'; + const url = data?.receipt_url ?? null; if (url) { window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes'); } @@ -188,6 +218,8 @@ async function tryInject(orderNumber: string): Promise { const container = findPaymentContainer(); if (!container) return false; + const receiptInfo = await fetchReceiptInfo(orderNumber); + patchPaymentMethodDisplay(container, receiptInfo?.payment_method_display_label); // 가상계좌 입금 안내 — 결제수단이 vbank 이고 가상계좌 번호 발급된 경우 표시 (paid_at 무관: 입금 전후 모두 표시). if (payment.payment_method === 'vbank' && payment.vbank_number && !document.getElementById(VBANK_BLOCK_ID)) { diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/orderCompleteReceiptInjector.ts b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/orderCompleteReceiptInjector.ts index 9c3836a2..2cd616b1 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/orderCompleteReceiptInjector.ts +++ b/plugins/_bundled/sirsoft-pay_nicepayments/resources/js/orderCompleteReceiptInjector.ts @@ -11,6 +11,11 @@ type Payment = { [key: string]: unknown; }; +interface ReceiptInfo { + receipt_url?: string | null; + payment_method_display_label?: string | null; +} + function getToken(): string | null { return localStorage.getItem('auth_token'); } @@ -31,7 +36,7 @@ async function fetchPayment(orderNumber: string): Promise { } } -async function fetchReceiptUrl(orderNumber: string): Promise { +async function fetchReceiptInfo(orderNumber: string): Promise { const token = getToken(); if (!token) return null; @@ -40,13 +45,37 @@ async function fetchReceiptUrl(orderNumber: string): Promise { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, }); if (!res.ok) return null; - const data = (await res.json()) as { receipt_url?: string }; - return data.receipt_url ?? null; + return (await res.json()) as ReceiptInfo; } catch { return null; } } +function patchPaymentMethodDisplay(displayLabel: string | null | undefined): boolean { + if (!displayLabel) return false; + + const rows = Array.from(document.querySelectorAll('div')); + for (const row of rows) { + const spans = Array.from(row.children).filter( + (child): child is HTMLElement => child instanceof HTMLElement && child.tagName === 'SPAN', + ); + if (spans.length < 2) continue; + + const label = spans[0].textContent?.trim(); + if (label !== '결제 방법' && label !== '결제수단' && label !== '결제 방식') continue; + + const value = spans[spans.length - 1]; + if (value.textContent?.trim() !== displayLabel) { + value.textContent = displayLabel; + value.dataset.nicepayPaymentMethodPatched = 'true'; + } + row.dataset.nicepayPaymentMethodRow = 'true'; + return true; + } + + return false; +} + async function injectOnOrderComplete(orderNumber: string): Promise { if (document.getElementById(BTN_ID)) return; @@ -59,6 +88,9 @@ async function injectOnOrderComplete(orderNumber: string): Promise { if (!blueBtn?.parentElement) return; + const receiptInfo = await fetchReceiptInfo(orderNumber); + patchPaymentMethodDisplay(receiptInfo?.payment_method_display_label); + const container = blueBtn.parentElement; const receiptBtn = document.createElement('button'); @@ -72,9 +104,10 @@ async function injectOnOrderComplete(orderNumber: string): Promise { receiptBtn.addEventListener('click', async () => { receiptBtn.disabled = true; receiptBtn.textContent = '로딩 중...'; - const url = await fetchReceiptUrl(orderNumber); + const data = await fetchReceiptInfo(orderNumber); receiptBtn.disabled = false; receiptBtn.textContent = '영수증 조회'; + const url = data?.receipt_url ?? null; if (url) { window.open(url, 'nicepay_receipt', 'width=800,height=600,scrollbars=yes,resizable=yes'); } diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Concerns/ResolvesEasyPayDisplay.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Concerns/ResolvesEasyPayDisplay.php new file mode 100644 index 00000000..6184513b --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Concerns/ResolvesEasyPayDisplay.php @@ -0,0 +1,227 @@ + [ + 'provider' => 'naverpay', + 'label' => ['ko' => '네이버페이', 'en' => 'NaverPay'], + ], + 'nicepay_kakaopay' => [ + 'provider' => 'kakaopay', + 'label' => ['ko' => '카카오페이', 'en' => 'KakaoPay'], + ], + 'nicepay_samsungpay' => [ + 'provider' => 'samsungpay', + 'label' => ['ko' => '삼성페이', 'en' => 'Samsung Pay'], + ], + 'nicepay_applepay' => [ + 'provider' => 'applepay', + 'label' => ['ko' => '애플페이', 'en' => 'Apple Pay'], + ], + 'nicepay_payco' => [ + 'provider' => 'payco', + 'label' => ['ko' => 'PAYCO (페이코)', 'en' => 'PAYCO'], + ], + 'nicepay_skpay' => [ + 'provider' => 'skpay', + 'label' => ['ko' => 'SK pay', 'en' => 'SK pay'], + ], + 'nicepay_ssgpay' => [ + 'provider' => 'ssgpay', + 'label' => ['ko' => 'SSG PAY', 'en' => 'SSG PAY'], + ], + 'nicepay_lpay' => [ + 'provider' => 'lpay', + 'label' => ['ko' => 'L.pay', 'en' => 'L.pay'], + ], + ]; + + /** + * @return array{method: string, provider: string, label: array{ko: string, en: string}}|array{} + */ + protected function resolveEasyPayMeta(array $payload): array + { + $method = $this->resolveEasyPayMethod($payload); + if ($method === null || ! isset(self::EASY_PAY_METHODS[$method])) { + return []; + } + + $definition = self::EASY_PAY_METHODS[$method]; + + return [ + 'method' => $method, + 'provider' => $definition['provider'], + 'label' => $definition['label'], + ]; + } + + /** + * @return array + */ + protected function decodePaymentMeta(mixed $paymentMeta): array + { + if (is_array($paymentMeta)) { + return $paymentMeta; + } + + if (! is_string($paymentMeta) || trim($paymentMeta) === '') { + return []; + } + + $decoded = json_decode($paymentMeta, true); + + return is_array($decoded) ? $decoded : []; + } + + /** + * @return array{ + * payment_method_label: string|null, + * payment_method_display_label: string|null, + * selected_payment_method: string|null, + * embedded_pg_provider: string|null, + * embedded_pg_provider_label: string|null + * } + */ + protected function resolvePaymentDisplay(object $payment): array + { + $paymentMeta = $this->decodePaymentMeta($payment->payment_meta ?? null); + $selectedPaymentMethod = $this->stringOrNull($paymentMeta['nicepay_easy_pay_method'] ?? null); + $provider = $this->stringOrNull( + $payment->embedded_pg_provider + ?? $paymentMeta['nicepay_easy_pay_provider'] + ?? $paymentMeta['embedded_pg_provider'] + ?? null + ); + + if ($provider === null && $selectedPaymentMethod !== null) { + $provider = $this->stringOrNull(self::EASY_PAY_METHODS[$selectedPaymentMethod]['provider'] ?? null); + } + + $baseLabel = $this->paymentMethodLabel($this->stringOrNull($payment->payment_method ?? null)); + $embeddedLabel = $this->embeddedPgProviderLabel($provider, $paymentMeta); + + return [ + 'payment_method_label' => $baseLabel, + 'payment_method_display_label' => $this->paymentMethodDisplayLabel($baseLabel, $embeddedLabel), + 'selected_payment_method' => $selectedPaymentMethod, + 'embedded_pg_provider' => $provider, + 'embedded_pg_provider_label' => $embeddedLabel, + ]; + } + + protected function paymentMethodLabel(?string $method): ?string + { + if ($method === null || $method === '') { + return null; + } + + return PaymentMethodEnum::tryFrom($method)?->label() ?? $method; + } + + protected function paymentMethodDisplayLabel(?string $baseLabel, ?string $embeddedLabel): ?string + { + if ($baseLabel === null || $baseLabel === '') { + return $embeddedLabel; + } + + if ($embeddedLabel === null || $embeddedLabel === '') { + return $baseLabel; + } + + return $embeddedLabel . ' (' . $baseLabel . ')'; + } + + /** + * @param array $paymentMeta + */ + protected function embeddedPgProviderLabel(?string $provider, array $paymentMeta = []): ?string + { + $metaLabel = $this->localizedLabel($paymentMeta['nicepay_easy_pay_label'] ?? null) + ?? $this->localizedLabel($paymentMeta['embedded_pg_provider_label'] ?? null); + + if ($metaLabel !== null) { + return $metaLabel; + } + + if ($provider === null || $provider === '') { + return null; + } + + foreach (self::EASY_PAY_METHODS as $definition) { + if (($definition['provider'] ?? null) === $provider) { + return $this->localizedLabel($definition['label'] ?? null); + } + } + + return null; + } + + /** + * @param array $payload + */ + private function resolveEasyPayMethod(array $payload): ?string + { + foreach (['nicepay_easy_pay_method', 'MallReserved1'] as $key) { + $method = $this->stringOrNull($payload[$key] ?? null); + if ($method !== null && isset(self::EASY_PAY_METHODS[$method])) { + return $method; + } + } + + $mallReserved = $this->stringOrNull($payload['MallReserved'] ?? null); + if ($mallReserved === null) { + return null; + } + + $decoded = json_decode($mallReserved, true); + if (is_array($decoded)) { + $method = $this->stringOrNull($decoded['nicepay_easy_pay_method'] ?? null); + if ($method !== null && isset(self::EASY_PAY_METHODS[$method])) { + return $method; + } + } + + parse_str($mallReserved, $parsed); + $method = $this->stringOrNull($parsed['nicepay_easy_pay_method'] ?? null); + if ($method !== null && isset(self::EASY_PAY_METHODS[$method])) { + return $method; + } + + return isset(self::EASY_PAY_METHODS[$mallReserved]) ? $mallReserved : null; + } + + private function localizedLabel(mixed $label): ?string + { + if (is_string($label) && trim($label) !== '') { + return $label; + } + + if (! is_array($label)) { + return null; + } + + $locale = app()->getLocale(); + + return $this->stringOrNull($label[$locale] ?? null) + ?? $this->stringOrNull($label['ko'] ?? null) + ?? $this->stringOrNull($label['en'] ?? null); + } + + private function stringOrNull(mixed $value): ?string + { + if (! is_string($value)) { + return null; + } + + $value = trim($value); + + return $value === '' ? null : $value; + } +} diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminOrderListController.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminOrderListController.php index 193d073e..841bc581 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminOrderListController.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminOrderListController.php @@ -8,9 +8,12 @@ use App\Helpers\ResponseHelper; use App\Http\Controllers\Api\Base\AdminBaseController; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; +use Plugins\Sirsoft\PayNicepayments\Concerns\ResolvesEasyPayDisplay; class AdminOrderListController extends AdminBaseController { + use ResolvesEasyPayDisplay; + public function __construct() { parent::__construct(); @@ -45,4 +48,44 @@ class AdminOrderListController extends AdminBaseController return ResponseHelper::success('messages.success', $map); } + + /** + * 나이스페이먼츠 간편결제 표시 맵 반환 + * + * 관리자 주문목록은 코어 결제 리소스의 기본 결제수단 라벨을 사용하므로, + * 플러그인 데이터소스로 실제 간편결제 표시명을 보강한다. + * + * @return JsonResponse 주문번호별 표시 맵 + */ + public function easyPayDisplayMap(): JsonResponse + { + $rows = DB::table('ecommerce_orders as o') + ->join('ecommerce_order_payments as p', 'p.order_id', '=', 'o.id') + ->whereIn('p.pg_provider', ['nicepayments', 'nicepay']) + ->where('p.created_at', '>=', now()->subMonths(6)) + ->select([ + 'o.order_number', + 'p.payment_method', + 'p.embedded_pg_provider', + 'p.payment_meta', + ]) + ->get(); + + $map = []; + foreach ($rows as $row) { + $display = $this->resolvePaymentDisplay($row); + if (($display['embedded_pg_provider_label'] ?? null) === null) { + continue; + } + + $map[$row->order_number] = [ + 'payment_method_label' => $display['payment_method_label'], + 'payment_method_display_label' => $display['payment_method_display_label'], + 'embedded_pg_provider' => $display['embedded_pg_provider'], + 'embedded_pg_provider_label' => $display['embedded_pg_provider_label'], + ]; + } + + return ResponseHelper::success('messages.success', $map); + } } diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminTransactionController.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminTransactionController.php index 95e4ac13..04631f52 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminTransactionController.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/AdminTransactionController.php @@ -8,10 +8,13 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Plugins\Sirsoft\PayNicepayments\Concerns\ResolvesEasyPayDisplay; use Plugins\Sirsoft\PayNicepayments\Services\NicePaymentsApiService; class AdminTransactionController extends AdminBaseController { + use ResolvesEasyPayDisplay; + public function __construct( private NicePaymentsApiService $apiService ) { @@ -72,10 +75,21 @@ class AdminTransactionController extends AdminBaseController $localPayment = DB::table('ecommerce_order_payments') ->where('transaction_id', $tid) - ->select(['is_escrow', 'payment_meta']) + ->select(['is_escrow', 'payment_meta', 'payment_method', 'embedded_pg_provider']) ->first(); $result['_local_is_escrow'] = (bool) ($localPayment?->is_escrow ?? false); + $display = $localPayment ? $this->resolvePaymentDisplay($localPayment) : [ + 'payment_method_label' => null, + 'payment_method_display_label' => null, + 'embedded_pg_provider' => null, + 'embedded_pg_provider_label' => null, + ]; + $result['_base_pay_method_label'] = $display['payment_method_label']; + $result['_embedded_pg_provider'] = $display['embedded_pg_provider']; + $result['_embedded_pg_provider_label'] = $display['embedded_pg_provider_label']; + $result['_pay_method_label'] = $display['payment_method_display_label']; + $result['payment_method_display_label'] = $display['payment_method_display_label']; if ($localPayment?->payment_meta) { $meta = json_decode($localPayment->payment_meta, true); diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/PaymentCallbackController.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/PaymentCallbackController.php index b91c8e39..d95854b7 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/PaymentCallbackController.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/PaymentCallbackController.php @@ -23,6 +23,7 @@ use Modules\Sirsoft\Ecommerce\Models\OrderPayment; use Modules\Sirsoft\Ecommerce\Services\OrderProcessingService; use Plugins\Sirsoft\PayNicepayments\Concerns\PreventsReplayCallback; use Plugins\Sirsoft\PayNicepayments\Concerns\RecordsPaymentWindowClosure; +use Plugins\Sirsoft\PayNicepayments\Concerns\ResolvesEasyPayDisplay; use Plugins\Sirsoft\PayNicepayments\Concerns\SanitizesPgResponse; use Plugins\Sirsoft\PayNicepayments\Http\Requests\AuthCallbackRequest; use Plugins\Sirsoft\PayNicepayments\Http\Requests\VbankNotifyRequest; @@ -40,6 +41,7 @@ class PaymentCallbackController { use PreventsReplayCallback; use RecordsPaymentWindowClosure; + use ResolvesEasyPayDisplay; use SanitizesPgResponse; private const PLUGIN_IDENTIFIER = 'sirsoft-pay_nicepayments'; @@ -72,6 +74,7 @@ class PaymentCallbackController 'RcptTID', 'EscrowYN', 'MallReserved', + 'MallReserved1', 'Currency', ]; @@ -92,6 +95,7 @@ class PaymentCallbackController 'RcptTID', 'EscrowYN', 'MallReserved', + 'MallReserved1', ]; private const VBANK_NOTIFY_RESPONSE_KEYS = [ @@ -306,7 +310,7 @@ class PaymentCallbackController ]); } else { $effectiveTid = (string) ($pgResponse['TID'] ?? $txTid); - $completionStatus = $this->completeAuthorizedPaymentWithLock($moid, $pgResponse, $txTid, $amt, $isEscrow, $request); + $completionStatus = $this->completeAuthorizedPaymentWithLock($moid, $pgResponse, $txTid, $amt, $isEscrow, $request, $validated); if ($completionStatus === 'already_paid') { $this->logReplayDetected($effectiveTid, $moid, 'authCallback (card)'); @@ -468,8 +472,9 @@ class PaymentCallbackController int $amt, bool $isEscrow, Request $request, + array $callbackPayload, ): string { - return DB::transaction(function () use ($moid, $pgResponse, $txTid, $amt, $isEscrow, $request): string { + return DB::transaction(function () use ($moid, $pgResponse, $txTid, $amt, $isEscrow, $request, $callbackPayload): string { $lockedOrder = Order::query() ->where('order_number', $moid) ->lockForUpdate() @@ -499,6 +504,7 @@ class PaymentCallbackController } $lockedOrder->setRelation('payment', $payment); + $easyPayMeta = $this->resolveEasyPayMeta(array_merge($pgResponse, $callbackPayload)); $this->orderService->completePayment($lockedOrder, [ 'transaction_id' => $effectiveTid, @@ -507,10 +513,17 @@ class PaymentCallbackController 'card_name' => $pgResponse['IssuCardName'] ?? $pgResponse['CardName'] ?? null, 'card_installment_months' => (int) ($pgResponse['CardQuota'] ?? 0), 'is_interest_free' => false, - 'embedded_pg_provider' => null, + 'embedded_pg_provider' => $easyPayMeta['provider'] ?? null, 'receipt_url' => 'https://npg.nicepay.co.kr/issue/IssueLoader.do?type=0&TID='.rawurlencode($effectiveTid), 'payment_meta' => [ 'result_code' => $pgResponse['ResultCode'] ?? null, + ...($easyPayMeta === [] ? [] : [ + 'nicepay_easy_pay_method' => $easyPayMeta['method'], + 'nicepay_easy_pay_provider' => $easyPayMeta['provider'], + 'nicepay_easy_pay_label' => $easyPayMeta['label'], + 'embedded_pg_provider' => $easyPayMeta['provider'], + 'embedded_pg_provider_label' => $easyPayMeta['label'], + ]), 'pay_method' => $pgResponse['PayMethod'] ?? null, 'auth_date' => $pgResponse['AuthDate'] ?? null, 'mid' => $this->apiService->getMid(), diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/UserReceiptController.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/UserReceiptController.php index a9bced46..fa4b97d5 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/UserReceiptController.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Controllers/UserReceiptController.php @@ -7,9 +7,12 @@ namespace Plugins\Sirsoft\PayNicepayments\Controllers; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Plugins\Sirsoft\PayNicepayments\Concerns\ResolvesEasyPayDisplay; class UserReceiptController { + use ResolvesEasyPayDisplay; + private const RECEIPT_BASE_URL = 'https://npg.nicepay.co.kr/issue/IssueLoader.do'; /** @@ -31,7 +34,13 @@ class UserReceiptController ->join('ecommerce_orders as o', 'o.id', '=', 'p.order_id') ->where('o.order_number', $orderNumber) ->where('o.user_id', $user->id) - ->select(['p.transaction_id', 'p.receipt_url', 'p.payment_meta']) + ->select([ + 'p.transaction_id', + 'p.receipt_url', + 'p.payment_meta', + 'p.payment_method', + 'p.embedded_pg_provider', + ]) ->first(); if (! $payment) { @@ -45,6 +54,7 @@ class UserReceiptController $cashReceiptUrl = null; $isTestMode = false; + $display = $this->resolvePaymentDisplay($payment); if ($payment->payment_meta) { $meta = json_decode($payment->payment_meta, true); $rcptTid = $meta['rcpt_tid'] ?? ($meta['pg_raw_response']['RcptTID'] ?? null); @@ -58,6 +68,11 @@ class UserReceiptController 'receipt_url' => $receiptUrl, 'cash_receipt_url' => $cashReceiptUrl, 'is_test_mode' => $isTestMode, + 'payment_method_label' => $display['payment_method_label'], + 'payment_method_display_label' => $display['payment_method_display_label'], + 'selected_payment_method' => $display['selected_payment_method'], + 'embedded_pg_provider' => $display['embedded_pg_provider'], + 'embedded_pg_provider_label' => $display['embedded_pg_provider_label'], ]); } } diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Http/Requests/AuthCallbackRequest.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Http/Requests/AuthCallbackRequest.php index 1ed08235..ccb5a958 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Http/Requests/AuthCallbackRequest.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Http/Requests/AuthCallbackRequest.php @@ -53,6 +53,8 @@ class AuthCallbackRequest extends FormRequest 'AuthResultMsg' => ['nullable', 'string'], 'Moid' => ['nullable', 'string'], 'Amt' => ['nullable', 'integer', 'min:1'], + 'MallReserved' => ['nullable', 'string', 'max:1000'], + 'MallReserved1' => ['nullable', 'string', 'max:20'], ]; } @@ -68,6 +70,8 @@ class AuthCallbackRequest extends FormRequest 'Amt' => ['required', 'integer', 'min:1'], 'NetCancelURL' => ['required', 'string', 'url'], 'Signature' => ['required', 'string'], + 'MallReserved' => ['nullable', 'string', 'max:1000'], + 'MallReserved1' => ['nullable', 'string', 'max:20'], ]; } diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/Listeners/EnsureAdminOrderListTestBadgeLayoutListener.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/Listeners/EnsureAdminOrderListTestBadgeLayoutListener.php index 8d4cbeaa..ff890baf 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/Listeners/EnsureAdminOrderListTestBadgeLayoutListener.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/Listeners/EnsureAdminOrderListTestBadgeLayoutListener.php @@ -14,8 +14,12 @@ class EnsureAdminOrderListTestBadgeLayoutListener implements HookListenerInterfa private const DATA_SOURCE_ID = 'nicepay_test_map'; + private const EASY_PAY_DISPLAY_DATA_SOURCE_ID = 'nicepay_easy_pay_display_map'; + private const BADGE_CONDITION = 'nicepay_test_map.data?.[row.order_number] === true'; + private const EASY_PAY_DISPLAY_EXPRESSION = 'nicepay_easy_pay_display_map.data?.[row.order_number]?.payment_method_display_label ?? row.payment?.payment_method_label'; + /** * @return array> */ @@ -65,24 +69,42 @@ class EnsureAdminOrderListTestBadgeLayoutListener implements HookListenerInterfa ? $layout['data_sources'] : []; + $existingIds = []; foreach ($dataSources as $dataSource) { if (is_array($dataSource) && ($dataSource['id'] ?? null) === self::DATA_SOURCE_ID) { - $layout['data_sources'] = $dataSources; + $existingIds[self::DATA_SOURCE_ID] = true; + } - return $layout; + if (is_array($dataSource) && ($dataSource['id'] ?? null) === self::EASY_PAY_DISPLAY_DATA_SOURCE_ID) { + $existingIds[self::EASY_PAY_DISPLAY_DATA_SOURCE_ID] = true; } } - $dataSources[] = [ - 'id' => self::DATA_SOURCE_ID, - 'label_key' => '$t:sirsoft-pay_nicepayments.editor.data_source.nicepay_test_map', - 'type' => 'api', - 'endpoint' => '/api/plugins/sirsoft-pay_nicepayments/admin/orders/test-mode-map', - 'method' => 'GET', - 'auto_fetch' => true, - 'auth_required' => true, - 'loading_strategy' => 'progressive', - ]; + if (! isset($existingIds[self::DATA_SOURCE_ID])) { + $dataSources[] = [ + 'id' => self::DATA_SOURCE_ID, + 'label_key' => '$t:sirsoft-pay_nicepayments.editor.data_source.nicepay_test_map', + 'type' => 'api', + 'endpoint' => '/api/plugins/sirsoft-pay_nicepayments/admin/orders/test-mode-map', + 'method' => 'GET', + 'auto_fetch' => true, + 'auth_required' => true, + 'loading_strategy' => 'progressive', + ]; + } + + if (! isset($existingIds[self::EASY_PAY_DISPLAY_DATA_SOURCE_ID])) { + $dataSources[] = [ + 'id' => self::EASY_PAY_DISPLAY_DATA_SOURCE_ID, + 'label_key' => '$t:sirsoft-pay_nicepayments.editor.data_source.nicepay_easy_pay_display_map', + 'type' => 'api', + 'endpoint' => '/api/plugins/sirsoft-pay_nicepayments/admin/orders/easy-pay-display-map', + 'method' => 'GET', + 'auto_fetch' => true, + 'auth_required' => true, + 'loading_strategy' => 'progressive', + ]; + } $layout['data_sources'] = $dataSources; @@ -131,12 +153,34 @@ class EnsureAdminOrderListTestBadgeLayoutListener implements HookListenerInterfa } $this->appendBadgeToColumn($column); + $this->ensurePaymentMethodDisplayExpression($column); return; } unset($column); } + /** + * @param array $node + */ + private function ensurePaymentMethodDisplayExpression(array &$node): bool + { + if (($node['text'] ?? null) === '{{row.payment?.payment_method_label}}') { + $node['text'] = '{{'.self::EASY_PAY_DISPLAY_EXPRESSION.'}}'; + + return true; + } + + foreach ($node as &$value) { + if (is_array($value) && $this->ensurePaymentMethodDisplayExpression($value)) { + return true; + } + } + unset($value); + + return false; + } + /** * @param array $column */ diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/src/routes/api.php b/plugins/_bundled/sirsoft-pay_nicepayments/src/routes/api.php index 508e1253..c5267487 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/src/routes/api.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/src/routes/api.php @@ -55,6 +55,11 @@ Route::prefix('admin')->name('admin.')->middleware(['auth:sanctum', 'admin'])->g ->middleware('permission:admin,sirsoft-ecommerce.orders.read') ->name('orders.test-mode-map'); + // 주문 목록 간편결제 표시 맵 조회 + Route::get('/orders/easy-pay-display-map', [AdminOrderListController::class, 'easyPayDisplayMap']) + ->middleware('permission:admin,sirsoft-ecommerce.orders.read') + ->name('orders.easy-pay-display-map'); + // 가상계좌 입금 완료 건 환불 (환불 계좌 정보 필요) Route::post('/vbank-refund', [AdminVbankRefundController::class, 'refund']) ->middleware('permission:admin,sirsoft-ecommerce.orders.update') diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/EasyPayDisplayControllerTest.php b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/EasyPayDisplayControllerTest.php new file mode 100644 index 00000000..a746100c --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/EasyPayDisplayControllerTest.php @@ -0,0 +1,147 @@ +setLocale('ko'); + + $admin = $this->createAdminUser(['sirsoft-ecommerce.orders.read']); + $order = $this->createPaidEasyPayOrder('nicepay_payco', 'payco', [ + 'ko' => 'PAYCO (페이코)', + 'en' => 'PAYCO', + ]); + + $response = $this->actingAs($admin) + ->getJson('/api/plugins/sirsoft-pay_nicepayments/admin/orders/easy-pay-display-map'); + + $response->assertOk(); + + $display = $response->json("data.{$order->order_number}"); + $this->assertSame('payco', $display['embedded_pg_provider'] ?? null); + $this->assertContains($display['embedded_pg_provider_label'] ?? null, ['PAYCO (페이코)', 'PAYCO']); + $this->assertSame( + ($display['embedded_pg_provider_label'] ?? '') . ' (' . ($display['payment_method_label'] ?? '') . ')', + $display['payment_method_display_label'] ?? null, + ); + } + + public function test_admin_transaction_status_includes_easy_pay_display_label(): void + { + app()->setLocale('ko'); + + $admin = $this->createAdminUser(['sirsoft-ecommerce.orders.read']); + $order = $this->createPaidEasyPayOrder('nicepay_kakaopay', 'kakaopay', [ + 'ko' => '카카오페이', + 'en' => 'KakaoPay', + ]); + + $apiServiceMock = $this->createMock(NicePaymentsApiService::class); + $apiServiceMock->expects($this->once()) + ->method('queryTransaction') + ->with('NICE_TID_' . $order->id) + ->willReturn([ + 'ResultCode' => '3001', + 'ResultMsg' => '정상처리', + 'TID' => 'NICE_TID_' . $order->id, + 'PayMethod' => 'CARD', + ]); + $this->app->instance(NicePaymentsApiService::class, $apiServiceMock); + + $response = $this->actingAs($admin) + ->getJson("/api/plugins/sirsoft-pay_nicepayments/admin/orders/{$order->order_number}/transaction-status"); + + $response->assertOk() + ->assertJsonPath('data._embedded_pg_provider', 'kakaopay'); + + $data = $response->json('data'); + $this->assertContains($data['_embedded_pg_provider_label'] ?? null, ['카카오페이', 'KakaoPay']); + $this->assertSame( + ($data['_embedded_pg_provider_label'] ?? '') . ' (' . ($data['_base_pay_method_label'] ?? '') . ')', + $data['_pay_method_label'] ?? null, + ); + } + + public function test_user_receipt_response_includes_easy_pay_display_label(): void + { + app()->setLocale('ko'); + + $user = User::factory()->create(); + $order = $this->createPaidEasyPayOrder('nicepay_naverpay', 'naverpay', [ + 'ko' => '네이버페이', + 'en' => 'NaverPay', + ], $user); + + $response = $this->actingAs($user) + ->getJson("/api/plugins/sirsoft-pay_nicepayments/user/orders/{$order->order_number}/receipt"); + + $response->assertOk() + ->assertJsonPath('selected_payment_method', 'nicepay_naverpay') + ->assertJsonPath('embedded_pg_provider', 'naverpay'); + + $data = $response->json(); + $this->assertContains($data['embedded_pg_provider_label'] ?? null, ['네이버페이', 'NaverPay']); + $this->assertSame( + ($data['embedded_pg_provider_label'] ?? '') . ' (' . ($data['payment_method_label'] ?? '') . ')', + $data['payment_method_display_label'] ?? null, + ); + } + + /** + * @param array{ko: string, en: string} $label + */ + private function createPaidEasyPayOrder( + string $method, + string $provider, + array $label, + ?User $user = null, + ): \Modules\Sirsoft\Ecommerce\Models\Order { + $order = OrderFactory::new()->create([ + 'user_id' => ($user ?? User::factory()->create())->id, + 'order_number' => 'ORD-NICE-EASY-' . random_int(10000, 99999), + 'order_status' => OrderStatusEnum::PAYMENT_COMPLETE, + 'subtotal_amount' => 50000, + 'total_amount' => 50000, + 'total_due_amount' => 0, + 'total_paid_amount' => 50000, + 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), + 'paid_at' => now(), + ]); + + OrderPaymentFactory::new()->create([ + 'order_id' => $order->id, + 'payment_status' => PaymentStatusEnum::PAID, + 'payment_method' => PaymentMethodEnum::CARD, + 'pg_provider' => 'nicepayments', + 'transaction_id' => 'NICE_TID_' . $order->id, + 'embedded_pg_provider' => $provider, + 'paid_amount_local' => 50000, + 'paid_at' => now(), + 'payment_meta' => [ + 'nicepay_easy_pay_method' => $method, + 'nicepay_easy_pay_provider' => $provider, + 'nicepay_easy_pay_label' => $label, + 'pg_raw_response' => [ + 'ResultCode' => '3001', + 'PayMethod' => 'CARD', + ], + ], + ]); + + return $order; + } +} diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCallbackControllerTest.php b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCallbackControllerTest.php index 03b1d034..8f3ddc1c 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCallbackControllerTest.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCallbackControllerTest.php @@ -63,6 +63,8 @@ class PaymentCallbackControllerTest extends PluginTestCase 'total_points_used_amount' => 0, 'total_deposit_used_amount' => 0, 'total_paid_amount' => 0, + 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), ]); OrderPaymentFactory::new()->create([ @@ -387,6 +389,42 @@ class PaymentCallbackControllerTest extends PluginTestCase $this->assertEquals('APP12345', $payment->card_approval_number); } + public function test_auth_callback_stores_easy_pay_display_metadata(): void + { + app()->setLocale('ko'); + + $order = $this->createTestOrder(50000); + $this->mockPluginSettings(); + + $tid = 'TID_EASY_PAY_' . uniqid(); + $params = $this->makeCallbackParams($order->order_number, 50000, [ + 'MallReserved' => 'nicepay_easy_pay_method=nicepay_kakaopay', + 'MallReserved1' => 'nicepay_kakaopay', + ]); + + Http::fake([ + 'pay.nicepay.co.kr/v1/authorize' => Http::response( + $this->makeAuthorizeResponse($tid, $order->order_number, 50000), + 200 + ), + ]); + + $response = $this->post('/plugins/sirsoft-pay_nicepayments/payment/callback', $params); + + $response->assertRedirect("/shop/orders/{$order->order_number}/complete"); + + $payment = $order->payment; + $payment->refresh(); + $meta = $payment->payment_meta; + + $this->assertSame('kakaopay', $payment->embedded_pg_provider); + $this->assertSame('nicepay_kakaopay', $meta['nicepay_easy_pay_method'] ?? null); + $this->assertSame('kakaopay', $meta['nicepay_easy_pay_provider'] ?? null); + $this->assertSame('카카오페이', $meta['nicepay_easy_pay_label']['ko'] ?? null); + $this->assertSame('KakaoPay', $meta['nicepay_easy_pay_label']['en'] ?? null); + $this->assertSame('kakaopay', $meta['embedded_pg_provider'] ?? null); + } + public function test_auth_callback_stores_only_whitelisted_pg_response_fields(): void { $order = $this->createTestOrder(50000); diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCloseReportControllerTest.php b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCloseReportControllerTest.php index a0af6d2e..a109302e 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCloseReportControllerTest.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Feature/Controllers/PaymentCloseReportControllerTest.php @@ -21,6 +21,7 @@ class PaymentCloseReportControllerTest extends PluginTestCase 'order_number' => 'ORD-NICE-CLOSE-001', 'order_status' => OrderStatusEnum::PENDING_ORDER, 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), 'subtotal_amount' => 10000, 'total_amount' => 10000, 'total_due_amount' => 10000, @@ -55,6 +56,7 @@ class PaymentCloseReportControllerTest extends PluginTestCase 'order_number' => 'ORD-NICE-CLOSE-REAL', 'order_status' => OrderStatusEnum::PENDING_ORDER, 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), 'subtotal_amount' => 10000, 'total_amount' => 10000, 'total_due_amount' => 10000, @@ -172,6 +174,7 @@ class PaymentCloseReportControllerTest extends PluginTestCase 'order_number' => 'ORD-NICE-CLOSE-RACE', 'order_status' => OrderStatusEnum::PENDING_ORDER, 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), 'subtotal_amount' => 10000, 'total_amount' => 10000, 'total_due_amount' => 10000, @@ -232,6 +235,7 @@ class PaymentCloseReportControllerTest extends PluginTestCase 'order_number' => 'ORD-NICE-CLOSE-PAID', 'order_status' => OrderStatusEnum::PENDING_ORDER, 'currency' => 'KRW', + 'currency_snapshot' => self::krwCurrencySnapshot(), 'subtotal_amount' => 10000, 'total_amount' => 10000, 'total_due_amount' => 10000, @@ -270,6 +274,7 @@ class PaymentCloseReportControllerTest extends PluginTestCase $order->order_number = $orderNumber; $order->order_status = OrderStatusEnum::PENDING_ORDER; $order->currency = 'KRW'; + $order->currency_snapshot = self::krwCurrencySnapshot(); $order->total_due_amount = $amount; return $order; diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/tests/PluginTestCase.php b/plugins/_bundled/sirsoft-pay_nicepayments/tests/PluginTestCase.php index f43d0e53..9b6c8b62 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/tests/PluginTestCase.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/tests/PluginTestCase.php @@ -51,6 +51,24 @@ abstract class PluginTestCase extends TestCase \Illuminate\Support\Facades\Config::set('app.url', env('APP_URL', 'http://localhost')); } + protected static function krwCurrencySnapshot(): array + { + return [ + 'base_currency' => 'KRW', + 'order_currency' => 'KRW', + 'base_unit' => 1, + 'exchange_rates' => [ + 'KRW' => [ + 'rate' => 1, + 'rounding_unit' => '1', + 'rounding_method' => 'round', + 'decimal_places' => 0, + 'base_unit' => 1, + ], + ], + ]; + } + protected function registerModuleAutoload(): void { $moduleBasePath = base_path('modules/sirsoft-ecommerce/src/'); diff --git a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Unit/Listeners/EnsureAdminOrderLayoutExtensionsListenerTest.php b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Unit/Listeners/EnsureAdminOrderLayoutExtensionsListenerTest.php index 6bdd05d3..e5038aa9 100644 --- a/plugins/_bundled/sirsoft-pay_nicepayments/tests/Unit/Listeners/EnsureAdminOrderLayoutExtensionsListenerTest.php +++ b/plugins/_bundled/sirsoft-pay_nicepayments/tests/Unit/Listeners/EnsureAdminOrderLayoutExtensionsListenerTest.php @@ -18,9 +18,12 @@ class EnsureAdminOrderLayoutExtensionsListenerTest extends TestCase $json = json_encode($result, JSON_UNESCAPED_SLASHES); $this->assertSame(1, $this->countDataSources($result, 'nicepay_test_map')); + $this->assertSame(1, $this->countDataSources($result, 'nicepay_easy_pay_display_map')); $this->assertSame(1, $this->countString($result, 'nicepay_test_map.data?.[row.order_number] === true')); + $this->assertSame(1, $this->countString($result, 'nicepay_easy_pay_display_map.data?.[row.order_number]?.payment_method_display_label ?? row.payment?.payment_method_label')); $this->assertIsString($json); $this->assertStringContainsString('/api/plugins/sirsoft-pay_nicepayments/admin/orders/test-mode-map', $json); + $this->assertStringContainsString('/api/plugins/sirsoft-pay_nicepayments/admin/orders/easy-pay-display-map', $json); $this->assertStringContainsString('sirsoft-pay_nicepayments.admin.test_mode_badge', $json); } @@ -32,7 +35,9 @@ class EnsureAdminOrderLayoutExtensionsListenerTest extends TestCase $twice = $listener->ensureTestBadgeLayout($once, 1); $this->assertSame(1, $this->countDataSources($twice, 'nicepay_test_map')); + $this->assertSame(1, $this->countDataSources($twice, 'nicepay_easy_pay_display_map')); $this->assertSame(1, $this->countString($twice, 'nicepay_test_map.data?.[row.order_number] === true')); + $this->assertSame(1, $this->countString($twice, 'nicepay_easy_pay_display_map.data?.[row.order_number]?.payment_method_display_label ?? row.payment?.payment_method_label')); } public function test_detail_listener_ensures_test_notice_and_payment_panels(): void