092a8bc7ce
- Removed all backup/duplicate files - Removed test files from root - Consolidated documentation to /docs/ - Moved scripts to /scripts/ - Renamed f_* directories (removed prefix) - Organized icons and assets - Removed unused vendor directories - Cleaned up redundant config files
813 lines
32 KiB
PHP
813 lines
32 KiB
PHP
<?php
|
|
/*******************************************************************************************************************
|
|
| Rainforest Pay Integration for EasyStream
|
|
| Complete payment processing with Rainforest Pay API
|
|
|*******************************************************************************************************************/
|
|
|
|
define('_ISVALID', true);
|
|
include_once '../../../f_core/config.core.php';
|
|
|
|
class RainforestPayHandler {
|
|
private $config;
|
|
private $class_database;
|
|
private $api_base_url;
|
|
private $headers;
|
|
|
|
public function __construct($class_database) {
|
|
$this->class_database = $class_database;
|
|
$this->config = require __DIR__ . '/config.rainforest.php';
|
|
|
|
$env = $this->config['rainforest']['environment'];
|
|
$this->api_base_url = $this->config['rainforest']['api_base_url'][$env];
|
|
|
|
$this->headers = [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $this->config['rainforest']['api_key'],
|
|
'X-Merchant-ID: ' . $this->config['rainforest']['merchant_id']
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Create a donation payment
|
|
*/
|
|
public function createDonation($streamer_id, $amount, $donor_name = '', $message = '', $payment_method = 'card') {
|
|
try {
|
|
// Validate amount
|
|
if ($amount < $this->config['rainforest']['min_donation'] ||
|
|
$amount > $this->config['rainforest']['max_donation']) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Invalid donation amount. Must be between $' .
|
|
$this->config['rainforest']['min_donation'] . ' and $' .
|
|
$this->config['rainforest']['max_donation']
|
|
];
|
|
}
|
|
|
|
// Get streamer information
|
|
$streamer = $this->getStreamerInfo($streamer_id);
|
|
if (!$streamer) {
|
|
return ['success' => false, 'message' => 'Streamer not found'];
|
|
}
|
|
|
|
// Calculate fees
|
|
$platform_fee = $this->calculatePlatformFee($amount);
|
|
$streamer_amount = $amount - $platform_fee;
|
|
|
|
// Create payment request
|
|
$payment_data = [
|
|
'amount' => $amount * 100, // Convert to cents
|
|
'currency' => $this->config['rainforest']['currency'],
|
|
'payment_method' => $payment_method,
|
|
'description' => "Donation to {$streamer['username']}",
|
|
'metadata' => [
|
|
'type' => 'donation',
|
|
'streamer_id' => $streamer_id,
|
|
'streamer_username' => $streamer['username'],
|
|
'donor_name' => $donor_name,
|
|
'message' => $message,
|
|
'platform_fee' => $platform_fee,
|
|
'streamer_amount' => $streamer_amount
|
|
],
|
|
'webhook_url' => $this->config['rainforest']['webhook_url'],
|
|
'return_url' => "https://{$_SERVER['HTTP_HOST']}/donations/success",
|
|
'cancel_url' => "https://{$_SERVER['HTTP_HOST']}/donations/cancel"
|
|
];
|
|
|
|
// Make API request
|
|
$response = $this->makeApiRequest('POST', '/payments', $payment_data);
|
|
|
|
if ($response && isset($response['id'])) {
|
|
// Store pending donation in database
|
|
$donation_id = $this->storePendingDonation([
|
|
'streamer_id' => $streamer_id,
|
|
'amount' => $amount,
|
|
'platform_fee' => $platform_fee,
|
|
'streamer_amount' => $streamer_amount,
|
|
'donor_name' => $donor_name,
|
|
'message' => $message,
|
|
'payment_method' => $payment_method,
|
|
'rainforest_payment_id' => $response['id'],
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
return [
|
|
'success' => true,
|
|
'payment_id' => $response['id'],
|
|
'donation_id' => $donation_id,
|
|
'payment_url' => $response['payment_url'] ?? null,
|
|
'message' => 'Payment created successfully'
|
|
];
|
|
} else {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Failed to create payment: ' . ($response['error'] ?? 'Unknown error')
|
|
];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Create Donation Error', $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error processing donation: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a payment (supports both donations and token purchases)
|
|
*/
|
|
public function createPayment($payment_data) {
|
|
try {
|
|
// Make API request
|
|
$response = $this->makeApiRequest('POST', '/payments', $payment_data);
|
|
|
|
if ($response && isset($response['id'])) {
|
|
return [
|
|
'success' => true,
|
|
'payment_id' => $response['id'],
|
|
'payment_url' => $response['payment_url'] ?? null,
|
|
'message' => 'Payment created successfully'
|
|
];
|
|
} else {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Failed to create payment: ' . ($response['error'] ?? 'Unknown error')
|
|
];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Create Payment Error', $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error creating payment: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a payout (for token redemptions)
|
|
*/
|
|
public function createPayout($payout_data) {
|
|
try {
|
|
// Make API request
|
|
$response = $this->makeApiRequest('POST', '/payouts', $payout_data);
|
|
|
|
if ($response && isset($response['id'])) {
|
|
return [
|
|
'success' => true,
|
|
'payout_id' => $response['id'],
|
|
'message' => 'Payout created successfully'
|
|
];
|
|
} else {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Failed to create payout: ' . ($response['error'] ?? 'Unknown error')
|
|
];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Create Payout Error', $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error creating payout: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process webhook from Rainforest Pay
|
|
*/
|
|
public function processWebhook($payload, $signature) {
|
|
try {
|
|
// Verify webhook signature
|
|
if (!$this->verifyWebhookSignature($payload, $signature)) {
|
|
return ['success' => false, 'message' => 'Invalid webhook signature'];
|
|
}
|
|
|
|
$data = json_decode($payload, true);
|
|
if (!$data) {
|
|
return ['success' => false, 'message' => 'Invalid webhook payload'];
|
|
}
|
|
|
|
$event_type = $data['event'] ?? '';
|
|
$payment_data = $data['data'] ?? [];
|
|
|
|
switch ($event_type) {
|
|
case 'payment.completed':
|
|
return $this->handlePaymentCompleted($payment_data);
|
|
|
|
case 'payment.failed':
|
|
return $this->handlePaymentFailed($payment_data);
|
|
|
|
case 'payment.cancelled':
|
|
return $this->handlePaymentCancelled($payment_data);
|
|
|
|
case 'payout.completed':
|
|
return $this->handlePayoutCompleted($payment_data);
|
|
|
|
case 'payout.failed':
|
|
return $this->handlePayoutFailed($payment_data);
|
|
|
|
default:
|
|
$this->logError('Unknown Webhook Event', $event_type);
|
|
return ['success' => false, 'message' => 'Unknown event type'];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Webhook Processing Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Webhook processing failed'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle completed payment
|
|
*/
|
|
private function handlePaymentCompleted($payment_data) {
|
|
$payment_id = $payment_data['id'];
|
|
$metadata = $payment_data['metadata'] ?? [];
|
|
$payment_type = $metadata['type'] ?? 'donation';
|
|
|
|
try {
|
|
if ($payment_type === 'token_purchase') {
|
|
return $this->handleTokenPurchaseCompleted($payment_data, $metadata);
|
|
} else {
|
|
// Handle regular donation
|
|
$sql = "UPDATE donations SET
|
|
status = 'completed',
|
|
completed_at = NOW(),
|
|
rainforest_data = ?
|
|
WHERE rainforest_payment_id = ?";
|
|
|
|
$this->class_database->execute($sql, [
|
|
json_encode($payment_data),
|
|
$payment_id
|
|
]);
|
|
|
|
// Update streamer balance
|
|
if (isset($metadata['streamer_id']) && isset($metadata['streamer_amount'])) {
|
|
$this->updateStreamerBalance($metadata['streamer_id'], $metadata['streamer_amount']);
|
|
}
|
|
|
|
// Send notification to streamer
|
|
$this->sendDonationNotification($metadata);
|
|
|
|
return ['success' => true, 'message' => 'Payment completed successfully'];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Payment Completion Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error updating payment status'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle completed token purchase
|
|
*/
|
|
private function handleTokenPurchaseCompleted($payment_data, $metadata) {
|
|
$payment_id = $payment_data['id'];
|
|
$purchase_id = $metadata['purchase_id'] ?? null;
|
|
$user_id = $metadata['user_id'] ?? null;
|
|
$token_amount = $metadata['token_amount'] ?? 0;
|
|
|
|
if (!$purchase_id || !$user_id || !$token_amount) {
|
|
return ['success' => false, 'message' => 'Invalid token purchase metadata'];
|
|
}
|
|
|
|
try {
|
|
$this->class_database->beginTransaction();
|
|
|
|
// Update purchase record
|
|
$sql = "UPDATE token_purchases SET status = 'completed', updated_at = NOW() WHERE id = ? AND payment_id = ?";
|
|
$this->class_database->execute($sql, [$purchase_id, $payment_id]);
|
|
|
|
// Add tokens to user balance
|
|
require_once '../../../f_core/f_classes/class.token.php';
|
|
VToken::recordTransaction($user_id, $token_amount, 'purchase', "Token purchase #{$purchase_id}");
|
|
|
|
$this->class_database->commitTransaction();
|
|
|
|
$this->logInfo('Token Purchase Completed', "Purchase ID: {$purchase_id}, User: {$user_id}, Tokens: {$token_amount}");
|
|
|
|
return ['success' => true, 'message' => 'Token purchase completed successfully'];
|
|
|
|
} catch (Exception $e) {
|
|
$this->class_database->rollbackTransaction();
|
|
$this->logError('Token Purchase Completion Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error completing token purchase'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle failed payment
|
|
*/
|
|
private function handlePaymentFailed($payment_data) {
|
|
$payment_id = $payment_data['id'];
|
|
|
|
$sql = "UPDATE donations SET
|
|
status = 'failed',
|
|
failed_at = NOW(),
|
|
failure_reason = ?,
|
|
rainforest_data = ?
|
|
WHERE rainforest_payment_id = ?";
|
|
|
|
$this->class_database->execute($sql, [
|
|
$payment_data['failure_reason'] ?? 'Payment failed',
|
|
json_encode($payment_data),
|
|
$payment_id
|
|
]);
|
|
|
|
return ['success' => true, 'message' => 'Payment failure recorded'];
|
|
}
|
|
|
|
/**
|
|
* Handle completed payout
|
|
*/
|
|
private function handlePayoutCompleted($payout_data) {
|
|
$payout_id = $payout_data['id'];
|
|
$metadata = $payout_data['metadata'] ?? [];
|
|
$payout_type = $metadata['type'] ?? 'streamer_payout';
|
|
|
|
try {
|
|
if ($payout_type === 'token_redemption') {
|
|
return $this->handleTokenRedemptionCompleted($payout_data, $metadata);
|
|
} else {
|
|
// Handle regular streamer payout
|
|
$sql = "UPDATE payouts SET
|
|
status = 'completed',
|
|
completed_at = NOW(),
|
|
rainforest_data = ?
|
|
WHERE rainforest_payout_id = ?";
|
|
|
|
$this->class_database->execute($sql, [
|
|
json_encode($payout_data),
|
|
$payout_id
|
|
]);
|
|
|
|
return ['success' => true, 'message' => 'Payout completed successfully'];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Payout Completion Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error updating payout status'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle failed payout
|
|
*/
|
|
private function handlePayoutFailed($payout_data) {
|
|
$payout_id = $payout_data['id'];
|
|
$metadata = $payout_data['metadata'] ?? [];
|
|
$payout_type = $metadata['type'] ?? 'streamer_payout';
|
|
|
|
try {
|
|
if ($payout_type === 'token_redemption') {
|
|
return $this->handleTokenRedemptionFailed($payout_data, $metadata);
|
|
} else {
|
|
// Handle regular streamer payout failure
|
|
$sql = "UPDATE payouts SET
|
|
status = 'failed',
|
|
failed_at = NOW(),
|
|
failure_reason = ?,
|
|
rainforest_data = ?
|
|
WHERE rainforest_payout_id = ?";
|
|
|
|
$this->class_database->execute($sql, [
|
|
$payout_data['failure_reason'] ?? 'Payout failed',
|
|
json_encode($payout_data),
|
|
$payout_id
|
|
]);
|
|
|
|
return ['success' => true, 'message' => 'Payout failure recorded'];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Payout Failure Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error updating payout status'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle completed token redemption
|
|
*/
|
|
private function handleTokenRedemptionCompleted($payout_data, $metadata) {
|
|
$payout_id = $payout_data['id'];
|
|
$redemption_id = $metadata['redemption_id'] ?? null;
|
|
$user_id = $metadata['user_id'] ?? null;
|
|
|
|
if (!$redemption_id || !$user_id) {
|
|
return ['success' => false, 'message' => 'Invalid token redemption metadata'];
|
|
}
|
|
|
|
try {
|
|
$this->class_database->beginTransaction();
|
|
|
|
// Update payout record
|
|
$sql = "UPDATE token_payouts SET status = 'completed', completed_at = NOW() WHERE rainforest_payout_id = ?";
|
|
$this->class_database->execute($sql, [$payout_id]);
|
|
|
|
// Update redemption record
|
|
$sql = "UPDATE token_redemptions SET status = 'completed', completed_at = NOW() WHERE id = ?";
|
|
$this->class_database->execute($sql, [$redemption_id]);
|
|
|
|
$this->class_database->commitTransaction();
|
|
|
|
$this->logInfo('Token Redemption Completed', "Redemption ID: {$redemption_id}, User: {$user_id}");
|
|
|
|
return ['success' => true, 'message' => 'Token redemption completed successfully'];
|
|
|
|
} catch (Exception $e) {
|
|
$this->class_database->rollbackTransaction();
|
|
$this->logError('Token Redemption Completion Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error completing token redemption'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle failed token redemption
|
|
*/
|
|
private function handleTokenRedemptionFailed($payout_data, $metadata) {
|
|
$payout_id = $payout_data['id'];
|
|
$redemption_id = $metadata['redemption_id'] ?? null;
|
|
$user_id = $metadata['user_id'] ?? null;
|
|
$token_amount = $metadata['token_amount'] ?? 0;
|
|
|
|
if (!$redemption_id || !$user_id || !$token_amount) {
|
|
return ['success' => false, 'message' => 'Invalid token redemption metadata'];
|
|
}
|
|
|
|
try {
|
|
$this->class_database->beginTransaction();
|
|
|
|
// Update payout record
|
|
$sql = "UPDATE token_payouts SET status = 'failed', failed_at = NOW(), failure_reason = ? WHERE rainforest_payout_id = ?";
|
|
$this->class_database->execute($sql, [
|
|
$payout_data['failure_reason'] ?? 'Payout failed',
|
|
$payout_id
|
|
]);
|
|
|
|
// Update redemption record
|
|
$sql = "UPDATE token_redemptions SET status = 'failed', failed_at = NOW() WHERE id = ?";
|
|
$this->class_database->execute($sql, [$redemption_id]);
|
|
|
|
// Refund tokens to user
|
|
require_once '../../../f_core/f_classes/class.token.php';
|
|
VToken::recordTransaction($user_id, $token_amount, 'refund', "Redemption refund #{$redemption_id}");
|
|
|
|
$this->class_database->commitTransaction();
|
|
|
|
$this->logInfo('Token Redemption Failed - Refunded', "Redemption ID: {$redemption_id}, User: {$user_id}, Tokens: {$token_amount}");
|
|
|
|
return ['success' => true, 'message' => 'Token redemption failed - tokens refunded'];
|
|
|
|
} catch (Exception $e) {
|
|
$this->class_database->rollbackTransaction();
|
|
$this->logError('Token Redemption Failure Error', $e->getMessage());
|
|
return ['success' => false, 'message' => 'Error handling token redemption failure'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request payout for streamer
|
|
*/
|
|
public function requestPayout($streamer_id) {
|
|
try {
|
|
$balance = $this->getStreamerBalance($streamer_id);
|
|
|
|
if ($balance < $this->config['rainforest']['min_payout']) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Insufficient balance for payout. Minimum: $' .
|
|
$this->config['rainforest']['min_payout']
|
|
];
|
|
}
|
|
|
|
// Get streamer payout details
|
|
$streamer = $this->getStreamerPayoutInfo($streamer_id);
|
|
if (!$streamer || !$streamer['payout_method']) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Payout method not configured. Please update your payout settings.'
|
|
];
|
|
}
|
|
|
|
// Calculate payout amount after fees
|
|
$fee_amount = $this->calculatePayoutFee($balance);
|
|
$payout_amount = $balance - $fee_amount;
|
|
|
|
// Create payout request
|
|
$payout_data = [
|
|
'amount' => $payout_amount * 100, // Convert to cents
|
|
'currency' => $this->config['rainforest']['currency'],
|
|
'recipient' => [
|
|
'type' => $streamer['payout_method'],
|
|
'details' => json_decode($streamer['payout_details'], true)
|
|
],
|
|
'description' => "Payout to {$streamer['username']}",
|
|
'metadata' => [
|
|
'type' => 'streamer_payout',
|
|
'streamer_id' => $streamer_id,
|
|
'original_balance' => $balance,
|
|
'fee_amount' => $fee_amount,
|
|
'payout_amount' => $payout_amount
|
|
]
|
|
];
|
|
|
|
$response = $this->makeApiRequest('POST', '/payouts', $payout_data);
|
|
|
|
if ($response && isset($response['id'])) {
|
|
// Record payout request
|
|
$this->recordPayoutRequest($streamer_id, $payout_amount, $fee_amount, $response['id']);
|
|
|
|
return [
|
|
'success' => true,
|
|
'payout_id' => $response['id'],
|
|
'amount' => $payout_amount,
|
|
'fee' => $fee_amount,
|
|
'message' => 'Payout request submitted successfully'
|
|
];
|
|
} else {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Failed to create payout: ' . ($response['error'] ?? 'Unknown error')
|
|
];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Payout Request Error', $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error processing payout: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get available payment methods
|
|
*/
|
|
public function getPaymentMethods() {
|
|
$methods = [];
|
|
$config_methods = $this->config['rainforest']['payment_methods'];
|
|
|
|
if ($config_methods['card']) {
|
|
$methods[] = [
|
|
'id' => 'card',
|
|
'name' => 'Credit/Debit Card',
|
|
'icon' => 'credit-card',
|
|
'description' => 'Visa, Mastercard, American Express'
|
|
];
|
|
}
|
|
|
|
if ($config_methods['bank_transfer']) {
|
|
$methods[] = [
|
|
'id' => 'bank_transfer',
|
|
'name' => 'Bank Transfer',
|
|
'icon' => 'bank',
|
|
'description' => 'Direct bank transfer'
|
|
];
|
|
}
|
|
|
|
if ($config_methods['mobile_money']) {
|
|
$methods[] = [
|
|
'id' => 'mobile_money',
|
|
'name' => 'Mobile Money',
|
|
'icon' => 'mobile',
|
|
'description' => 'MTN, Airtel, Vodafone'
|
|
];
|
|
}
|
|
|
|
if ($config_methods['wallet']) {
|
|
$methods[] = [
|
|
'id' => 'wallet',
|
|
'name' => 'Digital Wallet',
|
|
'icon' => 'wallet',
|
|
'description' => 'PayPal, Apple Pay, Google Pay'
|
|
];
|
|
}
|
|
|
|
return $methods;
|
|
}
|
|
|
|
/**
|
|
* Get donation history for streamer
|
|
*/
|
|
public function getDonationHistory($streamer_id, $limit = 20, $offset = 0) {
|
|
$sql = "SELECT d.*, u.usr_user as donor_username
|
|
FROM donations d
|
|
LEFT JOIN db_accountuser u ON d.donor_id = u.usr_id
|
|
WHERE d.streamer_id = ? AND d.status = 'completed'
|
|
ORDER BY d.completed_at DESC
|
|
LIMIT ? OFFSET ?";
|
|
|
|
return $this->class_database->getRows($sql, [$streamer_id, $limit, $offset]);
|
|
}
|
|
|
|
/**
|
|
* Get streamer earnings summary
|
|
*/
|
|
public function getStreamerEarnings($streamer_id, $period = '30_days') {
|
|
$date_condition = $this->getDateCondition($period);
|
|
|
|
$sql = "SELECT
|
|
COUNT(*) as total_donations,
|
|
SUM(amount) as total_amount,
|
|
SUM(streamer_amount) as total_earned,
|
|
SUM(platform_fee) as total_fees,
|
|
AVG(amount) as average_donation
|
|
FROM donations
|
|
WHERE streamer_id = ? AND status = 'completed' AND $date_condition";
|
|
|
|
return $this->class_database->getRow($sql, [$streamer_id]);
|
|
}
|
|
|
|
// Helper Methods
|
|
|
|
private function makeApiRequest($method, $endpoint, $data = null) {
|
|
$url = $this->api_base_url . $endpoint;
|
|
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => $this->headers,
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_SSL_VERIFYPEER => true
|
|
]);
|
|
|
|
if ($method === 'POST' && $data) {
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
}
|
|
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($response === false) {
|
|
throw new Exception('API request failed');
|
|
}
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
if ($http_code >= 400) {
|
|
$this->logError('API Error', "HTTP $http_code: " . ($decoded['message'] ?? $response));
|
|
return ['error' => $decoded['message'] ?? 'API request failed'];
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
private function verifyWebhookSignature($payload, $signature) {
|
|
$expected = hash_hmac('sha256', $payload, $this->config['rainforest']['webhook_secret']);
|
|
return hash_equals($expected, $signature);
|
|
}
|
|
|
|
private function calculatePlatformFee($amount) {
|
|
$percentage_fee = $amount * ($this->config['rainforest']['platform_fee_percentage'] / 100);
|
|
$fixed_fee = $this->config['rainforest']['platform_fee_fixed'];
|
|
return $percentage_fee + $fixed_fee;
|
|
}
|
|
|
|
private function calculatePayoutFee($amount) {
|
|
$percentage_fee = $amount * ($this->config['rainforest']['payout_fee_percentage'] / 100);
|
|
$fixed_fee = $this->config['rainforest']['payout_fee_fixed'];
|
|
return $percentage_fee + $fixed_fee;
|
|
}
|
|
|
|
private function getStreamerInfo($streamer_id) {
|
|
$sql = "SELECT usr_id, usr_user as username, usr_dname as display_name
|
|
FROM db_accountuser WHERE usr_id = ?";
|
|
return $this->class_database->getRow($sql, [$streamer_id]);
|
|
}
|
|
|
|
private function getStreamerBalance($streamer_id) {
|
|
$sql = "SELECT COALESCE(SUM(streamer_amount), 0) as balance
|
|
FROM donations
|
|
WHERE streamer_id = ? AND status = 'completed' AND payout_id IS NULL";
|
|
$result = $this->class_database->getRow($sql, [$streamer_id]);
|
|
return $result['balance'] ?? 0;
|
|
}
|
|
|
|
private function updateStreamerBalance($streamer_id, $amount) {
|
|
// Balance is calculated dynamically, but we can update a cached value if needed
|
|
$sql = "UPDATE db_accountuser SET
|
|
donation_balance = COALESCE(donation_balance, 0) + ?
|
|
WHERE usr_id = ?";
|
|
$this->class_database->execute($sql, [$amount, $streamer_id]);
|
|
}
|
|
|
|
private function storePendingDonation($data) {
|
|
$sql = "INSERT INTO donations (
|
|
streamer_id, amount, platform_fee, streamer_amount, donor_name,
|
|
message, payment_method, rainforest_payment_id, status, created_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
|
|
|
|
$this->class_database->execute($sql, [
|
|
$data['streamer_id'], $data['amount'], $data['platform_fee'],
|
|
$data['streamer_amount'], $data['donor_name'], $data['message'],
|
|
$data['payment_method'], $data['rainforest_payment_id'], $data['status']
|
|
]);
|
|
|
|
return $this->class_database->lastInsertId();
|
|
}
|
|
|
|
private function recordPayoutRequest($streamer_id, $amount, $fee, $payout_id) {
|
|
$sql = "INSERT INTO payouts (
|
|
streamer_id, amount, fee, rainforest_payout_id, status, created_at
|
|
) VALUES (?, ?, ?, ?, 'pending', NOW())";
|
|
|
|
$this->class_database->execute($sql, [$streamer_id, $amount, $fee, $payout_id]);
|
|
|
|
// Mark donations as paid out
|
|
$sql = "UPDATE donations SET payout_id = ?
|
|
WHERE streamer_id = ? AND status = 'completed' AND payout_id IS NULL";
|
|
$this->class_database->execute($sql, [$payout_id, $streamer_id]);
|
|
}
|
|
|
|
private function logError($type, $message) {
|
|
error_log(date('Y-m-d H:i:s') . " [RainforestPay] ERROR - $type: $message\n", 3, 'logs/rainforest_pay.log');
|
|
}
|
|
|
|
private function logInfo($type, $message) {
|
|
error_log(date('Y-m-d H:i:s') . " [RainforestPay] INFO - $type: $message\n", 3, 'logs/rainforest_pay.log');
|
|
}
|
|
|
|
private function sendDonationNotification($metadata) {
|
|
// Integration with EasyStream notification system
|
|
// This would trigger notifications to the streamer about new donations
|
|
}
|
|
|
|
private function getDateCondition($period) {
|
|
switch ($period) {
|
|
case '7_days':
|
|
return "completed_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
|
|
case '30_days':
|
|
return "completed_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
|
|
case '90_days':
|
|
return "completed_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)";
|
|
case '1_year':
|
|
return "completed_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR)";
|
|
default:
|
|
return "completed_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize handler if accessed directly
|
|
if (basename(__FILE__) == basename($_SERVER['SCRIPT_NAME'])) {
|
|
$handler = new RainforestPayHandler($class_database);
|
|
|
|
// Handle different actions
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
|
|
|
switch ($action) {
|
|
case 'create_donation':
|
|
$result = $handler->createDonation(
|
|
$_POST['streamer_id'],
|
|
floatval($_POST['amount']),
|
|
$_POST['donor_name'] ?? '',
|
|
$_POST['message'] ?? '',
|
|
$_POST['payment_method'] ?? 'card'
|
|
);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
case 'webhook':
|
|
$payload = file_get_contents('php://input');
|
|
$signature = $_SERVER['HTTP_X_RAINFOREST_SIGNATURE'] ?? '';
|
|
$result = $handler->processWebhook($payload, $signature);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
case 'request_payout':
|
|
$result = $handler->requestPayout($_POST['streamer_id']);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
case 'get_payment_methods':
|
|
$methods = $handler->getPaymentMethods();
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'methods' => $methods]);
|
|
break;
|
|
|
|
case 'create_payment':
|
|
$result = $handler->createPayment($_POST);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
case 'create_payout':
|
|
$result = $handler->createPayout($_POST);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
default:
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
|
}
|
|
}
|
|
?>
|