| @@ -5,16 +5,20 @@ | ||
| 5 | 5 | * Registers SureDonation's engagement admin notices: |
| 6 | 6 | * |
| 7 | 7 | * - Review notice (donation) : shown once the site has at least one live |
| 8 | 8 | * donation, after the 3-day install grace. |
| 9 | - * - Review notice (gateway) : shown when a payment gateway is connected but | |
| 10 | - * no live donation exists yet, after the 3-day | |
| 11 | - * install grace. | |
| 9 | + * - Test mode notice : shown when a payment gateway is connected but | |
| 10 | + * the site is still in test mode, nudging the | |
| 11 | + * admin to switch to live mode. | |
| 12 | + * - Review notice (gateway) : shown when a payment gateway is connected in | |
| 13 | + * live mode but no live donation exists yet, | |
| 14 | + * after the 3-day install grace. | |
| 12 | 15 | * - Setup gateway notice : shown instantly (no grace) when no payment |
| 13 | 16 | * gateway is connected at all. |
| 14 | 17 | * |
| 15 | - * The three notices are mutually exclusive by construction. Priority is: | |
| 16 | - * live donation > gateway configured > no gateway. | |
| 18 | + * The four notices are mutually exclusive by construction. Priority is: | |
| 19 | + * live donation > gateway configured (test mode) > gateway configured (live | |
| 20 | + * mode) > no gateway. | |
| 17 | 21 | * |
| 18 | 22 | * @package SureDonation |
| 19 | 23 | */ |
| 20 | 24 | |
| @@ -21,8 +25,9 @@ | ||
| 21 | 25 | namespace SureDonation\Inc\Admin; |
| 22 | 26 | |
| 23 | 27 | use SureDonation\Inc\Database\Tables\Donations; |
| 24 | 28 | use SureDonation\Inc\Helper; |
| 29 | +use SureDonation\Inc\Payments\Payment_Helper; | |
| 25 | 30 | use SureDonation\Inc\Payments\PayPal\PayPal_Helper; |
| 26 | 31 | use SureDonation\Inc\Payments\Stripe\Stripe_Helper; |
| 27 | 32 | |
| 28 | 33 | // Exit if accessed directly. |
| @@ -89,14 +94,17 @@ | ||
| 89 | 94 | require_once SUREDONATION_DIR . 'inc/lib/astra-notices/class-bsf-admin-notices.php'; |
| 90 | 95 | } |
| 91 | 96 | |
| 92 | 97 | add_action( 'admin_notices', [ $this, 'display_review_notice_donation' ] ); |
| 98 | + add_action( 'admin_notices', [ $this, 'display_test_mode_notice' ] ); | |
| 93 | 99 | add_action( 'admin_notices', [ $this, 'display_review_notice_gateway' ] ); |
| 94 | 100 | add_action( 'admin_notices', [ $this, 'display_setup_gateway_notice' ] ); |
| 101 | + add_action( 'admin_notices', [ $this, 'display_webhook_notice' ] ); | |
| 102 | + add_action( 'admin_notices', [ $this, 'display_paypal_reconnect_notice' ] ); | |
| 95 | 103 | |
| 96 | - // Load the setup-notice styles from the admin <head> (not the late | |
| 104 | + // Load the banner-notice styles from the admin <head> (not the late | |
| 97 | 105 | // after-markup hook) so the banner never renders unstyled first. |
| 98 | - add_action( 'admin_enqueue_scripts', [ $this, 'maybe_enqueue_setup_notice_style' ] ); | |
| 106 | + add_action( 'admin_enqueue_scripts', [ $this, 'maybe_enqueue_banner_notice_style' ] ); | |
| 99 | 107 | |
| 100 | 108 | add_action( 'wp_ajax_suredonation_notice_response', [ $this, 'handle_notice_response' ] ); |
| 101 | 109 | } |
| 102 | 110 | |
| @@ -146,9 +154,11 @@ | ||
| 146 | 154 | WEEK_IN_SECONDS, |
| 147 | 155 | true |
| 148 | 156 | ), |
| 149 | 157 | 'repeat-notice-after' => WEEK_IN_SECONDS, |
| 150 | - 'show_if' => $this->is_three_days_elapsed() && $this->has_live_donation(), | |
| 158 | + // Test-mode takes priority: while the site is in test mode the | |
| 159 | + // "switch to live" warning is more useful than a review ask. | |
| 160 | + 'show_if' => $this->is_three_days_elapsed() && $this->has_live_donation() && ! $this->should_show_test_mode_notice(), | |
| 151 | 161 | 'display-with-other-notices' => true, |
| 152 | 162 | ] |
| 153 | 163 | ); |
| 154 | 164 | |
| @@ -155,8 +165,92 @@ | ||
| 155 | 165 | add_action( 'astra_notice_after_markup_sd-review-donation', [ $this, 'enqueue_notice_response_script' ] ); |
| 156 | 166 | } |
| 157 | 167 | |
| 158 | 168 | /** |
| 169 | + * Test-mode notice shown when a gateway is connected but the site is still | |
| 170 | + * in test mode (notice A2). | |
| 171 | + * | |
| 172 | + * Nudges the admin to switch to live mode so real donations can be | |
| 173 | + * accepted. Takes priority over the gateway review notice: there is no | |
| 174 | + * point asking for a review while the site cannot yet take real money. | |
| 175 | + * | |
| 176 | + * @return void | |
| 177 | + * @since 1.3.0 | |
| 178 | + */ | |
| 179 | + public function display_test_mode_notice() { | |
| 180 | + if ( ! Helper::current_user_can() ) { | |
| 181 | + return; | |
| 182 | + } | |
| 183 | + | |
| 184 | + if ( ! apply_filters( 'suredonation_show_test_mode_notice', true ) ) { | |
| 185 | + return; | |
| 186 | + } | |
| 187 | + | |
| 188 | + if ( ! class_exists( 'BSF_Admin_Notices' ) ) { | |
| 189 | + return; | |
| 190 | + } | |
| 191 | + | |
| 192 | + \BSF_Admin_Notices::add_notice( | |
| 193 | + [ | |
| 194 | + 'id' => 'sd-test-mode', | |
| 195 | + 'type' => '', | |
| 196 | + 'message' => $this->build_test_mode_notice_markup(), | |
| 197 | + 'repeat-notice-after' => WEEK_IN_SECONDS, | |
| 198 | + 'show_if' => $this->should_show_test_mode_notice() && ! $this->should_show_webhook_notice(), | |
| 199 | + 'display-with-other-notices' => true, | |
| 200 | + ] | |
| 201 | + ); | |
| 202 | + | |
| 203 | + add_action( 'astra_notice_after_markup_sd-test-mode', [ $this, 'enqueue_notice_response_script' ] ); | |
| 204 | + } | |
| 205 | + | |
| 206 | + /** | |
| 207 | + * Stripe webhook-not-configured notice. | |
| 208 | + * | |
| 209 | + * Shown on wp-admin pages when Stripe is connected but its webhook is not | |
| 210 | + * configured for the current mode, so donation/subscription statuses may not | |
| 211 | + * sync. Mirrors the SureForms webhook notice: a standard dismissible core | |
| 212 | + * notice (dismissal is per-page-load and reappears until the webhook is set). | |
| 213 | + * Takes priority over the test-mode banner, which is suppressed while this | |
| 214 | + * shows (matching the React dashboard notice chain). | |
| 215 | + * | |
| 216 | + * Hooked - admin_notices | |
| 217 | + * | |
| 218 | + * @return void | |
| 219 | + * @since 1.3.0 | |
| 220 | + */ | |
| 221 | + public function display_webhook_notice() { | |
| 222 | + if ( ! Helper::current_user_can() ) { | |
| 223 | + return; | |
| 224 | + } | |
| 225 | + | |
| 226 | + if ( ! $this->should_show_webhook_notice() ) { | |
| 227 | + return; | |
| 228 | + } | |
| 229 | + | |
| 230 | + // Load the analytics tracker so the configure-click and dismiss are | |
| 231 | + // recorded, matching the other notices (see handle_notice_response()). | |
| 232 | + $this->enqueue_notice_response_script(); | |
| 233 | + ?> | |
| 234 | + <div id="sd-webhook-not-configured" class="notice notice-error is-dismissible"> | |
| 235 | + <p> | |
| 236 | + <?php | |
| 237 | + printf( | |
| 238 | + /* translators: %1$s: link to configure the Stripe webhook */ | |
| 239 | + esc_html__( 'Webhooks keep SureDonation in sync with Stripe by automatically updating donation and subscription data. Please %1$s the webhook.', 'suredonation' ), | |
| 240 | + sprintf( | |
| 241 | + '<a class="sd-notice-cta" href="%1$s">%2$s</a>', | |
| 242 | + esc_url( Payment_Helper::get_settings_url( 'stripe' ) ), | |
| 243 | + esc_html__( 'configure', 'suredonation' ) | |
| 244 | + ) | |
| 245 | + ); | |
| 246 | + ?> | |
| 247 | + </p> | |
| 248 | + </div> | |
| 249 | + <?php | |
| 250 | + } | |
| 251 | + | |
| 252 | + /** | |
| 159 | 253 | * Review notice shown when a gateway is configured but there are no live |
| 160 | 254 | * donations yet (notice B). |
| 161 | 255 | * |
| 162 | 256 | * @return void |
| @@ -189,9 +283,11 @@ | ||
| 189 | 283 | WEEK_IN_SECONDS, |
| 190 | 284 | true |
| 191 | 285 | ), |
| 192 | 286 | 'repeat-notice-after' => WEEK_IN_SECONDS, |
| 193 | - 'show_if' => $this->is_three_days_elapsed() && ! $this->has_live_donation() && $this->is_gateway_configured(), | |
| 287 | + // In test mode the test-mode notice takes this slot instead, | |
| 288 | + // keeping the notice chain mutually exclusive. | |
| 289 | + 'show_if' => $this->is_three_days_elapsed() && ! $this->has_live_donation() && $this->is_gateway_configured() && ! $this->should_show_test_mode_notice(), | |
| 194 | 290 | 'display-with-other-notices' => true, |
| 195 | 291 | ] |
| 196 | 292 | ); |
| 197 | 293 | |
| @@ -223,9 +319,9 @@ | ||
| 223 | 319 | 'id' => 'sd-setup-gateway', |
| 224 | 320 | 'type' => '', |
| 225 | 321 | 'message' => $this->build_setup_notice_markup(), |
| 226 | 322 | 'repeat-notice-after' => WEEK_IN_SECONDS, |
| 227 | - 'show_if' => ! $this->has_live_donation() && ! $this->is_gateway_configured(), | |
| 323 | + 'show_if' => $this->should_show_setup_gateway_notice(), | |
| 228 | 324 | 'display-with-other-notices' => true, |
| 229 | 325 | ] |
| 230 | 326 | ); |
| 231 | 327 | |
| @@ -285,23 +381,36 @@ | ||
| 285 | 381 | $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( wp_unslash( $_POST['notice_id'] ) ) : ''; |
| 286 | 382 | $button = isset( $_POST['button'] ) ? sanitize_text_field( wp_unslash( $_POST['button'] ) ) : ''; |
| 287 | 383 | |
| 288 | 384 | $valid = [ |
| 289 | - 'sd-review-donation' => [ | |
| 385 | + 'sd-review-donation' => [ | |
| 290 | 386 | 'rate_suredonation' => 'review_notice_donation_cta', |
| 291 | 387 | 'maybe_later' => 'review_notice_donation_snooze', |
| 292 | 388 | 'dismissed' => 'review_notice_donation_dismiss', |
| 293 | 389 | ], |
| 294 | - 'sd-review-gateway' => [ | |
| 390 | + 'sd-test-mode' => [ | |
| 391 | + 'switch_to_live' => 'test_mode_notice_cta', | |
| 392 | + 'maybe_later' => 'test_mode_notice_snooze', | |
| 393 | + 'dismissed' => 'test_mode_notice_dismiss', | |
| 394 | + ], | |
| 395 | + 'sd-review-gateway' => [ | |
| 295 | 396 | 'rate_suredonation' => 'review_notice_gateway_cta', |
| 296 | 397 | 'maybe_later' => 'review_notice_gateway_snooze', |
| 297 | 398 | 'dismissed' => 'review_notice_gateway_dismiss', |
| 298 | 399 | ], |
| 299 | - 'sd-setup-gateway' => [ | |
| 400 | + 'sd-setup-gateway' => [ | |
| 300 | 401 | 'configure_gateway' => 'setup_gateway_notice_cta', |
| 301 | 402 | 'maybe_later' => 'setup_gateway_notice_snooze', |
| 302 | 403 | 'dismissed' => 'setup_gateway_notice_dismiss', |
| 303 | 404 | ], |
| 405 | + 'sd-paypal-reconnect' => [ | |
| 406 | + 'paypal_reconnect_notice_cta' => 'paypal_reconnect_notice_cta', | |
| 407 | + 'paypal_reconnect_notice_dismiss' => 'paypal_reconnect_notice_dismiss', | |
| 408 | + ], | |
| 409 | + 'sd-webhook-not-configured' => [ | |
| 410 | + 'configure_webhook' => 'webhook_notice_cta', | |
| 411 | + 'dismissed' => 'webhook_notice_dismiss', | |
| 412 | + ], | |
| 304 | 413 | ]; |
| 305 | 414 | |
| 306 | 415 | if ( ! isset( $valid[ $notice_id ][ $button ] ) ) { |
| 307 | 416 | wp_send_json_error( [ 'message' => __( 'Invalid parameters.', 'suredonation' ) ], 400 ); |
| @@ -379,39 +488,209 @@ | ||
| 379 | 488 | /** |
| 380 | 489 | * Build the markup for the "configure a payment gateway" setup notice |
| 381 | 490 | * (notice C). |
| 382 | 491 | * |
| 383 | - * This notice uses a dedicated banner layout (accent bar, icon, heading, | |
| 384 | - * body, primary CTA and a right-side illustration) styled via | |
| 385 | - * setup-gateway-notice.css, rather than the shared review-notice markup. | |
| 386 | - * | |
| 387 | 492 | * @return string The notice HTML markup. |
| 388 | 493 | * @since 1.2.0 |
| 389 | 494 | */ |
| 390 | 495 | private function build_setup_notice_markup() { |
| 496 | + return $this->build_banner_notice_markup( | |
| 497 | + esc_html__( 'Your donation site is almost ready!', 'suredonation' ), | |
| 498 | + esc_html__( 'Connect Stripe or PayPal and you can start accepting donations today. It takes a few minutes.', 'suredonation' ), | |
| 499 | + Payment_Helper::get_settings_url( 'stripe' ), | |
| 500 | + esc_html__( 'Connect Stripe or PayPal', 'suredonation' ), | |
| 501 | + SUREDONATION_URL . 'images/payment-gateway-notice.png' | |
| 502 | + ); | |
| 503 | + } | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * PayPal needs reconnecting in live mode notice. | |
| 507 | + * | |
| 508 | + * The partner client id used to be stored without a mode, so on a site that | |
| 509 | + * connected both test and live the last connect overwrote the other's value. | |
| 510 | + * A site left holding the sandbox id renders the sandbox SDK against a live | |
| 511 | + * merchant, PayPal refuses to reconcile that pairing, and no live donation | |
| 512 | + * can be approved at all — silently, since the donor never reaches approval | |
| 513 | + * and nothing is logged. | |
| 514 | + * | |
| 515 | + * The stored value is an opaque string with nothing distinguishing sandbox | |
| 516 | + * from production, so the environment cannot be recovered after the fact. | |
| 517 | + * Reconnecting in live mode writes the mode-specific value and settles it, | |
| 518 | + * which is all this notice asks for. | |
| 519 | + * | |
| 520 | + * Hooked - admin_notices | |
| 521 | + * | |
| 522 | + * @return void | |
| 523 | + * @since 1.5.1 | |
| 524 | + */ | |
| 525 | + public function display_paypal_reconnect_notice() { | |
| 526 | + if ( ! Helper::current_user_can() ) { | |
| 527 | + return; | |
| 528 | + } | |
| 529 | + | |
| 530 | + if ( ! $this->should_show_paypal_reconnect_notice() ) { | |
| 531 | + return; | |
| 532 | + } | |
| 533 | + | |
| 534 | + $this->enqueue_notice_response_script(); | |
| 535 | + ?> | |
| 536 | + <div id="sd-paypal-reconnect" class="notice notice-error is-dismissible"> | |
| 537 | + <p> | |
| 538 | + <?php | |
| 539 | + printf( | |
| 540 | + /* translators: %1$s: link to the PayPal payment settings */ | |
| 541 | + esc_html__( 'PayPal needs reconnecting before it can take live donations on this site. Test and live are separate PayPal connections, and an earlier version stored one of them over the other. Please %1$s while in live mode. It takes a moment and no settings are lost.', 'suredonation' ), | |
| 542 | + sprintf( | |
| 543 | + '<a class="sd-notice-cta" href="%1$s">%2$s</a>', | |
| 544 | + esc_url( Payment_Helper::get_settings_url( 'paypal' ) ), | |
| 545 | + esc_html__( 'reconnect PayPal', 'suredonation' ) | |
| 546 | + ) | |
| 547 | + ); | |
| 548 | + ?> | |
| 549 | + </p> | |
| 550 | + </div> | |
| 551 | + <?php | |
| 552 | + } | |
| 553 | + | |
| 554 | + /** | |
| 555 | + * Whether the PayPal reconnect notice is eligible to show. | |
| 556 | + * | |
| 557 | + * Scoped as tightly as the stored data allows: | |
| 558 | + * | |
| 559 | + * - **Both modes connected.** The only state where the stored value is | |
| 560 | + * ambiguous. A live-only site's value must be the production id, because | |
| 561 | + * nothing else could have written it; a test-only site takes no live | |
| 562 | + * donations either way. | |
| 563 | + * - **The legacy key is still in use for live.** Keyed on the live value | |
| 564 | + * rather than on both being empty: reconnecting test first would otherwise | |
| 565 | + * clear the notice while live — the mode that handles real money — was | |
| 566 | + * still loading a possibly-sandbox id. | |
| 567 | + * - **No completed live PayPal donation.** One is proof the stored value is | |
| 568 | + * the production id, so there is nothing to fix. (A site whose only live | |
| 569 | + * PayPal donation was later refunded no longer counts here and would be | |
| 570 | + * asked to reconnect unnecessarily — one wasted reconnect, against | |
| 571 | + * silently losing every live donation.) | |
| 572 | + * | |
| 573 | + * A site that connected test and then live, so its last connect wrote the | |
| 574 | + * correct value, and which has not taken a live PayPal donation yet, is | |
| 575 | + * asked to reconnect when it does not need to. That case cannot be told | |
| 576 | + * apart from a broken one — which is exactly why the mode is not inferred. | |
| 577 | + * | |
| 578 | + * @return bool | |
| 579 | + * @since 1.5.1 | |
| 580 | + */ | |
| 581 | + private function should_show_paypal_reconnect_notice() { | |
| 582 | + if ( ! PayPal_Helper::is_paypal_connected( 'live' ) || ! PayPal_Helper::is_paypal_connected( 'test' ) ) { | |
| 583 | + return false; | |
| 584 | + } | |
| 585 | + | |
| 586 | + $settings = PayPal_Helper::get_all_paypal_settings(); | |
| 587 | + | |
| 588 | + $legacy_client_id = isset( $settings['partner_client_id'] ) && is_string( $settings['partner_client_id'] ) | |
| 589 | + ? $settings['partner_client_id'] | |
| 590 | + : ''; | |
| 591 | + | |
| 592 | + if ( '' === $legacy_client_id ) { | |
| 593 | + return false; | |
| 594 | + } | |
| 595 | + | |
| 596 | + $live_client_id = isset( $settings['partner_client_id_live'] ) && is_string( $settings['partner_client_id_live'] ) | |
| 597 | + ? $settings['partner_client_id_live'] | |
| 598 | + : ''; | |
| 599 | + | |
| 600 | + if ( '' !== $live_client_id ) { | |
| 601 | + return false; | |
| 602 | + } | |
| 603 | + | |
| 604 | + // Cached like has_live_donation(): once a live PayPal donation exists the | |
| 605 | + // answer can never go back, so the count runs at most once per site. | |
| 606 | + if ( get_option( 'suredonation_has_live_paypal_donation' ) ) { | |
| 607 | + return false; | |
| 608 | + } | |
| 609 | + | |
| 610 | + if ( Donations::count_live_completed( 'paypal' ) >= 1 ) { | |
| 611 | + update_option( 'suredonation_has_live_paypal_donation', 1, false ); | |
| 612 | + | |
| 613 | + return false; | |
| 614 | + } | |
| 615 | + | |
| 616 | + return true; | |
| 617 | + } | |
| 618 | + | |
| 619 | + /** | |
| 620 | + * Build the markup for the "switch to live mode" test-mode notice | |
| 621 | + * (notice A2). | |
| 622 | + * | |
| 623 | + * @return string The notice HTML markup. | |
| 624 | + * @since 1.3.0 | |
| 625 | + */ | |
| 626 | + private function build_test_mode_notice_markup() { | |
| 627 | + // The PayPal sandbox requirement is deliberately not here. It is gateway | |
| 628 | + // advice rather than something about the site being in test mode, and it | |
| 629 | + // belongs beside the mode control in payment settings, where a merchant | |
| 630 | + // is choosing the mode rather than being told about it. | |
| 631 | + $text = esc_html__( 'Supporters cannot donate while your site is in test mode. Anything they try now is a test and no money reaches you. Switch to live mode when you are ready to accept real donations.', 'suredonation' ); | |
| 632 | + | |
| 633 | + return $this->build_banner_notice_markup( | |
| 634 | + esc_html__( 'SureDonation is in test mode', 'suredonation' ), | |
| 635 | + $text, | |
| 636 | + Payment_Helper::get_settings_url(), | |
| 637 | + esc_html__( 'Switch to Live Mode', 'suredonation' ) | |
| 638 | + ); | |
| 639 | + } | |
| 640 | + | |
| 641 | + /** | |
| 642 | + * Build the shared banner-notice markup (accent bar, icon, heading, body and | |
| 643 | + * a primary CTA, with an optional right-side illustration). | |
| 644 | + * | |
| 645 | + * Shared by the setup-gateway and test-mode notices; each is scoped by its | |
| 646 | + * wrapper id (#sd-setup-gateway / #sd-test-mode) in setup-gateway-notice.css | |
| 647 | + * so they can carry different accent colors from the same template. | |
| 648 | + * | |
| 649 | + * The text parameters must be pre-escaped by the caller (e.g. via | |
| 650 | + * esc_html__()); the URL and art path are escaped here. | |
| 651 | + * | |
| 652 | + * @param string $title The notice heading (pre-escaped). | |
| 653 | + * @param string $text The notice body text (pre-escaped). | |
| 654 | + * @param string $cta_url The primary CTA URL (raw; escaped here). | |
| 655 | + * @param string $cta_text The primary CTA button text (pre-escaped). | |
| 656 | + * @param string $art Optional right-side illustration URL (raw; escaped | |
| 657 | + * here). When empty, the banner drops the reserved | |
| 658 | + * art space via the --no-art modifier. | |
| 659 | + * @return string The notice HTML markup. | |
| 660 | + * @since 1.3.0 | |
| 661 | + */ | |
| 662 | + private function build_banner_notice_markup( $title, $text, $cta_url, $cta_text, $art = '' ) { | |
| 663 | + $has_art = '' !== $art; | |
| 664 | + $notice_class = $has_art ? 'sd-setup-notice' : 'sd-setup-notice sd-setup-notice--no-art'; | |
| 665 | + $art_markup = $has_art | |
| 666 | + ? sprintf( '<img class="sd-setup-notice__art" src="%s" alt="" width="187" height="128" />', esc_url( $art ) ) | |
| 667 | + : ''; | |
| 668 | + | |
| 391 | 669 | return sprintf( |
| 392 | - '<div class="sd-setup-notice"> | |
| 670 | + '<div class="%1$s"> | |
| 393 | 671 | <div class="sd-setup-notice__main"> |
| 394 | - <img class="sd-setup-notice__icon" src="%5$s" alt="" width="28" height="28" /> | |
| 672 | + <img class="sd-setup-notice__icon" src="%2$s" alt="" width="28" height="28" /> | |
| 395 | 673 | <div class="sd-setup-notice__body"> |
| 396 | - <h2 class="sd-setup-notice__title">%1$s</h2> | |
| 397 | - <p class="sd-setup-notice__text">%2$s</p> | |
| 398 | - <a href="%3$s" class="button button-primary sd-setup-notice__button">%4$s</a> | |
| 674 | + <h2 class="sd-setup-notice__title">%3$s</h2> | |
| 675 | + <p class="sd-setup-notice__text">%4$s</p> | |
| 676 | + <a href="%5$s" class="button button-primary sd-setup-notice__button">%6$s</a> | |
| 399 | 677 | </div> |
| 400 | 678 | </div> |
| 401 | - <img class="sd-setup-notice__art" src="%6$s" alt="" width="187" height="128" /> | |
| 679 | + %7$s | |
| 402 | 680 | </div>', |
| 403 | - esc_html__( 'Your donation site is almost ready!', 'suredonation' ), | |
| 404 | - esc_html__( 'Connect a payment gateway to start accepting donations. Set up Stripe or PayPal in just a few clicks to go live.', 'suredonation' ), | |
| 405 | - esc_url( admin_url( 'admin.php?page=suredonation#/settings?tab=payments&subpage=stripe' ) ), | |
| 406 | - esc_html__( 'Configure Payment Gateway', 'suredonation' ), | |
| 681 | + esc_attr( $notice_class ), | |
| 407 | 682 | esc_url( SUREDONATION_URL . 'images/suredonation-icon.svg' ), |
| 408 | - esc_url( SUREDONATION_URL . 'images/payment-gateway-notice.png' ) | |
| 683 | + $title, | |
| 684 | + $text, | |
| 685 | + esc_url( $cta_url ), | |
| 686 | + $cta_text, | |
| 687 | + $art_markup | |
| 409 | 688 | ); |
| 410 | 689 | } |
| 411 | 690 | |
| 412 | 691 | /** |
| 413 | - * Enqueue the setup-notice stylesheet. | |
| 692 | + * Enqueue the banner-notice stylesheet. | |
| 414 | 693 | * |
| 415 | 694 | * @return void |
| 416 | 695 | * @since 1.2.0 |
| 417 | 696 | */ |
| @@ -428,14 +707,14 @@ | ||
| 428 | 707 | ); |
| 429 | 708 | } |
| 430 | 709 | |
| 431 | 710 | /** |
| 432 | - * Enqueue the setup-notice stylesheet from the admin <head> when the setup | |
| 433 | - * notice is eligible to show. | |
| 711 | + * Enqueue the banner-notice stylesheet from the admin <head> when either | |
| 712 | + * banner notice (setup-gateway or test-mode) is eligible to show. | |
| 434 | 713 | * |
| 435 | 714 | * Hooked on admin_enqueue_scripts (which runs before admin_head) and gated |
| 436 | - * by the same conditions as the notice itself, so the stylesheet is in the | |
| 437 | - * page head before the banner paints. This avoids the flash of unstyled | |
| 715 | + * by the same conditions as the notices themselves, so the stylesheet is in | |
| 716 | + * the page head before the banner paints. This avoids the flash of unstyled | |
| 438 | 717 | * content that occurred when the CSS was enqueued on the notice's |
| 439 | 718 | * after-markup hook (which fires at admin_notices priority 30, after styles |
| 440 | 719 | * have already been printed). |
| 441 | 720 | * |
| @@ -441,18 +720,17 @@ | ||
| 441 | 720 | * |
| 442 | 721 | * @return void |
| 443 | 722 | * @since 1.2.0 |
| 444 | 723 | */ |
| 445 | - public function maybe_enqueue_setup_notice_style() { | |
| 724 | + public function maybe_enqueue_banner_notice_style() { | |
| 446 | 725 | if ( ! Helper::current_user_can() ) { |
| 447 | 726 | return; |
| 448 | 727 | } |
| 449 | 728 | |
| 450 | - if ( ! apply_filters( 'suredonation_show_setup_gateway_notice', true ) ) { | |
| 451 | - return; | |
| 452 | - } | |
| 729 | + $setup_eligible = apply_filters( 'suredonation_show_setup_gateway_notice', true ) && $this->should_show_setup_gateway_notice(); | |
| 730 | + $test_eligible = apply_filters( 'suredonation_show_test_mode_notice', true ) && $this->should_show_test_mode_notice(); | |
| 453 | 731 | |
| 454 | - if ( $this->has_live_donation() || $this->is_gateway_configured() ) { | |
| 732 | + if ( ! $setup_eligible && ! $test_eligible ) { | |
| 455 | 733 | return; |
| 456 | 734 | } |
| 457 | 735 | |
| 458 | 736 | $this->enqueue_setup_notice_style(); |
| @@ -458,8 +736,46 @@ | ||
| 458 | 736 | $this->enqueue_setup_notice_style(); |
| 459 | 737 | } |
| 460 | 738 | |
| 461 | 739 | /** |
| 740 | + * Whether the test-mode notice is eligible to show: a gateway is connected | |
| 741 | + * (in any mode) but the site is currently running in test mode. This fires | |
| 742 | + * regardless of past donations — a site switched back to test mode still | |
| 743 | + * needs the "switch to live" nudge — and takes priority over the review | |
| 744 | + * notices, which are suppressed while it is showing. | |
| 745 | + * | |
| 746 | + * @return bool | |
| 747 | + * @since 1.3.0 | |
| 748 | + */ | |
| 749 | + private function should_show_test_mode_notice() { | |
| 750 | + return $this->is_gateway_configured() | |
| 751 | + && 'test' === Payment_Helper::get_payment_mode(); | |
| 752 | + } | |
| 753 | + | |
| 754 | + /** | |
| 755 | + * Whether the webhook-not-configured notice is eligible to show: Stripe is | |
| 756 | + * connected but its webhook is not configured for the current mode. | |
| 757 | + * | |
| 758 | + * @return bool | |
| 759 | + * @since 1.3.0 | |
| 760 | + */ | |
| 761 | + private function should_show_webhook_notice() { | |
| 762 | + return Stripe_Helper::is_stripe_connected() | |
| 763 | + && ! Stripe_Helper::is_webhook_configured(); | |
| 764 | + } | |
| 765 | + | |
| 766 | + /** | |
| 767 | + * Whether the setup-gateway notice is eligible to show: no gateway is | |
| 768 | + * connected and no live donation has been recorded. | |
| 769 | + * | |
| 770 | + * @return bool | |
| 771 | + * @since 1.3.0 | |
| 772 | + */ | |
| 773 | + private function should_show_setup_gateway_notice() { | |
| 774 | + return ! $this->has_live_donation() && ! $this->is_gateway_configured(); | |
| 775 | + } | |
| 776 | + | |
| 777 | + /** | |
| 462 | 778 | * Whether the 3-day install grace has elapsed. |
| 463 | 779 | * |
| 464 | 780 | * @return bool |
| 465 | 781 | * @since 1.2.0 |
| @@ -521,13 +837,12 @@ | ||
| 521 | 837 | * @since 1.2.0 |
| 522 | 838 | */ |
| 523 | 839 | private function is_gateway_configured() { |
| 524 | 840 | if ( null === $this->gateway_configured ) { |
| 525 | - // "Configured" means connected in any mode. Stripe's check is | |
| 526 | - // mode-agnostic; PayPal's is per-mode, so check both explicitly. | |
| 527 | - $this->gateway_configured = Stripe_Helper::is_stripe_connected() | |
| 528 | - || PayPal_Helper::is_paypal_connected( 'live' ) | |
| 529 | - || PayPal_Helper::is_paypal_connected( 'test' ); | |
| 841 | + // "Configured" means connected in any mode; delegated to the shared | |
| 842 | + // Payment_Helper check (memoized here so repeated notice-chain reads | |
| 843 | + // only resolve it once per request). | |
| 844 | + $this->gateway_configured = Payment_Helper::is_any_gateway_connected(); | |
| 530 | 845 | } |
| 531 | 846 | |
| 532 | 847 | return $this->gateway_configured; |
| 533 | 848 | } |