-
-
Save longnull/2eafe3b843a8152c8864f7b5ba698b19 to your computer and use it in GitHub Desktop.
AliExpress Card Master
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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