Files
formipay/includes/Token.php
2025-08-21 20:39:34 +07:00

156 lines
4.1 KiB
PHP

<?php
namespace Formipay;
use Exception;
if (!defined('ABSPATH')) exit;
class Token {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'formipay_tokens';
add_action( 'init', [$this, 'create_db'] );
// Setup cleanup hook
add_action( 'formipay_daily_cleanup', [$this, 'cleanup_expired_tokens']);
if (!wp_next_scheduled('formipay_daily_cleanup')) {
wp_schedule_event(time(), 'daily', 'formipay_daily_cleanup');
}
}
public function create_db() {
global $wpdb;
$table_name = $wpdb->prefix . 'formipay_tokens';
$sql = "CREATE TABLE $table_name (
token CHAR(64) NOT NULL,
form_id BIGINT UNSIGNED NOT NULL,
order_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL,
used TINYINT(1) NOT NULL DEFAULT 0,
use_count INT NOT NULL DEFAULT 0,
PRIMARY KEY (token),
KEY expires_at_idx (expires_at),
KEY order_id_idx (order_id)
) {$wpdb->get_charset_collate()};";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
/**
* Generate cryptographically secure token
*
* @param int $order_id
* @param int $form_id
* @param int $expires_in Seconds until expiration (default: 900 = 15 mins)
* @return string
*/
public function generate(int $order_id, int $form_id, int $expires_in = 900): string {
global $wpdb;
$token = bin2hex(random_bytes(32)); // 64-character token
$expires_at = formipay_date('Y-m-d H:i:s', time() + $expires_in);
$wpdb->insert($this->table_name, [
'token' => $token,
'form_id' => $form_id,
'order_id' => $order_id,
'expires_at' => $expires_at,
'created_at' => formipay_date('Y-m-d H:i:s')
]);
return $token;
}
/**
* Validate token and retrieve order data
*
* @param string $token
* @return array|false
*/
public function validate(string $token) {
global $wpdb;
// Validate token format
if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false;
$query = $wpdb->prepare(
"SELECT form_id, order_id
FROM {$this->table_name}
WHERE token = %s
AND expires_at > NOW()
AND used = 0",
$token
);
return $wpdb->get_row($query, ARRAY_A);
}
/**
* Mark token as used
*
* @param string $token
* @return bool
*/
public function mark_used(string $token): bool {
global $wpdb;
return (bool) $wpdb->update(
$this->table_name,
['used' => 1],
['token' => $token]
);
}
/**
* Cleanup expired tokens
*/
public function cleanup_expired_tokens() {
global $wpdb;
$wpdb->query("DELETE FROM {$this->table_name} WHERE expires_at < NOW()");
}
/**
* Get token usage count
*
* @param string $token
* @return int
*/
public function get_usage_count(string $token): int {
global $wpdb;
return (int) $wpdb->get_var($wpdb->prepare(
"SELECT use_count FROM {$this->table_name} WHERE token = %s",
$token
));
}
/**
* Increment token usage
*
* @param string $token
* @param int $max_uses
* @return bool
* @throws Exception
*/
public function increment_usage(string $token, int $max_uses = 5): bool {
global $wpdb;
$current_count = $this->get_usage_count($token);
if ($current_count >= $max_uses) {
throw new Exception('Token usage limit reached');
}
return (bool) $wpdb->update(
$this->table_name,
['use_count' => $current_count + 1],
['token' => $token]
);
}
}