Skip to content

Instantly share code, notes, and snippets.

@longnull
Last active November 10, 2022 16:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save longnull/2eafe3b843a8152c8864f7b5ba698b19 to your computer and use it in GitHub Desktop.
Save longnull/2eafe3b843a8152c8864f7b5ba698b19 to your computer and use it in GitHub Desktop.
AliExpress Card Master
// ==UserScript==
// @name AliExpress Card Master
// @description Заполняет данные карты для оплаты
// @author longnull
// @namespace longnull
// @version 1.0.10
// @updateURL https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMaster.user.js
// @downloadURL https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMaster.user.js
// @match https://shoppingcart.aliexpress.com/orders.htm*
// @match https://shoppingcart.aliexpress.com/order/confirm_order.htm*
// @match https://www.aliexpress.com/p/trade/confirm.html*
// @match https://www.aliexpress.com/p/second-payment/index.html*
// @match https://m.aliexpress.com/p/second-payment/index.html*
// @match https://m.aliexpress.com/p/trade/confirm.html*
// @match https://aliexpress.ru/checkout*
// @match https://intl-qk-global-inner.alipay.com/bindcard/selfaddcard.htm*
// ==/UserScript==
(() => {
'use strict';
// ================================================================================================================================================================
// Изменения
// ================================================================================================================================================================
// v1.0.10
// - Обновлён под новый сайт aliexpress.ru (также нужно обновить скрипт для карт - https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMasterCards.user.js)
// v1.0.9
// - Добавлен новый адрес (также нужно обновить скрипт для карт - https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMasterCards.user.js)
// - Обновлены селекторы для месяца и года
// - Изменено ожидание после ввода номера карты
// v1.0.8
// - Актуализированы адреса и селекторы под все страницы (также нужно обновить скрипт для карт - https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMasterCards.user.js)
// - Для параметра select добавлены значения 'place' и 'pay'
// v1.0.7
// - В параметрах карт добавлен параметр select (автоматический выбор карты)
// - Исправлено ожидание загрузки информации о карте после ввода номера
// v1.0.6
// - Обновлён под новую форму
// v1.0.5
// - Теперь карты можно вставить в отдельный скрипт
// - Добавлен ещё один селектор для кнопки подтверждения карты
// ================================================================================================================================================================
// Карты
// ================================================================================================================================================================
// Чтобы добавить карту, нужно скопировать имеющуюся от метки начала до метки конца, вставить рядом с другой картой и изменить поля
// Всегда храните свои карты в отдельном файле, не храните их только в скрипте, иначе потеряете их при обновлении скрипта
//
// Карты можно вставить в отдельный скрипт, чтобы не приходилось их вставлять обратно в основной скрипт после обновлений
// Скрипт для вставки карт: https://gist.github.com/longnull/2eafe3b843a8152c8864f7b5ba698b19/raw/AliExpressCardMasterCards.user.js
// Иногда может быть необходимо обновить скрипт с картами, если, например, в основном скрипте изменились @match строки вверху (они должны совпадать)
let cards = [
// --- Начало карты ---
{
// Имя карты в списке
name: 'Случайная',
// true - сохранить карту
// false - не сохранять карту
save: false,
// true - подтвердить после заполнения
// false - не подтверждать после заполнения
submit: false,
// Автоматический выбор карты
// true - выбирать эту карту автоматически
// false - не выбирать эту карту автоматически
// 'place' - выбирать эту карту автоматически только на странице оформления
// 'pay' - выбирать эту карту автоматически только на странице оплаты
select: false,
// Номер карты
// %rnd% - случайная карта
// %rndvisa% - случайная карта Visa
// %rndmaster% - случайная карта MasterCard
number: '%rnd%',
// Срок действия: месяц
expiryMonth: '01',
// Срок действия: год
expiryYear: '23',
// CVV/CVC
securityCode: '123',
// Держатель карты: имя
holderFirstName: '%rw(6-10,1)%',
// Держатель карты: фамилия
holderLastName: '%rw(6-10,1)%',
// Ниже - поля для Alipay
// Страна
country: 'US',
// Область/регион/штат
province: 'Colorado',
// Город
city: '%rw(6-12,1)%',
// Адрес
address: '%rw(8-20,a)%',
// Адрес 2
address2: '%rw(8-20,a)%',
// Индекс
zip: '%rn(10000-99999)%',
},
// --- Конец карты ---
];
// ================================================================================================================================
// Настройки
// ================================================================================================================================
const config = {
// BIN для генерации Visa
binVisa: [
'414734',
'415975',
'419402',
'421075',
'422438',
'424604',
'425838',
'426628',
'428038',
'430064',
],
// BIN для генерации MasterCard
binMastercard: [
'510594',
'511079',
'514138',
'517552',
'519280',
'521990',
'524366',
'527368',
'530860',
'533248',
],
// Количество элементов в выпадающем списке, отображаемое без прокрутки
itemsWithoutScroll: 6,
// Режим отладки
debug: false
};
// ================================================================================================================================
// https://github.com/grahamking/darkcoding-credit-card
const strRev = (str) => {
if (!str) {
return '';
}
let revstr = '';
for (let i = str.length - 1; i >= 0; i--) {
revstr += str.charAt(i);
}
return revstr;
};
const completedNumber = (prefix, length) => {
let ccnumber = prefix;
while (ccnumber.length < (length - 1)) {
ccnumber += Math.floor(Math.random() * 10);
}
const reversedCCnumberString = strRev(ccnumber);
const reversedCCnumber = [];
for (let i = 0; i < reversedCCnumberString.length; i++) {
reversedCCnumber[i] = parseInt(reversedCCnumberString.charAt(i));
}
let sum = 0;
let pos = 0;
while (pos < length - 1) {
let odd = reversedCCnumber[pos] * 2;
if (odd > 9) {
odd -= 9;
}
sum += odd;
if (pos != (length - 2)) {
sum += reversedCCnumber[pos + 1];
}
pos += 2;
}
const checkdigit = ((Math.floor(sum / 10) + 1) * 10 - sum) % 10;
ccnumber += checkdigit;
return ccnumber;
};
const creditCardNumber = (prefixList, length, howMany) => {
const result = [];
for (let i = 0; i < howMany; i++) {
const randomArrayIndex = Math.floor(Math.random() * prefixList.length);
const ccnumber = prefixList[randomArrayIndex];
result.push(completedNumber(ccnumber, length));
}
return result;
};
// ================================================================================================================================
const log = {
debug(...args) {
if (config && config.debug) {
const ar = Array.prototype.slice.call(args, 0);
ar.unshift('ACM ::');
console.log(...ar);
}
}
};
const sleep = async (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const waitForRequest = (url, startTimeout = 0, endTimeout = 0, max = 0) => {
log.debug('waitForRequest : url =', url, ', startTimeout =', startTimeout, ', endTimeout =', endTimeout, ', max =', max);
if (!Array.isArray(url)) {
url = [url];
}
const openOrig = unsafeWindow.XMLHttpRequest.prototype.open;
const promise = new Promise((resolve) => {
let startTimer;
let endTimer;
let requestCount = 0;
let responseCount = 0;
let urlIndex = 0;
const resolveAndRestore = () => {
resolve();
unsafeWindow.XMLHttpRequest.prototype.open = openOrig;
};
unsafeWindow.XMLHttpRequest.prototype.open = function () {
log.debug('waitForRequest : request :', arguments);
if (!arguments[1].includes(url[urlIndex])) {
return openOrig.apply(this, arguments);
}
requestCount++;
urlIndex++;
if (startTimer) {
clearTimeout(startTimer);
startTimer = null;
}
if (endTimer) {
clearTimeout(endTimer);
endTimer = null;
}
log.debug('waitForRequest : request match :', arguments[1]);
this.addEventListener('load', function () {
log.debug('waitForRequest : response :', this.responseURL);
responseCount++;
if (!endTimeout || (max && responseCount >= max)) {
return resolveAndRestore();
}
if (requestCount == responseCount) {
if (urlIndex >= url.length) {
resolveAndRestore();
}
endTimer = setTimeout(() => {
resolveAndRestore();
}, endTimeout);
}
});
this.addEventListener('error', () => {
log.debug('waitForRequest : error');
resolveAndRestore();
});
this.addEventListener('abort', () => {
log.debug('waitForRequest : abort');
resolveAndRestore();
});
return openOrig.apply(this, arguments);
};
if (startTimeout) {
startTimer = setTimeout(() => {
resolveAndRestore();
}, startTimeout);
}
});
promise.catch(() => {
log.debug('waitForRequest : catch');
unsafeWindow.XMLHttpRequest.prototype.open = openOrig;
});
return promise;
};
const waitForElement = (selectors, waitForExistence = true, visible = false, parent = document, interval = 250, seconds = 0) => {
log.debug('waitForElement : selectors =', selectors, ', waitForExistence =', waitForExistence, ', visible =', visible, ', parent =', parent, ', interval =', interval, ', seconds =', seconds);
const isVisible = (e) => {
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
};
return new Promise((resolve) => {
if (!Array.isArray(selectors)) {
selectors = [selectors];
}
seconds = seconds * 1000;
const startTime = Date.now();
const check = () => {
let found = true;
for (const s of selectors) {
const el = parent.querySelector(s);
if ((waitForExistence && (!el || (visible && !isVisible(el)))) || (!waitForExistence && el)) {
found = false;
break;
}
}
if (found) {
return resolve(true);
}
if (seconds > 0 && Date.now() - startTime > seconds) {
return resolve(false);
}
setTimeout(check, interval);
};
check();
});
};
function randomizeText(text, vars = {}, index = 0, type = 0) {
// https://stackoverflow.com/a/52789490
const formatDate = (date, patternStr) => {
if (!patternStr) {
patternStr = 'dd.MM.yyyy';
}
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December'
];
const dayOfWeekNames = [
'Sunday', 'Monday', 'Tuesday',
'Wednesday', 'Thursday', 'Friday', 'Saturday'
];
const day = date.getDate(),
month = date.getMonth(),
year = date.getFullYear(),
hour = date.getHours(),
minute = date.getMinutes(),
second = date.getSeconds(),
miliseconds = date.getMilliseconds(),
h = hour % 12,
hh = twoDigitPad(h),
HH = twoDigitPad(hour),
mm = twoDigitPad(minute),
ss = twoDigitPad(second),
aaa = hour < 12 ? 'AM' : 'PM',
EEEE = dayOfWeekNames[date.getDay()],
EEE = EEEE.substr(0, 3),
dd = twoDigitPad(day),
M = month + 1,
MM = twoDigitPad(M),
MMMM = monthNames[month],
MMM = MMMM.substr(0, 3),
yyyy = year + '',
yy = yyyy.substr(2, 2);
patternStr = patternStr
.replace('hh', hh).replace('h', h)
.replace('HH', HH).replace('H', hour)
.replace('mm', mm).replace('m', minute)
.replace('ss', ss).replace('s', second)
.replace('S', miliseconds)
.replace('dd', dd).replace('d', day)
.replace('EEEE', EEEE).replace('EEE', EEE)
.replace('yyyy', yyyy)
.replace('yy', yy)
.replace('aaa', aaa);
if (patternStr.indexOf('MMM') > -1) {
patternStr = patternStr
.replace('MMMM', MMMM)
.replace('MMM', MMM);
} else {
patternStr = patternStr
.replace('MM', MM)
.replace('M', M);
}
return patternStr;
};
const twoDigitPad = (num) => {
return num < 10 ? '0' + num : num;
};
const randomString = (length, chars = 'zZ0') => {
let characters = '';
if (chars.search(/[a-z]/) !== -1) {
characters += 'abcdefghijklmnopqrstuvwxyz';
}
if (chars.search(/[A-Z]/) !== -1) {
characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
if (chars.search(/[а-яё]/) !== -1) {
characters += 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
}
if (chars.search(/[А-ЯЁ]/) !== -1) {
characters += 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ';
}
if (chars.search(/[0-9]/) !== -1) {
characters += '0123456789';
}
const charactersLength = characters.length;
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
// http://jsfiddle.net/amando96/XjUJM/
const randomWord = (length, capitalize = false) => {
const consonants = 'bcdfghjlmnpqrstv'.split('');
const vowels = 'aeiou'.split('');
let word = '';
for (let i = 0; i < length / 2; i++) {
const randConsonant = consonants[randomRange(0, consonants.length - 1)];
const randVowel = vowels[randomRange(0, vowels.length - 1)];
word += capitalize && i === 0 ? randConsonant.toUpperCase() : randConsonant;
word += i * 2 < length - 1 ? randVowel : '';
}
return word;
};
const randomRange = (min, max) => {
return Math.floor(Math.random() * (max + 1 - min)) + min;
};
// https://medium.com/@nitinpatel_20236/how-to-shuffle-correctly-shuffle-an-array-in-javascript-15ea3f84bfb
const arrayShuffle = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
const funcs = {
rs(params) {
if (!params || !params.length) {
return '';
}
let len;
let chars = params.length > 1 ? params[1] : 'aA0';
const spl = params[0].split('-');
if (spl.length > 1) {
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (!min || !max) {
return '';
}
len = randomRange(min, max);
} else {
len = parseInt(spl[0]);
}
if (!len) {
return '';
}
return randomString(len, chars);
},
rw(params) {
if (!params || !params.length) {
return '';
}
let len;
let cap = (params.length > 1 && (params[1] === '1' || params[1].toLowerCase() === 'true')) ? true : false;
const spl = params[0].split('-');
if (spl.length > 1) {
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (!min || !max) {
return '';
}
len = randomRange(min, max);
} else {
len = parseInt(spl[0]);
}
if (!len) {
return '';
}
return randomWord(len, cap);
},
rn(params) {
if (!params || !params.length) {
return '';
}
const spl = params[0].split('-');
if (spl.length < 2) {
return '';
}
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (min === NaN || max === NaN) {
return '';
}
return randomRange(min, max);
},
dt(params) {
if (!params || !params.length) {
return formatDate(new Date());
}
return formatDate(new Date(), params[0]);
}
};
const sub = index > 0;
const arr = [];
let cur = '';
while (index < text.length) {
if (text[index] == '{' || text[index] == '[') {
const res = randomizeText(text, vars, index + 1, text[index] == '{' ? 0 : 1);
cur += res.text;
index = res.index;
} else {
if (text[index] == '|') {
arr.push(cur);
cur = '';
} else if (sub && ((type == 0 && text[index] == '}') || (type == 1 && text[index] == ']'))) {
arr.push(cur);
index++;
break;
} else {
cur += text[index];
}
index++;
}
}
if (sub) {
let result;
let value;
const varName = arr[arr.length - 1].match(/\$(.+)/);
if (varName) {
arr[arr.length - 1] = arr[arr.length - 1].replace(/\$(.+)/, '');
}
if (type == 0) {
if (arr.length > 1) {
value = arr[randomRange(0, arr.length - 1)];
result = {text: value, index: index};
} else if (arr.length == 1) {
if (randomRange(1, 100) > 50) {
value = arr[0];
result = {text: arr[0], index: index};
} else {
value = '';
result = {text: value, index: index};
}
}
} else if (type == 1) {
const sep = arr.pop();
arrayShuffle(arr);
value = arr.join(sep);
result = {text: value, index: index};
}
if (varName) {
vars[varName[1]] = value;
}
return result;
}
cur = cur.replace(/\%([a-zA-Z_]+?)\((.+?)\)(?:\$(.+?))?\%/g, (m, func, params, varName) => {
if (funcs[func]) {
let paramsArray = [];
if (params) {
paramsArray = params.trim().split(',').map((p) => p.trim());
}
const res = funcs[func](paramsArray);
if (varName) {
vars[varName] = res;
}
return res;
}
});
const keys = Object.keys(vars);
for (const k of keys) {
cur = cur.replace(new RegExp(`\\%${k}\\%`, 'g'), vars[k]);
}
return cur;
};
const setInput = (input, value) => {
if (typeof input === 'string') {
input = document.querySelector(input);
}
if (!input) {
return;
}
if (Object.keys(input).filter((i) => i.indexOf('__reactEventHandlers') >= 0).length) {
Object.getOwnPropertyDescriptor(window[input.constructor.name].prototype, 'value').set.call(input, value);
} else {
input.value = value;
}
input.dispatchEvent(new Event('change', {bubbles: true}));
input.dispatchEvent(new Event('keyup', {bubbles: true}));
input.dispatchEvent(new Event('keydown', {bubbles: true}));
input.dispatchEvent(new Event('keypress', {bubbles: true}));
input.dispatchEvent(new Event('input', {bubbles: true}));
input.dispatchEvent(new Event('blur', {bubbles: true}));
};
const init = () => {
if (document.querySelector('#card-master')) {
return;
}
const form = document.querySelector(`
.payment-container-body.comet-drawer-body > div[class] ~ div:nth-child(2),
div[class*="add-card--m-card-form"],
div[class*="CheckoutPaymentMethod_BackCardForm__cardForm"],
.card-info > div:first-child,
.new-card-wrap > div:first-child,
div[id*="paymentMethod_"],
#bindcard-form
`);
if (!form) {
return;
}
log.debug('init');
log.debug(form);
if (unsafeWindow.acmCards) {
cards = unsafeWindow.acmCards;
}
const payPage =
window.location.pathname == '/p/second-payment/index.html' ||
window.location.pathname == '/order/secondPayment.htm' ||
window.location.pathname == '/order/secpay.html';
const main = document.createElement('div');
main.id = 'card-master';
const select = document.createElement('div');
select.className = 'select';
select.innerHTML = `
<div>
<svg viewBox="0 0 24 24">
<path d="M20,8H4V6H20M20,18H4V12H20M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" />
</svg>
Выберите карту
</div>
`;
select.addEventListener('click', (event) => {
event.preventDefault();
if (!select.classList.contains('opened') && select.classList.contains('disabled')) {
return;
}
select.classList.toggle('opened');
return false;
});
const ul = document.createElement('ul')
select.appendChild(ul);
let autoSelect;
for (let i = 0; i < cards.length; i++) {
const card = cards[i];
const li = document.createElement('li');
li.textContent = card.name;
li.dataset.dataIdx = i;
li.addEventListener('click', async (event) => {
event.preventDefault();
select.classList.add('disabled');
const card = cards[i];
if (card.number) {
log.debug('waiting for queryCardBinInfo and fill card number');
await Promise.all([
waitForRequest(['/queryCardBinInfo.htm', '/h5/mtop.global.payment.ae.async'], 1000, 1000),
new Promise((resolve) => {
const number = card.number
.replace('%rnd%', creditCardNumber(config.binVisa.concat(config.binMastercard), 16, 1))
.replace('%rndvisa%', creditCardNumber(config.binVisa, 16, 1))
.replace('%rndmaster%', creditCardNumber(config.binMastercard, 16, 1));
setInput('#cardNum, #cardNumber, .card-num, input[name="card_number"], #j-card-number', number);
if (document.querySelector('.billing-address')) {
if (number.startsWith('4')) {
document.querySelector('#j-label-visa input').click();
} else {
document.querySelector('#j-label-mastercard input').click();
}
}
resolve();
})
]);
}
if (card.holderFirstName && card.holderLastName) {
log.debug('filling card holder');
if (document.querySelector('input[name="firstName"]')) {
setInput('input[name="firstName"]', randomizeText(card.holderFirstName));
setInput('input[name="lastName"]', randomizeText(card.holderLastName));
} else {
setInput(
'#cardHolder, #cardholder, .card-holder input, input[name="card_name"]',
`${randomizeText(card.holderFirstName)} ${randomizeText(card.holderLastName)}`
);
}
}
if (card.expiryMonth && card.expiryYear) {
log.debug(`filling card expiry : ${card.expiryMonth}/${card.expiryYear}`);
if (document.querySelector('input[name="expiryMonth"]')) {
setInput('input[name="expiryMonth"]', card.expiryMonth);
setInput('input[name="expiryYear"]', card.expiryYear);
} else {
let elMonth = document.querySelector('.payment-container-body .comet-select:first-child, div[class*="expire-input--expires-row"] .comet-select:first-child');
let elYear = document.querySelector('.payment-container-body .comet-select:last-child, div[class*="expire-input--expires-row"] .comet-select:last-child');
if (elMonth && elYear) {
elMonth.click();
await sleep(300);
elMonth = document.querySelector(`.comet-select-popup-wrap .comet-menu .comet-menu-item[label="${(card.expiryMonth.length == 1 ? '0' : '') + card.expiryMonth}"]`);
if (elMonth) {
elMonth.click();
}
elYear.click();
await sleep(300);
elYear = document.querySelector(`.comet-select-popup-wrap .comet-menu .comet-menu-item[label="${card.expiryYear}"]`);
if (elYear) {
elYear.click();
}
} else {
setInput(`
#expires,
#expire,
#cardExpire,
.card-expires input,
div[id*="paymentMethod_"] input[name="card_number"] + div input[inputmode="numeric"]
`,
`${card.expiryMonth}/${card.expiryYear}`
);
}
}
}
let el = document.querySelector('.new-card-wrap .next-btn, div[id*="paymentMethod_"] > div > div > div:nth-child(2) div');
if (el) {
log.debug('clicking next button');
el.click();
await sleep(300);
}
if (typeof card.save !== 'undefined') {
if (card.save) {
el = document.querySelector(`
.payment-container-body .comet-checkbox:not(.comet-checkbox-checked),
div[class*="save-card--save-card-row"] .comet-checkbox:not(.comet-checkbox-checked),
div[class*="save-card--save-card-row"] .comet-switch:not(.comet-switch-checked),
div[class*="CheckoutPaymentMethod_BackCardForm__saveCardCheckbox"]:not([class*="ali-kit_Checkbox__checked"]),
.save-card .next-checkbox-wrapper:not(.checked),
div[id*="paymentMethod_"] > div > div > div:nth-child(3) > div:last-child > div.hxYBDU
`);
} else {
el = document.querySelector(`
.payment-container-body .comet-checkbox.comet-checkbox-checked,
div[class*="save-card--save-card-row"] .comet-checkbox.comet-checkbox-checked,
div[class*="save-card--save-card-row"] .comet-switch.comet-switch-checked,
div[class*="CheckoutPaymentMethod_BackCardForm__saveCardCheckbox"][class*="ali-kit_Checkbox__checked"],
.save-card .next-checkbox-wrapper.checked,
div[id*="paymentMethod_"] > div > div > div:nth-child(3) > div:last-child > div.jzqzHB
`);
}
if (el) {
log.debug('clicking save switch');
el.click();
} else {
el = document.querySelector('.card-save-button .switch input');
if (el && el.checked != card.save) {
log.debug('clicking save switch');
document.querySelector('.card-save-button .switch').click();
}
}
if (!card.save) {
await sleep(300);
el = document.querySelector(`
.comet-modal-footer button:first-child,
div[class*="save-card--save-card-info-confirm"] button:first-child,
.save-card-info-confirm .next-btn-normal
`);
if (el) {
log.debug('clicking skip button');
el.click();
}
}
}
if (card.securityCode) {
log.debug('filling card cvc :', card.securityCode);
setInput(`
#cvv,
#cvc,
#cardCvv,
.card-cvv input,
div[id*="paymentMethod_"] > div > div > div > form > div:nth-child(2) input,
input[name="cvv2"]
`,
card.securityCode
);
}
if (document.querySelector('.billing-address')) {
log.debug('filling alipay info');
const addrRadio = document.querySelector('.billing-address .address-radio[value="1"]');
if (addrRadio && (card.address || card.address2 || card.city || card.country || card.province || card.zip)) {
addrRadio.click();
}
if (card.address) {
setInput('#address1', randomizeText(card.address));
}
if (card.address2) {
setInput('#address2', randomizeText(card.address2));
}
if (card.city) {
setInput('#city', randomizeText(card.city));
}
if (card.country) {
setInput('#country', randomizeText(card.country));
}
if (card.province) {
setInput('#state:not(.fn-hide), .i-select-province:not(.fn-hide) #state-select', randomizeText(card.province));
}
if (card.zip) {
setInput('#postCode', randomizeText(card.zip));
}
}
if (card.submit && !payPage) {
log.debug('submitting');
setTimeout(() => {
const submitEl = document.querySelector(`
#payment-botton-section button,
button[class*="CheckoutPaymentMethod_ButtonRow__controlAccent"],
.dialog-header .dialog-btn,
.i-button-submit
`);
if (submitEl) {
submitEl.click();
}
}, 500);
}
log.debug('done');
select.classList.remove('disabled');
return false;
});
ul.appendChild(li);
if (card.select === true || (card.select === 'place' && !payPage) || (card.select === 'pay' && payPage)) {
autoSelect = li;
}
}
main.appendChild(select);
const style = document.createElement('style');
style.innerHTML = `
#card-master {
font-size: 16px;
margin-bottom: 4px;
height: 30px;
}
.addcard-form-wrapper #card-master,
.self-bindcard #card-master {
margin-top: 4px;
}
.self-bindcard #card-master {
padding-left: 90px;
}
.pay-content #card-master,
.card-info #card-master,
.new-card-wrap #card-master {
margin-bottom: 8px;
}
#card-master .select {
position: relative;
display: flex;
flex-direction: column;
width: 250px;
background-color: white;
border: 1px solid #dcdee3;
border-radius: 4px;
z-index: 1000;
}
#card-master .select.disabled > div {
color: #bbb;
}
#card-master .select > div {
display: flex;
align-items: center;
padding: 2px 6px;
color: #333;
background-color: white;
border-radius: 4px;
user-select: none;
}
#card-master .select > div::after {
content: '▼';
right: 4px;
position: absolute;
color: #dcdee3;
}
#card-master .select.opened > div::after {
content: '▲';
}
#card-master .select > div > svg {
margin-right: 4px;
fill: #51b1f0;
width: 22px;
height: 22px;
}
#card-master .select.disabled > div > svg {
fill: #bbb;
}
#card-master .select > ul {
display: none;
border-radius: 0 0 4px 4px;
}
#card-master .select.opened > ul {
display: block;
background: white;
border-top: 2px solid #dcdee3;
max-height: ${config.itemsWithoutScroll * 32 - 1}px;
overflow-x: hidden;
overflow-y: auto;
}
#card-master .select.opened > ul > li {
height: 27px;
padding: 2px 6px;
color: #333;
user-select: none;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
#card-master .select.disabled.opened > ul > li {
color: #bbb;
}
#card-master .select.opened > ul > li:not(:last-child) {
border-bottom: 1px solid #dcdee3;
}
#card-master .select.opened > ul > li:hover {
background: #f7f7f7;
}
`;
main.appendChild(style);
document.addEventListener('click', (e) => {
if (!select.contains(e.target)) {
select.classList.remove('opened');
}
});
form.before(main);
if (autoSelect) {
autoSelect.click();
}
};
setInterval(() => {
init();
}, 500);
})();
// ==UserScript==
// @name AliExpress Card Master Cards
// @description Карты для AliExpress Card Master
// @author longnull
// @namespace longnull
// @version 1.0.3
// @match https://shoppingcart.aliexpress.com/orders.htm*
// @match https://shoppingcart.aliexpress.com/order/confirm_order.htm*
// @match https://www.aliexpress.com/p/trade/confirm.html*
// @match https://www.aliexpress.com/p/second-payment/index.html*
// @match https://m.aliexpress.com/p/second-payment/index.html*
// @match https://m.aliexpress.com/p/trade/confirm.html*
// @match https://aliexpress.ru/checkout*
// @match https://intl-qk-global-inner.alipay.com/bindcard/selfaddcard.htm*
// ==/UserScript==
(() => {
'use strict';
unsafeWindow.acmCards = [
// Карты
];
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment