'use strict' const fs=require('fs'),vm=require('vm'),assert=require('assert') const source=fs.readFileSync('ufutx_love_mp/src/utils/virtual-payment.js','utf8').replace(/export /g,'') const context={setTimeout:fn=>fn(),Promise,Error};vm.createContext(context);vm.runInContext(source+';this.buy=buyMembership;',context) let payments=0,posts=[],state='FULFILLED',enabled=true,mismatch=false,cancel=false const wx={getSystemInfoSync:()=>({platform:'android',system:'Android 14',version:'8.0.70'}),canIUse:()=>true,login:o=>o.success({code:'synthetic-code'}),requestVirtualPayment:o=>{payments++;cancel?o.fail({errMsg:'requestVirtualPayment:fail cancel'}):o.success({})}} const api={get:async()=>({enabled,products:[{sub_rank_id:1,product_id:'synthetic',price_fen:100}]}),post:async(path,data)=>{posts.push(path);return path==='/rank/buy/virtual'?{order_no:'local-order',sign_data:JSON.stringify({goodsPrice:mismatch?101:100,buyQuantity:1,productId:'synthetic',outTradeNo:'local-order'}),mode:'short_series_goods',paySig:'synthetic',signature:'synthetic'}:{state}}} ;(async()=>{ enabled=false;await assert.rejects(context.buy(wx,api,1,100,()=>{}),/暂不可用/);assert.equal(posts.length,0);assert.equal(payments,0) enabled=true;let order;await context.buy(wx,api,1,100,n=>order=n);assert.equal(order,'local-order');assert.equal(payments,1) wx.getSystemInfoSync=()=>({platform:'ios',system:'iOS 18',version:'8.0.70'});await context.buy(wx,api,1,100,()=>{});assert.equal(payments,2) wx.getSystemInfoSync=()=>({platform:'ios',system:'iOS 14',version:'8.0.70'});await assert.rejects(context.buy(wx,api,1,100,()=>{}),/升级/);assert.equal(payments,2) wx.getSystemInfoSync=()=>({platform:'windows',system:'Windows 11',version:'4.0'});await context.buy(wx,api,1,100,()=>{});assert.equal(payments,3) wx.getSystemInfoSync=()=>({platform:'ohos',system:'HarmonyOS',version:'8.0.70'});await context.buy(wx,api,1,100,()=>{});assert.equal(payments,4) mismatch=true;await assert.rejects(context.buy(wx,api,1,100,()=>{}),/不一致/);assert.equal(payments,4);mismatch=false await assert.rejects(context.buy(wx,api,1,200,()=>{}),/价格已更新/) cancel=true;const cancelStart=posts.length;let cancelledOrder;await assert.rejects(context.buy(wx,api,1,100,n=>cancelledOrder=n),/取消/);cancel=false assert.equal(cancelledOrder,'local-order','cancel must preserve order for later reconciliation') assert.deepEqual(posts.slice(cancelStart),['/rank/buy/virtual'],'cancel must not invoke fulfilment or confirmation') state='PENDING';const pendingStart=posts.length;await assert.rejects(context.buy(wx,api,1,100,()=>{}),/确认中/) assert.equal(posts.slice(pendingStart).filter(p=>p.endsWith('/confirm')).length,5,'pending query must stop after bounded retries') assert(!posts.some(p=>p==='/rank/buy' || p.includes('/callback'))) console.log('PASS: closed purchase, Android/iOS/Windows/HarmonyOS unified virtual pay, unsupported iOS, changed price/order mismatch, cancellation, pending confirmation; no legacy payment callback') })().catch(e=>{console.error(e.message);process.exitCode=1})