Files
tomtomgames/api/purchase.php
T
2026-05-22 12:52:50 +00:00

164 lines
7.7 KiB
PHP

<?php
require_once __DIR__ . '/../../includes/auth.php';
require_once __DIR__ . '/../../includes/square.php';
header('Content-Type: application/json');
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
$data = json_decode(file_get_contents('php://input'), true);
$sourceId = trim($data['source_id'] ?? '');
$tokens = (int)($data['tokens'] ?? 0);
$priceCents = (int)($data['price_cents'] ?? 0);
$method = trim($data['method'] ?? 'card');
$platformId = trim($data['platform_id'] ?? '');
$gameAlias = trim($data['game_alias'] ?? '');
$playerName = trim($data['player_name'] ?? '');
$isCustom = (bool)($data['is_custom'] ?? false);
$billing = $data['billing'] ?? [];
$userId = $_SESSION['user_id'];
// ─── Validate ─────────────────────────────────────────────
if ($tokens < 1 || $priceCents < 100) {
echo json_encode(['success'=>false,'error'=>'Minimum purchase is $1 (1 token).']); exit;
}
// Validate preset packages (custom bypasses preset check)
if (!$isCustom) {
$packages = json_decode(TOKEN_PACKAGES, true);
$validPkg = false;
foreach ($packages as $pkg) {
if ($pkg['tokens'] === $tokens && ($pkg['price'] * 100) === $priceCents) { $validPkg = true; break; }
}
if (!$validPkg) {
echo json_encode(['success'=>false,'error'=>'Invalid token package.']); exit;
}
} else {
// Custom: tokens must equal dollars (1:1 ratio), cap at $500
if ($tokens !== ($priceCents / 100) || $tokens > 500) {
echo json_encode(['success'=>false,'error'=>'Invalid custom amount.']); exit;
}
}
// Sanitize billing fields
$billingFirst = substr(trim($billing['first_name'] ?? ''), 0, 80);
$billingLast = substr(trim($billing['last_name'] ?? ''), 0, 80);
$billingAddress = substr(trim($billing['address'] ?? ''), 0, 200);
$billingCity = substr(trim($billing['city'] ?? ''), 0, 80);
$billingState = strtoupper(substr(trim($billing['state'] ?? ''), 0, 2));
$billingZip = substr(trim($billing['zip'] ?? ''), 0, 10);
$billingEmail = substr(strtolower(trim($billing['email'] ?? '')), 0, 150);
$cardholderName = trim("$billingFirst $billingLast");
$isManual = in_array($method, ['venmo','chime','cashapp','zelle']);
// ─── Manual payment ────────────────────────────────────────
if ($isManual) {
$stmt = db()->prepare("
INSERT INTO token_purchases
(user_id, tokens, amount_cents, payment_method, platform_id, game_alias,
player_name, billing_name, billing_email, is_custom, status)
VALUES (?,?,?,?,?,?,?,?,?,?,'pending')
");
$stmt->execute([
$userId, $tokens, $priceCents, $method, $platformId, $gameAlias,
$playerName, $cardholderName, $billingEmail, $isCustom ? 1 : 0
]);
$pid = db()->lastInsertId();
logActivity('manual_payment_pending', $userId, null, 'purchase', 0, "Manual payment pending: {$paymentMethod} \${$amountDollars}");
echo json_encode(['success'=>true,'manual'=>true,'purchase_id'=>$pid,
'message'=>"Request #{$pid} submitted! Tokens credited after payment verification."]);
exit;
}
// ─── Card payment via Square ───────────────────────────────
if (empty($sourceId)) { echo json_encode(['success'=>false,'error'=>'Card payment token required.']); exit; }
$square = new SquarePayment();
$note = "TomGames {$tokens}tok | {$platformId} | {$gameAlias} | user#{$userId}" . ($isCustom ? ' [CUSTOM]' : '');
// Build buyer info for Square
$buyerInfo = [];
if ($cardholderName) $buyerInfo['buyer_email_address'] = $billingEmail ?: null;
$billingAddr = [];
if ($billingAddress) $billingAddr['address_line_1'] = $billingAddress;
if ($billingCity) $billingAddr['locality'] = $billingCity;
if ($billingState) $billingAddr['administrative_district_level_1'] = $billingState;
if ($billingZip) $billingAddr['postal_code'] = $billingZip;
$billingAddr['country'] = 'US';
$result = $square->charge($sourceId, $priceCents, $note, $cardholderName, $billingAddr, $billingEmail);
if (!$result['success']) {
// Log failed attempt
db()->prepare("INSERT INTO token_purchases (user_id,tokens,amount_cents,payment_method,platform_id,game_alias,player_name,billing_name,billing_email,is_custom,status,failure_reason) VALUES (?,?,?,'card',?,?,?,?,?,?,'failed',?)")
->execute([$userId,$tokens,$priceCents,$platformId,$gameAlias,$playerName,$cardholderName,$billingEmail,$isCustom?1:0,$result['error']]);
echo json_encode(['success'=>false,'error'=>$result['error']]); exit;
}
// ─── Credit tokens ─────────────────────────────────────────
db()->beginTransaction();
try {
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$tokens,$userId]);
$cardBrand = $result['card_brand'] ?? null;
$cardLast4 = $result['last_4'] ?? null;
$receiptUrl= $result['receipt_url'] ?? null;
db()->prepare("
INSERT INTO token_purchases
(user_id, tokens, amount_cents, payment_method, square_payment_id,
platform_id, game_alias, player_name, billing_name, billing_address,
billing_city, billing_state, billing_zip, billing_email,
is_custom, card_brand, card_last4, receipt_url, status)
VALUES (?,?,?,'card',?,?,?,?,?,?,?,?,?,?,?,?,?,?,'completed')
")->execute([
$userId, $tokens, $priceCents, $result['payment_id'],
$platformId, $gameAlias, $playerName,
$cardholderName, $billingAddress, $billingCity, $billingState, $billingZip, $billingEmail,
$isCustom ? 1 : 0, $cardBrand, $cardLast4, $receiptUrl
]);
db()->commit();
} catch (Exception $e) {
db()->rollBack();
echo json_encode(['success'=>false,'error'=>'Token credit failed. Payment ID: '.$result['payment_id']]); exit;
}
// ─── Update saved billing (separate try — must NOT roll back token credit) ──
try {
db()->prepare("
INSERT INTO saved_billing (user_id,first_name,last_name,email,address,city,state,zip,card_brand,card_last4)
VALUES (?,?,?,?,?,?,?,?,?,?)
ON DUPLICATE KEY UPDATE
card_brand=VALUES(card_brand), card_last4=VALUES(card_last4),
first_name=COALESCE(NULLIF(VALUES(first_name),''),first_name),
last_name=COALESCE(NULLIF(VALUES(last_name),''),last_name),
email=COALESCE(NULLIF(VALUES(email),''),email),
address=COALESCE(NULLIF(VALUES(address),''),address),
city=COALESCE(NULLIF(VALUES(city),''),city),
state=COALESCE(NULLIF(VALUES(state),''),state),
zip=COALESCE(NULLIF(VALUES(zip),''),zip)
")->execute([
$userId, $billingFirst, $billingLast, $billingEmail,
$billingAddress, $billingCity, $billingState, $billingZip,
$cardBrand, $cardLast4
]);
} catch (Exception $e) { /* non-critical — tokens already credited */ }
$bal = db()->prepare("SELECT tokens FROM users WHERE id=?");
$bal->execute([$userId]);
$newBal = (float)$bal->fetchColumn();
logActivity('token_purchase', $userId, null, 'purchase', (int)db()->lastInsertId(),
"Bought {$tokens} tokens via {$paymentMethod} for \${$amountDollars}");
echo json_encode([
'success' => true,
'manual' => false,
'tokens_added' => (int)$tokens,
'new_balance' => $newBal,
'payment_id' => $result['payment_id'],
'card_brand' => $cardBrand,
'card_last4' => $cardLast4,
'receipt_url' => $receiptUrl,
]);