array( $this, 'portal' ) ) ); wp_set_script_translations( 'bm-ap-portal-block', 'buildmerce-agent-orders', BM_AP_DIR . 'languages' ); } public static function activate() { BM_AP_Settings::initialize( (int) get_option( 'bm_ap_page_id' ) > 0 ); add_role( self::ROLE, __( 'Payment Agent', 'buildmerce-agent-orders' ), array( 'read' => true, self::CAP => true ) ); $role = get_role( self::ROLE ); if ( $role ) { $role->add_cap( self::CAP ); } $page_id = (int) get_option( 'bm_ap_page_id' ); if ( ! $page_id || 'page' !== get_post_type( $page_id ) ) { $id = wp_insert_post( array( 'post_title' => __( 'Agent Payment Requests', 'buildmerce-agent-orders' ), 'post_name' => 'agent-payments', 'post_type' => 'page', 'post_status' => 'publish', 'post_content' => '', 'comment_status' => 'closed', 'ping_status' => 'closed', ), true ); if ( ! is_wp_error( $id ) ) { update_option( 'bm_ap_page_id', $id, false ); } } if ( ! wp_next_scheduled( 'bm_ap_cleanup_requests' ) ) { wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'bm_ap_cleanup_requests' ); } } public static function deactivate() { wp_clear_scheduled_hook( 'bm_ap_cleanup_requests' ); // Orders, agent accounts and the portal page remain intact. } public static function cleanup() { global $wpdb; $like = $wpdb->esc_like( 'bm_ap_req_' ) . '%'; $rows = $wpdb->get_results( $wpdb->prepare( "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE %s LIMIT 2000", $like ) ); foreach ( $rows as $row ) { $value = maybe_unserialize( $row->option_value ); // Request nonces cannot remain valid beyond 24 hours; keep records for at least 48. if ( is_array( $value ) && isset( $value['time'] ) && (int) $value['time'] < time() - 2 * DAY_IN_SECONDS ) { delete_option( $row->option_name ); } } } private function portal_url() { $page = (int) BM_AP_Settings::get( 'portal_page' ) ?: (int) get_option( 'bm_ap_page_id' ); $url = get_permalink( $page ); return $url ? $url : home_url( '/agent-payments/' ); } private function allowed() { return is_user_logged_in() && ( current_user_can( self::CAP ) || current_user_can( 'manage_woocommerce' ) ); } private function agent_only() { return current_user_can( self::CAP ) && ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ); } public function deny() { wp_die( esc_html__( 'You must sign in with an authorized agent account.', 'buildmerce-agent-orders' ), esc_html__( 'Access denied', 'buildmerce-agent-orders' ), array( 'response' => 403 ) ); } public function admin_bar( $show ) { return $this->agent_only() ? false : $show; } public function block_agent_admin() { $script = isset( $_SERVER['SCRIPT_NAME'] ) && is_string( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : ''; if ( $this->agent_only() && ! wp_doing_ajax() && 'admin-post.php' !== basename( $script ) ) { wp_safe_redirect( $this->portal_url() ); exit; } } public function login_redirect( $redirect, $requested, $user ) { if ( $user instanceof WP_User && $user->has_cap( self::CAP ) && ! $user->has_cap( 'manage_woocommerce' ) ) { return $this->portal_url(); } return $redirect; } private function is_portal() { $post = get_post(); $page_id = (int) BM_AP_Settings::get( 'portal_page' ) ?: (int) get_option( 'bm_ap_page_id' ); return ( $page_id > 0 && is_page( $page_id ) ) || ( $post && ( has_shortcode( $post->post_content, 'buildmerce_agent_paylater' ) || has_block( 'buildmerce/agent-portal', $post ) ) ); } public function protect_page() { $is_portal = $this->is_portal(); $is_agent_payment = is_wc_endpoint_url( 'order-pay' ) && $this->pay_order(); $is_agent_confirmation = is_wc_endpoint_url( 'order-received' ) && false === $this->email_verification_required( true, wc_get_order( absint( get_query_var( 'order-received' ) ) ), 'order-received' ); $is_agent_link = $is_agent_payment || $is_agent_confirmation; if ( ! $is_portal && ! $is_agent_link ) { return; } if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); } nocache_headers(); header( 'Cache-Control: no-store, private, max-age=0' ); header( 'X-Robots-Tag: noindex, nofollow', true ); header( 'Referrer-Policy: ' . ( $is_agent_link ? 'no-referrer' : 'same-origin' ) ); if ( $is_portal && 'portal' === BM_AP_Settings::get( 'login' ) && ! is_user_logged_in() && $this->is_post_request() ) { $this->login(); } } private function login() { if ( ! $this->is_post_request() || ! is_ssl() ) { $this->login_error( __( 'Use the HTTPS version of this page to sign in.', 'buildmerce-agent-orders' ) ); return; } $nonce = isset( $_POST['bm_ap_login_nonce'] ) && is_string( $_POST['bm_ap_login_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['bm_ap_login_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'bm_ap_login' ) ) { $this->login_error( __( 'Your sign-in form expired. Reload this page and try again.', 'buildmerce-agent-orders' ) ); return; } $ip = isset( $_SERVER['REMOTE_ADDR'] ) && is_string( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; $key = 'bm_ap_login_' . hash_hmac( 'sha256', $ip, wp_salt( 'auth' ) ); $attempts = (int) get_transient( $key ); if ( $attempts >= 10 ) { $this->login_error( __( 'Too many attempts. Please try again in 15 minutes.', 'buildmerce-agent-orders' ) ); return; } $login = isset( $_POST['log'] ) && is_string( $_POST['log'] ) ? sanitize_text_field( wp_unslash( $_POST['log'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Passwords must remain unchanged; nonce verification and wp_authenticate() handle this credential. $pass = isset( $_POST['pwd'] ) && is_string( $_POST['pwd'] ) ? wp_unslash( $_POST['pwd'] ) : ''; $user = wp_authenticate( $login, $pass ); if ( is_wp_error( $user ) || ! ( $user->has_cap( self::CAP ) || $user->has_cap( 'manage_woocommerce' ) ) ) { set_transient( $key, $attempts + 1, 15 * MINUTE_IN_SECONDS ); $this->login_error( __( 'Unable to sign in. Check your agent username and password.', 'buildmerce-agent-orders' ) ); return; } delete_transient( $key ); wp_set_current_user( $user->ID ); wp_set_auth_cookie( $user->ID, ! empty( $_POST['rememberme'] ), true ); do_action( 'wp_login', $user->user_login, $user ); wp_safe_redirect( $this->portal_url() ); exit; } private function login_error( $message ) { $GLOBALS['bm_ap_login_error'] = $message; } public function assets() { if ( $this->is_portal() ) { wp_enqueue_style( 'bm-agent-paylater', plugins_url( 'assets/portal.css', BM_AP_FILE ), array(), BM_AP_VERSION ); wp_enqueue_script( 'bm-agent-paylater', plugins_url( 'assets/portal.js', BM_AP_FILE ), array( 'wp-i18n' ), BM_AP_VERSION, true ); wp_set_script_translations( 'bm-agent-paylater', 'buildmerce-agent-orders', BM_AP_DIR . 'languages' ); } if ( is_wc_endpoint_url( 'order-pay' ) ) { wp_enqueue_script( 'wc-country-select' ); wp_enqueue_script( 'wc-address-i18n' ); if ( $this->pay_order() ) { wp_enqueue_style( 'bm-agent-payment', plugins_url( 'assets/payment.css', BM_AP_FILE ), array(), BM_AP_VERSION ); wp_enqueue_script( 'bm-agent-payment', plugins_url( 'assets/payment.js', BM_AP_FILE ), array( 'jquery', 'wc-country-select', 'wc-address-i18n' ), BM_AP_VERSION, true ); } } } private function flash( $message, $type = 'success', $fields = array() ) { $token = wp_generate_uuid4(); set_transient( 'bm_ap_flash_' . get_current_user_id() . '_' . $token, array( 'message' => $message, 'type' => $type, 'fields' => $fields ), 10 * MINUTE_IN_SECONDS ); return $token; } private function finish( $message, $type = 'success', $order_id = 0, $fields = array() ) { $args = array( 'notice' => $this->flash( $message, $type, $fields ) ); if ( $order_id ) { $args['request'] = $order_id; } wp_safe_redirect( add_query_arg( $args, $this->portal_url() ) ); exit; } private function is_post_request() { $method = isset( $_SERVER['REQUEST_METHOD'] ) && is_string( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : ''; return 'POST' === $method; } private function owns( $order ) { return $order && $order->get_meta( '_bm_ap_agent_id' ) && ( (int) $order->get_meta( '_bm_ap_agent_id' ) === get_current_user_id() || current_user_can( 'manage_woocommerce' ) ); } public function create() { if ( ! is_user_logged_in() || ( ! current_user_can( self::CAP ) && ! current_user_can( 'manage_woocommerce' ) ) ) { $this->deny(); return; } if ( ! $this->is_post_request() || ! is_ssl() ) { wp_die( esc_html__( 'Use a secure HTTPS form to submit payment requests.', 'buildmerce-agent-orders' ), esc_html__( 'Invalid request', 'buildmerce-agent-orders' ), array( 'response' => 400 ) ); return; } $token = isset( $_POST['request_token'] ) && is_string( $_POST['request_token'] ) ? sanitize_text_field( wp_unslash( $_POST['request_token'] ) ) : ''; $nonce = isset( $_POST['_wpnonce'] ) && is_string( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'bm_ap_create_' . $token ) ) { wp_die( esc_html__( 'Your form expired. Reload the agent page and try again.', 'buildmerce-agent-orders' ), esc_html__( 'Invalid request', 'buildmerce-agent-orders' ), array( 'response' => 403 ) ); return; } if ( ! preg_match( '/^[a-f0-9-]{36}$/', $token ) ) { wp_die( esc_html__( 'Invalid submission token.', 'buildmerce-agent-orders' ), '', array( 'response' => 400 ) ); } $fields = array(); try { $fields = BM_AP_Input::request( wp_unslash( $_POST ), wc_get_price_decimals() ); if ( $fields['quantity'] > (int) BM_AP_Settings::get( 'max_quantity' ) ) { /* translators: %d: maximum quantity allowed by the store. */ throw new InvalidArgumentException( esc_html( sprintf( __( 'Quantity exceeds this store’s maximum of %d.', 'buildmerce-agent-orders' ), (int) BM_AP_Settings::get( 'max_quantity' ) ) ) ); } $maximum = BM_AP_Settings::get( 'max_amount' ); if ( '' !== $maximum && BM_AP_Input::money( $fields['total'], wc_get_price_decimals() ) > BM_AP_Input::money( $maximum, wc_get_price_decimals() ) ) { throw new InvalidArgumentException( esc_html__( 'The request total exceeds this store’s configured maximum.', 'buildmerce-agent-orders' ) ); } } catch ( InvalidArgumentException $e ) { // Only retain validated scalar fields; never persist passwords or raw request contents. foreach ( array( 'product_name', 'price', 'sku', 'client_email', 'quantity', 'first_name', 'last_name', 'company', 'po', 'customer_note', 'internal_note' ) as $field ) { if ( isset( $_POST[ $field ] ) && is_string( $_POST[ $field ] ) ) { $fields[ $field ] = substr( sanitize_textarea_field( wp_unslash( $_POST[ $field ] ) ), 0, 2000 ); } } $this->finish( $e->getMessage(), 'error', 0, $fields ); } $key = 'bm_ap_req_' . hash( 'sha256', get_current_user_id() . ':' . $token ); $record = array( 'time' => time(), 'order_id' => 0, 'state' => 'creating' ); // Atomic unique option insert prevents two concurrent POSTs from creating two orders. if ( ! add_option( $key, $record, '', false ) ) { $existing = get_option( $key ); $existing_order = wc_get_order( isset( $existing['order_id'] ) ? $existing['order_id'] : 0 ); if ( $this->owns( $existing_order ) ) { $this->finish( __( 'This submission already created an order. Use the existing request below; you can resend its email.', 'buildmerce-agent-orders' ), 'success', $existing_order->get_id() ); } $this->finish( __( 'This submission is being processed or needs review. Refresh the request list before submitting again.', 'buildmerce-agent-orders' ), 'error' ); } $rate_key = 'bm_ap_new_request_' . get_current_user_id(); if ( WC_Rate_Limiter::retried_too_soon( $rate_key ) ) { delete_option( $key ); $this->finish( __( 'Please wait a few seconds before creating another request.', 'buildmerce-agent-orders' ), 'error', 0, $fields ); } WC_Rate_Limiter::set_rate_limit( $rate_key, (int) BM_AP_Settings::get( 'submission_delay' ) ); $order = null; try { $order = wc_create_order( array( 'status' => 'pending', 'customer_id' => 0, 'created_via' => 'buildmerce_agent_paylater' ) ); if ( is_wp_error( $order ) ) { throw new RuntimeException( esc_html__( 'Order creation failed.', 'buildmerce-agent-orders' ) ); } $order->set_currency( get_woocommerce_currency() ); $order->set_prices_include_tax( false ); $order->set_billing_email( $fields['client_email'] ); $order->set_billing_first_name( $fields['first_name'] ); $order->set_billing_last_name( $fields['last_name'] ); $order->set_billing_company( $fields['company'] ); $order->set_billing_country( WC()->countries->get_base_country() ); $order->set_customer_note( $fields['customer_note'] ); $order->update_meta_data( '_bm_ap_agent_id', get_current_user_id() ); $order->update_meta_data( '_bm_ap_po', $fields['po'] ); $order->update_meta_data( '_bm_ap_request_token', $token ); // Snapshot line, not a catalog product: a typed SKU never changes catalog prices or inventory. $item = new WC_Order_Item_Product(); $item->set_name( $fields['product_name'] ); $item->set_quantity( $fields['quantity'] ); $item->set_subtotal( $fields['total'] ); $item->set_total( $fields['total'] ); $item->set_tax_class( '' ); $item->add_meta_data( 'SKU', $fields['sku'], true ); $item->add_meta_data( 'Agreed unit price', $fields['price'], true ); $order->add_item( $item ); // Agreed final charge: no automatic tax, shipping, coupon or cart recalculation. $order->calculate_totals( false ); $order->save(); $record['order_id'] = $order->get_id(); $record['state'] = 'created'; update_option( $key, $record, false ); $agent = wp_get_current_user(); $order->add_order_note( __( 'Payment request created by agent ', 'buildmerce-agent-orders' ) . $agent->display_name . __( ' (user #', 'buildmerce-agent-orders' ) . $agent->ID . __( '). Agreed final total; no automatic tax or shipping.', 'buildmerce-agent-orders' ) ); if ( $fields['internal_note'] ) { $order->add_order_note( $fields['internal_note'] ); } } catch ( Throwable $e ) { if ( $order instanceof WC_Order ) { $order->update_status( 'cancelled', __( 'Incomplete agent request. Review before retrying; do not reopen for payment.', 'buildmerce-agent-orders' ) ); $record['order_id'] = $order->get_id(); } $record['state'] = 'failed'; update_option( $key, $record, false ); wc_get_logger()->error( 'Agent request creation failed (' . get_class( $e ) . ').', array( 'source' => 'buildmerce-agent-paylater' ) ); $this->finish( __( 'The request could not be completed. Check the request list and ask the store manager to review before retrying.', 'buildmerce-agent-orders' ), 'error' ); } $sent = $this->send_email( $order ); $this->finish( $sent ? __( 'Order created. The payment email was accepted by the mail service.', 'buildmerce-agent-orders' ) : __( 'Order created, but the email could not be confirmed as sent. Use Resend email below after checking your email configuration.', 'buildmerce-agent-orders' ), $sent ? 'success' : 'error', $order->get_id() ); } public function resend() { if ( ! is_user_logged_in() || ( ! current_user_can( self::CAP ) && ! current_user_can( 'manage_woocommerce' ) ) ) { $this->deny(); return; } if ( ! $this->is_post_request() || ! is_ssl() ) { wp_die( esc_html__( 'Use a secure HTTPS form to submit payment requests.', 'buildmerce-agent-orders' ), esc_html__( 'Invalid request', 'buildmerce-agent-orders' ), array( 'response' => 400 ) ); return; } $id = isset( $_POST['order_id'] ) && is_scalar( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; $nonce = isset( $_POST['_wpnonce'] ) && is_string( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'bm_ap_resend_' . $id ) ) { wp_die( esc_html__( 'Your form expired. Reload the agent page and try again.', 'buildmerce-agent-orders' ), esc_html__( 'Invalid request', 'buildmerce-agent-orders' ), array( 'response' => 403 ) ); return; } $order = wc_get_order( $id ); if ( ! $this->owns( $order ) ) { $this->deny(); } if ( ! $order->needs_payment() ) { $this->finish( __( 'This order no longer needs payment. Its payment email was not resent.', 'buildmerce-agent-orders' ), 'error', $id ); } $lock = 'bm_ap_send_' . $id; if ( get_transient( $lock ) ) { $this->finish( __( 'Please wait at least one minute before resending this request.', 'buildmerce-agent-orders' ), 'error', $id ); } $send_lock = 'bm_ap_req_send_' . $id; $previous_lock = get_option( $send_lock ); if ( is_array( $previous_lock ) && $previous_lock['time'] < time() - 15 * MINUTE_IN_SECONDS ) { delete_option( $send_lock ); } if ( ! add_option( $send_lock, array( 'time' => time() ), '', false ) ) { $this->finish( __( 'An email send is already in progress. Please try again later.', 'buildmerce-agent-orders' ), 'error', $id ); } try { $sent = $this->send_email( $order ); } finally { delete_option( $send_lock ); } $this->finish( $sent ? __( 'The payment email was accepted by the mail service.', 'buildmerce-agent-orders' ) : __( 'Email could not be confirmed as sent. Ask the store manager to check email configuration.', 'buildmerce-agent-orders' ), $sent ? 'success' : 'error', $id ); } public function mail_result( $ok, $email_id, $email ) { if ( 'customer_invoice' === $email_id && $email->object instanceof WC_Order && $email->object->get_id() === $this->email_order ) { $this->mail_attempted = true; $this->mail_ok = (bool) $ok; } } private function send_email( $order ) { set_transient( 'bm_ap_send_' . $order->get_id(), 1, MINUTE_IN_SECONDS ); $this->email_order = $order->get_id(); $this->mail_ok = false; $this->mail_attempted = false; add_action( 'woocommerce_email_sent', array( $this, 'mail_result' ), 10, 3 ); try { $emails = WC()->mailer()->get_emails(); if ( isset( $emails['WC_Email_Customer_Invoice'] ) ) { $emails['WC_Email_Customer_Invoice']->trigger( $order->get_id(), $order ); } } catch ( Throwable $e ) { wc_get_logger()->error( __( 'Payment email failed for order #', 'buildmerce-agent-orders' ) . $order->get_id(), array( 'source' => 'buildmerce-agent-paylater' ) ); } finally { remove_action( 'woocommerce_email_sent', array( $this, 'mail_result' ), 10 ); $this->email_order = 0; } $status = $this->mail_attempted && $this->mail_ok ? 'accepted' : 'failed_or_unconfirmed'; $order->update_meta_data( '_bm_ap_email_status', $status ); $order->update_meta_data( '_bm_ap_email_at', time() ); $order->save(); $order->add_order_note( __( 'Agent payment request email: ', 'buildmerce-agent-orders' ) . $status . __( '. Mail acceptance does not confirm inbox delivery.', 'buildmerce-agent-orders' ) ); set_transient( 'bm_ap_send_' . $order->get_id(), 1, MINUTE_IN_SECONDS ); return 'accepted' === $status; } public function legacy_agent_query( $args, $query_vars ) { if ( isset( $query_vars['bm_ap_agent'] ) ) { $args['meta_query'][] = array( 'key' => '_bm_ap_agent_id', 'value' => (int) $query_vars['bm_ap_agent'], 'compare' => '=' ); } return $args; } public function paid_status( $status, $order_id, $order ) { // Ad-hoc items have no catalog product, so WooCommerce would otherwise auto-complete them. return $order->get_meta( '_bm_ap_agent_id' ) && in_array( $status, array( 'processing', 'completed' ), true ) ? BM_AP_Settings::get( 'paid_status' ) : $status; } private function history_status() { $status = isset( $_GET['bm_ap_status'] ) && is_string( $_GET['bm_ap_status'] ) ? sanitize_key( wp_unslash( $_GET['bm_ap_status'] ) ) : ''; return isset( wc_get_order_statuses()[ $status ] ) ? $status : ''; } private function requests() { $args = array( 'limit' => (int) BM_AP_Settings::get( 'per_page' ), 'page' => max( 1, isset( $_GET['bm_ap_page'] ) && is_scalar( $_GET['bm_ap_page'] ) ? min( 100000, absint( $_GET['bm_ap_page'] ) ) : 1 ), 'paginate' => true, 'orderby' => 'date', 'order' => 'DESC', 'type' => 'shop_order', ); if ( \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) { $args['meta_query'] = array( array( 'key' => '_bm_ap_agent_id', 'value' => get_current_user_id(), 'compare' => '=' ) ); } else { // Legacy storage uses the registered custom-query adapter, not the HPOS-only meta_query argument. $args['bm_ap_agent'] = get_current_user_id(); } if ( $this->history_status() ) { $args['status'] = $this->history_status(); } return wc_get_orders( $args ); } private function history_filter() { echo '
'; } private function history_navigation( $result ) { if ( $result->max_num_pages < 2 ) { return; } $base = add_query_arg( array( 'bm_ap_status' => $this->history_status(), 'bm_ap_page' => 999999999 ), $this->portal_url() ); $links = paginate_links( array( 'base' => str_replace( '999999999', '%#%', esc_url( $base ) ), 'format' => '', 'current' => max( 1, isset( $_GET['bm_ap_page'] ) && is_scalar( $_GET['bm_ap_page'] ) ? absint( $_GET['bm_ap_page'] ) : 1 ), 'total' => (int) $result->max_num_pages, 'prev_text' => __( 'Previous', 'buildmerce-agent-orders' ), 'next_text' => __( 'Next', 'buildmerce-agent-orders' ), 'type' => 'list', ) ); echo ''; } public function portal() { ob_start(); echo '' . esc_html__( 'AGENT PORTAL', 'buildmerce-agent-orders' ) . '
' . esc_html__( 'Use the agent account provided by your store manager.', 'buildmerce-agent-orders' ) . '
' ); if ( 'wordpress' === BM_AP_Settings::get( 'login' ) ) { if ( is_ssl() ) { echo '' . esc_html__( 'Sign in with your agent account', 'buildmerce-agent-orders' ) . '
'; } else { echo '' . esc_html__( 'Open this page using HTTPS before signing in.', 'buildmerce-agent-orders' ) . '
'; } echo '' . esc_html__( 'This account does not have permission to send payment requests.', 'buildmerce-agent-orders' ) . '
' . esc_html__( 'Sign out', 'buildmerce-agent-orders' ) . '' ); return ob_get_clean(); } $fields = array(); $notice = isset( $_GET['notice'] ) && is_string( $_GET['notice'] ) ? sanitize_text_field( wp_unslash( $_GET['notice'] ) ) : ''; if ( preg_match( '/^[a-f0-9-]{36}$/', $notice ) ) { $flash_key = 'bm_ap_flash_' . get_current_user_id() . '_' . $notice; $flash = get_transient( $flash_key ); if ( is_array( $flash ) ) { $fields = $flash['fields']; echo '' . esc_html__( 'AGENT PORTAL', 'buildmerce-agent-orders' ) . '
' . esc_html__( 'Signed in as', 'buildmerce-agent-orders' ) . ' ' ) . esc_html( $agent->display_name ) . '
' . esc_html__( 'Filter requests by status or browse earlier pages. Reload to refresh payment status.', 'buildmerce-agent-orders' ) . '
'; $this->history_filter(); echo ( '| ' . esc_html__( 'Order', 'buildmerce-agent-orders' ) . ' | ' . esc_html__( 'Client', 'buildmerce-agent-orders' ) . ' | ' . esc_html__( 'Total', 'buildmerce-agent-orders' ) . ' | ' . esc_html__( 'Status', 'buildmerce-agent-orders' ) . ' | ' . esc_html__( 'Email', 'buildmerce-agent-orders' ) . ' | ' . esc_html__( 'Action', 'buildmerce-agent-orders' ) . ' |
|---|---|---|---|---|---|
| #' . esc_html( $order->get_order_number() ) . ' ' . esc_html( $order->get_date_created() ? wc_format_datetime( $order->get_date_created() ) : '' ) . ' | ' . esc_html( $order->get_billing_email() ) . ' | ' . wp_kses_post( $order->get_formatted_order_total() ) . ' | ' . esc_html( wc_get_order_status_name( $order->get_status() ) ) . ' | ' . esc_html( 'accepted' === $order->get_meta( '_bm_ap_email_status' ) ? __( 'Accepted by mail service', 'buildmerce-agent-orders' ) : __( 'Failed / unconfirmed', 'buildmerce-agent-orders' ) ) . ' | '; if ( $order->needs_payment() ) { echo '' . esc_html__( 'View link', 'buildmerce-agent-orders' ) . '' ); } else { echo '—'; } echo ' |
| ' . esc_html__( 'No requests match this view.', 'buildmerce-agent-orders' ) . ' | |||||
' . esc_html__( 'PO / Reference:', 'buildmerce-agent-orders' ) . ' ' . esc_html( $po ) . '
'; } } if ( ! $sent_to_admin && 'customer_invoice' === $email->id && $order->needs_payment() && ! $plain_text ) { echo '' . esc_html__( 'Pay now', 'buildmerce-agent-orders' ) . '
' . esc_html__( 'Enter your billing details and pay securely on our website.', 'buildmerce-agent-orders' ) . '
' ); } } public function admin_details( $order ) { if ( ! $order->get_meta( '_bm_ap_agent_id' ) ) { return; } $agent = get_userdata( (int) $order->get_meta( '_bm_ap_agent_id' ) ); echo ( '' . esc_html__( 'Agent payment request:', 'buildmerce-agent-orders' ) . ' ' ) . esc_html( $agent ? $agent->display_name : __( 'Deleted agent', 'buildmerce-agent-orders' ) ) . esc_html__( ' (user #', 'buildmerce-agent-orders' ) . esc_html( $order->get_meta( '_bm_ap_agent_id' ) ) . ( ')
' . esc_html__( 'PO:', 'buildmerce-agent-orders' ) . ' ' ) . esc_html( $order->get_meta( '_bm_ap_po' ) ) . ( '
' . esc_html__( 'Email:', 'buildmerce-agent-orders' ) . ' ' ) . esc_html( $order->get_meta( '_bm_ap_email_status' ) ) . '
' . esc_html__( 'Use the billing address associated with your payment method.', 'buildmerce-agent-orders' ) . '
' ); $this->render_address( $order, 'billing_', $base_country, $data ); echo ( '' ) . esc_html( $order->get_billing_email() ) . '