Done !
| Server IP : 46.105.57.169 / Your IP : 216.73.217.35 Web Server : Apache System : Linux webm002.cluster120.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64 User : verseaumee ( 152031) PHP Version : 8.5.7 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/verseaumee/blueversionx/wp-content/plugins/woo-rave/includes/ |
Upload File : |
<?php
namespace Tubiz\Flutterwave_Woocommerce;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class WC_Gateway_Flutterwave
*/
class WC_Gateway_Flutterwave extends \WC_Payment_Gateway {
/**
* Checkout page title
*
* @var string
*/
public $title;
/**
* Checkout page description
*
* @var string
*/
public $description;
/**
* Is gateway enabled?
*
* @var bool
*/
public $enabled;
/**
* Is test mode active?
*
* @var bool
*/
public bool $testmode;
/**
* Should orders be marked as complete after payment?
*
* @var bool
*/
public bool $autocomplete_order;
/**
* Text displayed as the title of the payment modal
*
* @var string
*/
public string $custom_title;
/**
* Text displayed as a short modal description
*
* @var string
*/
public string $custom_desc;
/**
* Image to be displayed on the payment popup
*
* @var string
*/
public string $custom_logo;
/**
* Flutterwave test public key.
*
* @var string
*/
public string $test_public_key;
/**
* Flutterwave test secret key.
*
* @var string
*/
public string $test_secret_key;
/**
* Flutterwave live public key.
*
* @var string
*/
public string $live_public_key;
/**
* Flutterwave live secret key.
*
* @var string
*/
public string $live_secret_key;
/**
* API public key.
*
* @var string
*/
public string $public_key;
/**
* API secret key.
*
* @var string
*/
public string $secret_key;
/**
* Should we save customer cards?
*
* @var bool
*/
public bool $saved_cards;
/**
* Constructor
*/
public function __construct() {
$this->id = 'tbz_rave';
$this->method_title = 'Flutterwave';
$this->method_description = sprintf( 'Flutterwave is the easiest way to collect payments from customers anywhere in the world. <a href="%1$s" target="_blank">Sign up</a> for a Flutterwave account, and <a href="%2$s" target="_blank">get your API keys</a>.', 'https://dashboard.flutterwave.com', 'https://dashboard.flutterwave.com/dashboard/settings/apis' );
$this->has_fields = true;
$this->supports = array(
'products',
'tokenization',
'subscriptions',
'multiple_subscriptions',
'subscription_cancellation',
'subscription_suspension',
'subscription_reactivation',
'subscription_amount_changes',
'subscription_date_changes',
'subscription_payment_method_change',
'subscription_payment_method_change_customer',
);
// Load the form fields.
$this->init_form_fields();
// Load the settings.
$this->init_settings();
// Get setting values.
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
$this->testmode = $this->get_option( 'testmode' ) === 'yes';
$this->autocomplete_order = $this->get_option( 'autocomplete_order' ) === 'yes';
$this->custom_title = $this->get_option( 'custom_title' );
$this->custom_desc = $this->get_option( 'custom_desc' );
$this->custom_logo = $this->get_option( 'custom_logo' );
$this->test_public_key = $this->get_option( 'test_public_key' );
$this->test_secret_key = $this->get_option( 'test_secret_key' );
$this->live_public_key = $this->get_option( 'live_public_key' );
$this->live_secret_key = $this->get_option( 'live_secret_key' );
$this->public_key = $this->testmode ? $this->test_public_key : $this->live_public_key;
$this->secret_key = $this->testmode ? $this->test_secret_key : $this->live_secret_key;
$this->saved_cards = $this->get_option( 'saved_cards' ) === 'yes';
// Hooks.
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
add_action( 'woocommerce_admin_order_totals_after_total', array( $this, 'display_flutterwave_fee' ) );
add_action( 'woocommerce_admin_order_totals_after_total', array( $this, 'display_order_payout' ), 20 );
add_action( 'woocommerce_receipt_' . $this->id, array( $this, 'receipt_page' ) );
// Payment listener/API hook.
add_action( 'woocommerce_api_tbz_wc_rave_gateway', array( $this, 'verify_flutterwave_transaction' ) );
// Webhook listener/API hook.
add_action( 'woocommerce_api_tbz_wc_rave_webhook', array( $this, 'process_webhooks' ) );
}
/**
* Display the payment icon on the checkout page
*/
public function get_icon() {
$icon = '<img src="' . \WC_HTTPS::force_https_url( plugins_url( 'assets/images/powered-by-rave.png', TBZ_WC_FLUTTERWAVE_MAIN_FILE ) ) . '" alt="cards" />';
return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
}
/**
* Check if Flutterwave merchant details is filled
*/
public function admin_notices() {
if ( 'no' === $this->enabled ) {
return;
}
// Check required fields.
if ( ! ( $this->public_key && $this->secret_key ) ) {
echo '<div class="error"><p>' . sprintf( 'Please enter your Flutterwave merchant details <a href="%s">here</a> to be able to use the Flutterwave WooCommerce plugin.', admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=tbz_rave' ) ) . '</p></div>';
}
}
/**
* Check if Flutterwave gateway is enabled.
*/
public function is_available() {
if ( 'yes' === $this->enabled ) {
if ( ! ( $this->public_key && $this->secret_key ) ) {
return false;
}
return true;
}
return false;
}
/**
* Admin Panel Options
*/
public function admin_options() {
?>
<h3>Flutterwave</h3>
<h4>Optional: To avoid situations where bad network makes it impossible to verify transactions, set your webhook
URL <a href="https://app.flutterwave.com/dashboard/settings/webhooks/" target="_blank"
rel="noopener noreferrer">here</a> to the URL below<strong style="color: red">
<pre><code><?php echo WC()->api_request_url( 'Tbz_WC_Rave_Webhook' ); ?></code></pre>
</strong></h4>
<?php
echo '<table class="form-table">';
$this->generate_settings_html();
echo '</table>';
}
/**
* Initialise Gateway Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woo-rave' ),
'label' => __( 'Enable Flutterwave', 'woo-rave' ),
'type' => 'checkbox',
'description' => __( 'Enable Flutterwave as a payment option on the checkout page.', 'woo-rave' ),
'default' => 'no',
'desc_tip' => true,
),
'title' => array(
'title' => __( 'Title', 'woo-rave' ),
'type' => 'text',
'description' => __( 'This controls the payment method title which the user sees during checkout.', 'woo-rave' ),
'desc_tip' => true,
'default' => __( 'Flutterwave', 'woo-rave' ),
),
'description' => array(
'title' => __( 'Description', 'woo-rave' ),
'type' => 'textarea',
'description' => __( 'This controls the payment method description which the user sees during checkout.', 'woo-rave' ),
'desc_tip' => true,
'default' => __( 'Make payment using your debit, credit card & bank account', 'woo-rave' ),
),
'testmode' => array(
'title' => __( 'Test mode', 'woo-rave' ),
'label' => __( 'Enable Test Mode', 'woo-rave' ),
'type' => 'checkbox',
'description' => __( 'Test mode enables you to test payments before going live. <br />Once you are live uncheck this.', 'woo-rave' ),
'default' => 'yes',
'desc_tip' => true,
),
'test_public_key' => array(
'title' => __( 'Test Public Key', 'woo-rave' ),
'type' => 'text',
'description' => __( 'Required: Enter your Test Public Key here.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'test_secret_key' => array(
'title' => __( 'Test Secret Key', 'woo-rave' ),
'type' => 'password',
'description' => __( 'Required: Enter your Test Secret Key here', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'live_public_key' => array(
'title' => __( 'Live Public Key', 'woo-rave' ),
'type' => 'text',
'description' => __( 'Required: Enter your Live Public Key here.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'live_secret_key' => array(
'title' => __( 'Live Secret Key', 'woo-rave' ),
'type' => 'password',
'description' => __( 'Required: Enter your Live Secret Key here.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'custom_title' => array(
'title' => __( 'Custom Title', 'woo-rave' ),
'type' => 'text',
'description' => __( 'Optional: Text to be displayed as the title of the payment modal.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'custom_desc' => array(
'title' => __( 'Custom Description', 'woo-rave' ),
'type' => 'text',
'description' => __( 'Optional: Text to be displayed as a short modal description.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'custom_logo' => array(
'title' => __( 'Custom Logo', 'woo-rave' ),
'type' => 'text',
'description' => __( 'Optional: Enter the link to a image to be displayed on the payment popup. Preferably a square image.', 'woo-rave' ),
'default' => '',
'desc_tip' => true,
),
'saved_cards' => array(
'title' => __( 'Saved Cards', 'woo-rave' ),
'label' => __( 'Enable Payment via Saved Cards', 'woo-rave' ),
'type' => 'checkbox',
'description' => __( 'If enabled, users will be able to pay with a saved card during checkout. Card details are saved on Flutterwave servers, not on your store.<br>Note that you need to have a valid SSL certificate installed.', 'woo-rave' ),
'default' => 'no',
'desc_tip' => true,
),
'autocomplete_order' => array(
'title' => __( 'Autocomplete Order After Payment', 'woo-rave' ),
'label' => __( 'Autocomplete Order', 'woo-rave' ),
'type' => 'checkbox',
'description' => __( 'If enabled, the order will be marked as complete after successful payment', 'woo-rave' ),
'default' => 'no',
'desc_tip' => true,
),
);
}
/**
* Payment form on checkout page
*/
public function payment_fields() {
if ( $this->description ) {
echo wpautop( wptexturize( $this->description ) );
}
if ( ! is_ssl() ) {
return;
}
if ( $this->saved_cards && $this->supports( 'tokenization' ) && is_checkout() && is_user_logged_in() ) {
$this->tokenization_script();
$this->saved_payment_methods();
$this->save_payment_method_checkbox();
}
}
/**
* Outputs scripts used by Rave.
*/
public function payment_scripts() {
if ( ! is_checkout_pay_page() ) {
return;
}
if ( 'no' === $this->enabled ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$order_key = urldecode( sanitize_text_field( $_GET['key'] ) );
$order_id = absint( get_query_var( 'order-pay' ) );
$order = wc_get_order( $order_id );
$payment_method = method_exists( $order, 'get_payment_method' ) ? $order->get_payment_method() : $order->payment_method;
if ( $this->id !== $payment_method ) {
return;
}
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'tbz_flutterwave', 'https://checkout.flutterwave.com/v3.js', array( 'jquery' ), TBZ_WC_FLUTTERWAVE_VERSION, false );
wp_enqueue_script(
'tbz_wc_flutterwave',
plugins_url( 'assets/js/flutterwave' . $suffix . '.js', TBZ_WC_FLUTTERWAVE_MAIN_FILE ),
array(
'jquery',
'tbz_flutterwave',
),
TBZ_WC_FLUTTERWAVE_VERSION,
false
);
$flutterwave_params = array(
'public_key' => $this->public_key,
);
if ( is_checkout_pay_page() && get_query_var( 'order-pay' ) ) {
$email = $order->get_billing_email();
$billing_phone = $order->get_billing_phone();
$first_name = $order->get_billing_first_name();
$last_name = $order->get_billing_last_name();
$amount = $order->get_total();
$txnref = 'WC|' . $order_id . '|' . time();
$the_order_id = $order->get_id();
$the_order_key = $order->get_order_key();
$currency = $order->get_currency();
$meta = array();
if ( $the_order_id == $order_id && $the_order_key == $order_key ) {
$meta['Order ID'] = $order_id;
$customer_name = trim( "$first_name $last_name" );
$location = wc_get_base_location();
$flutterwave_params['txref'] = $txnref;
$flutterwave_params['amount'] = $amount;
$flutterwave_params['currency'] = $currency;
$flutterwave_params['country'] = $location['country'];
$flutterwave_params['customer_email'] = $email;
$flutterwave_params['customer_phone'] = $billing_phone;
$flutterwave_params['customer_name'] = $customer_name;
$flutterwave_params['custom_title'] = $this->custom_title;
$flutterwave_params['custom_desc'] = $this->custom_desc;
$flutterwave_params['custom_logo'] = $this->custom_logo;
$flutterwave_params['meta'] = $meta;
$order->update_meta_data( '_rave_txn_ref', $txnref );
$order->save();
}
}
wp_localize_script( 'tbz_wc_flutterwave', 'tbz_wc_flutterwave_params', $flutterwave_params );
}
/**
* Load admin scripts
*/
public function admin_scripts() {
if ( 'woocommerce_page_wc-settings' !== get_current_screen()->id ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_enqueue_script( 'tbz_wc_flutterwave_admin', plugins_url( 'assets/js/flutterwave-admin' . $suffix . '.js', TBZ_WC_FLUTTERWAVE_MAIN_FILE ), array(), TBZ_WC_FLUTTERWAVE_VERSION, true );
}
/**
* Displays the Rave fee
*
* @param int $order_id WC Order ID.
*
* @since 2.1.0
*
*/
public function display_flutterwave_fee( $order_id ) {
$order = wc_get_order( $order_id );
$fee = $order->get_meta( '_rave_fee', true );
$currency = $order->get_meta( '_rave_currency', true );
if ( ! $fee || ! $currency ) {
return;
}
?>
<tr>
<td class="label rave-fee">
<?php echo wc_help_tip( __( 'This represents the fee Flutterwave collects for the transaction.', 'woo-rave' ) ); ?>
<?php esc_html_e( __( 'Flutterwave Fee:', 'woo-rave' ) ); ?>
</td>
<td width="1%"></td>
<td class="total">
- <?php echo wc_price( $fee, array( 'currency' => $currency ) ); ?>
</td>
</tr>
<?php
}
/**
* Displays the net total of the transaction without the charges of Rave.
*
* @param int $order_id WC Order ID.
*
* @since 2.1.0
*
*/
public function display_order_payout( $order_id ) {
$order = wc_get_order( $order_id );
$net = $order->get_meta( '_rave_net', true );
$currency = $order->get_meta( '_rave_currency', true );
if ( ! $net || ! $currency ) {
return;
}
?>
<tr>
<td class="label rave-payout">
<?php $message = __( 'This represents the net total that will be credited to your bank account for this order.', 'woo-rave' ); ?>
<?php if ( $net >= $order->get_total() ) : ?>
<?php $message .= __( ' Flutterwave transaction fees was passed to the customer.', 'woo-rave' ); ?>
<?php endif; ?>
<?php echo wc_help_tip( $message ); ?>
<?php esc_html_e( __( 'Flutterwave Payout:', 'woo-rave' ) ); ?>
</td>
<td width="1%"></td>
<td class="total">
<?php echo wc_price( $net, array( 'currency' => $currency ) ); ?>
</td>
</tr>
<?php
}
/**
* Process payment
*
* @param int $order_id WC Order ID.
*
* @return array|void
*/
public function process_payment( $order_id ) {
if ( isset( $_POST['wc-tbz_rave-payment-token'] ) && 'new' !== $_POST['wc-tbz_rave-payment-token'] ) {
$token_id = wc_clean( $_POST['wc-tbz_rave-payment-token'] );
$token = \WC_Payment_Tokens::get( $token_id );
if ( $token->get_user_id() !== get_current_user_id() ) {
wc_add_notice( __( 'Invalid token ID', 'woo-rave' ), 'error' );
} else {
$status = $this->process_token_payment( $token->get_token(), $order_id );
if ( $status ) {
$order = wc_get_order( $order_id );
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
);
}
}
} else {
$order = wc_get_order( $order_id );
if ( is_user_logged_in() && isset( $_POST['wc-tbz_rave-new-payment-method'] ) && true === (bool) $_POST['wc-tbz_rave-new-payment-method'] && $this->saved_cards ) {
$order->update_meta_data( '_wc_rave_save_card', true );
$order->save();
}
return array(
'result' => 'success',
'redirect' => $order->get_checkout_payment_url( true ),
);
}
}
/**
* Process a token payment
*/
public function process_token_payment( $token, $order_id ) {
if ( $token && $order_id ) {
$order = wc_get_order( $order_id );
$txnref = 'WC|' . $order_id . '|' . uniqid();
$order_amount = $order->get_total();
$order_currency = $order->get_currency();
$first_name = $order->get_billing_first_name();
$last_name = $order->get_billing_last_name();
$email = $order->get_billing_email();
$ip_address = $order->get_customer_ip_address();
$headers = array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->secret_key,
);
if ( strpos( $token, '##' ) !== false ) {
$payment_token = explode( '##', $token );
$token_code = $payment_token[0];
} else {
$token_code = $token;
}
$body = array(
'token' => $token_code,
'email' => $email,
'currency' => $order_currency,
'amount' => $order_amount,
'tx_ref' => $txnref,
'firstname' => $first_name,
'lastname' => $last_name,
'ip' => $ip_address,
'meta' => array(
array(
'metaname' => 'Order ID',
'metavalue' => $order_id,
),
),
);
$args = array(
'headers' => $headers,
'body' => wp_json_encode( $body ),
'timeout' => 60,
);
$tokenized_url = 'https://api.flutterwave.com/v3/tokenized-charges';
$request = wp_remote_post( $tokenized_url, $args );
if ( ! is_wp_error( $request ) && 200 === wp_remote_retrieve_response_code( $request ) ) {
$response = json_decode( wp_remote_retrieve_body( $request ) );
$status = $response->data->status;
$payment_currency = $response->data->currency;
$gateway_symbol = get_woocommerce_currency_symbol( $payment_currency );
if ( 'successful' === $status ) {
if ( in_array( $order->get_status(), array( 'processing', 'completed', 'on-hold' ), true ) ) {
// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
wp_redirect( $this->get_return_url( $order ) );
exit;
}
$order_currency = $order->get_currency();
$currency_symbol = get_woocommerce_currency_symbol( $order_currency );
$order_total = $order->get_total();
$amount_paid = $response->data->amount;
$txn_ref = $response->data->tx_ref;
$payment_ref = $response->data->flw_ref;
$amount_charged = $response->data->charged_amount;
$flutterwave_fee = $response->data->app_fee;
$flutterwave_net = $amount_charged - $flutterwave_fee;
$order->update_meta_data( '_rave_fee', $flutterwave_fee );
$order->update_meta_data( '_rave_net', $flutterwave_net );
$order->update_meta_data( '_rave_currency', $payment_currency );
// check if the amount paid is equal to the order amount.
if ( $amount_paid < $order_total ) {
$order->update_status( 'on-hold', '' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the amount paid is not the same as the total order amount.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
$notice_type = 'notice';
// Add Customer Order Note.
$order->add_order_note( $notice, 1 );
// Add Admin Order Note.
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Amount paid is less than the total order amount.<br />Amount Paid was <strong>' . $currency_symbol . $amount_paid . '</strong> while the total order amount is <strong>' . $currency_symbol . $order_total . '</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
wc_add_notice( $notice, $notice_type );
} else {
if ( $payment_currency !== $order_currency ) {
$order->update_status( 'on-hold' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the payment currency is different from the order currency.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
$notice_type = 'notice';
// Add Customer Order Note.
$order->add_order_note( $notice, 1 );
// Add Admin Order Note.
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Order currency is different from the payment currency.<br /> Order Currency is <strong>' . $order_currency . ' (' . $currency_symbol . ')</strong> while the payment currency is <strong>' . $payment_currency . ' (' . $gateway_symbol . ')</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
wc_add_notice( $notice, $notice_type );
} else {
$order->payment_complete( $txn_ref );
$order->add_order_note( sprintf( 'Payment via Flutterwave successful (<strong>Transaction ID:</strong> %s | <strong>Payment Reference:</strong> %s)', $txn_ref, $payment_ref ) );
if ( $this->autocomplete_order ) {
$order->update_status( 'completed' );
}
}
}
$order->save();
$this->save_subscription_payment_token( $order_id, $token );
wc_empty_cart();
return true;
}
$order = wc_get_order( $order_id );
$order->update_status( 'failed', 'Payment was declined by Flutterwave.' );
wc_add_notice( 'Payment Failed. Try again.', 'error' );
return false;
}
wc_add_notice( 'Payment failed using the saved card. Kindly use another payment method.', 'error' );
return false;
}
wc_add_notice( 'Payment Failed.', 'error' );
return false;
}
/**
* Show new card can only be added when placing an order notice
*/
public function add_payment_method() {
wc_add_notice( __( 'You can only add a new card when placing an order.', 'woo-rave' ), 'error' );
}
/**
* Displays the payment page
*/
public function receipt_page( $order_id ) {
$order = wc_get_order( $order_id );
echo '<div id="tbz_wc_flutterwave_form">';
echo '<p>Thank you for your order, please click the button below to pay via Flutterwave.</p>';
echo '<form id="order_review" method="post" action="' . WC()->api_request_url( 'Tbz_WC_Rave_Gateway' ) . '"></form><button class="button alt" id="tbz-rave-wc-payment-button">Pay Now</button> <a class="button cancel" href="' . esc_url( $order->get_cancel_order_url() ) . '">Cancel order & restore cart</a>';
echo '</div>';
}
/**
* Verify Rave payment
*/
public function verify_flutterwave_transaction() {
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
@ob_clean();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['tbz_wc_flutterwave_txnref'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$txn_id = sanitize_text_field( $_REQUEST['tbz_wc_flutterwave_txnref'] );
$flutterwave_txn = $this->verify_transaction( $txn_id );
$status = $flutterwave_txn->data->status ?? 'failed';
if ( 'successful' === $status ) {
$payment_currency = $flutterwave_txn->data->currency;
$gateway_symbol = get_woocommerce_currency_symbol( $payment_currency );
$order_details = explode( '|', $flutterwave_txn->data->tx_ref );
$order_id = (int) $order_details[1];
$order = wc_get_order( $order_id );
if ( in_array( $order->get_status(), array( 'processing', 'completed', 'on-hold' ), true ) ) {
wp_redirect( $this->get_return_url( $order ) );
exit;
}
$order_currency = $order->get_currency();
$currency_symbol = get_woocommerce_currency_symbol( $order_currency );
$order_total = $order->get_total();
$amount_paid = $flutterwave_txn->data->amount;
$txn_ref = $flutterwave_txn->data->tx_ref;
$payment_ref = $flutterwave_txn->data->flw_ref;
$amount_charged = $flutterwave_txn->data->charged_amount;
$flutterwave_fee = $flutterwave_txn->data->app_fee;
$flutterwave_net = $amount_charged - $flutterwave_fee;
$order->update_meta_data( '_rave_fee', $flutterwave_fee );
$order->update_meta_data( '_rave_net', $flutterwave_net );
$order->update_meta_data( '_rave_currency', $payment_currency );
// check if the amount paid is equal to the order amount.
if ( $amount_paid < $order_total ) {
$order->update_status( 'on-hold' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the amount paid is not the same as the total order amount.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
$notice_type = 'notice';
// Add Customer Order Note
$order->add_order_note( $notice, 1 );
// Add Admin Order Note
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Amount paid is less than the total order amount.<br />Amount Paid was <strong>' . $currency_symbol . $amount_paid . '</strong> while the total order amount is <strong>' . $currency_symbol . $order_total . '</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
wc_add_notice( $notice, $notice_type );
} else {
if ( $payment_currency !== $order_currency ) {
$order->update_status( 'on-hold' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the payment currency is different from the order currency.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
$notice_type = 'notice';
// Add Customer Order Note
$order->add_order_note( $notice, 1 );
// Add Admin Order Note
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Order currency is different from the payment currency.<br /> Order Currency is <strong>' . $order_currency . ' (' . $currency_symbol . ')</strong> while the payment currency is <strong>' . $payment_currency . ' (' . $gateway_symbol . ')</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
wc_add_notice( $notice, $notice_type );
} else {
$order->payment_complete( $txn_ref );
$order->add_order_note( sprintf( 'Payment via Flutterwave successful (<strong>Transaction ID:</strong> %s | <strong>Payment Reference:</strong> %s)', $txn_ref, $payment_ref ) );
if ( $this->autocomplete_order ) {
$order->update_status( 'completed' );
}
}
}
$order->save();
$this->save_card_details( $flutterwave_txn, $order->get_user_id(), $order_id );
wc_empty_cart();
wp_redirect( $this->get_return_url( $order ) );
exit;
} else {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$order_txn_ref = sanitize_text_field( $_REQUEST['tbz_wc_flutterwave_order_txnref'] );
$order_details = explode( '|', $order_txn_ref );
$order_id = (int) $order_details[1];
$order = wc_get_order( $order_id );
if ( $order ) {
$order->add_order_note( sprintf( 'Unable to retrieve transaction details from Flutterwave. This could be due to invalid Flutterwave API keys on the settings page. <strong>Transaction ID:</strong> %s', $order_txn_ref ) );
}
wc_add_notice( 'Unable to retrieve transaction details from Flutterwave. If debited contact website owner.', 'error' );
wp_redirect( wc_get_page_permalink( 'checkout' ) );
exit;
}
}
wc_add_notice( 'Payment failed. Try again.', 'error' );
wp_redirect( wc_get_page_permalink( 'checkout' ) );
exit;
}
/**
* Process Webhook
*/
public function process_webhooks() {
if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
exit;
}
sleep( 10 );
$body = file_get_contents( 'php://input' );
if ( $this->is_json( $body ) ) {
$webhook_body = (array) json_decode( $body, true );
} else {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$webhook_body = $_POST;
}
if ( isset( $webhook_body['data']['id'] ) ) {
$transaction_id = $webhook_body['data']['id'];
} elseif ( isset( $webhook_body['id'] ) ) {
$transaction_id = $webhook_body['id'];
} else {
$transaction_id = '';
}
if ( empty( $transaction_id ) ) {
exit;
}
$flutterwave_txn = $this->verify_transaction( $transaction_id );
if ( ! isset( $flutterwave_txn->data->status ) ) {
return;
}
// Payment failed.
if ( 'successful' !== strtolower( $flutterwave_txn->data->status ) ) {
$order_details = explode( '|', $flutterwave_txn->data->tx_ref );
$order_id = (int) $order_details[1];
$order = wc_get_order( $order_id );
if ( $order ) {
$order->update_status( 'failed', 'Payment was declined by Flutterwave.' );
}
exit;
}
$payment_currency = $flutterwave_txn->data->currency;
$gateway_symbol = get_woocommerce_currency_symbol( $payment_currency );
$order_details = explode( '|', $flutterwave_txn->data->tx_ref );
$order_id = (int) $order_details[1];
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$rave_txn_ref = $order->get_meta( '_rave_txn_ref' );
if ( strtolower( $flutterwave_txn->data->tx_ref ) !== strtolower( $rave_txn_ref ) ) {
exit;
}
http_response_code( 200 );
if ( in_array( $order->get_status(), array( 'processing', 'completed', 'on-hold' ), true ) ) {
exit;
}
$order_currency = $order->get_currency();
$currency_symbol = get_woocommerce_currency_symbol( $order_currency );
$order_total = $order->get_total();
$amount_paid = $flutterwave_txn->data->amount;
$txn_ref = $flutterwave_txn->data->tx_ref;
$payment_ref = $flutterwave_txn->data->flw_ref;
$amount_charged = $flutterwave_txn->data->charged_amount;
$flutterwave_fee = $flutterwave_txn->data->app_fee;
$flutterwave_net = $amount_charged - $flutterwave_fee;
$order->update_meta_data( '_rave_fee', $flutterwave_fee );
$order->update_meta_data( '_rave_net', $flutterwave_net );
$order->update_meta_data( '_rave_currency', $payment_currency );
// check if the amount paid is equal to the order amount.
if ( $amount_paid < $order_total ) {
$order->update_status( 'on-hold' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the amount paid is not the same as the total order amount.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
// Add Customer Order Note
$order->add_order_note( $notice, 1 );
// Add Admin Order Note.
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Amount paid is less than the total order amount.<br />Amount Paid was <strong>' . $currency_symbol . $amount_paid . '</strong> while the total order amount is <strong>' . $currency_symbol . $order_total . '</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
} elseif ( $payment_currency !== $order_currency ) {
$order->update_status( 'on-hold', '' );
$order->set_transaction_id( $txn_ref );
$notice = 'Thank you for shopping with us.<br />Your payment was successful, but the payment currency is different from the order currency.<br />Your order is currently on-hold.<br />Kindly contact us for more information regarding your order and payment status.';
// Add Customer Order Note.
$order->add_order_note( $notice, 1 );
// Add Admin Order Note.
$order->add_order_note( '<strong>Look into this order</strong><br />This order is currently on hold.<br />Reason: Order currency is different from the payment currency.<br /> Order Currency is <strong>' . $order_currency . ' (' . $currency_symbol . ')</strong> while the payment currency is <strong>' . $payment_currency . ' (' . $gateway_symbol . ')</strong><br /><strong>Transaction ID:</strong> ' . $txn_ref . ' | <strong>Payment Reference:</strong> ' . $payment_ref );
wc_reduce_stock_levels( $order_id );
} else {
$order->payment_complete( $txn_ref );
$order->add_order_note( sprintf( 'Payment via Flutterwave successful (<strong>Transaction ID:</strong> %s | <strong>Payment Reference:</strong> %s)', $txn_ref, $payment_ref ) );
if ( $this->autocomplete_order ) {
$order->update_status( 'completed' );
}
}
$order->save();
$this->save_card_details( $flutterwave_txn, $order->get_user_id(), $order_id );
wc_empty_cart();
exit;
}
/**
* Save Customer Card Details.
*/
public function save_card_details( $flutterwave_txn, $user_id, $order_id ) {
$token_code = $flutterwave_txn->data->card->token ?? '';
$customer_email = $flutterwave_txn->data->customer->email ?? '';
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
if ( empty( $token_code ) || empty( $customer_email ) ) {
$order->delete_meta_data( '_wc_rave_save_card' );
$order->save();
return;
}
$card_token = "$token_code###$customer_email";
$this->save_subscription_payment_token( $order_id, $card_token );
$save_card = $order->get_meta( '_wc_rave_save_card' );
if ( $user_id && $this->saved_cards && $save_card ) {
$brand = $flutterwave_txn->data->card->type;
$last4 = $flutterwave_txn->data->card->last_4digits;
$expiry_date = explode( '/', $flutterwave_txn->data->card->expiry );
$exp_month = $expiry_date[0];
$exp_year = $expiry_date[1];
if ( 2 === strlen( $exp_year ) ) {
$exp_year = date_create_from_format( 'y', $exp_year );
$exp_year = $exp_year->format( 'Y' );
}
$token = new \WC_Payment_Token_CC();
$token->set_token( $card_token );
$token->set_gateway_id( 'tbz_rave' );
$token->set_card_type( $brand );
$token->set_last4( $last4 );
$token->set_expiry_month( $exp_month );
$token->set_expiry_year( $exp_year );
$token->set_user_id( $user_id );
$token->save();
}
$order->delete_meta_data( '_wc_rave_save_card' );
$order->save();
}
/**
* Save payment token to the order for automatic renewal for further subscription payment
*/
public function save_subscription_payment_token( $order_id, $payment_token ) {
if ( ! function_exists( 'wcs_order_contains_subscription' ) ) {
return;
}
if ( empty( $payment_token ) ) {
return;
}
if ( ! $this->order_contains_subscription( $order_id ) ) {
return;
}
// Also store it on the subscriptions being purchased or paid for in the order.
if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
} elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
} else {
$subscriptions = array();
}
if ( empty( $subscriptions ) ) {
return;
}
foreach ( $subscriptions as $subscription ) {
$subscription->update_meta_data( '_tbz_rave_wc_token', $payment_token );
$subscription->save();
}
}
/**
* @param $string
*
* @return bool
*/
public function is_json( $string ) {
return is_string( $string ) && is_array( json_decode( $string, true ) ) ? true : false;
}
private function verify_transaction( $txn_id ) {
$api_url = "https://api.flutterwave.com/v3/transactions/$txn_id/verify";
$headers = array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->secret_key,
);
$args = array(
'headers' => $headers,
'timeout' => 60,
);
$request = wp_remote_get( $api_url, $args );
return json_decode( wp_remote_retrieve_body( $request ) );
}
}
class_alias( WC_Gateway_Flutterwave::class, 'Tbz_WC_Rave_Webhook' );