晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
|
Server : Apache System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64 User : rainic ( 1014) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/rainic/www/oldTZh/wp-content/plugins/persian-elementor/widget/zarinpal/ |
Upload File : |
<?php
namespace PersianElementor;
use PersianElementor\Classes\ZarinPal_Handler;
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
class ZarinPal_Ajax {
private $zarinpal_handler;
public function __construct() {
$this->zarinpal_handler = new ZarinPal_Handler();
// Register AJAX handlers
add_action('wp_ajax_zarinpal_payment_request', [$this, 'process_payment_request']);
add_action('wp_ajax_nopriv_zarinpal_payment_request', [$this, 'process_payment_request']);
// Handle verification requests with higher specificity to avoid conflicts
add_action('template_redirect', [$this, 'handle_verification'], 5); // Lower priority
}
/**
* Process payment request via AJAX
*/
public function process_payment_request() {
// Verify nonce
if (!isset($_POST['zarinpal_nonce']) || !wp_verify_nonce($_POST['zarinpal_nonce'], 'zarinpal_payment_request')) {
wp_die('نشست منقضی شده است. لطفا صفحه را بارگذاری مجدد کنید.');
}
// Get form data
$merchant_id = isset($_POST['merchant_id']) ? sanitize_text_field($_POST['merchant_id']) : '';
$amount = isset($_POST['amount']) ? intval($_POST['amount'] / 10) : 0; // Divide by 10 to remove one zero
$description = isset($_POST['description']) ? sanitize_text_field($_POST['description']) : '';
$callback_url = isset($_POST['callback_url']) ? esc_url_raw($_POST['callback_url']) : '';
$email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
$mobile = isset($_POST['mobile']) ? sanitize_text_field($_POST['mobile']) : '';
// Validate required fields for ZarinPal
$errors = [];
if (empty($merchant_id)) {
$errors[] = 'شناسه مرچنت زرینپال وارد نشده است.';
}
if (empty($amount)) {
$errors[] = 'مبلغ پرداخت وارد نشده است.';
}
if (empty($description)) {
$errors[] = 'توضیحات پرداخت وارد نشده است.';
}
if (empty($callback_url)) {
$errors[] = 'آدرس بازگشت وارد نشده است.';
}
if (!empty($errors)) {
wp_die('همه فیلدهای ضروری را پر کنید: ' . implode(' ', $errors));
}
// Store transaction data for later verification with unique prefix
$transaction_id = 'pe_' . wp_generate_uuid4(); // PE = Persian Elementor
$transaction_data = [
'merchant_id' => $merchant_id,
'amount' => $amount,
'description' => $description,
'transaction_id' => $transaction_id,
'created_at' => time(),
'source' => 'persian_elementor', // Mark as our transaction
];
update_option('zarinpal_transaction_' . $transaction_id, $transaction_data, false);
// Always add transaction_id parameter to the callback URL
$callback_url = add_query_arg(['transaction_id' => $transaction_id], $callback_url);
// Request payment from ZarinPal
$result = $this->zarinpal_handler->request_payment(
$merchant_id,
$amount,
$description,
$callback_url,
$email,
$mobile
);
if ($result['success']) {
// Update transaction with authority
$transaction_data['authority'] = $result['authority'];
update_option('zarinpal_transaction_' . $transaction_id, $transaction_data, false);
// Redirect to payment gateway
$this->zarinpal_handler->redirect($result['payment_url']);
} else {
wp_die('خطا در ایجاد تراکنش: ' . $result['message']);
}
}
/**
* Handle payment verification
*/
public function handle_verification() {
// More specific check to avoid conflicts with theme's ZarinPal system
if (!isset($_GET['Authority']) || !isset($_GET['Status']) || !isset($_GET['transaction_id'])) {
return; // Exit if not our specific Zarinpal callback
}
// Additional check: ensure this is our transaction by checking the transaction_id format
$transaction_id = isset($_GET['transaction_id']) ? sanitize_text_field($_GET['transaction_id']) : '';
if (empty($transaction_id) || !$this->is_our_transaction($transaction_id)) {
return; // Not our transaction, let theme handle it
}
// Get data from Zarinpal callback
$authority = isset($_GET['Authority']) ? sanitize_text_field($_GET['Authority']) : '';
$status = isset($_GET['Status']) ? sanitize_text_field($_GET['Status']) : '';
if (empty($authority) || empty($status)) {
wp_die('اطلاعات بازگشتی از درگاه ناقص است.');
}
// Get transaction data
$transaction_data = get_option('zarinpal_transaction_' . $transaction_id, false);
if (!$transaction_data) {
wp_die('تراکنش یافت نشد یا نامعتبر است.');
}
// Store the message to show after the payment
$zarinpal_message = '';
$zarinpal_message_class = '';
$verification_success = false; // Add this variable to track actual verification success
// Check if payment was successful according to ZarinPal status
if ($status === 'OK') {
// Verify payment with ZarinPal API
$result = $this->zarinpal_handler->verify_payment(
$transaction_data['merchant_id'],
$transaction_data['amount'],
$authority,
false // Assuming production mode
);
if ($result['success']) {
// Payment verified successfully
$transaction_data['status'] = 'completed';
$transaction_data['ref_id'] = $result['ref_id'];
update_option('zarinpal_transaction_' . $transaction_id, $transaction_data, false);
// Run action hook for successful payment with namespace
do_action('persian_elementor_zarinpal_payment_success', $transaction_data, $result);
// Prepare the success message
$zarinpal_message = sprintf('پرداخت با موفقیت انجام شد. کد پیگیری: %s', '<strong>' . esc_html($result['ref_id']) . '</strong>');
$zarinpal_message_class = 'zarinpal-success-message';
$verification_success = true; // Verification was successful
} else {
// Payment verification failed
$transaction_data['status'] = 'failed';
$transaction_data['error'] = $result['message'];
update_option('zarinpal_transaction_' . $transaction_id, $transaction_data, false);
// Prepare the error message
$zarinpal_message = sprintf('تایید پرداخت ناموفق بود. پیام درگاه: %s (کد خطا: %s)', esc_html($result['message']), esc_html($result['status']));
$zarinpal_message_class = 'zarinpal-error-message';
$verification_success = false; // Verification failed
}
} else {
// Payment canceled or failed (Status != OK)
$transaction_data['status'] = 'canceled';
update_option('zarinpal_transaction_' . $transaction_id, $transaction_data, false);
// Prepare the cancel message
$zarinpal_message = 'پرداخت توسط کاربر لغو شد یا در مرحله اولیه ناموفق بود.';
$zarinpal_message_class = 'zarinpal-cancel-message';
}
// Add the message to display it in the right location
add_action('wp_footer', function() use ($zarinpal_message, $zarinpal_message_class, $status, $verification_success) { // Pass verification_success
if (empty($zarinpal_message)) return;
// Determine notification type based on status AND verification result
$notification_type = 'info'; // Default
// First check for message class, which is more specific than status
if ($zarinpal_message_class === 'zarinpal-success-message' && $verification_success === true) {
$notification_type = 'success';
} elseif ($zarinpal_message_class === 'zarinpal-error-message') {
$notification_type = 'error';
} elseif ($zarinpal_message_class === 'zarinpal-cancel-message') {
$notification_type = 'warning'; // Use warning for cancel
} else {
$notification_type = 'info'; // Fallback
}
// Add CSS for notifications
?>
<style>
.zarinpal-notification-container {
position: fixed;
top: 40px; /* Increased top margin */
right: 20px; /* Positioned to the right */
left: auto; /* Reset left positioning */
transform: none; /* Remove horizontal centering */
z-index: 9999;
direction: rtl;
text-align: right; /* Align text to the right */
min-width: 300px;
max-width: 90%;
}
.zarinpal-notification {
padding: 15px 20px;
margin-bottom: 15px;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: flex-start; /* Keep content aligned to start (right in RTL) */
animation: zarinpal-notification-fadein 0.5s forwards; /* Use forwards to keep final state */
opacity: 0;
}
.zarinpal-notification-success {
background-color: #e7f8f0;
color: #0a6245;
border: 1px solid #a3e2c7;
}
.zarinpal-notification-error {
background-color: #ffeeee;
color: #d92626;
border: 1px solid #ffbdbd;
}
.zarinpal-notification-warning {
background-color: #fff8e6;
color: #b7750f;
border: 1px solid #ffe0a3;
}
.zarinpal-notification-info {
background-color: #e6f3ff;
color: #0d5db6;
border: 1px solid #a8d1ff;
}
/* Inherit typography from button text */
.zarinpal-notification span {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
letter-spacing: inherit;
}
.zarinpal-notification .close-btn {
margin-right: auto;
cursor: pointer;
font-size: 18px;
opacity: 0.5;
transition: opacity 0.3s;
}
.zarinpal-notification .close-btn:hover {
opacity: 1;
}
@keyframes zarinpal-notification-fadein {
from {opacity: 0; transform: translateY(-20px);}
to {opacity: 1; transform: translateY(0);}
}
</style>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
// Create container for notifications if it doesn't exist
var container = document.querySelector('.zarinpal-notification-container');
if (!container) {
container = document.createElement('div');
container.className = 'zarinpal-notification-container';
document.body.appendChild(container);
}
// Create notification element
var notification = document.createElement('div');
notification.className = 'zarinpal-notification zarinpal-notification-<?php echo esc_js($notification_type); ?>';
// Create close button
var closeBtn = '<span class="close-btn">×</span>';
// Find the ZarinPal button to copy its styles
var zarinpalButton = document.querySelector('.elementor-zarinpal-button');
// Set content (without icon)
notification.innerHTML = '<span class="elementor-button-text"><?php echo wp_kses_post($zarinpal_message); ?></span>' + closeBtn;
// Add the notification to the container
container.appendChild(notification);
// Copy typography styles from button to notification text if button exists
if (zarinpalButton) {
var buttonTextElement = zarinpalButton.querySelector('.elementor-button-text');
if (buttonTextElement) {
var styles = window.getComputedStyle(buttonTextElement);
var textElement = notification.querySelector('span');
if (textElement) {
textElement.style.fontFamily = styles.fontFamily;
textElement.style.fontSize = styles.fontSize;
textElement.style.fontWeight = styles.fontWeight;
textElement.style.lineHeight = styles.lineHeight;
textElement.style.letterSpacing = styles.letterSpacing;
}
}
}
// Show the notification (trigger animation)
setTimeout(function() {
notification.style.opacity = '1';
}, 100);
// Add click event to close button
notification.querySelector('.close-btn').addEventListener('click', function() {
notification.style.opacity = '0';
notification.style.transform = 'translateY(-20px)';
setTimeout(function() {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 500);
});
// The auto-remove functionality has been removed
// Now the notification will only disappear when the close button is clicked
});
</script>
<?php
}, 99);
}
/**
* Check if transaction belongs to our plugin
*
* @param string $transaction_id
* @return bool
*/
private function is_our_transaction($transaction_id) {
// Check if transaction ID has our prefix
if (!str_starts_with($transaction_id, 'pe_')) {
return false;
}
// Check if transaction exists in our options and has our source marker
$transaction_data = get_option('zarinpal_transaction_' . $transaction_id, false);
if ($transaction_data === false) {
return false;
}
// Verify it's marked as our transaction
return isset($transaction_data['source']) && $transaction_data['source'] === 'persian_elementor';
}
}
// Initialize the class
new ZarinPal_Ajax();