From d17d3b6662d6acbf68fee79e3ca55515619f1115 Mon Sep 17 00:00:00 2001 From: Abraham Olaobaju Date: Thu, 11 Jun 2026 04:04:34 +0100 Subject: [PATCH 1/6] redact signoz service api key --- .../rave-woocommerce-payment-gateway.pot | 161 ++-- includes/class-flutterwave.php | 2 +- ...s-flw-wc-payment-gateway-event-handler.php | 39 +- ...s-flw-wc-payment-gateway-subscriptions.php | 2 +- includes/class-flw-wc-payment-gateway.php | 84 +- .../class-flw-wc-payment-gateway-sdk.php | 446 +++++------ .../util/class-flutterwave-signoz-logger.php | 298 ++++++++ package-lock.json | 4 +- package.json | 2 +- rave-woocommerce-payment-gateway.php | 2 +- readme.txt | 4 +- .../test-flutterwave-signoz-logger.php | 717 ++++++++++++++++++ ...wave-wc-payment-gateway-blocks-support.php | 29 +- 13 files changed, 1456 insertions(+), 334 deletions(-) create mode 100644 includes/util/class-flutterwave-signoz-logger.php create mode 100644 tests/PHPUnit/test-flutterwave-signoz-logger.php diff --git a/i18n/languages/rave-woocommerce-payment-gateway.pot b/i18n/languages/rave-woocommerce-payment-gateway.pot index 9cf6ebe..bd7e7ee 100644 --- a/i18n/languages/rave-woocommerce-payment-gateway.pot +++ b/i18n/languages/rave-woocommerce-payment-gateway.pot @@ -1,15 +1,15 @@ -# Copyright (C) 2025 Flutterwave Developers +# Copyright (C) 2026 Flutterwave Developers # This file is distributed under the MIT License. msgid "" msgstr "" -"Project-Id-Version: Flutterwave WooCommerce 2.3.6\n" +"Project-Id-Version: Flutterwave WooCommerce 3.2.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/rave-woocommerce-payment-gateway\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2025-08-27T20:31:46+00:00\n" +"POT-Creation-Date: 2026-06-11T02:41:28+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: rave-woocommerce-payment-gateway\n" @@ -17,7 +17,7 @@ msgstr "" #. Plugin Name of the plugin #. Translators: %s Plugin name. #: rave-woocommerce-payment-gateway.php -#: includes/class-flw-wc-payment-gateway.php:230 +#: includes/class-flw-wc-payment-gateway.php:270 #: includes/views/html-admin-missing-woocommerce.php:15 msgid "Flutterwave WooCommerce" msgstr "" @@ -67,292 +67,293 @@ msgstr "" msgid "Diners" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:64 +#: includes/class-flw-wc-payment-gateway-event-handler.php:66 msgid "Payment initialized via Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:66 +#: includes/class-flw-wc-payment-gateway-event-handler.php:68 msgid "Your transaction reference: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:83 +#: includes/class-flw-wc-payment-gateway-event-handler.php:85 msgid "Attention: New order has been placed on hold because of incorrect payment amount or currency. Please, look into it." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid "Amount paid: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid "Order amount: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid " Reference: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:124 +#. translators: 1: payment reference 2: transaction reference +#: includes/class-flw-wc-payment-gateway-event-handler.php:97 +#: includes/class-flw-wc-payment-gateway-subscriptions.php:163 +#, php-format +msgid "Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)" +msgstr "" + +#: includes/class-flw-wc-payment-gateway-event-handler.php:145 msgid "The payment failed on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:129 +#: includes/class-flw-wc-payment-gateway-event-handler.php:150 msgid "Reason for Failure : " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:141 +#: includes/class-flw-wc-payment-gateway-event-handler.php:164 msgid "Confirming payment on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:151 +#: includes/class-flw-wc-payment-gateway-event-handler.php:174 msgid "An error occured while confirming payment on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:156 +#: includes/class-flw-wc-payment-gateway-event-handler.php:179 msgid "Attention: New order has been placed on hold because we could not confirm the payment. Please, look into it." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:172 +#: includes/class-flw-wc-payment-gateway-event-handler.php:196 msgid "The customer clicked on the cancel button on Checkout." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:174 +#: includes/class-flw-wc-payment-gateway-event-handler.php:198 msgid "Attention: Customer clicked on the cancel button on the payment gateway. We have updated the order to cancelled status. " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:175 +#: includes/class-flw-wc-payment-gateway-event-handler.php:199 msgid "Please, confirm from the order notes that there is no note of a successful transaction. If there is, this means that the user was debited and you either have to give value for the transaction or refund the customer." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:189 +#: includes/class-flw-wc-payment-gateway-event-handler.php:213 msgid "The payment didn't return a valid response. It could have timed out or abandoned by the customer on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:194 -msgid "Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Rave support team at hi@flutterwave.com to confirm the payment." +#: includes/class-flw-wc-payment-gateway-event-handler.php:218 +msgid "Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Flutterwave support team at hi@flutterwave.com to confirm the payment." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:195 +#: includes/class-flw-wc-payment-gateway-event-handler.php:219 msgid "Payment Reference: " msgstr "" -#. translators: 1: payment reference 2: transaction reference -#: includes/class-flw-wc-payment-gateway-subscriptions.php:163 -#, php-format -msgid "Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)" -msgstr "" - -#: includes/class-flw-wc-payment-gateway.php:154 +#: includes/class-flw-wc-payment-gateway.php:162 msgid "allows you to accept payment from cards and bank accounts in multiple currencies. You can also accept payment offline via USSD and POS." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:234 +#: includes/class-flw-wc-payment-gateway.php:274 msgid "Webhook Instruction" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:238 +#: includes/class-flw-wc-payment-gateway.php:278 msgid "Please copy this webhook URL and paste on the webhook section on your dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:259 +#: includes/class-flw-wc-payment-gateway.php:299 msgid "Enable/Disable" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:260 +#: includes/class-flw-wc-payment-gateway.php:300 msgid "Enable Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:262 +#: includes/class-flw-wc-payment-gateway.php:302 msgid "Enable Flutterwave as a payment option on the checkout page" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:267 +#: includes/class-flw-wc-payment-gateway.php:307 msgid "Enter Secret Hash" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:269 +#: includes/class-flw-wc-payment-gateway.php:309 msgid "Please change from default hash and ensure that SECRET HASH is the same with the one on your Flutterwave dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:273 +#: includes/class-flw-wc-payment-gateway.php:313 msgid "Payment method title" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:275 -#: includes/class-flw-wc-payment-gateway.php:281 +#: includes/class-flw-wc-payment-gateway.php:315 +#: includes/class-flw-wc-payment-gateway.php:321 msgid "Optional" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:279 +#: includes/class-flw-wc-payment-gateway.php:319 msgid "Payment method description" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:285 +#: includes/class-flw-wc-payment-gateway.php:325 msgid "Test Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:287 +#: includes/class-flw-wc-payment-gateway.php:327 msgid "Required! Enter your Flutterwave test public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:291 +#: includes/class-flw-wc-payment-gateway.php:331 msgid "Test Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:293 +#: includes/class-flw-wc-payment-gateway.php:333 msgid "Required! Enter your Flutterwave test secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:297 +#: includes/class-flw-wc-payment-gateway.php:337 msgid "Live Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:299 +#: includes/class-flw-wc-payment-gateway.php:339 msgid "Required! Enter your Flutterwave live public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:303 +#: includes/class-flw-wc-payment-gateway.php:343 msgid "Live Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:305 +#: includes/class-flw-wc-payment-gateway.php:345 msgid "Required! Enter your Flutterwave live secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:309 +#: includes/class-flw-wc-payment-gateway.php:349 msgid "Payment Style on checkout" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:311 +#: includes/class-flw-wc-payment-gateway.php:351 msgid "Optional - Choice of payment style to use. Either inline or redirect. (Default: inline)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:313 +#: includes/class-flw-wc-payment-gateway.php:353 msgctxt "payment_style" msgid "Popup(Keep payment experience on the website)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:314 +#: includes/class-flw-wc-payment-gateway.php:354 msgctxt "payment_style" msgid "Redirect" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:319 +#: includes/class-flw-wc-payment-gateway.php:359 msgid "Autocomplete Order After Payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:320 +#: includes/class-flw-wc-payment-gateway.php:360 msgid "Autocomplete Order" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:323 +#: includes/class-flw-wc-payment-gateway.php:363 msgid "If enabled, the order will be marked as complete after successful payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:328 +#: includes/class-flw-wc-payment-gateway.php:368 msgid "Payment Options" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:330 +#: includes/class-flw-wc-payment-gateway.php:370 msgid "Optional - Choice of payment method to use. Card, Account etc." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:332 +#: includes/class-flw-wc-payment-gateway.php:372 msgctxt "payment_options" msgid "All" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:333 +#: includes/class-flw-wc-payment-gateway.php:373 msgctxt "payment_options" msgid "Card Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:334 +#: includes/class-flw-wc-payment-gateway.php:374 msgctxt "payment_options" msgid "Account Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:335 +#: includes/class-flw-wc-payment-gateway.php:375 msgctxt "payment_options" msgid "USSD Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:336 +#: includes/class-flw-wc-payment-gateway.php:376 msgctxt "payment_options" msgid "QR Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:337 +#: includes/class-flw-wc-payment-gateway.php:377 msgctxt "payment_options" msgid "Mpesa Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:338 +#: includes/class-flw-wc-payment-gateway.php:378 msgctxt "payment_options" msgid "Ghana MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:339 +#: includes/class-flw-wc-payment-gateway.php:379 msgctxt "payment_options" msgid "Rwanda MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:340 +#: includes/class-flw-wc-payment-gateway.php:380 msgctxt "payment_options" msgid "Zambia MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:341 +#: includes/class-flw-wc-payment-gateway.php:381 msgctxt "payment_options" msgid "Tanzania MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:346 +#: includes/class-flw-wc-payment-gateway.php:386 msgid "Mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:347 +#: includes/class-flw-wc-payment-gateway.php:387 msgid "Live mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:349 +#: includes/class-flw-wc-payment-gateway.php:389 msgid "Check this box if you're using your live keys." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:354 -#: includes/class-flw-wc-payment-gateway.php:355 +#: includes/class-flw-wc-payment-gateway.php:394 +#: includes/class-flw-wc-payment-gateway.php:395 msgid "Disable Logging" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:357 +#: includes/class-flw-wc-payment-gateway.php:397 msgid "Check this box if you're disabling logging." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:362 -#: includes/class-flw-wc-payment-gateway.php:363 +#: includes/class-flw-wc-payment-gateway.php:402 +#: includes/class-flw-wc-payment-gateway.php:403 msgid "Disable Barter" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:365 +#: includes/class-flw-wc-payment-gateway.php:405 msgid "Check the box if you want to disable barter." msgstr "" #. translators: %s: url -#: includes/class-flw-wc-payment-gateway.php:456 +#: includes/class-flw-wc-payment-gateway.php:504 #, php-format msgid "Flutterwave is enabled, but the API keys are not set. Please set your Flutterwave API keys to be able to accept payments." msgstr "" #. translators: %s: shop cart url -#: includes/class-flw-wc-payment-gateway.php:493 +#: includes/class-flw-wc-payment-gateway.php:541 #, php-format msgid "Sorry, your session has expired. Return to shop" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:507 +#: includes/class-flw-wc-payment-gateway.php:555 msgid "We were unable to process your order, please try again." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:582 +#: includes/class-flw-wc-payment-gateway.php:636 msgid "Order Payment" msgstr "" diff --git a/includes/class-flutterwave.php b/includes/class-flutterwave.php index 049be44..288911a 100644 --- a/includes/class-flutterwave.php +++ b/includes/class-flutterwave.php @@ -18,7 +18,7 @@ final class Flutterwave { * * @var string */ - public string $version = '3.1.0'; + public string $version = '3.2.0'; /** * Plugin API version. diff --git a/includes/class-flw-wc-payment-gateway-event-handler.php b/includes/class-flw-wc-payment-gateway-event-handler.php index eb2ec9e..dfebeb9 100644 --- a/includes/class-flw-wc-payment-gateway-event-handler.php +++ b/includes/class-flw-wc-payment-gateway-event-handler.php @@ -2,7 +2,7 @@ /** * The client-specific functionality of the plugin. * - * This class handles all events from the Rave class + * This class handles all events from the Flutterwave class * * @link https://flutterwave.com * @since 2.3.2 @@ -14,10 +14,12 @@ declare(strict_types=1); require FLW_WC_DIR_PATH . 'includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php'; +require_once FLW_WC_DIR_PATH . 'includes/util/class-flutterwave-signoz-logger.php'; use Flutterwave\WooCommerce\Contracts\FLW_WC_Payment_Gateway_Event_Handler_Interface; +use Flutterwave\WooCommerce\Util\Flutterwave_Signoz_Logger; /** - * This is the class that handles all events from the Rave class + * This is the class that handles all events from the Flutterwave class * */ class FLW_WC_Payment_Gateway_Event_Handler implements FLW_WC_Payment_Gateway_Event_Handler_Interface { /** @@ -89,7 +91,14 @@ public function on_successful( object $transaction_data ) { } else { $this->order->payment_complete( $this->order->get_id() ); $payment_method = $this->order->get_payment_method(); - + $this->order->add_order_note( + sprintf( + /* translators: 1: payment reference 2: transaction reference */ + __( 'Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)', 'rave-woocommerce-payment-gateway' ), + $transaction_data->flw_ref, + $transaction_data->tx_ref + ) + ); $flw_settings = get_option( 'woocommerce_' . $payment_method . '_settings' ); if ( isset( $flw_settings['autocomplete_order'] ) && 'yes' === $flw_settings['autocomplete_order'] ) { @@ -101,6 +110,18 @@ public function on_successful( object $transaction_data ) { $customer_note = 'Thank you for your order.
'; $customer_note .= 'Your payment was successful, we are now processing your order.'; $this->order->add_order_note( $customer_note, 1 ); + + $is_production = Flutterwave_Signoz_Logger::instance()->get_current_environment() === 'production'; + + if ( $is_production ) { + Flutterwave_Signoz_Logger::instance()->track_transaction( + $transaction_data->tx_ref, + $transaction_data->currency, + (float) $transaction_data->amount, + $transaction_data->payment_type ?? 'card', + (float) $transaction_data->app_fee ?? 0 + ); + } } wc_add_notice( $customer_note, 'notice' ); // get order_id from the txref. @@ -124,10 +145,12 @@ public function on_failure( object $transaction_data ) { $this->order->add_order_note( esc_html__( 'The payment failed on Flutterwave', 'rave-woocommerce-payment-gateway' ) ); $customer_note = 'Your payment failed. '; $customer_note .= 'Please, try again or use another Payment Method on the modal.'; - $reason = $transaction_data->processor_response ?? ' - '; + $reason = $transaction_data->processor_response ?? $transaction_data->message ?? 'Payment processing failed'; $this->order->add_order_note( esc_html__( 'Reason for Failure : ', 'rave-woocommerce-payment-gateway' ) . $reason ); + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_FAILED', (string) $reason ); + wc_add_notice( $customer_note, 'notice' ); } @@ -156,9 +179,10 @@ public function on_requery_error( $requery_response ) { $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not confirm the payment. Please, look into it.', 'rave-woocommerce-payment-gateway' ) . '
'; $admin_note .= esc_html( 'Payment Responce: ' ) . $requery_response->message; + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_FAILED', (string) $requery_response->message ?? 'Payment Requery Failed' ); + $this->order->add_order_note( $customer_note, 1 ); $this->order->add_order_note( $admin_note ); - wc_add_notice( $customer_note, 'notice' ); } @@ -191,12 +215,13 @@ public function on_timeout( string $transaction_reference, object $data ) { $customer_note = 'Thank you for your order.
'; $customer_note .= 'We had an issue confirming your payment, but we have put your order on-hold. '; $customer_note .= esc_html__( 'Please, contact us for information regarding this order.', 'woocomerce-rave' ); - $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Rave support team at hi@flutterwave.com to confirm the payment.', 'rave-woocommerce-payment-gateway' ) . '
'; + $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Flutterwave support team at hi@flutterwave.com to confirm the payment.', 'rave-woocommerce-payment-gateway' ) . '
'; $admin_note .= esc_html__( 'Payment Reference: ', 'rave-woocommerce-payment-gateway' ) . $transaction_reference; + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_CONFIRMATION_TIMEOUT', 'Payment Confirmation timed out' ); + $this->order->add_order_note( $customer_note, 1 ); $this->order->add_order_note( $admin_note ); - wc_add_notice( $customer_note, 'notice' ); } diff --git a/includes/class-flw-wc-payment-gateway-subscriptions.php b/includes/class-flw-wc-payment-gateway-subscriptions.php index 4c4aa3b..2f01af6 100644 --- a/includes/class-flw-wc-payment-gateway-subscriptions.php +++ b/includes/class-flw-wc-payment-gateway-subscriptions.php @@ -28,7 +28,7 @@ class FLW_WC_Payment_Gateway_Subscriptions extends FLW_WC_Payment_Gateway { /** - * Constructor + * Constructor. */ public function __construct() { diff --git a/includes/class-flw-wc-payment-gateway.php b/includes/class-flw-wc-payment-gateway.php index ac0407c..a9f08c7 100644 --- a/includes/class-flw-wc-payment-gateway.php +++ b/includes/class-flw-wc-payment-gateway.php @@ -26,11 +26,13 @@ require_once __DIR__ . '/client/class-flw-wc-payment-gateway-request.php'; require_once __DIR__ . '/client/class-flw-wc-payment-gateway-sdk.php'; require_once __DIR__ . '/util/class-flutterwave-logger.php'; +require_once __DIR__ . '/util/class-flutterwave-signoz-logger.php'; use Flutterwave\WooCommerce\Client\Flw_WC_Payment_Gateway_Request; use Flutterwave\WooCommerce\Client\FLW_WC_Payment_Gateway_Sdk as FlwSdk; use FLW_WC_Payment_Gateway_Event_Handler as FlwEventHandler; use Flutterwave\WooCommerce\Util\Flutterwave_Logger; +use Flutterwave\WooCommerce\Util\Flutterwave_Signoz_Logger; /** * Main Flutterwave Gateway Class @@ -97,6 +99,12 @@ class FLW_WC_Payment_Gateway extends WC_Payment_Gateway { * @var Flutterwave_Logger the logger */ private Flutterwave_Logger $logger; + /** + * SigNoz observability logger. + * + * @var Flutterwave_Signoz_Logger + */ + private Flutterwave_Signoz_Logger $signoz_logger; /** * Flutterwave Sdk * @@ -206,9 +214,11 @@ public function __construct() { $this->secret_key = $this->live_secret_key; } - $this->logger = Flutterwave_Logger::instance(); - $this->sdk = new FlwSdk( $this->secret_key, self::$log_enabled ); + $this->logger = Flutterwave_Logger::instance(); + $this->signoz_logger = Flutterwave_Signoz_Logger::instance(); + $this->sdk = new FlwSdk( $this->secret_key, self::$log_enabled ); + add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'send_app_created_event' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) ); } @@ -222,6 +232,36 @@ public function get_secret_key(): string { return $this->secret_key; } + /** + * Fire the app.created SigNoz event using the freshly-saved settings. + * Hooked into woocommerce_update_options_payment_gateways_{id} so it runs + * after process_admin_options() has persisted the new values to the DB. + * + * @return void + */ + public function send_app_created_event(): void { + $settings = get_option( 'woocommerce_rave_settings', array() ); + + if ( isset( $settings['merchant_id'] ) && $settings['app_registered'] ) { + return; + } + + $go_live = $settings['go_live'] ?? 'no'; + $public_key = 'yes' === $go_live + ? ( $settings['live_public_key'] ?? '' ) + : ( $settings['test_public_key'] ?? '' ); + + if ( empty( $public_key ) ) { + return; + } + + $this->logger->info( 'Starting Application Integration' ); + $merchant_info = $this->signoz_logger->init( $public_key ); + $this->signoz_logger->track_app_created( $public_key, $merchant_info ); + $this->signoz_logger->mark_app_registered(); + $this->logger->info( 'Successfully Registered Application Integration' ); + } + /** * WooCommerce admin settings override. */ @@ -409,6 +449,12 @@ public function process_redirect_payments( $order_id ) { try { $flutterwave_request = ( new FLW_WC_Payment_Gateway_Request() )->get_prepared_payload( $order, $this->get_secret_key() ); + $this->signoz_logger->track_request_sent( + 'POST', + $flutterwave_request['tx_ref'], + '/payments', + $this->logger + ); } catch ( \InvalidArgumentException $flw_e ) { wc_add_notice( $flw_e, 'error' ); // redirect user to check out page. @@ -421,9 +467,11 @@ public function process_redirect_payments( $order_id ) { $flutterwave_request['payment_options'] = $this->payment_options; $custom_nonce = wp_create_nonce(); $flutterwave_request['redirect_url'] = $flutterwave_request['redirect_url'] . '&_wpnonce=' . $custom_nonce; - $sdk = $this->sdk->set_event_handler( new FlwEventHandler( $order ) ); + + $sdk = $this->sdk->set_event_handler( new FlwEventHandler( $order ) ); $response = $sdk->get_client()->request( $this->sdk::$standard_inline_endpoint, 'POST', $flutterwave_request ); + if ( ! is_wp_error( $response ) ) { $response = json_decode( $response['body'] ); return array( @@ -537,6 +585,12 @@ public function payment_scripts() { $custom_nonce = wp_create_nonce(); $redirect_url = ''; + $this->signoz_logger->track_request_sent( + 'GET', + $txnref, + '/inline' + ); + $flutterwave_woo_url = WC()->api_request_url( 'FLW_WC_Payment_Gateway' ); // Parse the base URL to check for existing query parameters. @@ -620,6 +674,12 @@ public function flw_verify_payment() { die(); } + Flutterwave_Signoz_Logger::instance()->track_request_sent( + 'GET', + $txn_ref, + '/transactions/verify_by_reference?tx_ref=' + ); + $sdk->set_event_handler( new FlwEventHandler( $order ) )->requery_transaction( $txn_ref ); $redirect_url = $this->get_return_url( $order ); @@ -656,6 +716,10 @@ public function flutterwave_webhooks() { if ( $signature !== $local_signature ) { $this->logger->info( 'Faudulent Webhook Notification Attempt [Access Restricted]' ); + $this->signoz_logger->track_error( + 'WEBHOOK_SIGNATURE_MISMATCH', + 'Webhook signature mismatch. Access Denied. Hash does not match.' + ); wp_send_json( array( 'status' => 'error', @@ -670,6 +734,10 @@ public function flutterwave_webhooks() { $event = json_decode( $event ); if ( empty( $event->event ) && empty( $event->data ) ) { + $this->signoz_logger->track_error( + 'WEBHOOK_BODY_DEFORMED', + 'Webhook body is malformed. Missing event or data properties.' + ); wp_send_json( array( 'status' => 'error', @@ -714,6 +782,10 @@ public function flutterwave_webhooks() { $order = wc_get_order( $order_id ); if ( ! $order ) { + $this->signoz_logger->track_error( + 'INVALID_ORDER_REFERENCE_FROM_WEBHOOK', + 'Webhook sent an invalid order reference. No order found with ID: ' . $order_id + ); wp_send_json( array( 'status' => 'error', @@ -751,6 +823,12 @@ public function flutterwave_webhooks() { ); } + Flutterwave_Signoz_Logger::instance()->track_request_sent( + 'GET', + $txn_ref, + '/transactions/verify_by_reference?tx_ref=' + ); + $sdk->set_event_handler( new FlwEventHandler( $order ) )->webhook_verify( $event_type, $event_data ); wp_send_json( array( diff --git a/includes/client/class-flw-wc-payment-gateway-sdk.php b/includes/client/class-flw-wc-payment-gateway-sdk.php index 7fa9761..65f7d5d 100644 --- a/includes/client/class-flw-wc-payment-gateway-sdk.php +++ b/includes/client/class-flw-wc-payment-gateway-sdk.php @@ -15,7 +15,7 @@ use FLW_WC_Payment_Gateway; if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } // Prevent direct access to this class. @@ -30,247 +30,247 @@ * @package Flutterwave\WooCommerce\Client */ final class FLW_WC_Payment_Gateway_Sdk { - /** - * Count of the number of times the server has been queried. - * - * @var int The number of times to requery the server. - */ - protected int $requery_count = 0; - /** - * Client - * - * @var FLW_WC_Payment_Gateway_Client The client. - */ - public FLW_WC_Payment_Gateway_Client $client; - /** - * Event handler - * - * @var FLW_WC_Payment_Gateway_Event_Handler_Interface - */ - private FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler; - /** - * Data - * - * @var array The data to be sent to the server. - */ - private array $data; - /** - * Standard inline endpoint - * - * @var string The endpoint to send the data to. - */ - public static string $standard_inline_endpoint = 'https://api.flutterwave.com/v3/payments'; - /** - * Inline Checkout Script - * - * @var string The script to initiate the payment. - */ - public static string $checkout_url = 'https://checkout.flutterwave.com/v3.js'; - /** - * Logger - * - * @var mixed - */ - private \WC_Logger $logger; - /** - * Gateway - * - * @var FLW_WC_Payment_Gateway The gateway. - */ - private FLW_WC_Payment_Gateway $gateway; + /** + * Count of the number of times the server has been queried. + * + * @var int The number of times to requery the server. + */ + protected int $requery_count = 0; + /** + * Client + * + * @var FLW_WC_Payment_Gateway_Client The client. + */ + public FLW_WC_Payment_Gateway_Client $client; + /** + * Event handler + * + * @var FLW_WC_Payment_Gateway_Event_Handler_Interface + */ + private FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler; + /** + * Data + * + * @var array The data to be sent to the server. + */ + private array $data; + /** + * Standard inline endpoint + * + * @var string The endpoint to send the data to. + */ + public static string $standard_inline_endpoint = 'https://api.flutterwave.com/v3/payments'; + /** + * Inline Checkout Script + * + * @var string The script to initiate the payment. + */ + public static string $checkout_url = 'https://checkout.flutterwave.com/v3.js'; + /** + * Logger + * + * @var mixed + */ + private \WC_Logger $logger; + /** + * Gateway + * + * @var FLW_WC_Payment_Gateway The gateway. + */ + private FLW_WC_Payment_Gateway $gateway; - /** - * FLW_WC_Payment_Gateway_Sdk constructor. - * - * @param string $secret_key The secret key. - * @param bool $log_enabled Whether to log or not. - */ - public function __construct( string $secret_key, bool $log_enabled = false ) { - $this->client = new FLW_WC_Payment_Gateway_Client( $secret_key, $log_enabled ); - $this->logger = wc_get_logger(); - return $this; - } + /** + * FLW_WC_Payment_Gateway_Sdk constructor. + * + * @param string $secret_key The secret key. + * @param bool $log_enabled Whether to log or not. + */ + public function __construct( string $secret_key, bool $log_enabled = false ) { + $this->client = new FLW_WC_Payment_Gateway_Client( $secret_key, $log_enabled ); + $this->logger = wc_get_logger(); + return $this; + } - /** - * Clone method. - * Prevents cloning of this class. - * - * @since 2.3.2 - * - * @return null The clone method. - */ - public function __clone() { - return null; - } + /** + * Clone method. + * Prevents cloning of this class. + * + * @since 2.3.2 + * + * @return null The clone method. + */ + public function __clone() { + return null; + } - /** - * Get the client. - * Returns the client. - * - * @since 2.3.2 - * - * @return FLW_WC_Payment_Gateway_Client The client. - */ - public function get_client(): FLW_WC_Payment_Gateway_Client { - return $this->client; - } + /** + * Get the client. + * Returns the client. + * + * @since 2.3.2 + * + * @return FLW_WC_Payment_Gateway_Client The client. + */ + public function get_client(): FLW_WC_Payment_Gateway_Client { + return $this->client; + } - /** - * Set the event handler. - * - * @param FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler The event handler. - * - * @return FLW_WC_Payment_Gateway_Sdk The sdk. - */ - public function set_event_handler( FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler ): FLW_WC_Payment_Gateway_Sdk { - $this->event_handler = $event_handler; + /** + * Set the event handler. + * + * @param FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler The event handler. + * + * @return FLW_WC_Payment_Gateway_Sdk The sdk. + */ + public function set_event_handler( FLW_WC_Payment_Gateway_Event_Handler_Interface $event_handler ): FLW_WC_Payment_Gateway_Sdk { + $this->event_handler = $event_handler; - return $this; - } + return $this; + } - /** - * Prepares the html to be rendered. - * - * @param array $clean_data This is the data that will be sent to the server. - * @param int $order_id The order id. - * - * @return string The html to be rendered. - */ - private function prepare_html( array $clean_data, int $order_id ): string { - // TODO: enqueue inline checkout script. + /** + * Prepares the html to be rendered. + * + * @param array $clean_data This is the data that will be sent to the server. + * @param int $order_id The order id. + * + * @return string The html to be rendered. + */ + private function prepare_html( array $clean_data, int $order_id ): string { + // TODO: enqueue inline checkout script. - $html_array = array( - '', - '', - ' ', - ' ', - '', - '', - ); + $html_array = array( + '', + '', + ' ', + ' ', + '', + '', + ); - return implode( '', $html_array ); - } + return implode( '', $html_array ); + } - /** - * Render the modal. - * - * @param array $data This is the data to be sent to the payment gateway. - * @param int $order_id This is the order id. - * - * @return false|string The json encoded data. - */ - public function render_modal( array $data, int $order_id ) { - $clean_data = $data; - $html = $this->prepare_html( $clean_data, $order_id ); - $this->logger->notice( 'Loading Payment Modal for order:' . $order_id ); - echo wp_kses_post( $html ); - return wp_json_encode( $clean_data ); - } + /** + * Render the modal. + * + * @param array $data This is the data to be sent to the payment gateway. + * @param int $order_id This is the order id. + * + * @return false|string The json encoded data. + */ + public function render_modal( array $data, int $order_id ) { + $clean_data = $data; + $html = $this->prepare_html( $clean_data, $order_id ); + $this->logger->notice( 'Loading Payment Modal for order:' . $order_id ); + echo wp_kses_post( $html ); + return wp_json_encode( $clean_data ); + } - /** - * Transaction Reference. - * - * @param string $tx_ref This is the unique reference for the transaction. - * - * @return void The requery transaction. - */ - public function requery_transaction( string $tx_ref ) { - $this->requery_count ++; - $this->logger->notice( 'Requerying Transaction....' . $tx_ref ); + /** + * Transaction Reference. + * + * @param string $tx_ref This is the unique reference for the transaction. + * + * @return void The requery transaction. + */ + public function requery_transaction( string $tx_ref ) { + $this->requery_count ++; + $this->logger->notice( 'Requerying Transaction....' . $tx_ref ); - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_requery( $tx_ref ); - } + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_requery( $tx_ref ); + } - $url = $this->client::API_BASE_URL . '/' . $this->client::API_VERSION . '/transactions/verify_by_reference?tx_ref=' . $tx_ref; + $url = $this->client::API_BASE_URL . '/' . $this->client::API_VERSION . '/transactions/verify_by_reference?tx_ref=' . $tx_ref; - $response = $this->client->request( $url ); + $response = $this->client->request( $url ); - if ( ! is_wp_error( $response ) ) { - $response = json_decode( $response['body'] ); - if ( 'success' === $response->status ) { - $this->logger->notice( 'Transaction Requeried Successfully' ); + if ( ! is_wp_error( $response ) ) { + $response = json_decode( $response['body'] ); + if ( 'success' === $response->status ) { + $this->logger->notice( 'Transaction Requeried Successfully' ); - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_successful( $response->data ); - } - } else { - $this->logger->notice( 'Transaction Requeried Failed' ); - $this->event_handler->on_failure( $response ); - } - } else { - // TODO: handle request errors. - $this->logger->notice( 'Transaction Requeried Failed. Awaiting Webhook Verification...' ); - } + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_successful( $response->data ); + } + } else { + $this->logger->notice( 'Transaction Requeried Failed' ); + $this->event_handler->on_failure( $response ); + } + } else { + // TODO: handle request errors. + $this->logger->notice( 'Transaction Requeried Failed. Awaiting Webhook Verification...' ); + } - } + } - /** - * Webhook Verification. - * - * @param string $event_type The event type. - * @param object $event_data The event data. - */ - public function webhook_verify( string $event_type, object $event_data ) { - $this->logger->notice( 'Webhook Verification Started' ); - $this->logger->notice( 'Event Type: ' . $event_type ); + /** + * Webhook Verification. + * + * @param string $event_type The event type. + * @param object $event_data The event data. + */ + public function webhook_verify( string $event_type, object $event_data ) { + $this->logger->notice( 'Webhook Verification Started' ); + $this->logger->notice( 'Event Type: ' . $event_type ); - $event_type = strtolower( $event_type ); + $event_type = strtolower( $event_type ); - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_webhook( $event_type, $event_data ); - } + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_webhook( $event_type, $event_data ); + } - if ( method_exists( $this, 'requery_transaction' ) ) { - $this->requery_transaction( $event_data->tx_ref ); - } else { - $this->logger->notice( 'Webhook Verification Failed' ); - } + if ( method_exists( $this, 'requery_transaction' ) ) { + $this->requery_transaction( $event_data->tx_ref ); + } else { + $this->logger->notice( 'Webhook Verification Failed' ); + } - } + } - /** - * Cancel Payment. - * - * @param string $tx_ref This is the transaction ref to be cancelled. - * - * @return void - */ - public function cancel_payment( string $tx_ref ) { - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_cancel( $tx_ref ); - } - } -} + /** + * Cancel Payment. + * + * @param string $tx_ref This is the transaction ref to be cancelled. + * + * @return void + */ + public function cancel_payment( string $tx_ref ) { + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_cancel( $tx_ref ); + } + } +} \ No newline at end of file diff --git a/includes/util/class-flutterwave-signoz-logger.php b/includes/util/class-flutterwave-signoz-logger.php new file mode 100644 index 0000000..d58737d --- /dev/null +++ b/includes/util/class-flutterwave-signoz-logger.php @@ -0,0 +1,298 @@ +). + * + * @var string + */ + private string $app_id = ''; + + /** + * Merchant environment: "production" or "sandbox". + * + * @var string + */ + private string $environment = 'sandbox'; + + /** + * Private constructor — use instance(). + */ + private function __construct() {} + + /** + * Get or create the singleton. + * + * @return self + */ + public static function instance(): self { + if ( null === self::$instance ) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * @return array + */ + public function get_settings(): array { + return get_option( 'woocommerce_rave_settings', array() ); + } + + /** + * Configure the logger for a specific merchant public key and environment. + * Must be called before any track_* methods. + * + * @param string $public_key Merchant Flutterwave public key. + * @return string + */ + public function init( string $public_key ): string { + $merchant_id = $this->get_merchant_id( $public_key ); + if ( $merchant_id ) { + $merchant_id = str_replace( ' ', '_', $merchant_id ); + } + $this->app_id = $merchant_id ?? $public_key; + $this->environment = $this->get_current_environment(); + $new_options = $this->get_settings(); + $new_options['merchant_id'] = $this->app_id; + $new_options['app_registered'] = false; + update_option( 'woocommerce_rave_settings', $new_options ); + return $merchant_id; + } + + /** + * Get the merchant's Flutterwave account name using the public key. + * + * @param string $public_key Merchant Flutterwave public key. + * @return string|null Account name or null on failure. + */ + public function get_merchant_id( string $public_key ): ?string { + $settings = $this->get_settings(); + + if ( isset( $settings['merchant_id'] ) && $settings['merchant_id'] ) { + return $settings['merchant_id']; + } + // Extract the merchant ID from the public key (assuming format "FLWPUBK--..."). + // Make a request to https://api.ravepay.co/flwv3-pug/getpaidx/api/mercinfo?PBFPubKey=$public_key. + $url = 'https://api.ravepay.co/flwv3-pug/getpaidx/api/mercinfo?PBFPubKey=' . $public_key; + $response = wp_remote_get( $url ); + + if ( is_wp_error( $response ) ) { + return null; + } + + $body = wp_remote_retrieve_body( $response ); + $body = json_decode( $body, true ); + + return $body['mn'] ?? null; + } + + /** + * Fire the `app.created` event. + * Should be called once when the merchant first configures their keys. + * + * @param string $public_key Merchant Flutterwave public key. + * @param string $merchant_info Merchants actual name. + * @return void + */ + public function track_app_created( string $public_key, string $merchant_info ): void { + if ( empty( $public_key ) && empty( $merchant_info ) ) { + return; + } + $this->app_id = $merchant_info; + $this->send( + 'app.created', + array( + 'app_id' => $this->app_id, + 'client_id' => null, + 'public_key' => $public_key, + 'library' => self::LIBRARY, + 'library_version' => $this->library_version(), + ) + ); + } + + /** + * Mark App as Registered. + * + * @return void + */ + public function mark_app_registered() { + $settings = $this->get_settings(); + $new_settings = $settings; + $new_settings['app_registered'] = true; + update_option( 'woocommerce_rave_settings', $new_settings ); + } + + /** + * Get an Application Identifier. + */ + public function get_app_id(): string { + $settings = $this->get_settings(); + return $settings['merchant_id']; + } + + /** + * @return string + */ + public function get_current_environment(): string { + $settings = $this->get_settings(); + return 'yes' === $settings['go_live'] ? 'production' : 'sandbox'; + } + + /** + * Fire the `request.sent` event when a payment request is initiated. + * + * @param string $method Payment method (e.g. "card"). + * @param string $reference Transaction reference (tx_ref). + * @param string $path Request path (e.g. "/v3/charges"). + * @return void + */ + public function track_request_sent( string $method, string $reference, string $path, $logger = null ): void { + $payload = array( + 'app_id' => $this->get_app_id(), + 'environment' => $this->get_current_environment(), + 'api_version' => 'v3', + 'library_version' => $this->library_version(), + 'method' => $method, + 'path' => $path, + 'reference' => $reference, + ); + + if ( ! is_null( $logger ) ) { + $logger->info( 'request.sent: ' . wp_json_encode( $payload ) ); + } + + $this->send( + 'request.sent', + $payload + ); + } + + /** + * Fire the `app.transaction` event after a successful payment. + * + * @param string $reference Transaction reference (tx_ref). + * @param string $currency ISO 4217 currency code. + * @param float $amount Transaction amount. + * @param string $method Payment method (e.g. "card"). + * @param float $fee Transaction fee. + * @return void + */ + public function track_transaction( + string $reference, + string $currency, + float $amount, + string $method, + float $fee + ): void { + $this->app_id = $this->get_app_id(); + $this->environment = $this->get_current_environment(); + $this->send( + 'app.transaction', + array( + 'app_id' => $this->app_id, + 'reference' => $reference, + 'currency' => $currency, + 'amount' => $amount, + 'fee' => $fee, + 'method' => $method, + ) + ); + } + + /** + * Fire the `app.error` event when a payment fails. + * + * @param string $error_code Short machine-readable error code. + * @param string $error_message Human-readable error description. + * @return void + */ + public function track_error( string $error_code, string $error_message ): void { + $this->app_id = $this->get_app_id(); + $this->environment = $this->get_current_environment(); + $this->send( + 'app.error', + array( + 'app_id' => $this->app_id, + 'library' => self::LIBRARY, + 'library_version' => $this->library_version(), + 'error_code' => $error_code, + 'error_message' => $error_message, + ) + ); + } + + /** + * Dispatch an event to the SigNoz service. + * Uses non-blocking HTTP so the payment flow is never delayed. + * + * @param string $event_name SigNoz event name (e.g. "app.created"). + * @param array $data Event payload. + * @return void + */ + private function send( string $event_name, array $data ): void { + $payload = array( + 'name' => $event_name, + 'data' => $data, + 'timestamp' => gmdate( 'Y-m-d\TH:i:s.000\Z' ), + ); + + wp_remote_post( + self::BASE_URL . '/events', + array( + 'headers' => array( + 'Content-Type' => 'application/json', + 'x-api-key' => self::API_KEY, + ), + 'body' => wp_json_encode( $payload ), + 'blocking' => false, + 'timeout' => 5, + ) + ); + } + + /** + * Return the plugin version, falling back gracefully if the constant is not yet defined. + * + * @return string + */ + private function library_version(): string { + return defined( 'FLW_WC_VERSION' ) ? FLW_WC_VERSION : '3.1.0'; + } +} diff --git a/package-lock.json b/package-lock.json index 90fc3c3..e2ee61f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rave-woocommerce-payment-gateway", - "version": "2.3.6", + "version": "3.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rave-woocommerce-payment-gateway", - "version": "2.3.6", + "version": "3.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 56746e5..d86597b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rave-woocommerce-payment-gateway", "title": "flutterwave-woocommerce", - "version": "3.1.0", + "version": "3.2.0", "description": "Official WooCommerce payment gateway for Flutterwave", "scripts": { "postinstall": "composer install", diff --git a/rave-woocommerce-payment-gateway.php b/rave-woocommerce-payment-gateway.php index a524463..16782db 100644 --- a/rave-woocommerce-payment-gateway.php +++ b/rave-woocommerce-payment-gateway.php @@ -3,7 +3,7 @@ * Plugin Name: Flutterwave WooCommerce * Plugin URI: https://developer.flutterwave.com/ * Description: Official WooCommerce payment gateway for Flutterwave. - * Version: 3.1.0 + * Version: 3.2.0 * Author: Flutterwave Developers * Author URI: http://flutterwave.com/us * License: MIT License diff --git a/readme.txt b/readme.txt index 1874664..a83f454 100644 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: theflutterwave Tags: fintech,flutterwave, woocommerce, payments, nigeria, mastercard, visa, target,Naira,payments,verve,donation,church,shop,store, ghana, kenya, international, mastercard, visa Requires at least: 3.1 -Tested up to: 6.7.1 -Stable tag: 3.1.0 +Tested up to: 7.0.0 +Stable tag: 3.2.0 License: MIT License URI: https://github.com/Flutterwave/Woocommerce/blob/master/LICENSE diff --git a/tests/PHPUnit/test-flutterwave-signoz-logger.php b/tests/PHPUnit/test-flutterwave-signoz-logger.php new file mode 100644 index 0000000..20588f9 --- /dev/null +++ b/tests/PHPUnit/test-flutterwave-signoz-logger.php @@ -0,0 +1,717 @@ +reset_singleton(); + $this->logger = Flutterwave_Signoz_Logger::instance(); + $this->captured_requests = []; + } + + public function tear_down(): void { + parent::tear_down(); + remove_all_filters( 'pre_http_request' ); + $this->reset_singleton(); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + /** + * Reset the singleton so each test starts with a fresh instance. + */ + private function reset_singleton(): void { + $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); + $prop = $ref->getProperty( 'instance' ); + $prop->setAccessible( true ); + $prop->setValue( null, null ); + } + + /** + * Read a private instance property via reflection. + * + * @param string $name Property name. + * @return mixed Property value. + * @throws ReflectionException If the property does not exist. + */ + private function get_property( string $name ) { + $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); + $prop = $ref->getProperty( $name ); + $prop->setAccessible( true ); + return $prop->getValue( $this->logger ); + } + + /** + * Add a filter that stubs the merchant-info API response. + * + * @param string|null $merchant_name Value to return in `mn`; null omits the key. + * @param bool $wp_error Return a WP_Error instead of a response. + * @param string|null $raw_body Override the full response body (raw JSON string). + */ + private function mock_merchant_api( + ?string $merchant_name = null, + bool $wp_error = false, + ?string $raw_body = null + ): void { + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) use ( $merchant_name, $wp_error, $raw_body ) { + if ( strpos( $url, 'api.ravepay.co' ) === false ) { + return $response; + } + if ( $wp_error ) { + return new WP_Error( 'http_request_failed', 'Connection timed out' ); + } + if ( null !== $raw_body ) { + $body = $raw_body; + } elseif ( null !== $merchant_name ) { + $body = wp_json_encode( + [ + 'mn' => $merchant_name, + 'status' => 'success', + ] + ); + } else { + $body = wp_json_encode( [ 'status' => 'success' ] ); + } + return [ + 'body' => $body, + 'headers' => [], + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }, + 10, + 3 + ); + } + + /** + * Add a filter that captures every request sent to the SigNoz endpoint. + */ + private function capture_signoz_requests(): void { + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) { + if ( strpos( $url, 'signozservice-prod' ) === false ) { + return $response; + } + $this->captured_requests[] = [ + 'url' => $url, + 'args' => $args, + 'body' => json_decode( $args['body'], true ), + ]; + return [ + 'body' => wp_json_encode( [ 'status' => 'ok' ] ), + 'headers' => [], + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }, + 10, + 3 + ); + } + + // ========================================================================= + // Singleton + // ========================================================================= + + public function test_instance_returns_flutterwave_signoz_logger(): void { + $this->assertInstanceOf( Flutterwave_Signoz_Logger::class, Flutterwave_Signoz_Logger::instance() ); + } + + public function test_instance_returns_same_object_on_repeated_calls(): void { + $a = Flutterwave_Signoz_Logger::instance(); + $b = Flutterwave_Signoz_Logger::instance(); + $this->assertSame( $a, $b ); + } + + // ========================================================================= + // get_merchant_id() + // ========================================================================= + + public function test_get_merchant_id_returns_name_from_mn_field(): void { + $this->mock_merchant_api( 'AcmeCorp' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertEquals( 'AcmeCorp', $result ); + } + + public function test_get_merchant_id_returns_null_on_wp_error(): void { + $this->mock_merchant_api( null, true ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-bad-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_when_mn_key_absent(): void { + $this->mock_merchant_api( null ); // response has no 'mn' key + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_on_malformed_json(): void { + $this->mock_merchant_api( null, false, 'not-valid-json' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_on_empty_body(): void { + $this->mock_merchant_api( null, false, '' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_when_mn_is_null_in_response(): void { + $this->mock_merchant_api( null, false, wp_json_encode( [ 'mn' => null ] ) ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + // ========================================================================= + // init() + // ========================================================================= + + public function test_init_sets_app_id_from_merchant_name(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->assertEquals( 'TestMerchant', $this->get_property( 'app_id' ) ); + } + + public function test_init_falls_back_to_public_key_when_merchant_id_is_null(): void { + $this->mock_merchant_api( null ); // no 'mn' in response. + $this->logger->init( 'FLWPUBK-fallback-key', 'sandbox' ); + $this->assertEquals( 'FLWPUBK-fallback-key', $this->get_property( 'app_id' ) ); + } + + public function test_init_falls_back_to_public_key_on_wp_error(): void { + $this->mock_merchant_api( null, true ); + $this->logger->init( 'FLWPUBK-network-fail', 'sandbox' ); + $this->assertEquals( 'FLWPUBK-network-fail', $this->get_property( 'app_id' ) ); + } + + public function test_init_sets_environment_to_sandbox(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); + } + + public function test_init_sets_environment_to_production(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->assertEquals( 'production', $this->get_property( 'environment' ) ); + } + + public function test_init_defaults_environment_to_sandbox(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); + } + + public function test_app_id_is_empty_before_init(): void { + $this->assertEquals( '', $this->get_property( 'app_id' ) ); + } + + // ========================================================================= + // track_app_created() + // ========================================================================= + + public function test_track_app_created_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_app_created_sends_exactly_one_request(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $this->assertCount( 1, $this->captured_requests ); + } + + public function test_track_app_created_sends_correct_event_name(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $this->assertEquals( 'app.created', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_app_created_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'TestMerchant', $data['app_id'] ); + $this->assertNull( $data['client_id'] ); + $this->assertEquals( 'FLWPUBK-test-key', $data['public_key'] ); + $this->assertEquals( 'woocommerce', $data['library'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + // ========================================================================= + // track_request_sent() + // ========================================================================= + + public function test_track_request_sent_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_request_sent_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + + $this->assertEquals( 'request.sent', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_request_sent_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'production', $data['environment'] ); + $this->assertEquals( 'v3', $data['api_version'] ); + $this->assertEquals( 'card', $data['method'] ); + $this->assertEquals( 'txn-ref-123', $data['reference'] ); + $this->assertEquals( '/v3/charges', $data['path'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + public function test_track_request_sent_includes_sandbox_environment(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'banktransfer', 'ref-456', '/v3/transfers' ); + + $this->assertEquals( 'sandbox', $this->captured_requests[0]['body']['data']['environment'] ); + } + + // ========================================================================= + // track_transaction() + // ========================================================================= + + public function test_track_transaction_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_transaction_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + + $this->assertEquals( 'app.transaction', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_transaction_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'ref-123', $data['reference'] ); + $this->assertEquals( 'NGN', $data['currency'] ); + $this->assertEquals( 5000.00, $data['amount'] ); + $this->assertEquals( 70.00, $data['fee'] ); + $this->assertEquals( 'card', $data['method'] ); + } + + public function test_track_transaction_with_zero_amount_and_fee(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-zero', 'USD', 0.0, 'card', 0.0 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 0.0, $data['amount'] ); + $this->assertEquals( 0.0, $data['fee'] ); + } + + public function test_track_transaction_with_fractional_amounts(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-frac', 'GHS', 99.99, 'mobilemoney', 1.50 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 99.99, $data['amount'] ); + $this->assertEquals( 1.50, $data['fee'] ); + } + + public function test_track_transaction_supports_different_currencies(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + foreach ( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ] as $currency ) { + $this->logger->track_transaction( 'ref-' . $currency, $currency, 100.0, 'card', 1.0 ); + } + + $this->assertCount( 5, $this->captured_requests ); + $sent_currencies = array_column( + array_column( array_column( $this->captured_requests, 'body' ), 'data' ), + 'currency' + ); + $this->assertEquals( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ], $sent_currencies ); + } + + // ========================================================================= + // track_error() + // ========================================================================= + + public function test_track_error_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_error_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); + + $this->assertEquals( 'app.error', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_error_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'woocommerce', $data['library'] ); + $this->assertEquals( 'PAYMENT_FAILED', $data['error_code'] ); + $this->assertEquals( 'Card declined by issuer', $data['error_message'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + public function test_track_error_with_empty_strings(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( '', '' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( '', $data['error_code'] ); + $this->assertEquals( '', $data['error_message'] ); + } + + public function test_track_error_with_special_characters(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $code = 'ERR_', - '', - '', - ); + $html_array = array( + '', + '', + ' ', + ' ', + '', + '', + ); - return implode( '', $html_array ); - } + return implode( '', $html_array ); + } - /** - * Render the modal. - * - * @param array $data This is the data to be sent to the payment gateway. - * @param int $order_id This is the order id. - * - * @return false|string The json encoded data. - */ - public function render_modal( array $data, int $order_id ) { - $clean_data = $data; - $html = $this->prepare_html( $clean_data, $order_id ); - $this->logger->notice( 'Loading Payment Modal for order:' . $order_id ); - echo wp_kses_post( $html ); - return wp_json_encode( $clean_data ); - } + /** + * Render the modal. + * + * @param array $data This is the data to be sent to the payment gateway. + * @param int $order_id This is the order id. + * + * @return false|string The json encoded data. + */ + public function render_modal( array $data, int $order_id ) { + $clean_data = $data; + $html = $this->prepare_html( $clean_data, $order_id ); + $this->logger->notice( 'Loading Payment Modal for order:' . $order_id ); + echo wp_kses_post( $html ); + return wp_json_encode( $clean_data ); + } - /** - * Transaction Reference. - * - * @param string $tx_ref This is the unique reference for the transaction. - * - * @return void The requery transaction. - */ - public function requery_transaction( string $tx_ref ) { - $this->requery_count ++; - $this->logger->notice( 'Requerying Transaction....' . $tx_ref ); + /** + * Transaction Reference. + * + * @param string $tx_ref This is the unique reference for the transaction. + * + * @return void The requery transaction. + */ + public function requery_transaction( string $tx_ref ) { + ++$this->requery_count; + $this->logger->notice( 'Requerying Transaction....' . $tx_ref ); - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_requery( $tx_ref ); - } + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_requery( $tx_ref ); + } - $url = $this->client::API_BASE_URL . '/' . $this->client::API_VERSION . '/transactions/verify_by_reference?tx_ref=' . $tx_ref; + $url = $this->client::API_BASE_URL . '/' . $this->client::API_VERSION . '/transactions/verify_by_reference?tx_ref=' . $tx_ref; - $response = $this->client->request( $url ); + $response = $this->client->request( $url ); - if ( ! is_wp_error( $response ) ) { - $response = json_decode( $response['body'] ); - if ( 'success' === $response->status ) { - $this->logger->notice( 'Transaction Requeried Successfully' ); + if ( ! is_wp_error( $response ) ) { + $response = json_decode( $response['body'] ); + if ( 'success' === $response->status ) { + $this->logger->notice( 'Transaction Requeried Successfully' ); - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_successful( $response->data ); - } - } else { - $this->logger->notice( 'Transaction Requeried Failed' ); - $this->event_handler->on_failure( $response ); - } - } else { - // TODO: handle request errors. - $this->logger->notice( 'Transaction Requeried Failed. Awaiting Webhook Verification...' ); - } + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_successful( $response->data ); + } + } else { + $this->logger->notice( 'Transaction Requeried Failed' ); + $this->event_handler->on_failure( $response ); + } + } else { + // TODO: handle request errors. + $this->logger->notice( 'Transaction Requeried Failed. Awaiting Webhook Verification...' ); + } + } - } + /** + * Webhook Verification. + * + * @param string $event_type The event type. + * @param object $event_data The event data. + */ + public function webhook_verify( string $event_type, object $event_data ) { + $this->logger->notice( 'Webhook Verification Started' ); + $this->logger->notice( 'Event Type: ' . $event_type ); - /** - * Webhook Verification. - * - * @param string $event_type The event type. - * @param object $event_data The event data. - */ - public function webhook_verify( string $event_type, object $event_data ) { - $this->logger->notice( 'Webhook Verification Started' ); - $this->logger->notice( 'Event Type: ' . $event_type ); + $event_type = strtolower( $event_type ); - $event_type = strtolower( $event_type ); + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_webhook( $event_type, $event_data ); + } - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_webhook( $event_type, $event_data ); - } + if ( method_exists( $this, 'requery_transaction' ) ) { + $this->requery_transaction( $event_data->tx_ref ); + } else { + $this->logger->notice( 'Webhook Verification Failed' ); + } + } - if ( method_exists( $this, 'requery_transaction' ) ) { - $this->requery_transaction( $event_data->tx_ref ); - } else { - $this->logger->notice( 'Webhook Verification Failed' ); - } - - } - - /** - * Cancel Payment. - * - * @param string $tx_ref This is the transaction ref to be cancelled. - * - * @return void - */ - public function cancel_payment( string $tx_ref ) { - if ( isset( $this->event_handler ) ) { - $this->event_handler->on_cancel( $tx_ref ); - } - } -} \ No newline at end of file + /** + * Cancel Payment. + * + * @param string $tx_ref This is the transaction ref to be cancelled. + * + * @return void + */ + public function cancel_payment( string $tx_ref ) { + if ( isset( $this->event_handler ) ) { + $this->event_handler->on_cancel( $tx_ref ); + } + } +} diff --git a/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php b/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php index fea3a6d..3f12857 100644 --- a/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php +++ b/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php @@ -18,42 +18,42 @@ interface FLW_WC_Payment_Gateway_Event_Handler_Interface { * * @param object $initialization_data This is the initial transaction data as passed. * */ - public function on_init( object $initialization_data); + public function on_init( object $initialization_data ); /** * This is called only when a transaction is successful * * @param object $transaction_data This is the transaction data as returned from the Rave payment. gateway. * */ - public function on_successful( object $transaction_data); + public function on_successful( object $transaction_data ); /** * This is called only when a transaction failed * * @param object $transaction_data This is the transaction data as returned from the Rave payment gateway. * */ - public function on_failure( object $transaction_data); + public function on_failure( object $transaction_data ); /** * This is called when a transaction is requeryed from the payment gateway * * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * */ - public function on_requery( string $transaction_reference); + public function on_requery( string $transaction_reference ); /** * This is called a transaction requery returns with an error * * @param string $requery_response This is the error response gotten from the Rave payment gateway requery call. * */ - public function on_requery_error( $requery_response); + public function on_requery_error( $requery_response ); /** * This is called when a transaction is canceled by the user * * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * */ - public function on_cancel( string $transaction_reference); + public function on_cancel( string $transaction_reference ); /** * This is called when a transaction doesn't return with a success or a failure response. @@ -61,7 +61,7 @@ public function on_cancel( string $transaction_reference); * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * @param object $data This is the data returned from the requery call. * */ - public function on_timeout( string $transaction_reference, object $data); + public function on_timeout( string $transaction_reference, object $data ); /** * This is called when a webhook is received diff --git a/includes/util/class-flutterwave-signoz-logger.php b/includes/util/class-flutterwave-signoz-logger.php index d58737d..25b45dc 100644 --- a/includes/util/class-flutterwave-signoz-logger.php +++ b/includes/util/class-flutterwave-signoz-logger.php @@ -68,6 +68,8 @@ public static function instance(): self { } /** + * Get the current Flutterwave WooCommerce settings. + * * @return array */ public function get_settings(): array { @@ -168,6 +170,8 @@ public function get_app_id(): string { } /** + * Get the current environment. + * * @return string */ public function get_current_environment(): string { @@ -181,6 +185,7 @@ public function get_current_environment(): string { * @param string $method Payment method (e.g. "card"). * @param string $reference Transaction reference (tx_ref). * @param string $path Request path (e.g. "/v3/charges"). + * @param null $logger Log to the wc logger instance for flutterwave woocoomerce. * @return void */ public function track_request_sent( string $method, string $reference, string $path, $logger = null ): void { diff --git a/includes/views/html-admin-missing-woocommerce.php b/includes/views/html-admin-missing-woocommerce.php index 712b5ca..d86ec3e 100644 --- a/includes/views/html-admin-missing-woocommerce.php +++ b/includes/views/html-admin-missing-woocommerce.php @@ -12,7 +12,7 @@

' . esc_html__( 'Flutterwave WooCommerce', 'rave-woocommerce-payment-gateway' ) . '' ); + printf( esc_html__( '%s requires WooCommerce to be installed and activated in order to serve updates.', 'rave-woocommerce-payment-gateway' ), '' . esc_html__( 'Flutterwave WooCommerce', 'rave-woocommerce-payment-gateway' ) . '' ); ?>

diff --git a/rave-woocommerce-payment-gateway.php b/rave-woocommerce-payment-gateway.php index 16782db..6e877fb 100644 --- a/rave-woocommerce-payment-gateway.php +++ b/rave-woocommerce-payment-gateway.php @@ -62,7 +62,7 @@ function ( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment add_action( 'before_woocommerce_init', - function() { + function () { if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); } @@ -82,14 +82,6 @@ function flw_plugin_action_links( array $links ): array { array_unshift( $links, "Settings" ); return $links; - } add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'flw_plugin_action_links' ); - - - - - - - From 7a60b97df91cc519116f863d030a7242b735ccbc Mon Sep 17 00:00:00 2001 From: Abraham Olaobaju Date: Thu, 11 Jun 2026 04:57:38 +0100 Subject: [PATCH 3/6] update composer.lock --- composer.lock | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index 2e9260e..82c5a49 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "534c5eda9b153befa60b52d7db2df6fa", + "content-hash": "6791c8281ca8faa6770d27f79d098947", "packages": [], "packages-dev": [ { @@ -105,29 +105,30 @@ }, { "name": "doctrine/instantiator", - "version": "2.1.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", - "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.4" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^14", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5.58" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -154,7 +155,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.1.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -170,7 +171,7 @@ "type": "tidelift" } ], - "time": "2026-01-05T06:47:08+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", @@ -2545,5 +2546,8 @@ "ext-json": "*" }, "platform-dev": {}, + "platform-overrides": { + "php": "7.4.33" + }, "plugin-api-version": "2.6.0" } From b8abec0474d040e7579701c80433c458ff14ae52 Mon Sep 17 00:00:00 2001 From: Abraham Olaobaju Date: Thu, 11 Jun 2026 10:29:55 +0100 Subject: [PATCH 4/6] resolve 7.4 deps compatibility --- .github/actions/make-build/action.yml | 11 + .github/workflows/package-publish.yml | 74 +- composer.json | 6 +- composer.lock | 302 +--- .../rave-woocommerce-payment-gateway.pot | 110 +- .../test-flutterwave-signoz-logger.php | 1419 +++++++++-------- 6 files changed, 870 insertions(+), 1052 deletions(-) diff --git a/.github/actions/make-build/action.yml b/.github/actions/make-build/action.yml index e2b2a96..b191849 100644 --- a/.github/actions/make-build/action.yml +++ b/.github/actions/make-build/action.yml @@ -1,6 +1,11 @@ name: "Build Flutterwave WooCommerce" description: "Build Flutterwave WooCommerce plugin" +inputs: + signoz-api-key: + description: "API key for the SigNoz observability service" + required: true + outputs: release-filename: description: "The name of the release filename" @@ -16,6 +21,12 @@ runs: curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp + - name: "Inject SigNoz API key" + shell: bash + env: + SIGNOZ_API_KEY: ${{ inputs.signoz-api-key }} + run: | + sed -i "s|%%SIGNOZ_API_KEY%%|${SIGNOZ_API_KEY}|g" includes/util/class-flutterwave-signoz-logger.php - name: "Build the plugin" id: build_plugin shell: bash diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml index ac2fbe4..e91f050 100644 --- a/.github/workflows/package-publish.yml +++ b/.github/workflows/package-publish.yml @@ -1,40 +1,42 @@ name: Publish to WordPress.org on: - release: - types: [released] + release: + types: [released] jobs: - tag: - name: New Release - runs-on: ubuntu-latest + tag: + name: New Release + runs-on: ubuntu-latest + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + steps: + - uses: act10ns/slack@v2 + with: + status: starting + if: ${{ always() }} + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Node.js environment + uses: actions/setup-node@v2.5.2 + with: + node-version-file: .nvmrc + - name: Build Plugin + uses: ./.github/actions/make-build + with: + signoz-api-key: ${{ secrets.SIGNOZ_API_KEY }} + - name: Install SVN + run: sudo apt-get update && sudo apt-get install -y subversion + - name: WordPress Plugin Deploy + id: deploy + uses: 10up/action-wordpress-plugin-deploy@stable env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - steps: - - uses: act10ns/slack@v2 - with: - status: starting - if: ${{ always() }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Setup Node.js environment - uses: actions/setup-node@v2.5.2 - with: - node-version-file: .nvmrc - - name: Build Plugin - uses: ./.github/actions/make-build - - name: Install SVN - run: sudo apt-get update && sudo apt-get install -y subversion - - name: WordPress Plugin Deploy - id: deploy - uses: 10up/action-wordpress-plugin-deploy@stable - env: - SVN_USERNAME: ${{ secrets.SVN_USERNAME }} - SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} - SLUG: rave-woocommerce-payment-gateway - - name: push build status to Slack - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow,job,took,pullRequest - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - if: always() + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + SLUG: rave-woocommerce-payment-gateway + - name: push build status to Slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took,pullRequest + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + if: always() diff --git a/composer.json b/composer.json index 22c1b4b..c704584 100644 --- a/composer.json +++ b/composer.json @@ -18,10 +18,10 @@ "ext-json": "*" }, "require-dev": { - "wp-coding-standards/wpcs": "^3.3", + "wp-coding-standards/wpcs": "^2.3", "phpcompatibility/phpcompatibility-wp": "*", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "woocommerce/woocommerce-sniffs": "^1.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "woocommerce/woocommerce-sniffs": "^0.1.3", "yoast/phpunit-polyfills": "^1.0" }, "config": { diff --git a/composer.lock b/composer.lock index 82c5a49..82ea6a5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,43 +4,40 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6791c8281ca8faa6770d27f79d098947", + "content-hash": "80374ee24eeb123215e12b0de097cbd0", "packages": [], "packages-dev": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.2.1", + "version": "v0.7.2", "source": { "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", - "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", "shasum": "" }, "require": { - "composer-plugin-api": "^2.2", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "^2.2", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", - "yoast/phpunit-polyfills": "^1.0" + "composer/composer": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0" }, "type": "composer-plugin", "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -50,16 +47,17 @@ "authors": [ { "name": "Franck Nijhof", - "email": "opensource@frenck.dev", - "homepage": "https://frenck.dev", - "role": "Open source developer" + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" }, { "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -79,29 +77,10 @@ "tests" ], "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", - "source": "https://github.com/PHPCSStandards/composer-installer" + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2026-05-06T08:26:05+00:00" + "time": "2022-02-04T12:51:07+00:00" }, { "name": "doctrine/instantiator", @@ -622,181 +601,6 @@ ], "time": "2025-10-18T00:05:59+00:00" }, - { - "name": "phpcsstandards/phpcsextra", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "b598aa890815b8df16363271b659d73280129101" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101", - "reference": "b598aa890815b8df16363271b659d73280129101", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.2.0", - "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" - }, - "require-dev": { - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.2.0", - "phpcsstandards/phpcsdevtools": "^1.2.1", - "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-stable": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" - } - ], - "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", - "keywords": [ - "PHP_CodeSniffer", - "phpcbf", - "phpcodesniffer-standard", - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", - "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", - "source": "https://github.com/PHPCSStandards/PHPCSExtra" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2025-11-12T23:06:57+00:00" - }, - { - "name": "phpcsstandards/phpcsutils", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", - "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" - }, - "require-dev": { - "ext-filter": "*", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.2.0", - "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-stable": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPCSUtils/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" - } - ], - "description": "A suite of utility functions for use with PHP_CodeSniffer", - "homepage": "https://phpcsutils.com/", - "keywords": [ - "PHP_CodeSniffer", - "phpcbf", - "phpcodesniffer-standard", - "phpcs", - "phpcs3", - "phpcs4", - "standards", - "static analysis", - "tokens", - "utility" - ], - "support": { - "docs": "https://phpcsutils.com/", - "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", - "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", - "source": "https://github.com/PHPCSStandards/PHPCSUtils" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2025-12-08T14:27:58+00:00" - }, { "name": "phpunit/php-code-coverage", "version": "9.2.32", @@ -2369,77 +2173,74 @@ }, { "name": "woocommerce/woocommerce-sniffs", - "version": "1.0.1", + "version": "0.1.3", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-sniffs.git", - "reference": "e6da0c372573724806b270ec1d5d94988b8aec52" + "reference": "4576d54595614d689bc4436acff8baaece3c5bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/e6da0c372573724806b270ec1d5d94988b8aec52", - "reference": "e6da0c372573724806b270ec1d5d94988b8aec52", + "url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/4576d54595614d689bc4436acff8baaece3c5bb0", + "reference": "4576d54595614d689bc4436acff8baaece3c5bb0", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "php": ">=7.0", "phpcompatibility/phpcompatibility-wp": "^2.1.0", - "wp-coding-standards/wpcs": "^3.0.0" + "wp-coding-standards/wpcs": "^2.3.0" }, "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], + "authors": [ + { + "name": "Claudio Sanches", + "email": "claudio@automattic.com" + } + ], "description": "WooCommerce sniffs", "keywords": [ "phpcs", "standards", - "static analysis", "woocommerce", "wordpress" ], "support": { "issues": "https://github.com/woocommerce/woocommerce-sniffs/issues", - "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/1.0.1" + "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/0.1.3" }, - "time": "2025-09-03T13:34:27+00:00" + "time": "2022-02-17T15:34:51+00:00" }, { "name": "wp-coding-standards/wpcs", - "version": "3.3.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6" + "reference": "7da1894633f168fe244afc6de00d141f27517b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", - "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", + "reference": "7da1894633f168fe244afc6de00d141f27517b62", "shasum": "" }, "require": { - "ext-filter": "*", - "ext-libxml": "*", - "ext-tokenizer": "*", - "ext-xmlreader": "*", - "php": ">=7.2", - "phpcsstandards/phpcsextra": "^1.5.0", - "phpcsstandards/phpcsutils": "^1.1.0", - "squizlabs/php_codesniffer": "^3.13.4" + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.3.1" }, "require-dev": { - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^10.0.0@dev", - "phpcsstandards/phpcsdevtools": "^1.2.0", - "phpunit/phpunit": "^8.0 || ^9.0" + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "suggest": { - "ext-iconv": "For improved results", - "ext-mbstring": "For improved results" + "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." }, "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", @@ -2456,7 +2257,6 @@ "keywords": [ "phpcs", "standards", - "static analysis", "wordpress" ], "support": { @@ -2464,13 +2264,7 @@ "source": "https://github.com/WordPress/WordPress-Coding-Standards", "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" }, - "funding": [ - { - "url": "https://opencollective.com/php_codesniffer", - "type": "custom" - } - ], - "time": "2025-11-25T12:08:04+00:00" + "time": "2020-05-13T23:57:56+00:00" }, { "name": "yoast/phpunit-polyfills", diff --git a/i18n/languages/rave-woocommerce-payment-gateway.pot b/i18n/languages/rave-woocommerce-payment-gateway.pot index bd7e7ee..884d25e 100644 --- a/i18n/languages/rave-woocommerce-payment-gateway.pot +++ b/i18n/languages/rave-woocommerce-payment-gateway.pot @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-06-11T02:41:28+00:00\n" +"POT-Creation-Date: 2026-06-11T09:27:51+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: rave-woocommerce-payment-gateway\n" @@ -17,7 +17,7 @@ msgstr "" #. Plugin Name of the plugin #. Translators: %s Plugin name. #: rave-woocommerce-payment-gateway.php -#: includes/class-flw-wc-payment-gateway.php:270 +#: includes/class-flw-wc-payment-gateway.php:269 #: includes/views/html-admin-missing-woocommerce.php:15 msgid "Flutterwave WooCommerce" msgstr "" @@ -146,214 +146,214 @@ msgstr "" msgid "allows you to accept payment from cards and bank accounts in multiple currencies. You can also accept payment offline via USSD and POS." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:274 +#: includes/class-flw-wc-payment-gateway.php:273 msgid "Webhook Instruction" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:278 +#: includes/class-flw-wc-payment-gateway.php:277 msgid "Please copy this webhook URL and paste on the webhook section on your dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:299 +#: includes/class-flw-wc-payment-gateway.php:298 msgid "Enable/Disable" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:300 +#: includes/class-flw-wc-payment-gateway.php:299 msgid "Enable Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:302 +#: includes/class-flw-wc-payment-gateway.php:301 msgid "Enable Flutterwave as a payment option on the checkout page" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:307 +#: includes/class-flw-wc-payment-gateway.php:306 msgid "Enter Secret Hash" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:309 +#: includes/class-flw-wc-payment-gateway.php:308 msgid "Please change from default hash and ensure that SECRET HASH is the same with the one on your Flutterwave dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:313 +#: includes/class-flw-wc-payment-gateway.php:312 msgid "Payment method title" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:315 -#: includes/class-flw-wc-payment-gateway.php:321 +#: includes/class-flw-wc-payment-gateway.php:314 +#: includes/class-flw-wc-payment-gateway.php:320 msgid "Optional" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:319 +#: includes/class-flw-wc-payment-gateway.php:318 msgid "Payment method description" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:325 +#: includes/class-flw-wc-payment-gateway.php:324 msgid "Test Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:327 +#: includes/class-flw-wc-payment-gateway.php:326 msgid "Required! Enter your Flutterwave test public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:331 +#: includes/class-flw-wc-payment-gateway.php:330 msgid "Test Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:333 +#: includes/class-flw-wc-payment-gateway.php:332 msgid "Required! Enter your Flutterwave test secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:337 +#: includes/class-flw-wc-payment-gateway.php:336 msgid "Live Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:339 +#: includes/class-flw-wc-payment-gateway.php:338 msgid "Required! Enter your Flutterwave live public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:343 +#: includes/class-flw-wc-payment-gateway.php:342 msgid "Live Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:345 +#: includes/class-flw-wc-payment-gateway.php:344 msgid "Required! Enter your Flutterwave live secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:349 +#: includes/class-flw-wc-payment-gateway.php:348 msgid "Payment Style on checkout" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:351 +#: includes/class-flw-wc-payment-gateway.php:350 msgid "Optional - Choice of payment style to use. Either inline or redirect. (Default: inline)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:353 +#: includes/class-flw-wc-payment-gateway.php:352 msgctxt "payment_style" msgid "Popup(Keep payment experience on the website)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:354 +#: includes/class-flw-wc-payment-gateway.php:353 msgctxt "payment_style" msgid "Redirect" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:359 +#: includes/class-flw-wc-payment-gateway.php:358 msgid "Autocomplete Order After Payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:360 +#: includes/class-flw-wc-payment-gateway.php:359 msgid "Autocomplete Order" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:363 +#: includes/class-flw-wc-payment-gateway.php:362 msgid "If enabled, the order will be marked as complete after successful payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:368 +#: includes/class-flw-wc-payment-gateway.php:367 msgid "Payment Options" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:370 +#: includes/class-flw-wc-payment-gateway.php:369 msgid "Optional - Choice of payment method to use. Card, Account etc." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:372 +#: includes/class-flw-wc-payment-gateway.php:371 msgctxt "payment_options" msgid "All" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:373 +#: includes/class-flw-wc-payment-gateway.php:372 msgctxt "payment_options" msgid "Card Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:374 +#: includes/class-flw-wc-payment-gateway.php:373 msgctxt "payment_options" msgid "Account Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:375 +#: includes/class-flw-wc-payment-gateway.php:374 msgctxt "payment_options" msgid "USSD Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:376 +#: includes/class-flw-wc-payment-gateway.php:375 msgctxt "payment_options" msgid "QR Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:377 +#: includes/class-flw-wc-payment-gateway.php:376 msgctxt "payment_options" msgid "Mpesa Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:378 +#: includes/class-flw-wc-payment-gateway.php:377 msgctxt "payment_options" msgid "Ghana MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:379 +#: includes/class-flw-wc-payment-gateway.php:378 msgctxt "payment_options" msgid "Rwanda MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:380 +#: includes/class-flw-wc-payment-gateway.php:379 msgctxt "payment_options" msgid "Zambia MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:381 +#: includes/class-flw-wc-payment-gateway.php:380 msgctxt "payment_options" msgid "Tanzania MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:386 +#: includes/class-flw-wc-payment-gateway.php:385 msgid "Mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:387 +#: includes/class-flw-wc-payment-gateway.php:386 msgid "Live mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:389 +#: includes/class-flw-wc-payment-gateway.php:388 msgid "Check this box if you're using your live keys." msgstr "" +#: includes/class-flw-wc-payment-gateway.php:393 #: includes/class-flw-wc-payment-gateway.php:394 -#: includes/class-flw-wc-payment-gateway.php:395 msgid "Disable Logging" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:397 +#: includes/class-flw-wc-payment-gateway.php:396 msgid "Check this box if you're disabling logging." msgstr "" +#: includes/class-flw-wc-payment-gateway.php:401 #: includes/class-flw-wc-payment-gateway.php:402 -#: includes/class-flw-wc-payment-gateway.php:403 msgid "Disable Barter" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:405 +#: includes/class-flw-wc-payment-gateway.php:404 msgid "Check the box if you want to disable barter." msgstr "" #. translators: %s: url -#: includes/class-flw-wc-payment-gateway.php:504 +#: includes/class-flw-wc-payment-gateway.php:502 #, php-format msgid "Flutterwave is enabled, but the API keys are not set. Please set your Flutterwave API keys to be able to accept payments." msgstr "" #. translators: %s: shop cart url -#: includes/class-flw-wc-payment-gateway.php:541 +#: includes/class-flw-wc-payment-gateway.php:538 #, php-format msgid "Sorry, your session has expired. Return to shop" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:555 +#: includes/class-flw-wc-payment-gateway.php:552 msgid "We were unable to process your order, please try again." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:636 +#: includes/class-flw-wc-payment-gateway.php:633 msgid "Order Payment" msgstr "" @@ -386,27 +386,19 @@ msgstr "" msgid "Install WooCommerce" msgstr "" -#: build/index.js:1 #: client/blocks/payment-method/index.js:19 -#: build/index.js:102 msgid "You may be redirected to a secure page to complete your payment." msgstr "" -#: build/index.js:1 #: client/blocks/payment-method/index.js:32 -#: build/index.js:115 msgid "Flutterwave" msgstr "" -#: build/index.js:1 #: client/blocks/payment-method/index.js:38 -#: build/index.js:121 msgid "Proceed to Flutterwave" msgstr "" -#: build/index.js:1 #: client/blocks/payment-method/index.js:44 -#: build/index.js:127 msgid "Payment via Flutterwave" msgstr "" diff --git a/tests/PHPUnit/test-flutterwave-signoz-logger.php b/tests/PHPUnit/test-flutterwave-signoz-logger.php index 20588f9..79764f4 100644 --- a/tests/PHPUnit/test-flutterwave-signoz-logger.php +++ b/tests/PHPUnit/test-flutterwave-signoz-logger.php @@ -12,706 +12,725 @@ */ class Test_Flutterwave_Signoz_Logger extends \WP_UnitTestCase { - /** - * Logger under test. - * - * @var Flutterwave_Signoz_Logger - */ - private Flutterwave_Signoz_Logger $logger; - - /** - * HTTP requests captured by the pre_http_request filter. - * - * @var array - */ - private array $captured_requests = []; - - // ------------------------------------------------------------------------- - // Lifecycle - // ------------------------------------------------------------------------- - - public function set_up(): void { - parent::set_up(); - $this->reset_singleton(); - $this->logger = Flutterwave_Signoz_Logger::instance(); - $this->captured_requests = []; - } - - public function tear_down(): void { - parent::tear_down(); - remove_all_filters( 'pre_http_request' ); - $this->reset_singleton(); - } - - // ------------------------------------------------------------------------- - // Helpers - // ------------------------------------------------------------------------- - - /** - * Reset the singleton so each test starts with a fresh instance. - */ - private function reset_singleton(): void { - $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); - $prop = $ref->getProperty( 'instance' ); - $prop->setAccessible( true ); - $prop->setValue( null, null ); - } - - /** - * Read a private instance property via reflection. - * - * @param string $name Property name. - * @return mixed Property value. - * @throws ReflectionException If the property does not exist. - */ - private function get_property( string $name ) { - $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); - $prop = $ref->getProperty( $name ); - $prop->setAccessible( true ); - return $prop->getValue( $this->logger ); - } - - /** - * Add a filter that stubs the merchant-info API response. - * - * @param string|null $merchant_name Value to return in `mn`; null omits the key. - * @param bool $wp_error Return a WP_Error instead of a response. - * @param string|null $raw_body Override the full response body (raw JSON string). - */ - private function mock_merchant_api( - ?string $merchant_name = null, - bool $wp_error = false, - ?string $raw_body = null - ): void { - add_filter( - 'pre_http_request', - function ( $response, $args, $url ) use ( $merchant_name, $wp_error, $raw_body ) { - if ( strpos( $url, 'api.ravepay.co' ) === false ) { - return $response; - } - if ( $wp_error ) { - return new WP_Error( 'http_request_failed', 'Connection timed out' ); - } - if ( null !== $raw_body ) { - $body = $raw_body; - } elseif ( null !== $merchant_name ) { - $body = wp_json_encode( - [ - 'mn' => $merchant_name, - 'status' => 'success', - ] - ); - } else { - $body = wp_json_encode( [ 'status' => 'success' ] ); - } - return [ - 'body' => $body, - 'headers' => [], - 'response' => [ - 'code' => 200, - 'message' => 'OK', - ], - ]; - }, - 10, - 3 - ); - } - - /** - * Add a filter that captures every request sent to the SigNoz endpoint. - */ - private function capture_signoz_requests(): void { - add_filter( - 'pre_http_request', - function ( $response, $args, $url ) { - if ( strpos( $url, 'signozservice-prod' ) === false ) { - return $response; - } - $this->captured_requests[] = [ - 'url' => $url, - 'args' => $args, - 'body' => json_decode( $args['body'], true ), - ]; - return [ - 'body' => wp_json_encode( [ 'status' => 'ok' ] ), - 'headers' => [], - 'response' => [ - 'code' => 200, - 'message' => 'OK', - ], - ]; - }, - 10, - 3 - ); - } - - // ========================================================================= - // Singleton - // ========================================================================= - - public function test_instance_returns_flutterwave_signoz_logger(): void { - $this->assertInstanceOf( Flutterwave_Signoz_Logger::class, Flutterwave_Signoz_Logger::instance() ); - } - - public function test_instance_returns_same_object_on_repeated_calls(): void { - $a = Flutterwave_Signoz_Logger::instance(); - $b = Flutterwave_Signoz_Logger::instance(); - $this->assertSame( $a, $b ); - } - - // ========================================================================= - // get_merchant_id() - // ========================================================================= - - public function test_get_merchant_id_returns_name_from_mn_field(): void { - $this->mock_merchant_api( 'AcmeCorp' ); - $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); - $this->assertEquals( 'AcmeCorp', $result ); - } - - public function test_get_merchant_id_returns_null_on_wp_error(): void { - $this->mock_merchant_api( null, true ); - $result = $this->logger->get_merchant_id( 'FLWPUBK-bad-key' ); - $this->assertNull( $result ); - } - - public function test_get_merchant_id_returns_null_when_mn_key_absent(): void { - $this->mock_merchant_api( null ); // response has no 'mn' key - $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); - $this->assertNull( $result ); - } - - public function test_get_merchant_id_returns_null_on_malformed_json(): void { - $this->mock_merchant_api( null, false, 'not-valid-json' ); - $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); - $this->assertNull( $result ); - } - - public function test_get_merchant_id_returns_null_on_empty_body(): void { - $this->mock_merchant_api( null, false, '' ); - $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); - $this->assertNull( $result ); - } - - public function test_get_merchant_id_returns_null_when_mn_is_null_in_response(): void { - $this->mock_merchant_api( null, false, wp_json_encode( [ 'mn' => null ] ) ); - $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); - $this->assertNull( $result ); - } - - // ========================================================================= - // init() - // ========================================================================= - - public function test_init_sets_app_id_from_merchant_name(): void { - $this->mock_merchant_api( 'TestMerchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); - $this->assertEquals( 'TestMerchant', $this->get_property( 'app_id' ) ); - } - - public function test_init_falls_back_to_public_key_when_merchant_id_is_null(): void { - $this->mock_merchant_api( null ); // no 'mn' in response. - $this->logger->init( 'FLWPUBK-fallback-key', 'sandbox' ); - $this->assertEquals( 'FLWPUBK-fallback-key', $this->get_property( 'app_id' ) ); - } - - public function test_init_falls_back_to_public_key_on_wp_error(): void { - $this->mock_merchant_api( null, true ); - $this->logger->init( 'FLWPUBK-network-fail', 'sandbox' ); - $this->assertEquals( 'FLWPUBK-network-fail', $this->get_property( 'app_id' ) ); - } - - public function test_init_sets_environment_to_sandbox(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); - $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); - } - - public function test_init_sets_environment_to_production(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'production' ); - $this->assertEquals( 'production', $this->get_property( 'environment' ) ); - } - - public function test_init_defaults_environment_to_sandbox(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); - } - - public function test_app_id_is_empty_before_init(): void { - $this->assertEquals( '', $this->get_property( 'app_id' ) ); - } - - // ========================================================================= - // track_app_created() - // ========================================================================= - - public function test_track_app_created_does_not_send_before_init(): void { - $this->capture_signoz_requests(); - $this->logger->track_app_created( 'FLWPUBK-test-key' ); - $this->assertEmpty( $this->captured_requests ); - } - - public function test_track_app_created_sends_exactly_one_request(): void { - $this->mock_merchant_api( 'TestMerchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_app_created( 'FLWPUBK-test-key' ); - - $this->assertCount( 1, $this->captured_requests ); - } - - public function test_track_app_created_sends_correct_event_name(): void { - $this->mock_merchant_api( 'TestMerchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_app_created( 'FLWPUBK-test-key' ); - - $this->assertEquals( 'app.created', $this->captured_requests[0]['body']['name'] ); - } - - public function test_track_app_created_payload_contains_required_fields(): void { - $this->mock_merchant_api( 'TestMerchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_app_created( 'FLWPUBK-test-key' ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 'TestMerchant', $data['app_id'] ); - $this->assertNull( $data['client_id'] ); - $this->assertEquals( 'FLWPUBK-test-key', $data['public_key'] ); - $this->assertEquals( 'woocommerce', $data['library'] ); - $this->assertArrayHasKey( 'library_version', $data ); - } - - // ========================================================================= - // track_request_sent() - // ========================================================================= - - public function test_track_request_sent_does_not_send_before_init(): void { - $this->capture_signoz_requests(); - $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); - $this->assertEmpty( $this->captured_requests ); - } - - public function test_track_request_sent_sends_correct_event_name(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'production' ); - $this->capture_signoz_requests(); - - $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); - - $this->assertEquals( 'request.sent', $this->captured_requests[0]['body']['name'] ); - } - - public function test_track_request_sent_payload_contains_required_fields(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'production' ); - $this->capture_signoz_requests(); - - $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 'Merchant', $data['app_id'] ); - $this->assertEquals( 'production', $data['environment'] ); - $this->assertEquals( 'v3', $data['api_version'] ); - $this->assertEquals( 'card', $data['method'] ); - $this->assertEquals( 'txn-ref-123', $data['reference'] ); - $this->assertEquals( '/v3/charges', $data['path'] ); - $this->assertArrayHasKey( 'library_version', $data ); - } - - public function test_track_request_sent_includes_sandbox_environment(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); - $this->capture_signoz_requests(); - - $this->logger->track_request_sent( 'banktransfer', 'ref-456', '/v3/transfers' ); - - $this->assertEquals( 'sandbox', $this->captured_requests[0]['body']['data']['environment'] ); - } - - // ========================================================================= - // track_transaction() - // ========================================================================= - - public function test_track_transaction_does_not_send_before_init(): void { - $this->capture_signoz_requests(); - $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); - $this->assertEmpty( $this->captured_requests ); - } - - public function test_track_transaction_sends_correct_event_name(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); - - $this->assertEquals( 'app.transaction', $this->captured_requests[0]['body']['name'] ); - } - - public function test_track_transaction_payload_contains_required_fields(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 'Merchant', $data['app_id'] ); - $this->assertEquals( 'ref-123', $data['reference'] ); - $this->assertEquals( 'NGN', $data['currency'] ); - $this->assertEquals( 5000.00, $data['amount'] ); - $this->assertEquals( 70.00, $data['fee'] ); - $this->assertEquals( 'card', $data['method'] ); - } - - public function test_track_transaction_with_zero_amount_and_fee(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_transaction( 'ref-zero', 'USD', 0.0, 'card', 0.0 ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 0.0, $data['amount'] ); - $this->assertEquals( 0.0, $data['fee'] ); - } - - public function test_track_transaction_with_fractional_amounts(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_transaction( 'ref-frac', 'GHS', 99.99, 'mobilemoney', 1.50 ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 99.99, $data['amount'] ); - $this->assertEquals( 1.50, $data['fee'] ); - } - - public function test_track_transaction_supports_different_currencies(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - foreach ( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ] as $currency ) { - $this->logger->track_transaction( 'ref-' . $currency, $currency, 100.0, 'card', 1.0 ); - } - - $this->assertCount( 5, $this->captured_requests ); - $sent_currencies = array_column( - array_column( array_column( $this->captured_requests, 'body' ), 'data' ), - 'currency' - ); - $this->assertEquals( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ], $sent_currencies ); - } - - // ========================================================================= - // track_error() - // ========================================================================= - - public function test_track_error_does_not_send_before_init(): void { - $this->capture_signoz_requests(); - $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined' ); - $this->assertEmpty( $this->captured_requests ); - } - - public function test_track_error_sends_correct_event_name(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); - - $this->assertEquals( 'app.error', $this->captured_requests[0]['body']['name'] ); - } - - public function test_track_error_payload_contains_required_fields(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( 'Merchant', $data['app_id'] ); - $this->assertEquals( 'woocommerce', $data['library'] ); - $this->assertEquals( 'PAYMENT_FAILED', $data['error_code'] ); - $this->assertEquals( 'Card declined by issuer', $data['error_message'] ); - $this->assertArrayHasKey( 'library_version', $data ); - } - - public function test_track_error_with_empty_strings(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $this->logger->track_error( '', '' ); - - $data = $this->captured_requests[0]['body']['data']; - $this->assertEquals( '', $data['error_code'] ); - $this->assertEquals( '', $data['error_message'] ); - } - - public function test_track_error_with_special_characters(): void { - $this->mock_merchant_api( 'Merchant' ); - $this->logger->init( 'FLWPUBK-test-key' ); - $this->capture_signoz_requests(); - - $code = 'ERR_