Legal, registers * the same text with WordPress (Settings -> Privacy -> Policy Guide), and * shows one dismissible reminder on the OrderGuard screens until an admin * confirms that the text has been added. * * Deliberately NOT done: the plugin never edits the privacy page itself (page * builders, translations and the store's own responsibility for its notice), * makes no external call, and shows the reminder only on OrderGuard screens. */ defined( 'ABSPATH' ) || exit; /** * Version of the wording below. Raise it when the text changes in a way the * store should copy again; a changed version shows the reminder again. */ define( 'ORDERGUARD_STORE_NOTICE_TEXT_VERSION', '1' ); /** * Optional features that change what the store must tell its customers. * * Local flag/block records count as switched on while the setting is on, and * also while any such record is still kept, because the notice must describe * the records the store actually holds. * * @return string[] Subset of 'cod', 'local', 'signals', in that order. */ function orderguard_store_notice_features() { $features = array(); if ( function_exists( 'orderguard_cod_gating_enabled' ) && orderguard_cod_gating_enabled() ) { $features[] = 'cod'; } $local = function_exists( 'orderguard_manual_flag_enabled' ) && orderguard_manual_flag_enabled(); if ( ! $local && function_exists( 'orderguard_flags_count' ) ) { $local = orderguard_flags_count() > 0; } if ( $local ) { $features[] = 'local'; } if ( function_exists( 'orderguard_network_signal_enabled' ) && orderguard_network_signal_enabled() ) { $features[] = 'signals'; } return $features; } /** * What a confirmation applies to: the wording version and the feature set. * * @return string */ function orderguard_store_notice_signature() { return ORDERGUARD_STORE_NOTICE_TEXT_VERSION . ':' . implode( ',', orderguard_store_notice_features() ); } /** * The plugin's short customer notice (unchanged since 0.2.x; the same source * string as before, so existing translations still apply). * * @return string */ function orderguard_store_notice_disclosure() { return __( "This store uses OrderGuard to help prevent uncollected and fraudulent cash-on-delivery orders. When you place an order, a one-way coded (hashed) form of your phone number and email — never the raw details themselves — is shared with OrderGuard's EU-based network. This coded identifier is still personal data, processed on the basis of our legitimate interest in preventing non-collection and loss (GDPR Art. 6(1)(f)). The network records whether orders are collected or returned, and this store may add a signal that it has flagged or blocked a customer; other stores see only aggregated counts, never your name, your contact details, or which store reported. You have the right to access your data, to object to this processing, and to request its deletion at any time — please contact us, or OrderGuard at info@orderguard.net. The OrderGuard privacy policy is published at https://www.orderguard.net/privacy/.", 'orderguard' ); } /** * The tailored text, as paragraphs, in the current OrderGuard language. * * Paragraph 1 is the short notice, paragraph 2 the merchant guide 1.1 * supplement, paragraph 3 describes this store's optional features. * * @return string[] */ function orderguard_store_notice_paragraphs() { $paragraphs = array( orderguard_store_notice_disclosure() ); $paragraphs[] = __( 'Checks and reporting also cover orders paid by methods other than cash on delivery. In addition to phone and email hashes, OrderGuard receives order dates, the billing country, internal order/item references, whether the order was cash on delivery, order-status changes, any refunded proportion, and the reporting courier where applicable. Matching exclusions suppress routine checks and reporting; a phone or email exclusion sends hashes to erase records previously submitted by this store. The full policy explains local records, optional features, retention and how erasure requests are handled.', 'orderguard' ); $features = orderguard_store_notice_features(); if ( empty( $features ) ) { $paragraphs[] = __( 'This store has not switched on OrderGuard’s optional features (automatic cash-on-delivery check, local flag/block records and Merchant Signals).', 'orderguard' ); return $paragraphs; } $lines = array( __( 'This store has switched on the following optional OrderGuard features.', 'orderguard' ) ); if ( in_array( 'cod', $features, true ) ) { $lines[] = __( 'Automatic cash-on-delivery check: where the reliability score indicates a high risk of refusal, the cash-on-delivery option may be hidden or restricted at checkout automatically. All other payment methods remain available. You can ask us to review this decision, express your point of view and contest it.', 'orderguard' ); } if ( in_array( 'local', $features, true ) ) { $lines[] = __( 'Local flag/block records: we may mark a customer as flagged or blocked in our own store, for example after repeated refused deliveries. This record may include your name, email and phone number, is kept on our own website and, on its own, is not shared with other stores.', 'orderguard' ); } if ( in_array( 'signals', $features, true ) ) { $lines[] = __( 'Merchant Signals: we share a coded (pseudonymised) “suspicious” or “blocked” signal with other participating stores and see signals from them. A signal is shown only once at least two independent stores have reported the same person, and no readable personal data is exchanged between stores.', 'orderguard' ); } $paragraphs[] = implode( ' ', $lines ); return $paragraphs; } /** * The tailored text as plain text (paragraphs separated by a blank line). * * @return string */ function orderguard_store_notice_text() { return implode( "\n\n", orderguard_store_notice_paragraphs() ); } /** * Run a callback with OrderGuard's own strings in one language, whatever the * current admin's OrderGuard language is. * * @param string $lang 'bg' or 'en'. * @param callable $callback Callback. * @return mixed */ function orderguard_with_language( $lang, $callback ) { $switched = function_exists( 'switch_to_locale' ) && switch_to_locale( 'bg' === $lang ? 'bg_BG' : 'en_US' ); $GLOBALS['orderguard_forced_language'] = ( 'bg' === $lang ) ? 'bg' : 'en'; try { return call_user_func( $callback ); } finally { unset( $GLOBALS['orderguard_forced_language'] ); if ( $switched ) { restore_previous_locale(); } } } /** * Register the text with WordPress's Privacy Policy Guide, in the site's * language (not the admin's OrderGuard language), so the suggested text does * not change with whoever is looking at it. */ function orderguard_store_notice_register_policy_content() { if ( ! function_exists( 'wp_add_privacy_policy_content' ) || ! function_exists( 'orderguard_is_activated' ) || ! orderguard_is_activated() ) { return; } $lang = ( 0 === strpos( get_locale(), 'bg' ) ) ? 'bg' : 'en'; $html = orderguard_with_language( $lang, function () { $out = '

' . esc_html__( 'Text for your store’s privacy notice, built from the OrderGuard features this store has switched on. Add it to your privacy notice in full; it does not replace the rest of your notice.', 'orderguard' ) . '

'; foreach ( orderguard_store_notice_paragraphs() as $paragraph ) { $out .= '

' . esc_html( $paragraph ) . '

'; } return $out; } ); wp_add_privacy_policy_content( 'OrderGuard', wp_kses_post( $html ) ); } add_action( 'admin_init', 'orderguard_store_notice_register_policy_content' ); /** * The stored confirmation, if it applies to the current text. * * @return array|null { signature, at (UTC timestamp), user_id, user_login } */ function orderguard_store_notice_confirmation() { $record = orderguard_get_option( 'orderguard_store_notice_confirmed', array() ); if ( ! is_array( $record ) || empty( $record['signature'] ) || empty( $record['at'] ) ) { return null; } return $record; } /** * Whether the current text has been confirmed as added. * * @return bool */ function orderguard_store_notice_is_confirmed() { $record = orderguard_store_notice_confirmation(); return null !== $record && orderguard_store_notice_signature() === $record['signature']; } /** * True on OrderGuard's own admin screens. * * @return bool */ function orderguard_store_notice_screen_ok() { if ( ! function_exists( 'get_current_screen' ) ) { return false; } $screen = get_current_screen(); return $screen && isset( $screen->id ) && false !== strpos( (string) $screen->id, 'orderguard' ); } /** * The reminder: OrderGuard screens only, dismissible per admin, gone for * everyone once the text is confirmed as added. Not shown on the Legal tab, * which already holds the text. */ function orderguard_store_notice_admin_notice() { if ( ! current_user_can( 'manage_woocommerce' ) || ! function_exists( 'orderguard_is_activated' ) || ! orderguard_is_activated() ) { return; } if ( ! orderguard_store_notice_screen_ok() || orderguard_store_notice_is_confirmed() ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only check of the current tab. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; if ( 'legal' === $tab ) { return; } $signature = orderguard_store_notice_signature(); if ( orderguard_get_user_meta( get_current_user_id(), 'orderguard_store_notice_dismissed' ) === $signature ) { return; } $record = orderguard_store_notice_confirmation(); $dismiss_url = wp_nonce_url( admin_url( 'admin-post.php?action=orderguard_store_notice_dismiss' ), 'orderguard_store_notice_dismiss' ); $open_url = admin_url( 'admin.php?page=orderguard&tab=legal#og-store-notice' ); echo '

OrderGuard: '; if ( null !== $record ) { echo esc_html__( 'the OrderGuard features switched on in this store, or the suggested wording, have changed since the text was last added to your privacy notice. Please update your privacy notice with the current text.', 'orderguard' ); } else { echo esc_html__( 'your store’s privacy notice must tell your customers that you use OrderGuard. The text is ready, built from the features switched on in this store.', 'orderguard' ); } echo '

' . esc_html__( 'Open the text', 'orderguard' ) . ' ' . '' . esc_html__( 'Dismiss', 'orderguard' ) . '

'; } add_action( 'admin_notices', 'orderguard_store_notice_admin_notice' ); /** * Dismiss the reminder for this admin, for the current text only. */ function orderguard_store_notice_dismiss() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Not allowed.', 'orderguard' ) ); } if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'orderguard_store_notice_dismiss' ) ) { wp_die( esc_html__( 'Bad nonce.', 'orderguard' ) ); } update_user_meta( get_current_user_id(), 'orderguard_store_notice_dismissed', orderguard_store_notice_signature() ); wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'admin.php?page=orderguard' ) ); exit; } add_action( 'admin_post_orderguard_store_notice_dismiss', 'orderguard_store_notice_dismiss' ); /** * "I have added it": the merchant's own record of when, and by whom, the * current text was confirmed as added. Kept on this site only. */ function orderguard_store_notice_confirm() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Not allowed.', 'orderguard' ) ); } check_admin_referer( 'orderguard_store_notice_confirm' ); $user = wp_get_current_user(); orderguard_update_option( 'orderguard_store_notice_confirmed', array( 'signature' => orderguard_store_notice_signature(), 'at' => time(), 'user_id' => (int) $user->ID, 'user_login' => (string) $user->user_login, ), false ); wp_safe_redirect( admin_url( 'admin.php?page=orderguard&tab=legal#og-store-notice' ) ); exit; } add_action( 'admin_post_orderguard_store_notice_confirm', 'orderguard_store_notice_confirm' ); /** * The Legal tab card: the tailored text, Copy text, and the record. */ function orderguard_store_notice_render_card() { $features = orderguard_store_notice_features(); $record = orderguard_store_notice_confirmation(); $confirmed = orderguard_store_notice_is_confirmed(); $names = array( 'cod' => __( 'Automatic cash-on-delivery check', 'orderguard' ), 'local' => __( 'Local flag/block records', 'orderguard' ), 'signals' => __( 'Merchant Signals', 'orderguard' ), ); $btn_sec = 'display:inline-flex; align-items:center; gap:5px; text-decoration:none; font-size:13px; font-weight:600; background:#fff; border:1px solid #D5DCDA; border-radius:8px; padding:7px 14px; color:#34423F; white-space:nowrap; cursor:pointer;'; ?>