feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
370
f_core/f_classes/class.token.php
Normal file
370
f_core/f_classes/class.token.php
Normal file
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| EasyStream Token System Class
|
||||
| Handles customizable platform tokens for monetization
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VToken {
|
||||
private static $settings = null;
|
||||
private static $db;
|
||||
private static $cfg;
|
||||
|
||||
public function __construct() {
|
||||
global $class_database, $cfg;
|
||||
self::$db = $class_database;
|
||||
self::$cfg = $cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token settings from database
|
||||
*/
|
||||
public static function getSettings() {
|
||||
if (self::$settings !== null) {
|
||||
return self::$settings;
|
||||
}
|
||||
|
||||
global $class_database;
|
||||
|
||||
// Default settings
|
||||
$defaults = [
|
||||
'name' => 'EasyCoins',
|
||||
'symbol' => 'EC',
|
||||
'plural' => 'EasyCoins',
|
||||
'description' => 'Platform currency for tips and donations',
|
||||
'icon' => '/f_templates/tpl_frontend/img/default-token.png',
|
||||
'color_primary' => '#FFD700',
|
||||
'color_secondary' => '#FFA500',
|
||||
'exchange_rate' => 1.00,
|
||||
'min_purchase' => 1.00,
|
||||
'max_purchase' => 1000.00,
|
||||
'enabled' => true
|
||||
];
|
||||
|
||||
try {
|
||||
$sql = "SELECT setting_key, setting_value FROM db_settings WHERE setting_key LIKE 'token_%'";
|
||||
$result = $class_database->execute($sql);
|
||||
|
||||
$settings = $defaults;
|
||||
while (!$result->EOF) {
|
||||
$key = str_replace('token_', '', $result->fields['setting_key']);
|
||||
$value = $result->fields['setting_value'];
|
||||
|
||||
// Convert numeric values
|
||||
if (in_array($key, ['exchange_rate', 'min_purchase', 'max_purchase'])) {
|
||||
$value = floatval($value);
|
||||
} elseif ($key === 'enabled') {
|
||||
$value = $value === '1';
|
||||
}
|
||||
|
||||
$settings[$key] = $value;
|
||||
$result->MoveNext();
|
||||
}
|
||||
|
||||
self::$settings = $settings;
|
||||
return $settings;
|
||||
|
||||
} catch (Exception $e) {
|
||||
self::$settings = $defaults;
|
||||
return $defaults;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token display HTML
|
||||
*/
|
||||
public static function getTokenDisplay($amount, $size = 'normal', $show_icon = true) {
|
||||
$settings = self::getSettings();
|
||||
|
||||
if (!$settings['enabled']) {
|
||||
return '$' . number_format($amount, 2);
|
||||
}
|
||||
|
||||
$sizes = [
|
||||
'small' => ['icon' => '16px', 'font' => '0.875rem'],
|
||||
'normal' => ['icon' => '20px', 'font' => '1rem'],
|
||||
'large' => ['icon' => '24px', 'font' => '1.25rem'],
|
||||
'xlarge' => ['icon' => '32px', 'font' => '1.5rem']
|
||||
];
|
||||
|
||||
$size_config = $sizes[$size] ?? $sizes['normal'];
|
||||
|
||||
$icon_html = '';
|
||||
if ($show_icon) {
|
||||
$icon_html = sprintf(
|
||||
'<img src="%s" alt="%s" style="width: %s; height: %s; margin-right: 4px; vertical-align: middle; border-radius: 50%%;">',
|
||||
htmlspecialchars($settings['icon']),
|
||||
htmlspecialchars($settings['symbol']),
|
||||
$size_config['icon'],
|
||||
$size_config['icon']
|
||||
);
|
||||
}
|
||||
|
||||
$token_name = $amount == 1 ? $settings['name'] : $settings['plural'];
|
||||
|
||||
return sprintf(
|
||||
'<span class="token-display" style="color: %s; font-size: %s; font-weight: 500; display: inline-flex; align-items: center;">%s%s %s</span>',
|
||||
$settings['color_primary'],
|
||||
$size_config['font'],
|
||||
$icon_html,
|
||||
number_format($amount, 0),
|
||||
htmlspecialchars($token_name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token symbol only
|
||||
*/
|
||||
public static function getSymbol() {
|
||||
$settings = self::getSettings();
|
||||
return $settings['symbol'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token name
|
||||
*/
|
||||
public static function getName($plural = false) {
|
||||
$settings = self::getSettings();
|
||||
return $plural ? $settings['plural'] : $settings['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token icon HTML
|
||||
*/
|
||||
public static function getIcon($size = '20px') {
|
||||
$settings = self::getSettings();
|
||||
|
||||
return sprintf(
|
||||
'<img src="%s" alt="%s" style="width: %s; height: %s; border-radius: 50%%; object-fit: cover;">',
|
||||
htmlspecialchars($settings['icon']),
|
||||
htmlspecialchars($settings['symbol']),
|
||||
$size,
|
||||
$size
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert USD to tokens
|
||||
*/
|
||||
public static function usdToTokens($usd_amount) {
|
||||
$settings = self::getSettings();
|
||||
return round($usd_amount / $settings['exchange_rate']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert tokens to USD
|
||||
*/
|
||||
public static function tokensToUsd($token_amount) {
|
||||
$settings = self::getSettings();
|
||||
return $token_amount * $settings['exchange_rate'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get purchase limits
|
||||
*/
|
||||
public static function getPurchaseLimits() {
|
||||
$settings = self::getSettings();
|
||||
return [
|
||||
'min' => $settings['min_purchase'],
|
||||
'max' => $settings['max_purchase'],
|
||||
'min_tokens' => self::usdToTokens($settings['min_purchase']),
|
||||
'max_tokens' => self::usdToTokens($settings['max_purchase'])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate token amount
|
||||
*/
|
||||
public static function validateAmount($amount, $type = 'usd') {
|
||||
$limits = self::getPurchaseLimits();
|
||||
|
||||
if ($type === 'tokens') {
|
||||
$min = $limits['min_tokens'];
|
||||
$max = $limits['max_tokens'];
|
||||
} else {
|
||||
$min = $limits['min'];
|
||||
$max = $limits['max'];
|
||||
}
|
||||
|
||||
if ($amount < $min) {
|
||||
return ['valid' => false, 'message' => "Minimum amount is $min"];
|
||||
}
|
||||
|
||||
if ($amount > $max) {
|
||||
return ['valid' => false, 'message' => "Maximum amount is $max"];
|
||||
}
|
||||
|
||||
return ['valid' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token CSS for styling
|
||||
*/
|
||||
public static function getTokenCSS() {
|
||||
$settings = self::getSettings();
|
||||
|
||||
return sprintf('
|
||||
<style>
|
||||
:root {
|
||||
--token-primary: %s;
|
||||
--token-secondary: %s;
|
||||
}
|
||||
.token-display {
|
||||
color: var(--token-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
.token-button {
|
||||
background: linear-gradient(135deg, var(--token-primary), var(--token-secondary));
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.token-button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
.token-badge {
|
||||
background: var(--token-primary);
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
', $settings['color_primary'], $settings['color_secondary']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get donation button HTML
|
||||
*/
|
||||
public static function getDonationButton($streamer_id, $suggested_amount = null) {
|
||||
$settings = self::getSettings();
|
||||
|
||||
if (!$settings['enabled']) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$suggested_tokens = $suggested_amount ? self::usdToTokens($suggested_amount) : 10;
|
||||
|
||||
return sprintf('
|
||||
<button class="token-button" onclick="openTokenDonation(%d, %d)">
|
||||
%s Donate %s %s
|
||||
</button>
|
||||
',
|
||||
$streamer_id,
|
||||
$suggested_tokens,
|
||||
self::getIcon('16px'),
|
||||
number_format($suggested_tokens),
|
||||
$settings['name']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token purchase form HTML
|
||||
*/
|
||||
public static function getPurchaseForm($user_id) {
|
||||
$settings = self::getSettings();
|
||||
$limits = self::getPurchaseLimits();
|
||||
|
||||
if (!$settings['enabled']) {
|
||||
return '<p>Token system is currently disabled.</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="token-purchase-form">
|
||||
<div class="token-header">
|
||||
<h3><?php echo self::getIcon('32px'); ?> Purchase <?php echo htmlspecialchars($settings['plural']); ?></h3>
|
||||
<p><?php echo htmlspecialchars($settings['description']); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="purchase-options">
|
||||
<div class="amount-selector">
|
||||
<label>Amount (USD)</label>
|
||||
<input type="number" id="usd-amount" min="<?php echo $limits['min']; ?>" max="<?php echo $limits['max']; ?>" step="0.01" value="10.00">
|
||||
</div>
|
||||
|
||||
<div class="token-preview">
|
||||
<span>You will receive:</span>
|
||||
<div class="token-amount" id="token-amount">
|
||||
<?php echo self::getTokenDisplay(self::usdToTokens(10), 'large'); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="token-button" onclick="processPurchase()">
|
||||
💳 Purchase <?php echo htmlspecialchars($settings['plural']); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('usd-amount').addEventListener('input', function() {
|
||||
const usdAmount = parseFloat(this.value) || 0;
|
||||
const tokenAmount = Math.round(usdAmount / <?php echo $settings['exchange_rate']; ?>);
|
||||
document.getElementById('token-amount').innerHTML = '<?php echo self::getIcon('24px'); ?>' + tokenAmount + ' <?php echo htmlspecialchars($settings['plural']); ?>';
|
||||
});
|
||||
|
||||
function processPurchase() {
|
||||
const amount = document.getElementById('usd-amount').value;
|
||||
window.location.href = '/donations/process?amount=' + amount + '&user_id=<?php echo $user_id; ?>';
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record token transaction
|
||||
*/
|
||||
public static function recordTransaction($user_id, $amount, $type, $description = '') {
|
||||
global $class_database;
|
||||
|
||||
try {
|
||||
$sql = "INSERT INTO token_transactions (user_id, amount, transaction_type, description, created_at)
|
||||
VALUES (?, ?, ?, ?, NOW())";
|
||||
|
||||
$class_database->execute($sql, [$user_id, $amount, $type, $description]);
|
||||
|
||||
// Update user balance
|
||||
$balance_sql = "UPDATE db_accountuser SET token_balance = COALESCE(token_balance, 0) + ? WHERE usr_id = ?";
|
||||
$class_database->execute($balance_sql, [$amount, $user_id]);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user token balance
|
||||
*/
|
||||
public static function getUserBalance($user_id) {
|
||||
global $class_database;
|
||||
|
||||
try {
|
||||
$sql = "SELECT COALESCE(token_balance, 0) as balance FROM db_accountuser WHERE usr_id = ?";
|
||||
$result = $class_database->execute($sql, [$user_id]);
|
||||
|
||||
return $result->fields['balance'] ?? 0;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can afford amount
|
||||
*/
|
||||
public static function canAfford($user_id, $amount) {
|
||||
$balance = self::getUserBalance($user_id);
|
||||
return $balance >= $amount;
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user