mirror of
https://github.com/myronblair/tomsjavajive-app
synced 2026-06-30 17:50:56 -05:00
215 lines
6.5 KiB
PHP
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';
|
|
}
|