| @@ -11,8 +11,9 @@ | ||
| 11 | 11 | namespace Better_Payment\Lite\Blocks; |
| 12 | 12 | |
| 13 | 13 | use Better_Payment\Lite\Admin\DB; |
| 14 | 14 | use Better_Payment\Lite\Classes\Handler; |
| 15 | +use Better_Payment\Lite\Classes\Helper; | |
| 15 | 16 | use Better_Payment\Lite\Traits\Helper as TraitsHelper; |
| 16 | 17 | |
| 17 | 18 | if ( ! defined( 'ABSPATH' ) ) { |
| 18 | 19 | exit; |
| @@ -149,14 +150,34 @@ | ||
| 149 | 150 | $wrapper_class = 'better-payment-block bp-form-' . esc_attr( $form_layout ); |
| 150 | 151 | $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $wrapper_class ) ); |
| 151 | 152 | |
| 152 | 153 | if ( ! $has_payment_gateway ) { |
| 154 | + // A visitor gets nothing: the block has no form to show, and the | |
| 155 | + // instruction that follows is addressed to whoever can act on it. | |
| 156 | + // It used to be printed for everyone, so a public page advertised | |
| 157 | + // that the site owner had not finished setting up payments and named | |
| 158 | + // the admin screen where it is done. | |
| 159 | + if ( ! current_user_can( 'manage_options' ) ) { | |
| 160 | + return ''; | |
| 161 | + } | |
| 162 | + | |
| 153 | 163 | return '<div ' . $wrapper_attributes . '>' . |
| 154 | - '<div class="better-payment-block__notice">' . | |
| 155 | - '<p>' . esc_html__( 'Please configure at least one payment gateway (PayPal, Stripe, or Paystack) in Better Payment settings.', 'better-payment' ) . '</p>' . | |
| 156 | - '</div></div>'; | |
| 164 | + $this->render_gateway_notice( $attributes, $global_settings ) . | |
| 165 | + '</div>'; | |
| 157 | 166 | } |
| 158 | 167 | |
| 168 | + // Enqueue scripts/styles before manage_response() so they are present even when the | |
| 169 | + // block returns the pending or success state early (the AJAX poller needs better-payment.js). | |
| 170 | + wp_enqueue_style( 'better-payment-el' ); | |
| 171 | + wp_enqueue_style( 'bp-icon-front' ); | |
| 172 | + wp_enqueue_style( 'better-payment-style' ); | |
| 173 | + wp_enqueue_style( 'better-payment-common-style' ); | |
| 174 | + wp_enqueue_style( 'better-payment-admin-style' ); | |
| 175 | + $this->enqueue_font_awesome(); | |
| 176 | + wp_enqueue_style( 'dashicons' ); | |
| 177 | + wp_enqueue_script( 'better-payment-common-script' ); | |
| 178 | + wp_enqueue_script( 'better-payment' ); | |
| 179 | + | |
| 159 | 180 | // Match Elementor flow: |
| 160 | 181 | // - Handle response before rendering form. |
| 161 | 182 | // - On success: render success notice only. |
| 162 | 183 | // - On error: render error notice and continue rendering the form. |
| @@ -193,19 +214,8 @@ | ||
| 193 | 214 | ) |
| 194 | 215 | ), |
| 195 | 216 | ); |
| 196 | 217 | |
| 197 | - // Enqueue the necessary scripts and styles. | |
| 198 | - wp_enqueue_style( 'better-payment-el' ); | |
| 199 | - wp_enqueue_style( 'bp-icon-front' ); | |
| 200 | - wp_enqueue_style( 'better-payment-style' ); | |
| 201 | - wp_enqueue_style( 'better-payment-common-style' ); | |
| 202 | - wp_enqueue_style( 'better-payment-admin-style' ); | |
| 203 | - $this->enqueue_font_awesome(); | |
| 204 | - wp_enqueue_style( 'dashicons' ); | |
| 205 | - wp_enqueue_script( 'better-payment-common-script' ); | |
| 206 | - wp_enqueue_script( 'better-payment' ); | |
| 207 | - | |
| 208 | 218 | // Use output buffering to capture the layout output. |
| 209 | 219 | ob_start(); |
| 210 | 220 | ?> |
| 211 | 221 | <div <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() is a safe WordPress core function. ?>> |
| @@ -239,10 +249,12 @@ | ||
| 239 | 249 | public function render_user_dashboard_frontend( $attributes, $content = '', $block = null ) { |
| 240 | 250 | // Get block attributes with defaults |
| 241 | 251 | $dashboard_layout = isset( $attributes['dashboardLayout'] ) ? sanitize_text_field( $attributes['dashboardLayout'] ) : 'layout-1'; |
| 242 | 252 | |
| 243 | - // Validate layout - only allow layout-1, layout-2, layout-3 plus pro versions | |
| 244 | - $allowed_layouts = array( 'layout-1', 'layout-2', 'layout-3', 'layout-1-pro', 'layout-2-pro', 'layout-3-pro' ); | |
| 253 | + // Validate layout. There are no Pro dashboard layouts — Pro extends the | |
| 254 | + // dashboard through the `better_payment/widget/user-dashboard/*` actions — | |
| 255 | + // so any other key (a stale `layout-*-pro` included) falls back to layout-1. | |
| 256 | + $allowed_layouts = array( 'layout-1', 'layout-2', 'layout-3' ); | |
| 245 | 257 | if ( ! in_array( $dashboard_layout, $allowed_layouts, true ) ) { |
| 246 | 258 | $dashboard_layout = 'layout-1'; |
| 247 | 259 | } |
| 248 | 260 | |
| @@ -248,14 +260,8 @@ | ||
| 248 | 260 | |
| 249 | 261 | // Check if layout file exists, fallback to layout-1 if not |
| 250 | 262 | $template_file = BETTER_PAYMENT_ADMIN_VIEWS_PATH . '/elementor/user-dashboard/' . $dashboard_layout . '.php'; |
| 251 | 263 | |
| 252 | - // Check for pro layout if pro plugin is active | |
| 253 | - $is_pro_layout = strpos( $dashboard_layout, '-pro' ) !== false; | |
| 254 | - if ( $is_pro_layout && defined( 'BETTER_PAYMENT_PRO_ADMIN_VIEWS_PATH' ) ) { | |
| 255 | - $template_file = BETTER_PAYMENT_PRO_ADMIN_VIEWS_PATH . '/elementor/layouts/' . $dashboard_layout . '.php'; | |
| 256 | - } | |
| 257 | - | |
| 258 | 264 | if ( ! file_exists( $template_file ) ) { |
| 259 | 265 | $template_file = BETTER_PAYMENT_ADMIN_VIEWS_PATH . '/elementor/user-dashboard/layout-1.php'; |
| 260 | 266 | if ( ! file_exists( $template_file ) ) { |
| 261 | 267 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
| @@ -637,9 +643,14 @@ | ||
| 637 | 643 | $stripe_price_id = ''; |
| 638 | 644 | } |
| 639 | 645 | |
| 640 | 646 | // Get email notification setting from block attributes. |
| 641 | - $email_notification_enabled = isset( $attributes['emailNotificationEnabled'] ) && $attributes['emailNotificationEnabled'] | |
| 647 | + // The editor default for emailNotificationEnabled is true, but WordPress does not | |
| 648 | + // serialize default attribute values into the block markup and the registered | |
| 649 | + // block.json declares no schema default, so the attribute is absent at render time | |
| 650 | + // unless explicitly toggled. Treat "unset" as enabled to match the editor default, | |
| 651 | + // while still honoring an explicit OFF (serialized as false). | |
| 652 | + $email_notification_enabled = ! isset( $attributes['emailNotificationEnabled'] ) || $attributes['emailNotificationEnabled'] | |
| 642 | 653 | ? 'yes' |
| 643 | 654 | : ''; |
| 644 | 655 | |
| 645 | 656 | // Get site domain for default email addresses. |
| @@ -997,8 +1008,165 @@ | ||
| 997 | 1008 | ); |
| 998 | 1009 | } |
| 999 | 1010 | |
| 1000 | 1011 | /** |
| 1012 | + * Why a Payment Form block has no gateway, decided per gateway. | |
| 1013 | + * | |
| 1014 | + * Each gateway resolves in `build_block_settings()` from the block's own | |
| 1015 | + * toggle when the block saved one, and from the site setting only when it | |
| 1016 | + * did not. So a gateway is off for one of two reasons, and they are fixed in | |
| 1017 | + * different places: | |
| 1018 | + * | |
| 1019 | + * - `block` — the site has a gateway enabled, but this block switches it | |
| 1020 | + * off. Settings cannot fix it: the gateway is already on there. | |
| 1021 | + * - `global` — no gateway is enabled site-wide. Settings fixes it on its own | |
| 1022 | + * for any gateway the block leaves to the site (`settings_fixes`); a | |
| 1023 | + * gateway the block also switched off needs both (`needs_block`). A new | |
| 1024 | + * block seeds its toggles from the settings, so on a site with nothing | |
| 1025 | + * enabled `needs_block` is the usual case. | |
| 1026 | + * | |
| 1027 | + * Mirrors the editor's `gatewayNoticeState()` (helpers/gatewayNotice.js), so | |
| 1028 | + * the editor and the page never give two different answers. | |
| 1029 | + * | |
| 1030 | + * @param array $attributes Block attributes. | |
| 1031 | + * @param array $global_settings Global plugin settings. | |
| 1032 | + * @return array{reason: string, site_gateways: string[], settings_fixes: string[], needs_block: bool} | |
| 1033 | + */ | |
| 1034 | + public static function gateway_notice_state( $attributes, $global_settings ) { | |
| 1035 | + $gateways = array( | |
| 1036 | + 'PayPal' => array( 'paypalEnabled', 'better_payment_settings_general_general_paypal' ), | |
| 1037 | + 'Stripe' => array( 'stripeEnabled', 'better_payment_settings_general_general_stripe' ), | |
| 1038 | + 'Paystack' => array( 'paystackEnabled', 'better_payment_settings_general_general_paystack' ), | |
| 1039 | + ); | |
| 1040 | + | |
| 1041 | + $site_gateways = array(); | |
| 1042 | + $settings_fixes = array(); | |
| 1043 | + | |
| 1044 | + foreach ( $gateways as $label => $keys ) { | |
| 1045 | + if ( isset( $global_settings[ $keys[1] ] ) && 'yes' === $global_settings[ $keys[1] ] ) { | |
| 1046 | + $site_gateways[] = $label; | |
| 1047 | + } | |
| 1048 | + if ( ! isset( $attributes[ $keys[0] ] ) ) { | |
| 1049 | + $settings_fixes[] = $label; | |
| 1050 | + } | |
| 1051 | + } | |
| 1052 | + | |
| 1053 | + $reason = empty( $site_gateways ) ? 'global' : 'block'; | |
| 1054 | + | |
| 1055 | + return array( | |
| 1056 | + 'reason' => $reason, | |
| 1057 | + 'site_gateways' => $site_gateways, | |
| 1058 | + 'settings_fixes' => $settings_fixes, | |
| 1059 | + 'needs_block' => 'block' === $reason || empty( $settings_fixes ), | |
| 1060 | + ); | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + /** | |
| 1064 | + * The admin-only notice printed in place of a form with no gateway. | |
| 1065 | + * | |
| 1066 | + * Names the gateways and the place that fixes them, and links to each place | |
| 1067 | + * the fix needs: Better Payment settings, this block's editor, or both. The | |
| 1068 | + * editor link is offered only when the block is really in the current post's | |
| 1069 | + * content — in a template, template part, synced pattern or widget area the | |
| 1070 | + * current post's editor does not contain it, so the notice says where to look | |
| 1071 | + * instead of linking to the wrong screen. | |
| 1072 | + * | |
| 1073 | + * @param array $attributes Block attributes. | |
| 1074 | + * @param array $global_settings Global plugin settings. | |
| 1075 | + * @return string Notice HTML. | |
| 1076 | + */ | |
| 1077 | + private function render_gateway_notice( $attributes, $global_settings ) { | |
| 1078 | + $state = self::gateway_notice_state( $attributes, $global_settings ); | |
| 1079 | + | |
| 1080 | + if ( 'block' === $state['reason'] ) { | |
| 1081 | + $message = sprintf( | |
| 1082 | + /* translators: %s: gateway names, e.g. "PayPal or Stripe". */ | |
| 1083 | + __( 'Payment form: every payment method is switched off in this block’s settings. Turn on %s under Payment Settings in the block sidebar.', 'better-payment' ), | |
| 1084 | + self::join_gateway_names( $state['site_gateways'] ) | |
| 1085 | + ); | |
| 1086 | + } elseif ( $state['needs_block'] ) { | |
| 1087 | + $message = __( 'Payment form: no payment gateway is enabled in Better Payment settings, and every payment method is switched off in this block’s settings. Enable PayPal, Stripe or Paystack in Better Payment settings, then turn the same gateway on under Payment Settings in the block sidebar.', 'better-payment' ); | |
| 1088 | + } else { | |
| 1089 | + $message = sprintf( | |
| 1090 | + /* translators: %s: gateway names, e.g. "PayPal, Stripe or Paystack". */ | |
| 1091 | + __( 'Payment form: enable at least one payment gateway (%s) in Better Payment settings.', 'better-payment' ), | |
| 1092 | + self::join_gateway_names( $state['settings_fixes'] ) | |
| 1093 | + ); | |
| 1094 | + } | |
| 1095 | + | |
| 1096 | + $links = array(); | |
| 1097 | + | |
| 1098 | + if ( 'global' === $state['reason'] ) { | |
| 1099 | + $links[] = '<a href="' . esc_url( admin_url( 'admin.php?page=better-payment-admin&tab=settings§ion=general' ) ) . '">' . | |
| 1100 | + esc_html__( 'Open Better Payment settings', 'better-payment' ) . '</a>'; | |
| 1101 | + } | |
| 1102 | + | |
| 1103 | + $hint = ''; | |
| 1104 | + | |
| 1105 | + if ( $state['needs_block'] ) { | |
| 1106 | + $edit_url = self::block_edit_url(); | |
| 1107 | + | |
| 1108 | + if ( '' !== $edit_url ) { | |
| 1109 | + $links[] = '<a href="' . esc_url( $edit_url ) . '">' . | |
| 1110 | + esc_html__( 'Edit this form in the block editor', 'better-payment' ) . '</a>'; | |
| 1111 | + } else { | |
| 1112 | + $hint = '<p>' . esc_html__( 'This form is not part of this page’s content. Open the template, template part, pattern or widget area that contains it to change its settings.', 'better-payment' ) . '</p>'; | |
| 1113 | + } | |
| 1114 | + } | |
| 1115 | + | |
| 1116 | + return '<div class="better-payment-block__notice">' . | |
| 1117 | + '<p>' . esc_html( $message ) . ' ' . esc_html__( 'This notice is only visible to you.', 'better-payment' ) . '</p>' . | |
| 1118 | + $hint . | |
| 1119 | + ( $links ? '<p class="better-payment-block__notice-actions">' . implode( ' ', $links ) . '</p>' : '' ) . | |
| 1120 | + '</div>'; | |
| 1121 | + } | |
| 1122 | + | |
| 1123 | + /** | |
| 1124 | + * The editor URL of the current post, when it holds a Payment Form block. | |
| 1125 | + * | |
| 1126 | + * Empty when there is no current post, the user may not edit it, or its | |
| 1127 | + * content has no Payment Form block (the block came from a template, template | |
| 1128 | + * part, synced pattern or widget area). | |
| 1129 | + * | |
| 1130 | + * @return string Raw edit URL, or ''. | |
| 1131 | + */ | |
| 1132 | + private static function block_edit_url() { | |
| 1133 | + $post_id = (int) get_the_ID(); | |
| 1134 | + | |
| 1135 | + if ( $post_id <= 0 || ! current_user_can( 'edit_post', $post_id ) ) { | |
| 1136 | + return ''; | |
| 1137 | + } | |
| 1138 | + | |
| 1139 | + if ( ! has_block( 'better-payment/payment-form', $post_id ) ) { | |
| 1140 | + return ''; | |
| 1141 | + } | |
| 1142 | + | |
| 1143 | + return (string) get_edit_post_link( $post_id, 'raw' ); | |
| 1144 | + } | |
| 1145 | + | |
| 1146 | + /** | |
| 1147 | + * "A", "A or B", "A, B or C". | |
| 1148 | + * | |
| 1149 | + * @param string[] $names Gateway names (brand names, not translated). | |
| 1150 | + * @return string Joined list. | |
| 1151 | + */ | |
| 1152 | + private static function join_gateway_names( $names ) { | |
| 1153 | + $names = array_values( $names ); | |
| 1154 | + $last = array_pop( $names ); | |
| 1155 | + | |
| 1156 | + if ( empty( $names ) ) { | |
| 1157 | + return (string) $last; | |
| 1158 | + } | |
| 1159 | + | |
| 1160 | + return sprintf( | |
| 1161 | + /* translators: 1: comma-separated gateway names, 2: the last gateway name. */ | |
| 1162 | + __( '%1$s or %2$s', 'better-payment' ), | |
| 1163 | + implode( ', ', $names ), | |
| 1164 | + $last | |
| 1165 | + ); | |
| 1166 | + } | |
| 1167 | + | |
| 1168 | + /** | |
| 1001 | 1169 | * Resolve the correct Stripe key based on live mode setting. |
| 1002 | 1170 | * |
| 1003 | 1171 | * This matches Elementor's behavior where the key is resolved before being stored |
| 1004 | 1172 | * in better_payment_stripe_public_key/better_payment_stripe_secret_key. |
| @@ -1599,8 +1767,13 @@ | ||
| 1599 | 1767 | 'better_payment_settings_payment_stripe_live_secret', |
| 1600 | 1768 | 'better_payment_settings_payment_stripe_test_secret', |
| 1601 | 1769 | 'better_payment_settings_payment_paystack_live_secret', |
| 1602 | 1770 | 'better_payment_settings_payment_paystack_test_secret', |
| 1771 | + // AI provider API keys (Campaign Builder); the block editor never needs them. | |
| 1772 | + 'better_payment_settings_ai_api_key_openai', | |
| 1773 | + 'better_payment_settings_ai_api_key_claude', | |
| 1774 | + 'better_payment_settings_ai_api_key_gemini', | |
| 1775 | + 'better_payment_settings_ai_api_key_openrouter', | |
| 1603 | 1776 | ); |
| 1604 | 1777 | |
| 1605 | 1778 | foreach ( $sensitive_keys as $key ) { |
| 1606 | 1779 | unset( $settings[ $key ] ); |
| @@ -1642,8 +1815,16 @@ | ||
| 1642 | 1815 | private function get_payment_form_editor_data() { |
| 1643 | 1816 | return array( |
| 1644 | 1817 | 'betterPaymentSettings' => $this->get_safe_settings_for_editor(), |
| 1645 | 1818 | 'adminEmail' => sanitize_email( get_option( 'admin_email' ) ), |
| 1819 | + // A form with no gateway enabled shows an editor notice instead of | |
| 1820 | + // nothing (components/gateway-notice.js). Both keys are about what to | |
| 1821 | + // tell the user: the link is only offered to someone who can act on | |
| 1822 | + // it, since an author who cannot open Better Payment's settings is | |
| 1823 | + // better told to ask an administrator than handed a link to a screen | |
| 1824 | + // they will be refused. | |
| 1825 | + 'settingsUrl' => admin_url( 'admin.php?page=better-payment-admin&tab=settings§ion=general' ), | |
| 1826 | + 'canManageSettings' => current_user_can( 'manage_options' ), | |
| 1646 | 1827 | ); |
| 1647 | 1828 | } |
| 1648 | 1829 | |
| 1649 | 1830 | /** |
| @@ -1662,9 +1843,12 @@ | ||
| 1662 | 1843 | // Cache the 5 DB queries for the duration of the browser session in the editor. |
| 1663 | 1844 | // The cache is keyed per user so each editor sees only their own data. |
| 1664 | 1845 | // TTL: 5 minutes — short enough to reflect recent transactions without hammering |
| 1665 | 1846 | // the DB on every page navigation inside the block editor. |
| 1666 | - $cache_key = 'bp_editor_dash_' . ( $current_user->ID ?? 0 ); | |
| 1847 | + // Entitlement is part of the key: subscription rows are fetched only for | |
| 1848 | + // Pro (below), so a cached free payload must never be served to Pro. | |
| 1849 | + $pro_enabled = (bool) apply_filters( 'better_payment/pro_enabled', false ); | |
| 1850 | + $cache_key = 'bp_editor_dash_' . ( $current_user->ID ?? 0 ) . ( $pro_enabled ? '_pro' : '' ); | |
| 1667 | 1851 | $cache_group = 'better_payment'; |
| 1668 | 1852 | $cached = wp_cache_get( $cache_key, $cache_group ); |
| 1669 | 1853 | if ( false !== $cached ) { |
| 1670 | 1854 | // Nonces are request-scoped and must never be cached; regenerate on every hit. |
| @@ -1702,8 +1886,12 @@ | ||
| 1702 | 1886 | ); |
| 1703 | 1887 | |
| 1704 | 1888 | // Unserialize and normalise to match UserAPI REST response format. |
| 1705 | 1889 | foreach ( $raw_transactions as $tx ) { |
| 1890 | + // The status bucket, as UserAPI's `status_type`: the preview labels the row from it | |
| 1891 | + // (an unconfirmed pre-2.4.0 free checkout reads Incomplete). | |
| 1892 | + $tx->status_type = \Better_Payment\Lite\Classes\Helper::transaction_status_type( $tx, 'v2' ); | |
| 1893 | + | |
| 1706 | 1894 | if ( ! empty( $tx->form_fields_info ) ) { |
| 1707 | 1895 | $tx->form_fields_info = maybe_unserialize( $tx->form_fields_info ); |
| 1708 | 1896 | } |
| 1709 | 1897 | if ( ! empty( $tx->customer_info ) ) { |
| @@ -1720,23 +1908,33 @@ | ||
| 1720 | 1908 | : ''; |
| 1721 | 1909 | } |
| 1722 | 1910 | $tx->customer_name = $customer_name; |
| 1723 | 1911 | $tx->customer_email = sanitize_text_field( $ffi['primary_email'] ?? ( $ffi['email'] ?? '' ) ); |
| 1912 | + | |
| 1913 | + // 'One Time' unless Pro labels the row (better_payment/transaction/payment_type), as UserAPI does. | |
| 1914 | + $tx->payment_type_label = \Better_Payment\Lite\Classes\Helper::payment_type_label( $ffi ); | |
| 1915 | + $tx->is_subscription = $tx->payment_type_label; | |
| 1724 | 1916 | } |
| 1725 | 1917 | |
| 1726 | 1918 | $user_transactions = apply_filters( 'better_payment/block/user_dashboard/enrich_user_transactions', $raw_transactions ); |
| 1727 | 1919 | |
| 1728 | 1920 | // Separate query for subscriptions (not a subset of transactions page 1). |
| 1729 | - $sub_result = \Better_Payment\Lite\Admin\DB::get_user_transactions_paginated( | |
| 1730 | - $current_user->user_email, | |
| 1731 | - array( | |
| 1732 | - 'page' => 1, | |
| 1733 | - 'per_page' => 20, | |
| 1734 | - 'type' => 'subscriptions', | |
| 1735 | - ) | |
| 1736 | - ); | |
| 1921 | + // These rows are Better Payment's own Stripe recurring and split payments — | |
| 1922 | + // Pro features that only Pro's Subscriptions tab displays — so a free | |
| 1923 | + // install skips the query and keeps the empty default. | |
| 1924 | + $raw_subscriptions = array(); | |
| 1925 | + if ( $pro_enabled ) { | |
| 1926 | + $sub_result = \Better_Payment\Lite\Admin\DB::get_user_transactions_paginated( | |
| 1927 | + $current_user->user_email, | |
| 1928 | + array( | |
| 1929 | + 'page' => 1, | |
| 1930 | + 'per_page' => 20, | |
| 1931 | + 'type' => 'subscriptions', | |
| 1932 | + ) | |
| 1933 | + ); | |
| 1934 | + $raw_subscriptions = $sub_result['transactions']; | |
| 1935 | + } | |
| 1737 | 1936 | |
| 1738 | - $raw_subscriptions = $sub_result['transactions']; | |
| 1739 | 1937 | foreach ( $raw_subscriptions as $tx ) { |
| 1740 | 1938 | if ( ! empty( $tx->form_fields_info ) ) { |
| 1741 | 1939 | $tx->form_fields_info = maybe_unserialize( $tx->form_fields_info ); |
| 1742 | 1940 | } |
| @@ -1746,17 +1944,24 @@ | ||
| 1746 | 1944 | // Flatten each subscription to match UserAPI flat format so JS handles one structure. |
| 1747 | 1945 | foreach ( $user_subscriptions as $tx ) { |
| 1748 | 1946 | $ffi = is_array( $tx->form_fields_info ) ? $tx->form_fields_info : array(); |
| 1749 | 1947 | |
| 1750 | - $tx->subscription_id = sanitize_text_field( $ffi['subscription_id'] ?? '' ); | |
| 1751 | - $tx->subscription_plan_id = sanitize_text_field( $ffi['subscription_plan_id'] ?? '' ); | |
| 1752 | - $tx->subscription_status = sanitize_text_field( $ffi['subscription_status'] ?? '' ); | |
| 1753 | - $tx->subscription_interval = sanitize_text_field( $ffi['subscription_interval'] ?? '' ); | |
| 1754 | - $tx->subscription_created_date = intval( $ffi['subscription_created_date'] ?? 0 ); | |
| 1755 | - $tx->subscription_current_period_end = intval( $ffi['subscription_current_period_end'] ?? 0 ); | |
| 1756 | - $tx->is_payment_split_payment = ! empty( $ffi['is_payment_split_payment'] ) ? intval( $ffi['is_payment_split_payment'] ) : 0; | |
| 1757 | - $tx->subscription_product_name = sanitize_text_field( $ffi['subscription_product_name'] ?? '' ); | |
| 1758 | - $tx->is_subscription = ! empty( $ffi['subscription_id'] ) ? 'Subscription' : 'One Time'; | |
| 1948 | + // The subscription columns come from the same place as UserAPI's REST rows: Pro fills | |
| 1949 | + // `better_payment/user_api/subscription_row`. | |
| 1950 | + $row = array(); | |
| 1951 | + $built = apply_filters( 'better_payment/user_api/subscription_row', $row, $ffi, $tx ); | |
| 1952 | + $row = is_array( $built ) ? $built : $row; | |
| 1953 | + | |
| 1954 | + foreach ( $row as $key => $value ) { | |
| 1955 | + $tx->{$key} = $value; | |
| 1956 | + } | |
| 1957 | + | |
| 1958 | + // The enrich_user_subscriptions filter above stores the product name in form_fields_info. | |
| 1959 | + if ( ! empty( $ffi['subscription_product_name'] ) ) { | |
| 1960 | + $tx->subscription_product_name = sanitize_text_field( $ffi['subscription_product_name'] ); | |
| 1961 | + } | |
| 1962 | + | |
| 1963 | + $tx->is_subscription = Helper::payment_type_label( $ffi ); | |
| 1759 | 1964 | |
| 1760 | 1965 | $customer_name = isset( $ffi['primary_first_name'] ) |
| 1761 | 1966 | ? trim( sanitize_text_field( $ffi['primary_first_name'] ) . ' ' . sanitize_text_field( $ffi['primary_last_name'] ?? '' ) ) |
| 1762 | 1967 | : ''; |