store = $store; $this->client = $client; add_filter( 'woocommerce_get_sections_integration', array( $this, 'section' ) ); add_action( 'admin_menu', array( $this, 'menu' ) ); add_action( 'admin_post_miplata_pair', array( $this, 'handle_pair' ) ); add_action( 'admin_post_miplata_disconnect', array( $this, 'handle_disconnect' ) ); add_action( 'admin_post_miplata_save', array( $this, 'handle_save' ) ); add_action( 'admin_post_miplata_resend', array( $this, 'handle_resend' ) ); add_action( 'admin_notices', array( $this, 'notices' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'assets' ) ); add_filter( 'plugin_action_links_' . MIPLATA_BASENAME, array( $this, 'action_links' ) ); add_filter( 'manage_woocommerce_page_wc-orders_columns', array( $this, 'order_column' ), 20 ); add_action( 'manage_woocommerce_page_wc-orders_custom_column', array( $this, 'order_column_value' ), 20, 2 ); add_filter( 'manage_edit-shop_order_columns', array( $this, 'order_column' ), 20 ); add_action( 'manage_shop_order_posts_custom_column', array( $this, 'order_column_value_legacy' ), 20, 2 ); add_action( 'add_meta_boxes', array( $this, 'order_box' ) ); } public function section( array $sections ): array { return $sections; } public function menu(): void { add_submenu_page( 'woocommerce', __( 'miPlata FACTURAR', 'miplata' ), __( 'miPlata', 'miplata' ), 'manage_woocommerce', self::PAGE, array( $this, 'render' ) ); } public function action_links( array $links ): array { array_unshift( $links, '' . esc_html__( 'Ajustes', 'miplata' ) . '' ); return $links; } public function assets( string $hook ): void { if ( false !== strpos( $hook, self::PAGE ) ) { wp_enqueue_style( 'miplata-admin', MIPLATA_URL . 'assets/css/admin.css', array(), MIPLATA_VERSION ); } } private function url( array $args = array() ): string { return add_query_arg( array_merge( array( 'page' => self::PAGE ), $args ), admin_url( 'admin.php' ) ); } /* ─────────────── notices ─────────────── */ /** Notices only on our page, the Plugins list and WooCommerce order screens — never site-wide. */ private function notice_screen(): bool { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen ) { return false; } return false !== strpos( (string) $screen->id, self::PAGE ) || in_array( $screen->id, array( 'plugins', 'woocommerce_page_wc-orders', 'edit-shop_order' ), true ); } public function notices(): void { if ( ! current_user_can( 'manage_woocommerce' ) || ! $this->notice_screen() ) { return; } $outdated = get_option( 'miplata_plugin_outdated', '' ); if ( '' !== $outdated ) { echo '

' . sprintf( /* translators: %s: version */ esc_html__( 'miPlata: esta versión del plugin ya no es aceptada (mínimo %s). Actualícelo para seguir facturando.', 'miplata' ), esc_html( $outdated ) ) . '

'; } $conn = $this->store->connection(); if ( ! empty( $conn['rejected'] ) ) { echo '

' . esc_html__( 'miPlata rechazó la conexión de esta tienda (revocada o clave inválida). Vuelva a conectarla con un código nuevo.', 'miplata' ) . ' ' . esc_html__( 'Abrir miPlata', 'miplata' ) . '

'; } if ( ! MiPlata_Crypto::sodium_available() ) { echo '

' . esc_html__( 'miPlata requiere la extensión sodium de PHP (incluida desde PHP 7.2). Pida a su proveedor de hosting que la active.', 'miplata' ) . '

'; } if ( ! is_ssl() && ! ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) { // phpcs:ignore echo '

' . esc_html__( 'miPlata: la tienda debe servirse por HTTPS para conectarse.', 'miplata' ) . '

'; } } /* ─────────────── page ─────────────── */ public function render(): void { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sin permiso.', 'miplata' ) ); } $conn = $this->store->connection(); $connected = $this->client->connected(); $caps = $connected ? $this->client->capabilities() : null; $s = $this->store->settings(); $msg = isset( $_GET['miplata_msg'] ) ? sanitize_key( wp_unslash( $_GET['miplata_msg'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $err = isset( $_GET['miplata_err'] ) ? sanitize_text_field( wp_unslash( $_GET['miplata_err'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended echo '

' . esc_html__( 'miPlata FACTURAR', 'miplata' ) . '

'; if ( '' !== $err ) { echo '

' . esc_html( $err ) . '

'; } if ( 'paired' === $msg ) { echo '

' . esc_html__( 'Tienda conectada. Revise el correo que miPlata envió a los administradores de su cuenta.', 'miplata' ) . '

'; } elseif ( 'saved' === $msg ) { echo '

' . esc_html__( 'Ajustes guardados.', 'miplata' ) . '

'; } elseif ( 'disconnected' === $msg ) { echo '

' . esc_html__( 'Tienda desconectada. Las claves se eliminaron de este sitio.', 'miplata' ) . '

'; } if ( ! $connected ) { $this->render_pairing(); } else { $this->render_status( $conn, $caps ); $this->render_settings( $s, $caps ); } echo '
'; } private function render_pairing(): void { $site = MiPlata_Protocol::normalize_site_host( (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ) ); echo '

' . esc_html__( 'Conectar con miPlata', 'miplata' ) . '

'; echo '
    '; echo '
  1. ' . wp_kses_post( __( 'En miPlata vaya a Configuración → Integraciones → Conectar tienda → WooCommerce, escriba el dominio de esta tienda y genere el código.', 'miplata' ) ) . '
  2. '; echo '
  3. ' . esc_html__( 'Pegue el código aquí en los próximos 10 minutos. Solo un administrador de miPlata puede generarlo; miPlata nunca se lo pedirá por correo ni por teléfono.', 'miplata' ) . '
  4. '; echo '
'; if ( '' === $site ) { echo '

' . esc_html__( 'Este sitio no tiene un dominio público válido (HTTPS, sin IP ni puerto). La conexión no es posible desde aquí.', 'miplata' ) . '

'; } echo '
'; wp_nonce_field( 'miplata_pair' ); echo ''; echo '


'; echo '

'; echo '

' . sprintf( /* translators: %s: site host */ esc_html__( 'Dominio que se verificará: %s', 'miplata' ), '' . esc_html( $site ) . '' ) . '

'; echo '

'; echo '
'; if ( ! $this->store->uses_configured_key() ) { echo '

' . wp_kses_post( __( 'Recomendado: defina MIPLATA_ENCRYPTION_KEY (32 bytes aleatorios en hex) en wp-config.php antes de conectar. Sin ella, la clave de cifrado se deriva de las sales de WordPress y cambiarlas obligará a reconectar.', 'miplata' ) ) . '

'; } echo '
'; } private function render_status( array $conn, ?array $caps ): void { $health = $caps['health'] ?? null; $wava_pl = MiPlata_Plugin::wava_plugin_active(); echo '

' . esc_html__( 'Conexión', 'miplata' ) . '

'; echo '
' . esc_html__( 'Nombre en miPlata', 'miplata' ) . '
' . esc_html( (string) ( $conn['name'] ?? '' ) ) . '
'; echo '
' . esc_html__( 'Cuenta', 'miplata' ) . '
' . esc_html( (string) ( $conn['host'] ?? '' ) ) . '
'; echo '
' . esc_html__( 'Permisos', 'miplata' ) . '
' . esc_html( (string) ( $conn['preset'] ?? '' ) ) . '
'; echo '
' . esc_html__( 'Huella de clave', 'miplata' ) . '
' . esc_html( $this->store->key_fingerprint() ) . '
'; echo '
' . esc_html__( 'Conectada el', 'miplata' ) . '
' . esc_html( ! empty( $conn['paired_at'] ) ? wp_date( 'Y-m-d H:i', (int) $conn['paired_at'] ) : '—' ) . '
'; if ( null === $caps ) { echo '
' . esc_html__( 'Estado', 'miplata' ) . '
' . esc_html__( 'No se pudo consultar miPlata (ver registro).', 'miplata' ) . '
'; } else { $ok = ! empty( $health['ok'] ); echo '
' . esc_html__( 'Listo para facturar', 'miplata' ) . '
' . ( $ok ? esc_html__( 'Sí', 'miplata' ) : esc_html( (string) ( $health['range']['warning'] ?? $health['certificate']['warning'] ?? __( 'No', 'miplata' ) ) ) ) . '
'; echo '
' . esc_html__( 'Ambiente DIAN', 'miplata' ) . '
' . esc_html( (string) ( $health['environment'] ?? '' ) ) . ( ! empty( $caps['settings']['test_mode'] ) ? ' · ' . esc_html__( 'Modo prueba', 'miplata' ) . '' : '' ) . '
'; if ( ! empty( $health['range']['left'] ) && (int) $health['range']['left'] < 100 ) { echo '
' . sprintf( /* translators: %d: numbers left */ esc_html__( 'Quedan %d números en el rango de facturación.', 'miplata' ), (int) $health['range']['left'] ) . '
'; } echo '
' . esc_html__( 'Pagos WAVA', 'miplata' ) . '
'; if ( ! empty( $health['wava_verified'] ) ) { echo '' . esc_html__( 'Pagos WAVA verificados ✓', 'miplata' ) . ''; } elseif ( ! empty( $health['wava_link'] ) ) { echo esc_html__( 'Cuenta WAVA vinculada; se verificará con el primer pago.', 'miplata' ); } else { echo esc_html__( 'Sin cuenta WAVA vinculada en miPlata (los pedidos pagados con WAVA quedarán sin verificar).', 'miplata' ); } echo $wava_pl ? ' · ' . esc_html__( 'Plugin de WAVA activo', 'miplata' ) : ''; echo '
'; } $lh = get_option( 'miplata_last_health', array() ); if ( is_array( $lh ) && ! empty( $lh['at'] ) ) { echo '
' . esc_html__( 'Diagnóstico', 'miplata' ) . '
' . esc_html( sprintf( 'cron %s · smtp %s · %s', empty( $lh['cron_ok'] ) ? '✗' : '✓', empty( $lh['smtp_ok'] ) ? '?' : '✓', wp_date( 'Y-m-d H:i', (int) $lh['at'] ) ) ) . '
'; } echo '
'; if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && ! wp_next_scheduled( 'miplata_poll' ) ) { echo '

' . esc_html__( 'WP-Cron está desactivado: asegúrese de que un cron del sistema llame a wp-cron.php, o las facturas tardarán en reflejarse.', 'miplata' ) . '

'; } echo '
'; wp_nonce_field( 'miplata_disconnect' ); echo '
'; echo '
'; } private function render_settings( array $s, ?array $caps ): void { $classes = array( '' => __( 'Estándar', 'miplata' ) ); if ( class_exists( 'WC_Tax' ) ) { foreach ( WC_Tax::get_tax_classes() as $name ) { $classes[ sanitize_title( $name ) ] = $name; } } $treatments = array( 'iva19' => 'IVA 19 %', 'iva5' => 'IVA 5 %', 'iva0' => 'IVA 0 %', 'exento' => __( 'Exento de IVA', 'miplata' ), 'excluido' => __( 'Excluido de IVA', 'miplata' ), 'inc8' => 'INC 8 %', 'inc4' => 'INC 4 %', 'native' => __( 'Según la tasa del pedido', 'miplata' ), ); $mode_b_ok = in_array( $this->store->preset(), array( 'pay_invoice', 'pay_only' ), true ); echo '

' . esc_html__( 'Ajustes', 'miplata' ) . '

'; wp_nonce_field( 'miplata_save' ); echo ''; $this->row_checkbox( 'enabled', __( 'Facturar pedidos automáticamente', 'miplata' ), $s['enabled'], __( 'Al pasar a Procesando / Completado se registra la factura en miPlata.', 'miplata' ) ); $this->row_select( 'delivery', __( 'Envío de la factura', 'miplata' ), $s['delivery'], array( 'shop' => __( 'Correo de esta tienda, con PDF + XML adjuntos (recomendado)', 'miplata' ), 'miplata' => __( 'Correo de miPlata', 'miplata' ), 'both' => __( 'Ambos', 'miplata' ), ), __( 'Con el correo de la tienda se usa su propio SMTP, plantilla y logo. Recomendamos un plugin SMTP para evitar que el correo caiga en spam.', 'miplata' ) ); $this->row_select( 'doc_fields', __( 'Campo de documento en el checkout', 'miplata' ), $s['doc_fields'], array( 'auto' => __( 'Automático (no agregar si otro plugin ya lo tiene)', 'miplata' ), 'ours' => __( 'Agregar siempre tipo y número de documento', 'miplata' ), 'none' => __( 'No agregar nada', 'miplata' ), ) ); $this->row_checkbox( 'doc_required', __( 'Documento obligatorio', 'miplata' ), $s['doc_required'], __( 'Si no es obligatorio, sin documento se factura a consumidor final (según la política de la conexión en miPlata).', 'miplata' ) ); $this->row_checkbox( 'city_dropdown', __( 'Ciudad como lista (DANE)', 'miplata' ), $s['city_dropdown'], __( 'Reemplaza el campo de ciudad por un desplegable Departamento → Municipio, lo que garantiza el código DANE en la factura.', 'miplata' ) ); $this->row_checkbox( 'refund_credit_note', __( 'Reembolso → nota crédito', 'miplata' ), $s['refund_credit_note'] ); if ( ! empty( $caps['settings']['export_type_02'] ) ) { $this->row_select( 'invoice_type', __( 'Tipo de factura', 'miplata' ), $s['invoice_type'], array( '01' => __( '01 Venta nacional', 'miplata' ), '02' => __( '02 Exportación (compradores fuera de Colombia)', 'miplata' ) ) ); } echo ''; if ( $mode_b_ok ) { $this->row_checkbox( 'mode_b', __( 'Pasarelas de miPlata (Modo B)', 'miplata' ), $s['mode_b'], __( 'Activa "Pagar con WAVA" (COP) y "Pagar con PayPal (miPlata)" (moneda extranjera) en Pagos de WooCommerce. La pasarela WAVA se oculta si el plugin oficial de WAVA está activo.', 'miplata' ) ); } echo '

'; } private function row_checkbox( string $k, string $label, $val, string $desc = '' ): void { echo ''; if ( '' !== $desc ) { echo ' ' . esc_html( $desc ) . ''; } echo ''; } private function row_select( string $k, string $label, $val, array $opts, string $desc = '' ): void { echo ''; if ( '' !== $desc ) { echo '

' . esc_html( $desc ) . '

'; } echo ''; } /* ─────────────── handlers ─────────────── */ private function back( array $args ): void { wp_safe_redirect( $this->url( $args ) ); exit; } public function handle_pair(): void { check_admin_referer( 'miplata_pair' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sin permiso.', 'miplata' ) ); } if ( ! MiPlata_Crypto::sodium_available() ) { $this->back( array( 'miplata_err' => __( 'Falta la extensión sodium de PHP.', 'miplata' ) ) ); } $code = MiPlata_Protocol::normalize_pairing_code( isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : '' ); if ( '' === $code ) { $this->back( array( 'miplata_err' => __( 'El código no tiene el formato MPC-cuenta-XXXX-XXXX-XXXX.', 'miplata' ) ) ); } $host = MiPlata_Protocol::host_for_code( $code, MIPLATA_ROOT_DOMAIN ); $site = MiPlata_Protocol::normalize_site_host( (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ) ); if ( '' === $host || '' === $site ) { $this->back( array( 'miplata_err' => __( 'No se pudo determinar la cuenta de miPlata o el dominio de la tienda.', 'miplata' ) ) ); } $kp = $this->store->begin_pairing(); // Make sure our ownership endpoint is reachable before asking miPlata to fetch it. flush_rewrite_rules( false ); $body = array( 'code' => $code, 'public_key' => $kp['public_key'], 'plugin_version' => MIPLATA_VERSION, 'site_host' => $site, 'proof' => MiPlata_Protocol::redeem_proof( $kp['seed'], $code, $site ), ); $r = MiPlata_Client::redeem( $host, $body ); if ( ! $r['ok'] ) { delete_option( MiPlata_Store::OPT_PENDING ); $code_err = (string) ( $r['body']['code'] ?? '' ); $hint = array( 'ownership_failed' => __( 'miPlata no pudo leer /wp-json/miplata/v1/connect-key en su dominio. Revise que el sitio sea público por HTTPS y que un firewall no bloquee el agente "miPlata-Connect".', 'miplata' ), 'ownership_key_mismatch' => __( 'Otra instalación responde por este dominio (¿caché o un sitio de staging con el mismo dominio?).', 'miplata' ), 'site_mismatch' => __( 'El dominio escrito en miPlata no coincide con el de esta tienda.', 'miplata' ), 'code_unknown' => __( 'Código desconocido, usado o vencido. Genere uno nuevo en miPlata.', 'miplata' ), 'code_burned' => __( 'Demasiados intentos con este código. Genere uno nuevo.', 'miplata' ), 'plugin_outdated' => __( 'Actualice el plugin de miPlata.', 'miplata' ), 'too_many_attempts' => __( 'Demasiados intentos desde esta IP; espere unos minutos.', 'miplata' ), 'unreachable' => __( 'No se pudo contactar a miPlata desde el servidor de la tienda (salida HTTPS bloqueada).', 'miplata' ), ); MiPlata_Log::warn( 'pair_failed', array( 'status' => $r['status'], 'code' => $code_err ) ); $this->back( array( 'miplata_err' => $hint[ $code_err ] ?? ( (string) ( $r['body']['message'] ?? __( 'Error al conectar.', 'miplata' ) ) . ' (' . $r['status'] . ')' ) ) ); } $this->store->finish_pairing( $kp, $r['body'] ); MiPlata_Plugin::schedule_cron(); MiPlata_Log::info( 'paired', array( 'connection' => $r['body']['connection_id'] ?? '' ) ); $this->client->capabilities( true ); $this->back( array( 'miplata_msg' => 'paired' ) ); } public function handle_disconnect(): void { check_admin_referer( 'miplata_disconnect' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sin permiso.', 'miplata' ) ); } if ( $this->client->connected() ) { $this->client->revoke_self(); } $this->store->wipe(); MiPlata_Plugin::unschedule_cron(); $this->back( array( 'miplata_msg' => 'disconnected' ) ); } public function handle_save(): void { check_admin_referer( 'miplata_save' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sin permiso.', 'miplata' ) ); } $p = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $map = array(); foreach ( (array) ( $p['tax_map'] ?? array() ) as $slug => $treat ) { $slug = sanitize_title( (string) $slug ); $treat = sanitize_key( (string) $treat ); if ( isset( MiPlata_Mapper::TAX_TREATMENTS[ $treat ] ) || 'native' === $treat ) { $map[ $slug ] = $treat; } } $this->store->save_settings( array( 'enabled' => isset( $p['enabled'] ) ? 'yes' : 'no', 'mode_b' => isset( $p['mode_b'] ) ? 'yes' : 'no', 'delivery' => in_array( $p['delivery'] ?? '', array( 'shop', 'miplata', 'both' ), true ) ? $p['delivery'] : 'shop', 'doc_fields' => in_array( $p['doc_fields'] ?? '', array( 'auto', 'ours', 'none' ), true ) ? $p['doc_fields'] : 'auto', 'doc_required' => isset( $p['doc_required'] ) ? 'yes' : 'no', 'city_dropdown' => isset( $p['city_dropdown'] ) ? 'yes' : 'no', 'refund_credit_note' => isset( $p['refund_credit_note'] ) ? 'yes' : 'no', 'invoice_type' => in_array( $p['invoice_type'] ?? '', array( '01', '02' ), true ) ? $p['invoice_type'] : '01', 'tax_map' => $map, ) ); $this->back( array( 'miplata_msg' => 'saved' ) ); } public function handle_resend(): void { check_admin_referer( 'miplata_resend' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sin permiso.', 'miplata' ) ); } $order_id = isset( $_GET['order'] ) ? absint( wp_unslash( $_GET['order'] ) ) : 0; $order = $order_id ? wc_get_order( $order_id ) : null; if ( $order ) { $chk = (string) $order->get_meta( MiPlata_Orders::META_CHECKOUT, true ); if ( '' === $chk ) { miplata()->orders->create_for_order( $order_id ); } else { $order->delete_meta_data( MiPlata_Orders::META_MAIL ); $order->save(); miplata()->orders->refresh( $order, $chk ); if ( '' !== (string) $order->get_meta( MiPlata_Orders::META_PDF, true ) ) { miplata()->orders->send_mail( $order ); } } } $back = wp_get_referer(); wp_safe_redirect( $back ? $back : admin_url( 'admin.php?page=wc-orders' ) ); exit; } /* ─────────────── order list / order box ─────────────── */ public function order_column( array $cols ): array { $out = array(); foreach ( $cols as $k => $v ) { $out[ $k ] = $v; if ( 'order_status' === $k ) { $out['miplata'] = __( 'Factura', 'miplata' ); } } return $out; } public function order_column_value( string $col, $order ): void { if ( 'miplata' !== $col || ! $order ) { return; } $inv = $order->get_meta( MiPlata_Orders::META_INVOICE, true ); $st = (string) $order->get_meta( MiPlata_Orders::META_STATUS, true ); if ( is_array( $inv ) && ! empty( $inv['number'] ) ) { echo '' . esc_html( (string) $inv['number'] ) . ''; } elseif ( '' !== $st ) { echo '' . esc_html( MiPlata_Orders::status_label( $st ) ) . ''; } } public function order_column_value_legacy( string $col, $post_id ): void { $this->order_column_value( $col, wc_get_order( $post_id ) ); } public function order_box(): void { $screen = function_exists( 'wc_get_page_screen_id' ) ? wc_get_page_screen_id( 'shop-order' ) : 'shop_order'; add_meta_box( 'miplata_order', __( 'miPlata · Factura electrónica', 'miplata' ), array( $this, 'order_box_render' ), $screen, 'side', 'high' ); } public function order_box_render( $post_or_order ): void { $order = $post_or_order instanceof WC_Order ? $post_or_order : wc_get_order( $post_or_order->ID ?? 0 ); if ( ! $order ) { return; } $inv = $order->get_meta( MiPlata_Orders::META_INVOICE, true ); $st = (string) $order->get_meta( MiPlata_Orders::META_STATUS, true ); $err = (string) $order->get_meta( MiPlata_Orders::META_ERROR, true ); $cn = $order->get_meta( MiPlata_Orders::META_CN, true ); echo '

' . esc_html__( 'Estado:', 'miplata' ) . ' ' . esc_html( '' === $st ? __( 'Sin registrar', 'miplata' ) : MiPlata_Orders::status_label( $st ) ) . '

'; if ( is_array( $inv ) && ! empty( $inv['number'] ) ) { echo '

' . esc_html( (string) $inv['number'] ) . ' · ' . esc_html( (string) ( $inv['dian_status'] ?? '' ) ) . '

'; if ( ! empty( $inv['cufe'] ) ) { echo '

CUFE ' . esc_html( (string) $inv['cufe'] ) . '

'; } $viewer = miplata()->viewer; if ( $viewer && '' !== (string) $order->get_meta( MiPlata_Orders::META_PDF, true ) ) { echo '

PDF XML

'; } } if ( is_array( $cn ) && ( ! empty( $cn['id'] ) || ! empty( $cn['number'] ) ) ) { echo '

' . ( empty( $cn['number'] ) ? esc_html__( 'Nota crédito emitida.', 'miplata' ) : sprintf( /* translators: %s: credit note number */ esc_html__( 'Nota crédito %s emitida.', 'miplata' ), '' . esc_html( (string) $cn['number'] ) . '' ) ) . '

'; } if ( '' !== $err && in_array( $st, array( 'error', 'failed', 'disputed' ), true ) ) { echo '

' . esc_html( $err ) . '

'; } $url = wp_nonce_url( add_query_arg( array( 'action' => 'miplata_resend', 'order' => $order->get_id() ), admin_url( 'admin-post.php' ) ), 'miplata_resend' ); echo '

' . ( is_array( $inv ) && ! empty( $inv['id'] ) ? esc_html__( 'Reenviar factura', 'miplata' ) : esc_html__( 'Registrar / actualizar', 'miplata' ) ) . '

'; } }