function playCountry(a){ const c=String((a&&a.play_country)||'').trim().toUpperCase(); if(/^[A-Z]{2}$/.test(c)) return c; return '-'; } const COUNTRY_ZH={AR:'阿根廷',US:'美国',TR:'土耳其',BR:'巴西',MX:'墨西哥',DE:'德国',GB:'英国',JP:'日本',KR:'韩国',IN:'印度',ID:'印尼',TH:'泰国',VN:'越南',PH:'菲律宾',MY:'马来西亚',SG:'新加坡',AU:'澳大利亚',CA:'加拿大',FR:'法国',IT:'意大利',ES:'西班牙',NL:'荷兰',PL:'波兰',UA:'乌克兰',RU:'俄罗斯',CL:'智利',CO:'哥伦比亚',PE:'秘鲁',UY:'乌拉圭',TW:'台湾',HK:'香港',CN:'中国',AE:'阿联酋',SA:'沙特',EG:'埃及',ZA:'南非',NG:'尼日利亚',IL:'以色列',SE:'瑞典',NO:'挪威',DK:'丹麦',FI:'芬兰',CH:'瑞士',AT:'奥地利',BE:'比利时',PT:'葡萄牙',IE:'爱尔兰',CZ:'捷克',RO:'罗马尼亚',HU:'匈牙利',NZ:'新西兰'}; function countryLabel(cc){ cc=String(cc||'').trim().toUpperCase(); if(!cc || cc==='XX' || cc==='-') return '未知地区'; return cc+(COUNTRY_ZH[cc]?(' '+COUNTRY_ZH[cc]):''); } function isoPeriod(p){ p=String(p||'').toUpperCase().trim(); const m=p.match(/^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/); if(!m) return p; const bits=[]; const one=function(n, u){ n=Number(n); if(!n) return ''; return n+u; }; if(m[1]) bits.push(one(m[1],'年')); if(m[2]) bits.push(one(m[2],'个月')); if(m[3]) bits.push(one(m[3],'周')); if(m[4]) bits.push(one(m[4],'天')); return bits.join('')||p; } function offerDuration(o){ if(!o) return ''; const bill=isoPeriod(o.billing_period||''); const intro=isoPeriod(o.intro_period||''); const n=Number(o.intro_cycles||0); const introTxt=intro?(intro+(n>1?('×'+n):'')):''; if(o.offer_kind==='one_time') return '一次性'; if(introTxt && bill){ const prefix=(o.offer_kind==='trial'||o.intro_price==='Free')?'试用':'优惠'; if(introTxt===bill) return prefix+bill+',之后每'+bill; return prefix+introTxt+',之后每'+bill; } if(introTxt) return introTxt; if(bill) return '每'+bill; return ''; } function offerPrice(o){ const intro=o&&o.intro_price?(o.intro_price+' → '):''; const price=String((o&&o.price)||'-'); const cur=String((o&&o.currency)||''); if(!cur || price.indexOf(cur)>=0 || /[¥$€£₺]|TRY|USD|EUR|GBP|JPY|SGD|HKD/.test(price)) return intro+price; return intro+price+' '+cur; } function pill(s){ const k=String(s||''); return ''+esc(statusName(k))+''; } function cardNeedsVerify(c){ const id=String((c&&c.instrument_id)||''); const st=String((c&&c.status)||''); if(st==='pending' || st==='needs_verification') return true; return (id==='play-cart-pending' || id.startsWith('play-') || id.startsWith('browser-') || id.startsWith('wallet-')) && st!=='instrument_ready' && st!=='ready'; } function cardVerify(c){ const st=String((c&&c.status)||''); if(cardNeedsVerify(c)) return pill('needs_verification'); if(st==='instrument_ready' || st==='ready') return '已验证'; if(!c || !c.instrument_id) return '已验证'; return pill(st||'-'); } function fmtTime(v){ if(!v) return '-'; const d=new Date(v); if(isNaN(d)) return String(v); const p=n=>String(n).padStart(2,'0'); return p(d.getMonth()+1)+'-'+p(d.getDate())+' '+p(d.getHours())+':'+p(d.getMinutes())+':'+p(d.getSeconds()); } function fmtDate(v, withYear){ if(!v) return '-'; const d=new Date(v); if(isNaN(d)) return String(v); const p=n=>String(n).padStart(2,'0'); const day=(withYear?d.getFullYear()+'-':'')+p(d.getMonth()+1)+'-'+p(d.getDate()); return day+' '+p(d.getHours())+':'+p(d.getMinutes())+':'+p(d.getSeconds()); } function fmtUnix(v, withYear){ if(v===null||v===undefined||v==='') return '-'; let n=Number(v); if(!n) return '-'; if(n>1e11 && n<1e12) n=Math.round(n); if(n<1e11) n*=1000; return withYear?fmtDate(n, true):fmtTime(n); } function purchaseTime(p){ if(p&&p.purchase_time) return fmtUnix(p.purchase_time, true); const raw=p&&p.raw_json; if(raw){ try{ const o=JSON.parse(raw); const n=Number(o.purchaseTime||o.purchaseTimeMillis||o.purchase_time||0); if(n) return fmtUnix(n, true); }catch(e){} } return fmtDate(p&&p.created_at, true); } function purchaseTimeUnix(p){ if(p&&p.purchase_time){ let n=Number(p.purchase_time); if(n>1e11) n=Math.floor(n/1000); if(n) return n; } const raw=p&&p.raw_json; if(raw){ try{ const o=JSON.parse(raw); let n=Number(o.purchaseTime||o.purchaseTimeMillis||o.purchase_time||0); if(n>1e11) n=Math.floor(n/1000); if(n) return n; }catch(e){} } const c=p&&p.created_at; if(c){ const d=new Date(c); if(!isNaN(d)) return Math.floor(d.getTime()/1000); } return 0; } function ackCell(s){ const v=subscriptionAcknowledged(s); if(v==='未确认' && s){ return esc(v)+' '; } return esc(v||'-'); } async function ackSub(ev, btn){ if(ev && ev.stopPropagation) ev.stopPropagation(); const aid=Number(btn.getAttribute('data-ack-account')); const tok=decodeURIComponent(btn.getAttribute('data-ack-token')||''); const pkg=decodeURIComponent(btn.getAttribute('data-ack-package')||''); const sku=decodeURIComponent(btn.getAttribute('data-ack-sku')||''); const order=decodeURIComponent(btn.getAttribute('data-ack-order')||''); if(btn){ btn.disabled=true; btn.innerHTML='处理中'; } const j=await api('/api/subscriptions/acknowledge',{method:'POST',body:JSON.stringify({account_id:aid,purchase_token:tok,package:pkg,sku:sku,order_id:order})}); if(j.error){ if(btn){ btn.disabled=false; btn.textContent='确认'; } return fail('无法确认', j.error); } notice('正在确认订阅', '任务 #'+(j.job_id||'')); if(btn) btn.outerHTML='确认中'; refreshList(); } function rawAcknowledged(raw){ if(!raw) return ''; try{ const o=JSON.parse(raw); if(o.acknowledged===true || o.acknowledged===1 || o.acknowledged==='true') return '已确认'; if(o.acknowledged===false || o.acknowledged===0 || o.acknowledged==='false') return '未确认'; }catch(e){} return ''; } function subscriptionAcknowledged(s){ if(!s) return ''; if(s.acknowledged===true) return '已确认'; if(s.acknowledged===false) return '未确认'; const own=rawAcknowledged(s.raw_json); if(own) return own; const hits=(cache.purchases||[]).filter(p=>p && p.account_id===s.account_id && p.package===s.package && p.purchase_token && p.purchase_token===s.purchase_token); for(const p of hits){ const v=purchaseAcknowledged(p); if(v) return v; } return ''; } function purchaseAcknowledged(p){ return rawAcknowledged(p&&p.raw_json); } function purchaseStatus(p){ const s=String((p&&p.status)||'').toLowerCase(); if(s==='refunded') return 'refunded'; if(s==='cancelled'||s==='canceled') return 'cancelled'; if(s==='purchased') return 'purchased'; const raw=p&&p.raw_json; if(raw){ try{ const o=JSON.parse(raw); if(Number(o.purchaseState||o.purchase_state)===1) return 'cancelled'; const st=String(o.status||'').toLowerCase(); if(st==='canceled'||st==='cancelled') return 'cancelled'; }catch(e){} } return 'purchased'; } function sortPurchasesDesc(list){ return (list||[]).slice().sort((a,b)=> (purchaseTimeUnix(b)-purchaseTimeUnix(a)) || ((Number(b&&b.id)||0)-(Number(a&&a.id)||0))); } const appIconCache={}; function appIconUrl(pkg){ pkg=String(pkg||''); if(!pkg) return ''; if(appIconCache[pkg]) return appIconCache[pkg]; if(appIconCache[pkg]==='') return ''; appIconCache[pkg]=''; api('/api/catalog/apps/'+encodeURIComponent(pkg)).then(j=>{ const url=(j&&!j.error&&j.icon)||''; appIconCache[pkg]=url; if(!url) return; document.querySelectorAll('img.app-ico[data-pkg="'+pkg.replace(/"/g,'')+'"]').forEach(img=>{ img.src=url; img.hidden=false; }); document.querySelectorAll('.app-ico.ph[data-pkg="'+pkg.replace(/"/g,'')+'"]').forEach(el=>el.remove()); }).catch(()=>{}); return ''; } function appIcoCell(pkg){ pkg=String(pkg||''); const u=appIconCache[pkg]||''; const letter=(pkg.split('.').pop()||'?').slice(0,1).toUpperCase(); appIconUrl(pkg); if(u) return '