Files
2026-05-16 23:00:37 -05:00

215 lines
6.5 KiB
PHP

<?php
/**
* Tom's Java Jive - Stripe Integration (cURL-based for vanilla PHP)
* Works without Composer - uses direct API calls
*/
class StripeAPI {
private $secretKey;
private $apiBase = 'https://api.stripe.com/v1';
public function __construct($secretKey = null) {
$this->secretKey = $secretKey ?: STRIPE_SECRET_KEY;
}
/**
* Make a cURL request to Stripe API
*/
private function request($method, $endpoint, $data = []) {
$url = $this->apiBase . $endpoint;
$ch = curl_init();
$headers = [
'Authorization: Bearer ' . $this->secretKey,
'Content-Type: application/x-www-form-urlencoded'
];
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
} elseif ($method === 'GET' && !empty($data)) {
$url .= '?' . http_build_query($data);
curl_setopt($ch, CURLOPT_URL, $url);
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception('Stripe API Error: ' . $error);
}
$decoded = json_decode($response, true);
if ($httpCode >= 400) {
$errorMsg = $decoded['error']['message'] ?? 'Unknown Stripe error';
throw new Exception($errorMsg);
}
return $decoded;
}
/**
* Create a Payment Intent
*/
public function createPaymentIntent($amount, $currency = 'usd', $options = []) {
$data = [
'amount' => (int)($amount * 100), // Convert to cents
'currency' => strtolower($currency),
'automatic_payment_methods' => ['enabled' => 'true']
];
if (!empty($options['metadata'])) {
foreach ($options['metadata'] as $key => $value) {
$data["metadata[$key]"] = $value;
}
}
if (!empty($options['receipt_email'])) {
$data['receipt_email'] = $options['receipt_email'];
}
if (!empty($options['description'])) {
$data['description'] = $options['description'];
}
return $this->request('POST', '/payment_intents', $data);
}
/**
* Retrieve a Payment Intent
*/
public function getPaymentIntent($paymentIntentId) {
return $this->request('GET', '/payment_intents/' . $paymentIntentId);
}
/**
* Create a Checkout Session (hosted payment page)
*/
public function createCheckoutSession($lineItems, $successUrl, $cancelUrl, $options = []) {
$data = [
'mode' => $options['mode'] ?? 'payment',
'success_url' => $successUrl,
'cancel_url' => $cancelUrl
];
// Add line items
foreach ($lineItems as $i => $item) {
$data["line_items[$i][price_data][currency]"] = $item['currency'] ?? 'usd';
$data["line_items[$i][price_data][product_data][name]"] = $item['name'];
$data["line_items[$i][price_data][unit_amount]"] = (int)($item['price'] * 100);
$data["line_items[$i][quantity]"] = $item['quantity'] ?? 1;
if (!empty($item['description'])) {
$data["line_items[$i][price_data][product_data][description]"] = $item['description'];
}
}
if (!empty($options['customer_email'])) {
$data['customer_email'] = $options['customer_email'];
}
if (!empty($options['metadata'])) {
foreach ($options['metadata'] as $key => $value) {
$data["metadata[$key]"] = $value;
}
}
return $this->request('POST', '/checkout/sessions', $data);
}
/**
* Retrieve a Checkout Session
*/
public function getCheckoutSession($sessionId) {
return $this->request('GET', '/checkout/sessions/' . $sessionId);
}
/**
* Verify webhook signature
*/
public function verifyWebhookSignature($payload, $sigHeader, $webhookSecret) {
if (empty($webhookSecret)) {
return true; // Skip verification if secret not configured
}
$elements = explode(',', $sigHeader);
$timestamp = null;
$signatures = [];
foreach ($elements as $element) {
$parts = explode('=', $element, 2);
if (count($parts) === 2) {
if ($parts[0] === 't') {
$timestamp = $parts[1];
} elseif ($parts[0] === 'v1') {
$signatures[] = $parts[1];
}
}
}
if (empty($timestamp) || empty($signatures)) {
throw new Exception('Invalid signature format');
}
// Check timestamp tolerance (5 minutes)
if (abs(time() - $timestamp) > 300) {
throw new Exception('Timestamp outside tolerance');
}
$signedPayload = $timestamp . '.' . $payload;
$expectedSignature = hash_hmac('sha256', $signedPayload, $webhookSecret);
foreach ($signatures as $sig) {
if (hash_equals($expectedSignature, $sig)) {
return true;
}
}
throw new Exception('Signature verification failed');
}
/**
* Create a refund
*/
public function createRefund($paymentIntentId, $amount = null) {
$data = ['payment_intent' => $paymentIntentId];
if ($amount !== null) {
$data['amount'] = (int)($amount * 100);
}
return $this->request('POST', '/refunds', $data);
}
}
/**
* Get Stripe instance
*/
function stripe() {
static $stripe = null;
if ($stripe === null) {
$stripe = new StripeAPI();
}
return $stripe;
}
/**
* Check if Stripe is properly configured
*/
function isStripeConfigured() {
return !empty(STRIPE_SECRET_KEY) &&
STRIPE_SECRET_KEY !== 'sk_test_your_stripe_key' &&
!empty(STRIPE_PUBLISHABLE_KEY) &&
STRIPE_PUBLISHABLE_KEY !== 'pk_test_your_stripe_key';
}