id = 'abank_pay_gateway'; $this->method_title = 'àPay'; $this->method_description = 'Оплата через àbank'; $this->has_fields = false; $this->init_form_fields(); $this->init_settings(); $this->title = $this->get_option('title'); $this->description = $this->get_option('description'); $this->is_preauth = $this->get_option('enable_preauth') === 'yes'; $this->is_production = $this->get_option('enable_production') === 'yes'; $this->api_token = $this->get_option('API_KEY'); $this->api_login = $this->get_option('API_LOGIN'); $this->status_on_success = $this->get_option('status_on_success') ?: 'wc-processing'; $this->redirect = $this->get_option('redirect'); add_action('woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ]); add_action('woocommerce_order_status_processing', [ $this, 'a_pay_complete' ]); add_action('woocommerce_api_abank_pay_check_status', [ $this, 'admin_check_status' ]); add_action('woocommerce_thankyou', [$this, 'post_payment_page'], 1); add_action('woocommerce_api_abank_pay_gateway', [ $this, 'callback_success' ]); add_action('woocommerce_api_abank_pay_gateway_webhook', [ $this, 'handleWebhook' ]); } public function init_form_fields() { $this->form_fields = [ 'enabled' => [ 'title' => 'Увімкнути', 'type' => 'checkbox', 'label' => 'Увімкнути платіж через àPay', 'default' => 'yes' ], 'title' => [ 'title' => 'Заголовок', 'type' => 'text', 'default' => 'Оплата карткою, Apple Pay / Google Pay' ], 'description' => [ 'title' => 'Опис', 'type' => 'textarea', 'default' => 'àPay - онлайн оплата банківськими картами' ], // 'enable_preauth' => [ // 'title' => 'Увімкнути предавторизацію', // 'type' => 'checkbox', // 'label' => 'Використовувати предавторизацію замість миттєвої оплати', // 'default' => 'no' // ], 'enable_production' => [ 'title' => 'Використовувати продакшн середовище', 'type' => 'checkbox', 'label' => '', 'default' => 'no' ], 'API_KEY' => [ 'title' => 'API ключ', 'type' => 'text', 'description' => 'Отримайте токен доступу від àbank', 'default' => '' ], 'API_LOGIN' => [ 'title' => 'API логін', 'type' => 'text', 'description' => 'Введіть логін доступу до API àbank', 'default' => '' ], 'status_on_success' => [ 'title' => 'Статус після успішної оплати', 'type' => 'select', 'options' => wc_get_order_statuses(), 'description' => 'Цей статус буде застосовано до замовлення після успішної оплати.', 'default' => 'wc-processing' ], 'redirect' => [ 'title' => 'Посилання на сторінку, куди буде потрапляти користувач після оплати', 'type' => 'text', 'description' => '', 'default' => '' ], ]; } private function buildOrderObject($order) { $basket_info = []; foreach ($order->get_items() as $item) { $product = $item->get_product(); $basket_info[] = [ 'message' => $product->get_name(), 'count' => $item->get_quantity(), 'amount' => number_format($product->get_price(), 2, '.', '') ]; } $orderObject = new AbankPay_Order(); $orderObject->setId($order->get_id()); $orderObject->setReference($order->get_id()); $orderObject->setAmount($order->get_total()); $orderObject->setDestination('Оплата замовлення #' . $order->get_id()); $orderObject->setBasketOrder($basket_info); $base = WC()->api_request_url( 'abank_pay_gateway_webhook' ); $webhook_url = add_query_arg( 'order_id', $order->get_id(), $base ); $orderObject->setWebHookUrl( $webhook_url ); $redirectBase = WC()->api_request_url( 'abank_pay_gateway' ); $redirectUrl = add_query_arg( 'order_id', $order->get_id(), $redirectBase ); $orderObject->setRedirectUrl($redirectUrl); return $orderObject; } public function process_payment($order_id) { $order = wc_get_order($order_id); if (!$order || $order->get_payment_method() !== $this->id) return; $order->set_payment_method('abank_pay_gateway'); $order->save(); $orderObject = $this->buildOrderObject($order); $handler = $this->is_preauth ? new AbankPay_Preauth_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success) : new AbankPay_Instant_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success); $response = $handler->createInvoice($orderObject); if ($response['result'] === 'success') { return [ 'result' => 'success', 'redirect' => $response['redirect_url'] ]; } wc_add_notice('Помилка створення інвойсу: ' . $response['message'], 'error'); return [ 'result' => 'fail' ]; } public function process_refund($order_id, $amount = null, $reason = '') { $order = wc_get_order($order_id); $handler = $this->is_preauth ? new AbankPay_Preauth_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success) : new AbankPay_Instant_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success); $handler->handleRefund($order->get_total(), $reason); wp_send_json_success(); } public function callback_success() { // Перевірка order_id і status // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces $order_id = isset($_REQUEST['order_id']) ? absint($_REQUEST['order_id']) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces $status = isset($_REQUEST['state']) ? sanitize_text_field(wp_unslash($_REQUEST['state'])) : ''; if (!$order_id || !$status) { wp_safe_redirect(wc_get_cart_url()); exit; } $order = wc_get_order($order_id); if (!$order || $order->get_payment_method() !== $this->id) { wp_safe_redirect(wc_get_cart_url()); exit; } if ($status === 'success') { // Визначаємо статус замовлення $target_status = $this->is_preauth ? 'on-hold' : $this->status_on_success; $target_status = str_replace('wc-', '', $target_status); if (!$this->is_preauth) { $notes = wc_get_order_notes([ 'order_id' => $order_id, 'type' => 'system', ]); $note_exists = false; foreach ($notes as $note) { if (strpos($note->content, "àPay: Було отримано оплату") !== false) { $note_exists = true; break; } } if (!$note_exists) { $order->add_order_note("àPay: Було отримано оплату {$order->get_total()} грн."); } } $order->update_status($target_status); } else { $handler = $this->is_preauth ? new AbankPay_Preauth_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success) : new AbankPay_Instant_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success); $handler->checkStatus(); } // Редірект $redirect_url = !empty($this->redirect) ? esc_url_raw(home_url($this->redirect)) : $order->get_checkout_order_received_url(); wp_safe_redirect($redirect_url); exit; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces public function a_pay_complete($order_id) { $order = wc_get_order($order_id); if (!$order || $order->get_payment_method() !== $this->id) return; $bad_statuses = ['refunded', 'cancelled', 'failed']; if (in_array($order->get_status(), $bad_statuses)) return; if ($order->get_meta('a_pay_hold_confirmed') === 'yes') return; if ($this->is_preauth) { $handler = new AbankPay_Preauth_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success); if ($handler->confirmHold()) { $order->update_meta_data('a_pay_hold_confirmed', 'yes'); $order->save(); } } } public function admin_check_status($order_id = null) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if (!$order_id && isset($_REQUEST['order_id'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order_id = intval($_REQUEST['order_id']); } if (!$order_id) { update_option('abankpay_admin_notice', [ 'type' => 'error', 'text' => '❌ Не знайдено ордер ID' ]); wp_send_json_success(); } $order = wc_get_order($order_id); if (!$order) { update_option('abankpay_admin_notice', [ 'type' => 'error', 'text' => '❌ Не знайдено замовлення за ID ' . $order_id ]); wp_send_json_success(); } $handler = $this->is_preauth ? new AbankPay_Preauth_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success) : new AbankPay_Instant_Payment_Handler($order, $this->is_production, $this->api_login, $this->api_token, $this->status_on_success); $handler->checkStatus(); wp_send_json_success(); } public function post_payment_page($order_id) { $order = wc_get_order($order_id); if (!$order || $order->get_payment_method() !== $this->id) { return; } global $wp_filter; if (isset($wp_filter['woocommerce_thankyou']->callbacks[10])) { $wp_filter['woocommerce_thankyou']->callbacks[10] = []; } } /** * Вебхук від àPay */ public function handleWebhook() { // Відповідь за замовчуванням — текст/200 nocache_headers(); header( 'Content-Type: text/plain; charset=utf-8' ); // 1) Дістаємо order_id з query // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces $order_id = isset($_GET['order_id']) ? absint($_GET['order_id']) : 0; if ( ! $order_id ) { status_header(400); echo 'NO-ORDER-ID'; return; } // 3) Дістаємо статус/код/TRANID/повідомлення // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces $status = isset($_REQUEST['state']) ? sanitize_text_field(wp_unslash($_REQUEST['state'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- external payment callback; cannot use WP nonces $message = isset($_REQUEST['message']) ? sanitize_text_field(wp_unslash($_REQUEST['message'])) : ''; // 4) Отримуємо замовлення $order = wc_get_order( $order_id ); if ( ! $order ) { status_header(404); echo 'ORDER-NOT-FOUND'; return; } $this->setOrderStatus($order, $status, $message); echo 'OK'; } /** * @param $order * @param $status * @param bool $message */ private function setOrderStatus($order, $status, $message = false) { $opts = get_option('woocommerce_abank_pay_gateway_settings', []); $success_status = ! empty($opts['status_on_success']) ? $opts['status_on_success'] : 'processing'; // Інакше мапимо по статусу switch ( $status ) { case 'success': if ( $order->get_status() !== $success_status ) { if ( $success_status !== 'processing' ) { $order->update_status( $success_status, wp_kses_post( $message ?: 'Оплату підтверджено (вебхук).' ) ); } else { $order->add_order_note( wp_kses_post( $message ?: 'Оплату підтверджено (вебхук).' ) ); } } break; case 'cancel': case 'error': case 'failed': $order->add_order_note( wp_kses_post( $message ?: 'Оплата не пройшла (вебхук).' ) ); break; default: // Невідомий статус — просто додамо нотатку для діагностики $order->add_order_note( 'àPay webhook: невідомий статус "' . esc_html( $status ) . '". ' . esc_html( $message ) ); break; } } }