| @@ -171,13 +171,10 @@ | ||
| 171 | 171 | if ( empty( $this->currency ) || 'USD' === $this->currency ) { |
| 172 | 172 | $this->currency = Payment_Helper::get_currency(); |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | - // Get the publishable key for this form's selected account (mode-aware). | |
| 176 | - $this->stripe_publishable_key = Stripe_Helper::get_stripe_publishable_key( | |
| 177 | - '', | |
| 178 | - Stripe_Helper::resolve_account_for_form( $this->form_id ) | |
| 179 | - ); | |
| 175 | + // Get appropriate Stripe publishable key based on mode. | |
| 176 | + $this->stripe_publishable_key = Stripe_Helper::get_stripe_publishable_key(); | |
| 180 | 177 | |
| 181 | 178 | // Fall back to one-time if subscription is configured but pro is not active. |
| 182 | 179 | $configured_type = $attributes['paymentType'] ?? 'one-time'; |
| 183 | 180 | $this->payment_type = 'subscription' === $configured_type && ! defined( 'SUREDONATION_PRO_VER' ) ? 'one-time' : $configured_type; |
| @@ -240,25 +237,24 @@ | ||
| 240 | 237 | * @return string |
| 241 | 238 | * @since 0.0.1 |
| 242 | 239 | */ |
| 243 | 240 | public function markup() { |
| 244 | - $has_stripe = in_array( 'stripe', $this->payment_methods, true ); | |
| 241 | + $has_stripe = in_array( 'stripe', $this->payment_methods, true ); | |
| 242 | + $has_offline = in_array( 'offline', $this->payment_methods, true ); | |
| 245 | 243 | |
| 246 | - // Determine which gateways are actually connected/available for this form. | |
| 247 | - // get_registered_payment_methods() returns only enabled methods (Stripe when | |
| 248 | - // connected, Offline when enabled, plus any added by extensions such as | |
| 249 | - // PayPal), so it is the single gateway-agnostic source of truth. | |
| 250 | - $methods = $this->get_registered_payment_methods(); | |
| 244 | + // If only Stripe is selected, it must be connected. | |
| 245 | + if ( $has_stripe && ! $has_offline && ( ! $this->stripe_connected || empty( $this->stripe_publishable_key ) ) ) { | |
| 246 | + return ''; | |
| 247 | + } | |
| 251 | 248 | |
| 252 | - // No payment gateway is connected/available. Show a clear message instead of | |
| 253 | - // returning an empty string, which previously left donors with a silently | |
| 254 | - // broken form and no way to donate. See issue #219. | |
| 255 | - if ( empty( $methods ) ) { | |
| 256 | - return $this->render_gateway_unavailable_notice(); | |
| 249 | + // If no gateway is available at all, hide the block. | |
| 250 | + if ( empty( $this->payment_methods ) ) { | |
| 251 | + return ''; | |
| 257 | 252 | } |
| 258 | 253 | |
| 259 | 254 | // Validate payment field requirements. |
| 260 | - if ( ! $this->validate_payment_requirements() ) { | |
| 255 | + $is_valid = $this->validate_payment_requirements(); | |
| 256 | + if ( ! $is_valid ) { | |
| 261 | 257 | return ''; |
| 262 | 258 | } |
| 263 | 259 | |
| 264 | 260 | $field_classes = $this->get_field_classes(); |
| @@ -308,18 +304,15 @@ | ||
| 308 | 304 | if ( 'test' === $this->payment_mode && $has_stripe ) { |
| 309 | 305 | echo wp_kses_post( $this->get_test_mode_notice() ); |
| 310 | 306 | } |
| 311 | 307 | |
| 312 | - // Admin-only: the form renders (Offline is enabled) but no real | |
| 313 | - // gateway is connected. | |
| 314 | - echo wp_kses_post( $this->get_gateway_setup_notice() ); | |
| 315 | - | |
| 316 | - // Payment methods accordion ( $methods computed above ). | |
| 308 | + // Payment methods accordion. | |
| 309 | + $methods = $this->get_registered_payment_methods(); | |
| 317 | 310 | echo $this->render_payment_methods_accordion( $methods ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Method builds markup with esc_attr/esc_html on all dynamic values and wp_kses_post on content. |
| 318 | 311 | ?> |
| 319 | 312 | |
| 320 | - <!-- Payment error display (hidden via CSS; shown by JS on error). --> | |
| 321 | - <div class="sd-payment-error"></div> | |
| 313 | + <!-- Payment error display --> | |
| 314 | + <div class="sd-payment-error" style="display: none;"></div> | |
| 322 | 315 | </div> |
| 323 | 316 | </div> |
| 324 | 317 | <?php |
| 325 | 318 | $output = ob_get_clean(); |
| @@ -342,9 +335,9 @@ | ||
| 342 | 335 | 'container_id' => 'sd-payment-element-' . $this->block_id, |
| 343 | 336 | ], |
| 344 | 337 | 'offline' => [ |
| 345 | 338 | 'id' => 'offline', |
| 346 | - 'label' => __( 'Offline Donation', 'suredonation' ), | |
| 339 | + 'label' => __( 'Offline Payment', 'suredonation' ), | |
| 347 | 340 | 'enabled' => Offline_Helper::is_offline_enabled(), |
| 348 | 341 | 'container_class' => 'sd-offline-instructions', |
| 349 | 342 | 'content' => Offline_Helper::get_offline_instructions( $this->get_campaign_name() ), |
| 350 | 343 | ], |
| @@ -414,20 +407,15 @@ | ||
| 414 | 407 | <div |
| 415 | 408 | class="sd-accordion-item <?php echo esc_attr( $is_first ? 'sd-payment-active' : '' ); ?>" |
| 416 | 409 | data-method="<?php echo esc_attr( $method['id'] ); ?>" |
| 417 | 410 | > |
| 418 | - <?php if ( $is_single_method ) { ?> | |
| 419 | - <?php // Single method: the header is a static label, not an accordion toggle, so omit the interactive button semantics (keyboard/screen-reader users shouldn't hit an inert "button"). ?> | |
| 420 | - <div class="sd-accordion-header"> | |
| 421 | - <?php } else { ?> | |
| 422 | - <div | |
| 423 | - class="sd-accordion-header" | |
| 424 | - role="button" | |
| 425 | - tabindex="0" | |
| 426 | - aria-expanded="<?php echo esc_attr( $is_first ? 'true' : 'false' ); ?>" | |
| 427 | - aria-controls="sd-accordion-content-<?php echo esc_attr( $method['id'] ); ?>-<?php echo esc_attr( $this->block_id ); ?>" | |
| 428 | - > | |
| 429 | - <?php } ?> | |
| 411 | + <div | |
| 412 | + class="sd-accordion-header" | |
| 413 | + role="button" | |
| 414 | + tabindex="0" | |
| 415 | + aria-expanded="<?php echo esc_attr( $is_first ? 'true' : 'false' ); ?>" | |
| 416 | + aria-controls="sd-accordion-content-<?php echo esc_attr( $method['id'] ); ?>-<?php echo esc_attr( $this->block_id ); ?>" | |
| 417 | + > | |
| 430 | 418 | <div class="sd-payment-input-wrapper"> |
| 431 | 419 | <input |
| 432 | 420 | type="radio" |
| 433 | 421 | name="sd-gateway-choice-<?php echo esc_attr( $this->block_id ); ?>" |
| @@ -443,9 +431,9 @@ | ||
| 443 | 431 | </div> |
| 444 | 432 | </div> |
| 445 | 433 | <div |
| 446 | 434 | id="sd-accordion-content-<?php echo esc_attr( $method['id'] ); ?>-<?php echo esc_attr( $this->block_id ); ?>" |
| 447 | - class="sd-accordion-content<?php echo Offline_Helper::is_blank_instructions( $method['content'] ?? '' ) ? ' sd-accordion-content-empty' : ''; ?>" | |
| 435 | + class="sd-accordion-content" | |
| 448 | 436 | role="region" |
| 449 | 437 | > |
| 450 | 438 | <div |
| 451 | 439 | class="sd-payment-method-content" |
| @@ -616,11 +604,17 @@ | ||
| 616 | 604 | * @return string |
| 617 | 605 | * @since 0.0.1 |
| 618 | 606 | */ |
| 619 | 607 | private function format_currency( $amount, $currency ) { |
| 620 | - // Delegate to the single source of truth so decimal handling and the | |
| 621 | - // currency sign position stay consistent with every other surface. | |
| 622 | - return Payment_Helper::format_amount( $amount, $currency ); | |
| 608 | + $symbol = Payment_Helper::get_currency_symbol( $currency ); | |
| 609 | + | |
| 610 | + // Format based on currency. | |
| 611 | + if ( in_array( $currency, [ 'JPY', 'KRW' ], true ) ) { | |
| 612 | + // No decimal places for these currencies. | |
| 613 | + return $symbol . number_format( $amount, 0 ); | |
| 614 | + } | |
| 615 | + | |
| 616 | + return $symbol . number_format( $amount, 2 ); | |
| 623 | 617 | } |
| 624 | 618 | |
| 625 | 619 | /** |
| 626 | 620 | * Get the human-readable label for a payment interval slug. |
| @@ -653,10 +647,10 @@ | ||
| 653 | 647 | if ( ! current_user_can( 'manage_options' ) ) { |
| 654 | 648 | return ''; |
| 655 | 649 | } |
| 656 | 650 | |
| 657 | - // Build dynamic link to payment settings (shared, hash-routed URL). | |
| 658 | - $settings_url = Payment_Helper::get_settings_url(); | |
| 651 | + // Build dynamic link to payment settings. | |
| 652 | + $settings_url = admin_url( 'admin.php?page=suredonation_settings&tab=payments' ); | |
| 659 | 653 | |
| 660 | 654 | ob_start(); |
| 661 | 655 | ?> |
| 662 | 656 | <div class="sd-test-mode-notice"> |
| @@ -664,138 +658,8 @@ | ||
| 664 | 658 | <a href="<?php echo esc_url( $settings_url ); ?>" target="_blank" rel="noopener noreferrer"> |
| 665 | 659 | <?php esc_html_e( 'Click here to enable live mode and accept payment', 'suredonation' ); ?> |
| 666 | 660 | </a> |
| 667 | 661 | </div> |
| 668 | - <?php | |
| 669 | - $output = ob_get_clean(); | |
| 670 | - return false !== $output ? $output : ''; | |
| 671 | - } | |
| 672 | - | |
| 673 | - /** | |
| 674 | - * Render the notice shown when no payment gateway is connected/available. | |
| 675 | - * | |
| 676 | - * Replaces the previous behavior of rendering nothing, which left donors with | |
| 677 | - * a silently broken form. The `data-payment-available="0"` marker lets the | |
| 678 | - * frontend script skip gateway init and hide the otherwise-inert submit | |
| 679 | - * button. See issue #219. | |
| 680 | - * | |
| 681 | - * @return string Notice markup. | |
| 682 | - * @since 1.1.1 | |
| 683 | - */ | |
| 684 | - private function render_gateway_unavailable_notice() { | |
| 685 | - $field_classes = $this->get_field_classes( [ 'sd-payment-unavailable' ] ); | |
| 686 | - | |
| 687 | - // Both audiences are told the gateways are not configured, but only donors | |
| 688 | - // are told to contact the administrator — an admin *is* that person and | |
| 689 | - // gets the actionable button below, so the sentence would send them in a | |
| 690 | - // circle. Mirrors the capability + settings-URL pattern used by | |
| 691 | - // get_test_mode_notice(). | |
| 692 | - $is_admin = current_user_can( 'manage_options' ); | |
| 693 | - $configure_url = ''; | |
| 694 | - $configure_text = ''; | |
| 695 | - $notice_text = $is_admin | |
| 696 | - ? __( 'Payment gateways are not configured.', 'suredonation' ) | |
| 697 | - : __( 'Payment gateways are not configured. Please contact the site administrator.', 'suredonation' ); | |
| 698 | - | |
| 699 | - if ( $is_admin ) { | |
| 700 | - // Route the admin to the right place. If a gateway is already usable | |
| 701 | - // on the site, the block simply hasn't selected it — send them to | |
| 702 | - // this form's editor to fix the payment block. Otherwise no gateway | |
| 703 | - // is set up at all — send them to global payment settings. | |
| 704 | - $edit_link = $this->form_id > 0 ? get_edit_post_link( (int) $this->form_id ) : ''; | |
| 705 | - | |
| 706 | - if ( Payment_Helper::has_usable_gateway() && $edit_link ) { | |
| 707 | - $configure_url = $edit_link; | |
| 708 | - $configure_text = __( 'Edit this form’s payment settings', 'suredonation' ); | |
| 709 | - } else { | |
| 710 | - // No gateway connected — send the admin straight to the gateway | |
| 711 | - // connect screen (Stripe) rather than the currency/mode page. | |
| 712 | - $configure_url = Payment_Helper::get_settings_url( 'stripe' ); | |
| 713 | - $configure_text = __( 'Configure payment gateway', 'suredonation' ); | |
| 714 | - } | |
| 715 | - } | |
| 716 | - | |
| 717 | - ob_start(); | |
| 718 | - ?> | |
| 719 | - <div | |
| 720 | - data-block-id="<?php echo esc_attr( $this->block_id ); ?>" | |
| 721 | - data-form-id="<?php echo esc_attr( $this->form_id ); ?>" | |
| 722 | - data-payment-available="0" | |
| 723 | - class="<?php echo esc_attr( $field_classes ); ?>" | |
| 724 | - > | |
| 725 | - <?php echo wp_kses_post( $this->label_markup ); ?> | |
| 726 | - <div class="sd-payment-field-wrapper"> | |
| 727 | - <div class="sd-payment-notice" role="status"> | |
| 728 | - <p><?php echo esc_html( $notice_text ); ?></p> | |
| 729 | - <?php echo wp_kses_post( $is_admin ? $this->render_configure_link( $configure_url, $configure_text ) : '' ); ?> | |
| 730 | - </div> | |
| 731 | - </div> | |
| 732 | - </div> | |
| 733 | - <?php | |
| 734 | - $output = ob_get_clean(); | |
| 735 | - return false !== $output ? $output : ''; | |
| 736 | - } | |
| 737 | - | |
| 738 | - /** | |
| 739 | - * Render the admin-only notice shown when the payment field renders but no | |
| 740 | - * real payment gateway is connected. | |
| 741 | - * | |
| 742 | - * Enabling Offline Donation makes the field render normally, so the | |
| 743 | - * donor-facing "gateways are not configured" notice never fires — an admin | |
| 744 | - * viewing the live form gets no signal that neither Stripe nor PayPal was | |
| 745 | - * ever connected. Offline is a manual method, not a gateway, so the prompt | |
| 746 | - * is still warranted. Donors see nothing: the form works for them. | |
| 747 | - * | |
| 748 | - * @return string Notice markup, or empty string when a gateway is connected | |
| 749 | - * or the viewer is not an administrator. | |
| 750 | - * @since 1.4.0 | |
| 751 | - */ | |
| 752 | - private function get_gateway_setup_notice() { | |
| 753 | - if ( ! current_user_can( 'manage_options' ) || Payment_Helper::is_any_gateway_connected() ) { | |
| 754 | - return ''; | |
| 755 | - } | |
| 756 | - | |
| 757 | - ob_start(); | |
| 758 | - ?> | |
| 759 | - <div class="sd-payment-notice sd-payment-notice--admin" role="status"> | |
| 760 | - <p><?php esc_html_e( 'No payment gateway is connected, so donors can only give using the offline method.', 'suredonation' ); ?></p> | |
| 761 | - <?php | |
| 762 | - echo wp_kses_post( | |
| 763 | - $this->render_configure_link( | |
| 764 | - Payment_Helper::get_settings_url( 'stripe' ), | |
| 765 | - __( 'Configure payment gateway', 'suredonation' ) | |
| 766 | - ) | |
| 767 | - ); | |
| 768 | - ?> | |
| 769 | - </div> | |
| 770 | - <?php | |
| 771 | - $output = ob_get_clean(); | |
| 772 | - return false !== $output ? $output : ''; | |
| 773 | - } | |
| 774 | - | |
| 775 | - /** | |
| 776 | - * Render the actionable settings link shared by the payment notices. | |
| 777 | - * | |
| 778 | - * @param string $url Target URL. | |
| 779 | - * @param string $text Link text. | |
| 780 | - * @return string Link markup, or empty string when there is nothing to link to. | |
| 781 | - * @since 1.4.0 | |
| 782 | - */ | |
| 783 | - private function render_configure_link( $url, $text ) { | |
| 784 | - if ( '' === $url || '' === $text ) { | |
| 785 | - return ''; | |
| 786 | - } | |
| 787 | - | |
| 788 | - ob_start(); | |
| 789 | - ?> | |
| 790 | - <a | |
| 791 | - class="sd-payment-notice__configure" | |
| 792 | - href="<?php echo esc_url( $url ); ?>" | |
| 793 | - target="_blank" | |
| 794 | - rel="noopener noreferrer" | |
| 795 | - > | |
| 796 | - <?php echo esc_html( $text ); ?> | |
| 797 | - </a> | |
| 798 | 662 | <?php |
| 799 | 663 | $output = ob_get_clean(); |
| 800 | 664 | return false !== $output ? $output : ''; |
| 801 | 665 | } |