= 1.7.3). * * WHY: when a 6rsh subscription payment fails, the account keeps FULL access * for a 3-day grace window. Before this notice, a store admin had no way to * know — they discovered it when sends stopped, three days later, with the * account already on Free. That is exactly the churn this exists to prevent. * * Design rules: * - Renders ONLY while 6rsh reports past_due/grace with a future * grace_ends_at. Absent fields (older 6rsh) render nothing at all. * - Dismissible per-user, and the dismissal auto-expires so the notice * returns the next day while the problem persists. * - Says plainly that NOTHING has been or will be deleted. That sentence is * load-bearing for trust — do not soften it. * - Bilingual EN + AR RTL, matched to the store's own admin locale. * - Never shown to users who cannot act on it (needs manage_woocommerce). * * @package SixRsh_WhatsApp */ defined( 'ABSPATH' ) || exit; class SixRsh_Billing_Notice { /** Per-user dismissal timestamp. */ const USER_META_DISMISSED = 'sixrsh_billing_notice_dismissed_at'; /** A dismissal only silences the notice for this long. */ const DISMISS_TTL = DAY_IN_SECONDS; /** Nonce action for the dismiss request. */ const NONCE = 'sixrsh_dismiss_billing_notice'; public static function init(): void { add_action( 'admin_notices', array( __CLASS__, 'render' ) ); add_action( 'wp_ajax_sixrsh_dismiss_billing_notice', array( __CLASS__, 'handle_dismiss' ) ); } /** Whether the current admin screen should show the notice. */ private static function should_render(): bool { if ( ! is_admin() || wp_doing_ajax() ) { return false; } // Only people who could actually fix it. if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) { return false; } if ( ! class_exists( 'SixRsh_Entitlements' ) ) { return false; } // The strict gate: past_due/grace AND a future deadline. if ( ! SixRsh_Entitlements::in_payment_grace() ) { return false; } return ! self::is_dismissed(); } private static function is_dismissed(): bool { $at = (int) get_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true ); return $at > 0 && ( time() - $at ) < self::DISMISS_TTL; } /** True when the store's admin language is Arabic. */ private static function is_arabic(): bool { $locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale(); return 0 === strpos( (string) $locale, 'ar' ); } public static function render(): void { if ( ! self::should_render() ) { return; } $days = SixRsh_Entitlements::grace_days_left(); $billing = 'https://www.6rsh.com/billing'; $ar = self::is_arabic(); if ( $ar ) { $title = __( 'تعذّرت عملية الدفع في 6rsh', '6rsh-messaging-notifications' ); /* translators: %d: whole days remaining in the payment grace window. */ $body = sprintf( __( 'لم تتم عملية الدفع لاشتراكك. لديك %d يوم/أيام من الوصول الكامل قبل أن ينتقل الحساب إلى الخطة المجانية.', '6rsh-messaging-notifications' ), $days ); $safe = __( 'لم يتم حذف أي شيء، ولن يتم حذف أي شيء. جهات الاتصال والمحادثات والحملات وسجلّ الطلبات كلها محفوظة، وتعود جميع الميزات فورًا بمجرد نجاح الدفع.', '6rsh-messaging-notifications' ); $cta = __( 'تحديث طريقة الدفع', '6rsh-messaging-notifications' ); $dis = __( 'إخفاء', '6rsh-messaging-notifications' ); } else { $title = __( 'Your 6rsh payment did not go through', '6rsh-messaging-notifications' ); /* translators: %d: whole days remaining in the payment grace window. */ $body = sprintf( _n( 'We could not charge your card. You have %d day of full access left before the account moves to the Free plan.', 'We could not charge your card. You have %d days of full access left before the account moves to the Free plan.', max( 1, $days ), '6rsh-messaging-notifications' ), $days ); $safe = __( 'Nothing has been deleted, and nothing will be. Your contacts, conversations, campaigns and order history are all kept, and every feature returns the moment your payment succeeds.', '6rsh-messaging-notifications' ); $cta = __( 'Update payment method', '6rsh-messaging-notifications' ); $dis = __( 'Dismiss', '6rsh-messaging-notifications' ); } $dir = $ar ? 'rtl' : 'ltr'; ?>
'bad_nonce' ), 403 ); } if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'reason' => 'forbidden' ), 403 ); } update_user_meta( get_current_user_id(), self::USER_META_DISMISSED, time() ); wp_send_json_success(); } }