打开https://www.cmlink.com/uk/en/topup/
输入手机号,然后f12, console运行
(async function cmlinkSmallTopup() {
'use strict';
const AMOUNT = '0.10';
const RUN_KEY = '__cmlinkSmallTopupState';
function pennies(value) {
if (!/^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/.test(String(value))) {
return NaN;
}
const [whole, fraction = ''] = String(value).split('.');
return Number(whole) * 100 +
Number(fraction.padEnd(2, '0'));
}
function normalizePhone(value) {
let phone = String(value ?? '').replace(/[\s()+-]/g, '');
if (phone.startsWith('0044')) {
phone = '0' + phone.slice(4);
} else if (phone.startsWith('44')) {
phone = '0' + phone.slice(2);
} else if (/^7\d{9}$/.test(phone)) {
phone = '0' + phone;
}
return phone;
}
function mustSucceed(response, label) {
if (!response || String(response.code) !== '200') {
throw new Error(
`${label}失败:${response?.code ?? '未知'} ${
response?.msg ?? response?.message ?? ''
}`
);
}
if (!response.data || typeof response.data !== 'object') {
throw new Error(`${label}未返回有效数据。`);
}
return response.data;
}
function verifyOrder(order, expected, phone) {
const cents = String(order.topupAmount ?? '');
if (
!/^\d+(?:\.0+)?$/.test(cents) ||
Number(cents) !== expected
) {
throw new Error(
`订单充值金额不一致(返回便士数:${
cents || '未返回'
}),停止。`
);
}
if (normalizePhone(order.phoneNum) !== phone) {
throw new Error('订单号码未返回或不匹配,停止。');
}
}
let ownsLock = false;
let orderAttempted = false;
try {
if (
location.origin !== 'https://www.cmlink.com' ||
!/^\/uk\/(?:en|zh)\/topup\/?$/.test(location.pathname)
) {
throw new Error(
'请先打开 https://www.cmlink.com/uk/en/topup/'
);
}
const expected = pennies(AMOUNT);
if (
!Number.isSafeInteger(expected) ||
expected < 1 ||
expected > 599
) {
throw new Error(
'金额应为 £0.01–£5.99,最多两位小数。'
);
}
if (window[RUN_KEY]) {
throw new Error(
'本页已运行过或正在运行,请勿重复建单。'
);
}
window[RUN_KEY] = 'running';
ownsLock = true;
const root =
window.$nuxt ||
document.querySelector('#__nuxt')?.__vue__;
if (!root) {
throw new Error('官网还没加载完成,请稍后再运行。');
}
const queue = [root];
const seen = new Set();
let vm;
while (queue.length) {
const item = queue.shift();
if (!item || seen.has(item)) continue;
seen.add(item);
if (
item.$options?.name === 'TopupContent' &&
item.topupForm
) {
vm = item;
break;
}
queue.push(...(item.$children || []));
}
const api = vm?.$clientApi?.topup;
if (
!vm ||
!['checkMobileNum', 'placeOrder', 'queryOrder']
.every(name => typeof api?.[name] === 'function')
) {
throw new Error(
'找不到官网充值组件或接口已变化,停止。'
);
}
if (String(vm.step) !== '1') {
throw new Error(
'当前已有支付订单,请先完成或退出该订单。'
);
}
if (vm.topupForm.couponCode || vm.couponValue) {
throw new Error(
'请先清空优惠码和所选优惠券,再运行。'
);
}
const phone = normalizePhone(
vm.topupForm.mobileNum ||
prompt('请输入你自己的 CMLink UK 英国主号码:')
);
if (!/^07\d{9}$/.test(phone)) {
throw new Error(
'请输入有效的英国移动号码,例如 07 开头的 11 位号码。'
);
}
const status = mustSucceed(
await api.checkMobileNum({ serviceNum: phone }),
'号码检查'
);
if (String(status.userStatus) === '4') {
throw new Error('官网返回号码不可充值状态,停止。');
}
console.info(
`正在尝试创建 £${AMOUNT} 未付款充值订单,不自动支付。`
);
orderAttempted = true;
const result = await api.placeOrder({
mobileNum: phone,
chargeAmount: String(expected),
orgAmount: String(expected),
operType: '3',
chlCode: vm.chlCode || '',
language: location.pathname.includes('/zh/')
? 'zh'
: 'en'
});
const created = mustSucceed(result, '创建订单');
if (!created.orderId || !created.crmOrderId) {
throw new Error(
'服务器未返回完整订单编号,停止。'
);
}
const order = mustSucceed(
await api.queryOrder({ sid: created.orderId }),
'查询订单'
);
console.info('服务器订单核对:', {
充值金额便士: order.topupAmount,
号码是否一致:
normalizePhone(order.phoneNum) === phone
});
verifyOrder(order, expected, phone);
vm.topupForm.mobileNum = phone;
vm.topupForm.couponCode = '';
vm.couponValue = '';
vm.discountFee = 0;
vm.chargeAmountJPY = expected / 100;
vm.isCheckPhone = true;
vm.phoneTip = false;
vm.amoutTip = false;
vm.mchOrderNo = created.orderId;
vm.payOrderNo = created.crmOrderId;
vm.orderfee = created.orderFee;
vm.isShowTotal = true;
vm.step = '2';
await vm.$nextTick();
window.scrollTo(0, 0);
window[RUN_KEY] = 'unpaid-order-created';
console.info(
`订单充值金额核对为 £${AMOUNT}。` +
'请在官网及支付机构页面核对实际应付金额,再自行付款。'
);
} catch (error) {
console.error(
'CMLink 小额充值尝试已停止:',
error instanceof Error
? error.message
: String(error)
);
if (ownsLock && orderAttempted) {
window[RUN_KEY] = 'order-attempted';
console.info(
'已尝试创建订单;本脚本不会自动重试。停止不代表付款成功。'
);
} else if (ownsLock) {
delete window[RUN_KEY];
}
}
})();