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'; }