/* portal/commerce/public/order.js — 주문 흐름(§6.2): 상품·옵션 → 적용 대상 → 가격·청구 주기 → 약관·제한 → 결제 방식 → 제출. 멱등 키로 연타·새로 고침 중복을 막는다. */ (() => { const el = document.getElementById('order-new'); const esc = aw.esc; const money = (n) => Number(n).toLocaleString('ko-KR') + '원'; const idem = sessionStorage.getItem('order-idem') || (crypto.randomUUID ? crypto.randomUUID() : String(Date.now())); sessionStorage.setItem('order-idem', idem); let data; let pv; const sel = { includeBase: true, items: [] }; async function load() { try { data = await aw.api('GET', '/api/commerce/products'); pv = data.products.find((p) => p.id === el.dataset.product) || data.products[0]; if (!pv) { aw.state(el, 'empty', '판매 중인 상품이 없습니다'); return; } if (el.dataset.option) { sel.includeBase = false; sel.items = [{ optionId: el.dataset.option, qty: 1, targetId: el.dataset.target || null }]; } render(); } catch (e) { aw.state(el, 'failed', e.message, { retry: load }); } } async function render() { let quote; try { quote = await aw.api('POST', '/api/commerce/quote', { productVersionId: pv.id, includeBase: sel.includeBase, items: sel.items }); } catch (e) { aw.state(el, 'failed', e.message, { retry: render }); return; } const q = quote.quote; const pi = quote.paymentInstructions; el.innerHTML = `
| 옵션 | 가격 | 수량 | 적용 대상 |
|---|---|---|---|
| ${esc(o.name)} | ${money(o.price)}${o.per === 'site' ? ' / 사이트' : ''} | ${o.per === 'site' ? `` : '사용자 그룹'} |
| ${esc(i.name)} × ${i.qty} | ${money(i.amount)} + 세금 ${money(i.tax)} |
| 합계(세금 포함) | ${money(q.total)} ${q.currency} |
|---|
청구 주기: ${pv.billingCycle === 'monthly' ? '매월' : '매년'} · 갱신 전 청구 예정일과 금액을 알려 드립니다
! 미확정 정책 ${pv.deferred.map((d) => esc(d.key)).join(', ')} — 확정 전에는 약속으로 보지 마세요
` : ''}${esc(pi.bank)} ${esc(pi.account)} (예금주 ${esc(pi.holder)}) · 입금 기한 ${pi.deadline_days}일 · ${esc(pi.note || '')}
운영자가 입금을 확인하고 승인한 뒤에만 사이트 수·용량·메일함·백업 권리를 쓸 수 있습니다.