#!/usr/bin/env python3 """ UpCloud card verification via API — password login + Braintree tokenize + verify. This is the working pipeline for verifying a card on an existing UpCloud account. Usage: python upcloud_verify_card.py Prerequisites: - Anti-captcha API key with balance - UpCloud account already registered (username + password) - Email IMAP credentials for OTP fallback - Card details (number, exp month/year, CVV) Flow: 1. Solve hCaptcha (login sitekey) 2. Password login via /v2/session 3. Get Braintree client token 4. Tokenize card via Braintree GraphQL 5. Verify card via /v2/account/main/verify (tries EUR/DE first, then USD/US) 6. On success: extract API token, try server creation If card is UA-BIN, Braintree will reject with 422 VERIFICATION_ERROR before 3DS. """ import requests, json, time, base64, re GATE = 'https://gate.upcloud.com' HUB = 'https://hub.upcloud.com' AC_KEY = 'YOUR_ANTICAPTCHA_KEY' # === CONFIG === USERNAME = 'upXXXXXXXX' PASSWORD = 'your_password' EMAIL = '[REDACTED-EMAIL]' EMAIL_PASS = 'your_email_password' CARD = '0000000000000000' EXP_M = 'MM' EXP_Y = 'YYYY' CVV = 'NNN' IMAP_HOST = 'secureimap.t-online.de' IMAP_PORT = 993 UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' SITEKEY = '13b63774-5d8c-4b1a-9cfc-8bae5b1abca0' def solve_captcha(): r = requests.post('https://api.anti-captcha.com/createTask', json={ 'clientKey': AC_KEY, 'task': {'type': 'HCaptchaTaskProxyless', 'websiteURL': HUB, 'websiteKey': SITEKEY, 'isInvisible': True, 'isEnterprise': True} }, timeout=30) d = r.json() tid = d.get('taskId') if not tid: return None for i in range(80): time.sleep(2) r = requests.post('https://api.anti-captcha.com/getTaskResult', json={'clientKey': AC_KEY, 'taskId': tid}, timeout=15) d = r.json() if d.get('status') == 'ready': return d['solution']['gRecaptchaResponse'] if d.get('errorId'): return None return None def get_otp(timeout=120): import imaplib, email as emaillib t0 = time.time() while time.time() - t0 < timeout: try: m = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT) m.login(EMAIL, EMAIL_PASS) m.select('INBOX') _, data = m.search(None, '(FROM "upcloud" UNSEEN)') ids = data[0].split() if ids: _, msg_data = m.fetch(ids[-1], '(RFC822)') msg = emaillib.message_from_bytes(msg_data[0][1]) body = '' if msg.is_multipart(): for part in msg.walk(): ct = part.get_content_type() if ct in ('text/plain', 'text/html'): body = part.get_payload(decode=True).decode(errors='replace') break else: body = msg.get_payload(decode=True).decode(errors='replace') match = re.search(r'\*([A-Z0-9]{4,10})\*', body) if match: m.logout() return match.group(1) match = re.search(r'\b(\d{6})\b', body) if match: m.logout() return match.group(1) m.logout() except: pass time.sleep(5) return None def main(): s = requests.Session() s.headers.update({ 'User-Agent': UA, 'Origin': 'https://hub.upcloud.com', 'Referer': 'https://hub.upcloud.com/', 'Content-Type': 'application/json' }) # 1. Password login print('[1] Password login...') t1 = solve_captcha() if not t1: return False r = s.post(f'{GATE}/v2/session', json={ 'captcha_token': t1, 'username': USERNAME, 'password': PASSWORD, 'token': True }, timeout=15) print(f' Login: {r.status_code}') if r.status_code == 422: print(' Captcha rejected, retry...') t1 = solve_captcha() if not t1: return False r = s.post(f'{GATE}/v2/session', json={ 'captcha_token': t1, 'username': USERNAME, 'password': PASSWORD, 'token': True }, timeout=15) print(f' Login retry: {r.status_code}') if r.status_code != 200: print(f' {r.text[:200]}') return False # Token is in cookies, not JSON auth_token = [REDACTED]'auth-token', '') if auth_token: [REDACTED]'Authorization': f'Bearer {auth_token}'}) r = s.get(f'{GATE}/v2/account', timeout=15) print(f' Account: {r.status_code}') if r.status_code != 200: return False acct = r.json().get('account', {}) print(f' active={acct.get("active")} credits={acct.get("credits")}') # 2. Braintree print('[2] Braintree...') r = s.get(f'{GATE}/v2/braintree/client_token/EUR', timeout=15) if r.status_code != 200: r = s.get(f'{GATE}/v2/braintree/client_token/USD', timeout=15) if r.status_code != 200: print(f' FAIL: {r.text[:200]}') return False dec = json.loads(base64.b64decode(r.json()['client_token']).decode()) afp = dec['authorizationFingerprint'] gql = dec['graphQL']['url'] # 3. Tokenize print('[3] Tokenize card...') mut = 'mutation T($input: TokenizeCreditCardInput!){tokenizeCreditCard(input:$input){token}}' r = requests.post(gql, json={"query": mut, "variables": { "input": {"creditCard": {"number": CARD, "expirationMonth": EXP_M, "expirationYear": EXP_Y, "cvv": CVV}, "options": {"validate": False}} }}, headers={"Authorization": f"Bearer {afp}", "Content-Type": "application/json", "Braintree-Version": "2023-08-01"}, timeout=30) resp = r.json() if 'errors' in resp: print(f' ERR: {resp["errors"]}') return False nonce = resp["data"]["tokenizeCreditCard"]["token"] print(f' Nonce: {nonce}') # 4. Verify (rate limit: wait 10s between attempts, max 2-3) print('[4] Verify card...') for curr, country, city, addr, postal, state in [ ('EUR', 'DEU', 'Berlin', 'Hauptstrasse 22', '10115', ''), ('USD', 'USA', 'New York', '350 Fifth Avenue', '10118', 'NY'), ]: time.sleep(10) print(f' {curr}/{country}...') v = {"account": {"address": addr, "city": city, "country": country, "first_name": "signup", "last_name": "user", "payment_method_nonce": nonce, "postal_code": postal, "company": "", "currency": curr, "device_data": json.dumps({"correlation_id": str(int(time.time()))}), "state": state, "vat_number": ""}} r = s.post(f'{GATE}/v2/account/main/verify', json=v, timeout=30) print(f' {r.status_code} {r.text[:400]}') if r.status_code in [200, 201]: print('\n=== VERIFIED! ===') r = s.get(f'{GATE}/v2/account', timeout=15) a = r.json().get('account', {}) print(f' active={a.get("active")} trial_started={a.get("trial_started")}') r = s.post(f'{GATE}/v2/account/tokens', json={ "name": "eni-prod", "expires_at": "2027-09-01T00:00:00.00001Z" }, timeout=15) tok = r.json().get('token', {}).get('token', '') if r.status_code == 201 else '' print(f' API token: {tok}') return True print('\n[FAIL] Card rejected or rate limited') return False if __name__ == '__main__': main()