Function bodies 131 total
scrapeE9pay function · javascript · L135-L161 (27 LOC)scraper/run-khm.js
async function scrapeE9pay(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.waitForSelector('#KH_USD', { state: 'attached', timeout: 10000 });
await page.evaluate(() => {
const radio = document.querySelector('#KH_USD');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
radio.dispatchEvent(new Event('click', { bubbles: true }));
});
await page.waitForTimeout(1000);
await page.click('#reverse'); await page.waitForTimeout(500);
await page.waitForSelector('#receive-money', { timeout: 5000 });
await page.click('#receive-money', { clickCount: 3 });
await page.fill('#receive-money', String(AMOUNT));
await page.dispatchEvent('#receive-money', 'blur'); await page.waitForTimeout(3000);
const raw = await page.$eval('#send-money', el => el.value)main function · javascript · L174-L255 (82 LOC)scraper/run-khm.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Cambodia USD 스크래핑 시작 — run_hour: ${runHour}\n`);
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(
SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
);
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.push(scrapeGme function · javascript · L15-L33 (19 LOC)scraper/run-lbr.js
async function scrapeGme() {
const body = new URLSearchParams({
method: 'GetExRate', pCurr: 'USD', pCountryName: 'Liberia',
collCurr: 'KRW', deliveryMethod: '1', cAmt: '', pAmt: String(AMOUNT),
cardOnline: 'false', calBy: 'P',
}).toString();
const res = await fetch('https://online.gmeremit.com/Default.aspx', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body, signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.errorCode !== '0') throw new Error(`GME API 오류: ${data.msg}`);
const total = parseFloat(data.collAmt?.toString().replace(/,/g, '') ?? '');
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total, service_fee: 0, total_sending_amount: total };
}scrapeGmoneytrans function · javascript · L36-L52 (17 LOC)scraper/run-lbr.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Liberia'
+ '&total_collected=0'
+ '&payment_type=Cash+Pickup'
+ '¤cyType=USD';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 3000;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}parseField function · javascript · L53-L56 (4 LOC)scraper/run-lbr.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}main function · javascript · L65-L137 (73 LOC)scraper/run-lbr.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Liberia USD 스크래핑 시작 — run_hour: ${runHour}\n`);
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(SCRAPERS.map(({ fn }) => fn()));
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.push({ name, error: result.reason?.message });
}
}
if (results.length === 0) {
console.error('\n모든 스크래퍼 실패. 종료합니다.');
process.exit(1);
}
const gmeRecord = resulscrapeGme function · javascript · L15-L34 (20 LOC)scraper/run-mmk.js
async function scrapeGme(browser) {
const page = await browser.newPage();
try {
await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector('#nCountry', { timeout: 10000 });
await page.click('#nCountry'); await page.waitForTimeout(500);
await page.fill('#CountryValue', 'Myanmar'); await page.waitForTimeout(300);
await page.click('#toCurrUl li[data-countrycode="MMK"]');
await page.waitForTimeout(1000);
await page.click('#recAmt', { clickCount: 3 });
await page.fill('#recAmt', String(AMOUNT));
await page.dispatchEvent('#recAmt', 'change');
await page.waitForTimeout(3000);
const raw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
const total = extractNumber(raw);
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total, service_fee: 0Powered by Repobility — scan your code at https://repobility.com
scrapeGmoneytrans function · javascript · L37-L53 (17 LOC)scraper/run-mmk.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Myanmar'
+ '&total_collected=0'
+ '&payment_type=Bank+Account'
+ '¤cyType=MMK';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 3000;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}parseField function · javascript · L54-L57 (4 LOC)scraper/run-mmk.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}scrapeHanpass function · javascript · L60-L77 (18 LOC)scraper/run-mmk.js
async function scrapeHanpass() {
const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'MMK',
fromCurrencyCode: 'KRW', toCurrencyCode: 'MMK', toCountryCode: 'MM',
memberSeq: '1', lang: 'en' }),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
const total = data.depositAmountIncludingFee;
const fee = data.transferFee ?? 0;
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}scrapeSbi function · javascript · L80-L103 (24 LOC)scraper/run-mmk.js
async function scrapeSbi(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.sbicosmoney.com/', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.click('button:has-text("Close")').catch(() => null); await page.waitForTimeout(500);
await page.click('.dest-country'); await page.waitForTimeout(500);
await page.click('a[data-currency="MMK"]'); await page.waitForTimeout(1500);
await page.click('#targetAmount', { clickCount: 3 });
await page.fill('#targetAmount', String(AMOUNT));
await page.dispatchEvent('#targetAmount', 'input'); await page.waitForTimeout(2000);
const raw = await page.inputValue('#krwAmount');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금scrapeE9pay function · javascript · L106-L132 (27 LOC)scraper/run-mmk.js
async function scrapeE9pay(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.waitForSelector('#MM_MMK', { state: 'attached', timeout: 10000 });
await page.evaluate(() => {
const radio = document.querySelector('#MM_MMK');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
radio.dispatchEvent(new Event('click', { bubbles: true }));
});
await page.waitForTimeout(1000);
await page.click('#reverse'); await page.waitForTimeout(500);
await page.waitForSelector('#receive-money', { timeout: 5000 });
await page.click('#receive-money', { clickCount: 3 });
await page.fill('#receive-money', String(AMOUNT));
await page.dispatchEvent('#receive-money', 'blur'); await page.waitForTimeout(3000);
const raw = await page.$eval('#send-money', el => el.value)main function · javascript · L144-L225 (82 LOC)scraper/run-mmk.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Myanmar MMK 스크래핑 시작 — run_hour: ${runHour}\n`);
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(
SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
);
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.push({scrapeGme function · javascript · L15-L35 (21 LOC)scraper/run-mnt.js
async function scrapeGme(browser) {
const page = await browser.newPage();
try {
await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector('#nCountry', { timeout: 10000 });
await page.click('#nCountry'); await page.waitForTimeout(500);
await page.waitForSelector('#CountryValue', { timeout: 5000 });
await page.fill('#CountryValue', 'Mongolia'); await page.waitForTimeout(300);
await page.click('#toCurrUl li[data-countrycode="MNT"]');
await page.waitForTimeout(1000);
await page.click('#recAmt', { clickCount: 3 });
await page.fill('#recAmt', String(AMOUNT));
await page.dispatchEvent('#recAmt', 'change');
await page.waitForTimeout(3000);
const raw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
const total = extractNumber(raw);
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'GME', receiving_country: COUNTRY,scrapeGmoneytrans function · javascript · L38-L54 (17 LOC)scraper/run-mnt.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Mongolia'
+ '&total_collected=0'
+ '&payment_type=Bank+Account'
+ '¤cyType=MNT';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 2500;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
parseField function · javascript · L55-L58 (4 LOC)scraper/run-mnt.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}scrapeUtransfer function · javascript · L61-L80 (20 LOC)scraper/run-mnt.js
async function scrapeUtransfer(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.utransfer.com', { waitUntil: 'networkidle', timeout: 30000 });
await page.waitForFunction(() => document.querySelectorAll('select').length >= 2, null, { timeout: 15000 });
await page.locator('select').nth(1).selectOption('MNT');
await page.waitForTimeout(1000);
await page.click('input[name="toAmount"]', { clickCount: 3 });
await page.fill('input[name="toAmount"]', String(AMOUNT));
await page.dispatchEvent('input[name="toAmount"]', 'change');
await page.waitForTimeout(3000);
const raw = await page.inputValue('input[name="fromAmount"]').catch(() => null);
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const feeRaw = await page.locator('.utransfer_fees').textContent().catch(() => null);
const fee = extractNumber(feeRaw) ?? 5000;
return { operator: 'Utransfer', receiving_country: COUscrapeCross function · javascript · L83-L104 (22 LOC)scraper/run-mnt.js
async function scrapeCross(browser) {
const page = await browser.newPage();
try {
await page.goto('https://crossenf.com/remittance', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.locator('div.relative:has(span:text("THB"))').click();
await page.waitForSelector('#aside-root ul', { timeout: 10000 });
await page.locator('#aside-root li:has(img[alt="MN flag"])').click();
await page.waitForTimeout(1000);
const receiveInput = page.locator('input[inputmode="numeric"]').nth(1);
await receiveInput.click({ clickCount: 3 });
await receiveInput.fill(String(AMOUNT));
await receiveInput.press('Tab');
await page.waitForTimeout(3000);
const totalRaw = await page.locator('input[inputmode="numeric"]').nth(0).inputValue();
const total = extractNumber(totalRaw);
if (!total) throw new Error('총 송금액 추출 실패');
const fee = 5000;
return { operator: 'Cross', receiving_country: COUNTRY, receive_amount: AMOUNT,scrapeE9pay function · javascript · L107-L134 (28 LOC)scraper/run-mnt.js
async function scrapeE9pay(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.waitForSelector('#MN_MNT', { state: 'attached', timeout: 10000 });
await page.evaluate(() => {
const radio = document.querySelector('#MN_MNT');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
radio.dispatchEvent(new Event('click', { bubbles: true }));
});
await page.waitForTimeout(1000);
await page.click('#reverse'); await page.waitForTimeout(500);
await page.waitForSelector('#receive-money', { timeout: 5000 });
await page.click('#receive-money', { clickCount: 3 });
await page.fill('#receive-money', String(AMOUNT));
await page.dispatchEvent('#receive-money', 'blur');
await page.waitForTimeout(3000);
const raw = await page.$eval('#send-money', el => el.valscrapeCoinshot function · javascript · L137-L160 (24 LOC)scraper/run-mnt.js
async function scrapeCoinshot(browser) {
const page = await browser.newPage();
try {
await page.goto('https://coinshot.org/main', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.waitForSelector('button.lang-btn[value="ko"]', { timeout: 10000 });
await page.click('button.lang-btn[value="ko"]');
await page.waitForTimeout(1000);
await page.click('#current-receiving-currency'); await page.waitForTimeout(500);
await page.click('#select-receiving-currency a[data-currency="MNT"]');
await page.waitForTimeout(1000);
await page.click('#receiving-input', { clickCount: 3 });
await page.fill('#receiving-input', String(AMOUNT));
await page.press('#receiving-input', 'Enter');
await page.waitForTimeout(3000);
const raw = await page.inputValue('#sending-input');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const feeRaw = await page.locator('h5.text-left').textContscrapeHanpass function · javascript · L163-L180 (18 LOC)scraper/run-mnt.js
async function scrapeHanpass() {
const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'MNT',
fromCurrencyCode: 'KRW', toCurrencyCode: 'MNT', toCountryCode: 'MN',
memberSeq: '1', lang: 'en' }),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
const total = data.depositAmountIncludingFee;
const fee = data.transferFee ?? 0;
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}main function · javascript · L194-L275 (82 LOC)scraper/run-mnt.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Mongolia MNT 스크래핑 시작 — run_hour: ${runHour}\n`);
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(
SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
);
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.push(scrapeGme function · javascript · L15-L34 (20 LOC)scraper/run-npr.js
async function scrapeGme(browser) {
const page = await browser.newPage();
try {
await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector('#nCountry', { timeout: 10000 });
await page.click('#nCountry'); await page.waitForTimeout(500);
await page.fill('#CountryValue', 'Nepal'); await page.waitForTimeout(300);
await page.click('#toCurrUl li[data-countrycode="NPR"]');
await page.waitForTimeout(1000);
await page.click('#recAmt', { clickCount: 3 });
await page.fill('#recAmt', String(AMOUNT));
await page.dispatchEvent('#recAmt', 'change');
await page.waitForTimeout(3000);
const raw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
const total = extractNumber(raw);
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total, service_fee: 0, Repobility · MCP-ready · https://repobility.com
scrapeGmoneytrans function · javascript · L37-L53 (17 LOC)scraper/run-npr.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Nepal'
+ '&total_collected=0'
+ '&payment_type=Bank+Account'
+ '¤cyType=NPR';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 2500;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}parseField function · javascript · L54-L57 (4 LOC)scraper/run-npr.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}scrapeSentbe function · javascript · L60-L84 (25 LOC)scraper/run-npr.js
async function scrapeSentbe(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.sentbe.com/ko', { waitUntil: 'networkidle', timeout: 30000 });
await page.click('button.close').catch(() => null); await page.waitForTimeout(300);
await page.click('article.app-download-popup .dim').catch(() => null); await page.waitForTimeout(500);
await page.waitForSelector('.receiveAmountInput .el-input-group__append', { timeout: 10000 });
await page.click('.receiveAmountInput .el-input-group__append'); await page.waitForTimeout(500);
await page.click('.receiveAmountInput .el-select-dropdown__item:has-text("네팔")');
await page.waitForTimeout(1000);
await page.click('#receiveAmount', { clickCount: 3 });
await page.fill('#receiveAmount', scrapeHanpass function · javascript · L87-L104 (18 LOC)scraper/run-npr.js
async function scrapeHanpass() {
const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'NPR',
fromCurrencyCode: 'KRW', toCurrencyCode: 'NPR', toCountryCode: 'NP',
memberSeq: '1', lang: 'en' }),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
const total = data.depositAmountIncludingFee;
const fee = data.transferFee ?? 0;
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}scrapeJrf function · javascript · L107-L126 (20 LOC)scraper/run-npr.js
async function scrapeJrf(browser) {
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
await page.goto('https://www.jpremit.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.click('#div_curr', { force: true });
await page.waitForSelector('li#NPR', { state: 'visible', timeout: 10000 });
await page.click('li#NPR'); await page.waitForTimeout(1500);
await page.click('#rec_money', { clickCount: 3 });
await page.fill('#rec_money', String(AMOUNT));
await page.dispatchEvent('#rec_money', 'keyup'); await page.waitForTimeout(3000);
const sendAmtRaw = await page.inputValue('#send_money');
const sendAmt = extractNumber(sendAmtRaw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const fee = 3000;
return { operator: 'JRF', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmt, service_fee: fee, totscrapeE9pay function · javascript · L129-L155 (27 LOC)scraper/run-npr.js
async function scrapeE9pay(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.waitForSelector('#NP_NPR', { state: 'attached', timeout: 10000 });
await page.evaluate(() => {
const radio = document.querySelector('#NP_NPR');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
radio.dispatchEvent(new Event('click', { bubbles: true }));
});
await page.waitForTimeout(1000);
await page.click('#reverse'); await page.waitForTimeout(500);
await page.waitForSelector('#receive-money', { timeout: 5000 });
await page.click('#receive-money', { clickCount: 3 });
await page.fill('#receive-money', String(AMOUNT));
await page.dispatchEvent('#receive-money', 'blur'); await page.waitForTimeout(3000);
const raw = await page.$eval('#send-money', el => el.value)scrapeCoinshot function · javascript · L158-L178 (21 LOC)scraper/run-npr.js
async function scrapeCoinshot(browser) {
const page = await browser.newPage();
try {
await page.goto('https://coinshot.org/main', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.waitForSelector('button.lang-btn[value="ko"]', { timeout: 10000 });
await page.click('button.lang-btn[value="ko"]'); await page.waitForTimeout(1000);
await page.click('#current-receiving-currency'); await page.waitForTimeout(500);
await page.click('#select-receiving-currency a[data-currency="NPR"]');
await page.waitForTimeout(1000);
await page.click('#receiving-input', { clickCount: 3 });
await page.fill('#receiving-input', String(AMOUNT));
await page.press('#receiving-input', 'Enter'); await page.waitForTimeout(3000);
const raw = await page.inputValue('#sending-input');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const fee = 5000;
return { operator: 'Coinshot', receiving_coumain function · javascript · L192-L273 (82 LOC)scraper/run-npr.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Nepal NPR 스크래핑 시작 — run_hour: ${runHour}\n`);
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(
SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
);
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.push({ nOpen data scored by Repobility · https://repobility.com
scrapeGme function · javascript · L15-L34 (20 LOC)scraper/run-php.js
async function scrapeGme(browser) {
const page = await browser.newPage();
try {
await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector('#nCountry', { timeout: 10000 });
await page.click('#nCountry'); await page.waitForTimeout(500);
await page.fill('#CountryValue', 'Philippines'); await page.waitForTimeout(300);
await page.click('#toCurrUl li[data-countrycode="PHP"]');
await page.waitForTimeout(1000);
await page.click('#recAmt', { clickCount: 3 });
await page.fill('#recAmt', String(AMOUNT));
await page.dispatchEvent('#recAmt', 'change');
await page.waitForTimeout(3000);
const raw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
const total = extractNumber(raw);
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total, service_fescrapeGmoneytrans function · javascript · L37-L53 (17 LOC)scraper/run-php.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Philippines'
+ '&total_collected=0'
+ '&payment_type=Bank+Account'
+ '¤cyType=PHP';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 3000;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}parseField function · javascript · L54-L57 (4 LOC)scraper/run-php.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}scrapeSbi function · javascript · L60-L82 (23 LOC)scraper/run-php.js
async function scrapeSbi(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.sbicosmoney.com/', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.click('button:has-text("Close")').catch(() => null); await page.waitForTimeout(500);
await page.click('.dest-country'); await page.waitForTimeout(500);
await page.click('a[data-currency="PHP"]'); await page.waitForTimeout(1500);
await page.click('#targetAmount', { clickCount: 3 });
await page.fill('#targetAmount', String(AMOUNT));
await page.dispatchEvent('#targetAmount', 'input'); await page.waitForTimeout(2000);
const raw = await page.inputValue('#krwAmount');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금scrapeCoinshot function · javascript · L85-L106 (22 LOC)scraper/run-php.js
async function scrapeCoinshot(browser) {
const page = await browser.newPage();
try {
await page.goto('https://coinshot.org/main', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.waitForSelector('button.lang-btn[value="ko"]', { timeout: 10000 });
await page.click('button.lang-btn[value="ko"]'); await page.waitForTimeout(1000);
await page.click('#current-receiving-currency'); await page.waitForTimeout(500);
await page.click('#select-receiving-currency a[data-currency="PHP"]');
await page.waitForTimeout(1000);
await page.click('#receiving-input', { clickCount: 3 });
await page.fill('#receiving-input', String(AMOUNT));
await page.press('#receiving-input', 'Enter'); await page.waitForTimeout(3000);
const raw = await page.inputValue('#sending-input');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const feeRaw = await page.locator('h5.text-left').textContent().cascrapeCross function · javascript · L109-L156 (48 LOC)scraper/run-php.js
async function scrapeCross(browser) {
const page = await browser.newPage();
try {
await page.goto('https://crossenf.com/remittance', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(3000);
// Open receiving currency dropdown (default may show THB or other currency)
const dropdown = page.locator('div.relative').filter({ hasText: /^(THB|IDR|PHP|VND|USD)$/ }).first();
await dropdown.click();
await page.waitForSelector('#aside-root ul', { state: 'visible', timeout: 15000 });
await page.waitForTimeout(500);
// Try multiple selectors for Philippines flag/label
const phSelectors = [
'#aside-root li:has(img[alt="PH flag"])',
'#aside-root li:has(img[alt="Philippines flag"])',
'#aside-root li:has(img[alt="ph flag"])',
'#aside-root li:has-text("PHP")',
'#aside-root li:has-text("Philippines")',
];
let selected = false;
for (const sel of phSelectors) {
const el = page.locator(sel);
if (ascrapeE9pay function · javascript · L159-L185 (27 LOC)scraper/run-php.js
async function scrapeE9pay(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.waitForSelector('#PH_PHP', { state: 'attached', timeout: 10000 });
await page.evaluate(() => {
const radio = document.querySelector('#PH_PHP');
radio.checked = true;
radio.dispatchEvent(new Event('change', { bubbles: true }));
radio.dispatchEvent(new Event('click', { bubbles: true }));
});
await page.waitForTimeout(1000);
await page.click('#reverse'); await page.waitForTimeout(500);
await page.waitForSelector('#receive-money', { timeout: 5000 });
await page.click('#receive-money', { clickCount: 3 });
await page.fill('#receive-money', String(AMOUNT));
await page.dispatchEvent('#receive-money', 'blur'); await page.waitForTimeout(3000);
const raw = await page.$eval('#send-money', el => el.value)scrapeJrf function · javascript · L188-L207 (20 LOC)scraper/run-php.js
async function scrapeJrf(browser) {
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
await page.goto('https://www.jpremit.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(3000);
await page.click('#div_curr', { force: true });
await page.waitForSelector('li#PHP', { state: 'visible', timeout: 10000 });
await page.click('li#PHP'); await page.waitForTimeout(1500);
await page.click('#rec_money', { clickCount: 3 });
await page.fill('#rec_money', String(AMOUNT));
await page.dispatchEvent('#rec_money', 'keyup'); await page.waitForTimeout(3000);
const raw = await page.inputValue('#send_money');
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const fee = 4000;
return { operator: 'JRF', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmt, service_fee: fee, total_sending_amoPowered by Repobility — scan your code at https://repobility.com
scrapeUtransfer function · javascript · L210-L229 (20 LOC)scraper/run-php.js
async function scrapeUtransfer(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.utransfer.com', { waitUntil: 'networkidle', timeout: 30000 });
await page.waitForFunction(() => document.querySelectorAll('select').length >= 2, null, { timeout: 15000 });
await page.locator('select').nth(1).selectOption('PHP');
await page.waitForTimeout(1000);
await page.click('input[name="toAmount"]', { clickCount: 3 });
await page.fill('input[name="toAmount"]', String(AMOUNT));
await page.dispatchEvent('input[name="toAmount"]', 'change');
await page.waitForTimeout(3000);
const raw = await page.inputValue('input[name="fromAmount"]').catch(() => null);
const sendAmt = extractNumber(raw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const feeRaw = await page.locator('.utransfer_fees').textContent().catch(() => null);
const fee = extractNumber(feeRaw) ?? 2500;
return { operator: 'Utransfer', receiving_country: COUscrapeHanpass function · javascript · L232-L249 (18 LOC)scraper/run-php.js
async function scrapeHanpass() {
const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'PHP',
fromCurrencyCode: 'KRW', toCurrencyCode: 'PHP', toCountryCode: 'PH',
memberSeq: '1', lang: 'en' }),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
const total = data.depositAmountIncludingFee;
const fee = data.transferFee ?? 0;
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}main function · javascript · L265-L346 (82 LOC)scraper/run-php.js
async function main() {
const runHour = getRunHour();
console.log(`\n[${new Date().toISOString()}] Philippines PHP 스크래핑 시작 — run_hour: ${runHour}\n`);
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const results = [];
const errors = [];
console.log(` 모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
const settled = await Promise.allSettled(
SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
);
for (let i = 0; i < settled.length; i++) {
const { name } = SCRAPERS[i];
const result = settled[i];
if (result.status === 'fulfilled') {
results.push(result.value);
console.log(` ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원 수수료 ${result.value.service_fee?.toLocaleString()}원 합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
} else {
console.error(` ✗ ${name} 실패: ${result.reason?.message}`);
errors.puscrapeGme function · javascript · L16-L39 (24 LOC)scraper/run-thb.js
async function scrapeGme(browser) {
const page = await browser.newPage();
try {
await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector('#nCountry', { timeout: 10000 });
await page.click('#nCountry');
await page.waitForTimeout(500);
await page.waitForSelector('#CountryValue', { timeout: 5000 });
await page.fill('#CountryValue', 'Thailand');
await page.waitForTimeout(300);
await page.click('#toCurrUl li[data-countrycode="THB"]');
await page.waitForTimeout(1000);
await page.waitForSelector('#recAmt', { timeout: 10000 });
await page.click('#recAmt', { clickCount: 3 });
await page.fill('#recAmt', String(AMOUNT));
await page.dispatchEvent('#recAmt', 'change');
await page.waitForTimeout(3000);
const sendAmtRaw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
const total = extractNumber(sendAmtRaw);
if (!total) throscrapeGmoneytrans function · javascript · L42-L58 (17 LOC)scraper/run-thb.js
async function scrapeGmoneytrans() {
const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
+ `?receive_amount=${AMOUNT}`
+ '&payout_country=Thailand'
+ '&total_collected=0'
+ '&payment_type=Bank+Account'
+ '¤cyType=THB';
const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
const serviceCharge = parseField(text, 'serviceCharge') ?? 2500;
const sendAmount = parseField(text, 'sendAmount');
if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmount, service_fee: serviceCharge,
total_sending_amount: sendAmount + serviceCharge };
}parseField function · javascript · L59-L62 (4 LOC)scraper/run-thb.js
function parseField(text, field) {
const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
return m ? parseFloat(m[1]) : null;
}scrapeWirebarley function · javascript · L65-L103 (39 LOC)scraper/run-thb.js
async function scrapeWirebarley(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.wirebarley.com/ko', { waitUntil: 'networkidle', timeout: 30000 });
await page.waitForTimeout(3000);
await page.locator('#lafc-popup button').click().catch(() => null);
await page.waitForTimeout(1000);
await page.locator('[data-title="currencyToMoneyBox"]').nth(1)
.locator('img[alt="드롭 다운"]').click();
await page.waitForTimeout(2000);
await page.locator('button:has(img[alt="TH"])').click();
await page.waitForTimeout(2000);
await page.locator('[data-title="currencyToMoneyBox"]').nth(1).locator('button').click();
await page.waitForTimeout(500);
await page.locator('input').nth(1).click({ clickCount: 3 });
await page.locatscrapeSentbe function · javascript · L106-L135 (30 LOC)scraper/run-thb.js
async function scrapeSentbe(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.sentbe.com/ko', { waitUntil: 'networkidle', timeout: 30000 });
await page.click('button.close').catch(() => null);
await page.waitForTimeout(300);
await page.click('article.app-download-popup .dim').catch(() => null);
await page.waitForTimeout(500);
await page.waitForSelector('.receiveAmountInput .el-input-group__append', { timeout: 10000 });
await page.click('.receiveAmountInput .el-input-group__append');
await page.waitForTimeout(500);
// 태국 / Thailand 선택
await page.click('.receiveAmountInput .el-select-dropdown__item:has-text("태국")');
await page.waitForTimeout(1000);
await page.click('#receiveAmount', { clickCount: 3 });
Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
scrapeHanpass function · javascript · L138-L155 (18 LOC)scraper/run-thb.js
async function scrapeHanpass() {
const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'THB',
fromCurrencyCode: 'KRW', toCurrencyCode: 'THB', toCountryCode: 'TH',
memberSeq: '1', lang: 'en' }),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
const total = data.depositAmountIncludingFee;
const fee = data.transferFee ?? 0;
if (!total) throw new Error('총 송금액 추출 실패');
return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}scrapeUtransfer function · javascript · L158-L177 (20 LOC)scraper/run-thb.js
async function scrapeUtransfer(browser) {
const page = await browser.newPage();
try {
await page.goto('https://www.utransfer.com', { waitUntil: 'networkidle', timeout: 30000 });
await page.waitForTimeout(2000);
await page.locator('select').nth(1).selectOption('THB');
await page.waitForTimeout(1000);
await page.click('input[name="toAmount"]', { clickCount: 3 });
await page.fill('input[name="toAmount"]', String(AMOUNT));
await page.dispatchEvent('input[name="toAmount"]', 'change');
await page.waitForTimeout(3000);
const sendAmtRaw = await page.inputValue('input[name="fromAmount"]').catch(() => null);
const sendAmt = extractNumber(sendAmtRaw);
if (!sendAmt) throw new Error('총 송금액 추출 실패');
const feeRaw = await page.locator('.utransfer_fees').textContent().catch(() => null);
const fee = extractNumber(feeRaw) ?? 5000;
return { operator: 'Utransfer', receiving_country: COUNTRY, receive_amount: AMOUNT,
send_amount_krw: sendAmt, scrapeSbi function · javascript · L180-L206 (27 LOC)scraper/run-thb.js
async function scrapeSbi(browser) {
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
locale: 'ko-KR',
});
const page = await context.newPage();
try {
await page.goto('https://www.sbicosmoney.com/', { waitUntil: 'load', timeout: 30000 });
await page.waitForTimeout(2000);
await page.click('button:has-text("Close")').catch(() => null);
await page.waitForTimeout(500);
await page.click('.dest-country');
await page.waitForTimeout(500);
await page.click('a[data-currency="THB"]');
await page.waitForTimeout(1500);
await page.click('#targetAmount', { clickCount: 3 });
await page.fill('#targetAmount', String(AMOUNT));
await page.dispatchEvent('#targetAmount', 'input');
await page.waitForTimeout(2000);
const sendAmtRaw = await page.inputValue('#krwAmount');
const sendAmt = extractNumber(sendAmtRaw);
if (!