first commit

This commit is contained in:
2026-03-05 13:07:40 +01:00
commit 64ba0721ee
25709 changed files with 4691006 additions and 0 deletions

View File

@@ -0,0 +1,306 @@
<?php
/**
* Revolut API Client
*
* @package WooCommerce
* @since 2.0
* @author Revolut
*/
defined( 'ABSPATH' ) || exit();
/**
* WC_Revolut_API_Client class.
*/
class WC_Revolut_API_Client {
use WC_Revolut_Logger_Trait;
/**
* Revolut Api Version
*
* @var string
*/
public $api_version = '2023-09-01';
/**
* Api url live mode
*
* @var string
*/
public $api_url_live = 'https://merchant.revolut.com';
/**
* Api url sandbox mode
*
* @var string
*/
public $api_url_sandbox = 'https://sandbox-merchant.revolut.com';
/**
* Api url dev mode
*
* @var string
*/
public $api_url_dev = 'https://merchant.revolut.codes';
/**
* Merchant management Api urls
*
* @var array
*/
public $mgmt_api_urls = array(
'live' => 'https://merchant-mgmt.revolut.com',
'dev' => 'https://merchant-mgmt.revolut.codes',
'sandbox' => 'https://sandbox-merchant-mgmt.revolut.com',
);
/**
* Api mode live|sandbox|develop
*
* @var string
*/
public $mode;
/**
* Api key
*
* @var string
*/
public $api_key;
/**
* Public key
*
* @var string
*/
public $public_key;
/**
* Api base url
*
* @var string
*/
public $base_url;
/**
* Api url
*
* @var string
*/
public $api_url;
/**
* Public Api url
*
* @var string
*/
public $mgmt_api_url;
/**
* API settings
*
* @var WC_Revolut_Settings_API
*/
private $api_settings;
/**
* Constructor
*
* @param WC_Revolut_Settings_API $api_settings Api settings.
* @param bool $new_api api version.
*/
public function __construct( WC_Revolut_Settings_API $api_settings, $new_api = false ) {
$this->api_settings = $api_settings;
$this->mode = $this->api_settings->get_option( 'mode' );
if ( 'live' === $this->mode ) {
$this->base_url = $this->api_url_live;
$this->api_key = $this->api_settings->get_option( 'api_key' );
} elseif ( 'sandbox' === $this->mode ) {
$this->base_url = $this->api_url_sandbox;
$this->api_key = $this->api_settings->get_option( 'api_key_sandbox' );
} elseif ( 'dev' === $this->mode ) {
$this->base_url = $this->api_url_dev;
$this->api_key = $this->api_settings->get_option( 'api_key_dev' );
}
// switch to the new api if required.
$this->api_url = $new_api ? $this->base_url . '/api' : $this->base_url . '/api/1.0';
$this->mgmt_api_url = $this->mgmt_api_urls[ $this->mode ];
}
/**
* Send post to API.
*
* @param String $path Api path.
* @param array|null $body Request body.
* @param bool $is_mgmt_endpoint Management API indicator.
* @param bool $new_api New API indicator.
*
* @return mixed
* @throws Exception Exception.
*/
public function post( $path, $body = null, $is_mgmt_endpoint = false, $new_api = false ) {
return $this->request( $path, 'POST', $body, $is_mgmt_endpoint, $new_api );
}
/**
* Send request to API
*
* @param String $path Api path.
* @param String $method Request method.
* @param array|null $body Request body.
* @param bool $is_mgmt_api_endpoint Management API endpoint indicator.
* @param bool $new_api New API indicator.
* @param bool $is_mgmt_endpoint Management endpoint indicator.
*
* @return mixed
* @throws Exception Exception.
*/
private function request( $path, $method, $body = null, $is_mgmt_api_endpoint = false, $new_api = false, $is_mgmt_endpoint = false ) {
global $wp_version;
global $woocommerce;
if ( empty( $this->api_key ) ) {
return array();
}
$api_key = $this->api_key;
if ( $is_mgmt_api_endpoint && WC_GATEWAY_PUBLIC_KEY_ENDPOINT !== $path ) {
$api_key = $this->public_key;
}
$request = array(
'headers' => array(
'Revolut-Api-Version' => $this->api_version,
'Authorization' => 'Bearer ' . $api_key,
'User-Agent' => 'Revolut Payment Gateway/' . WC_GATEWAY_REVOLUT_VERSION . ' WooCommerce/' . $woocommerce->version . ' Wordpress/' . $wp_version . ' PHP/' . PHP_VERSION,
'Content-Type' => 'application/json',
),
'method' => $method,
);
if ( null !== $body ) {
$request['body'] = wp_json_encode( $body );
}
$url = $this->api_url . $path;
if ( $new_api ) {
$url = $this->base_url . '/api' . $path;
}
if ( $is_mgmt_api_endpoint ) {
$url = $this->mgmt_api_url . '/api' . $path;
}
if ( $is_mgmt_endpoint ) {
$url = $this->mgmt_api_url . $path;
}
$response = wp_remote_request( $url, $request );
$response_body = wp_remote_retrieve_body( $response );
if ( wp_remote_retrieve_response_code( $response ) >= 400 && wp_remote_retrieve_response_code( $response ) < 500 && 'GET' !== $method ) {
$this->log_error( "Failed request to URL $method $url" );
$this->log_error( $response_body );
throw new Exception( "Something went wrong: $method $url\n" . $response_body );
}
return json_decode( $response_body, true );
}
/**
* Send request to public API
*
* @param String $path Api path.
* @param array $headers Request method.
* @param String $method Request method.
* @param array|null $body Request body.
*
* @return mixed
* @throws Exception Exception.
*/
public function public_request( $path, $headers, $method = 'POST', $body = null ) {
global $wp_version;
global $woocommerce;
$headers['User-Agent'] = 'Revolut Payment Gateway/' . WC_GATEWAY_REVOLUT_VERSION . ' WooCommerce/' . $woocommerce->version . ' Wordpress/' . $wp_version . ' PHP/' . PHP_VERSION;
$headers['Content-Type'] = 'application/json';
$request = array(
'headers' => $headers,
'method' => $method,
);
if ( null !== $body ) {
$request['body'] = wp_json_encode( $body );
}
$url = $this->mgmt_api_url . '/public/' . $path;
$response = wp_remote_request( $url, $request );
$response_body = wp_remote_retrieve_body( $response );
if ( wp_remote_retrieve_response_code( $response ) >= 400 && wp_remote_retrieve_response_code( $response ) < 500 && 'GET' !== $method ) {
$this->log_error( "Failed request to URL $method $url" );
$this->log_error( $response_body );
throw new Exception( "Something went wrong: $method $url\n" . $response_body );
}
return json_decode( $response_body, true );
}
/**
* Send GET request to API
*
* @param String $path Request path.
* @param Boolean $is_mgmt_api Management API endpoint indicator.
* @param Boolean $is_mgmt Management API indicator.
*
* @return mixed
* @throws Exception Exception.
*/
public function get( $path, $is_mgmt_api = false, $is_mgmt = false ) {
return $this->request( $path, 'GET', null, $is_mgmt_api, false, $is_mgmt );
}
/**
* Revolut API patch
*
* @param String $path Request path.
* @param array|null $body Request body.
*
* @return mixed
* @throws Exception Exception.
*/
public function patch( $path, $body ) {
return $this->request( $path, 'PATCH', $body );
}
/**
* Revolut API delete
*
* @param String $path Request path.
*
* @return mixed
* @throws Exception Exception.
*/
public function delete( $path ) {
return $this->request( $path, 'DELETE' );
}
/**
* Set Revolut Merchant Public Key
*
* @param String $public_key public key.
*
* @return void
*/
public function set_public_key( $public_key ) {
$this->public_key = $public_key;
}
}

View File

@@ -0,0 +1,421 @@
<?php
/**
* Revolut Apple Pay Merchant On-boarding Class.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 3.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Revolut_Apple_Pay_OnBoarding class.
*/
class WC_Revolut_Apple_Pay_OnBoarding {
use WC_Revolut_Logger_Trait;
/**
* Onboarding file name.
*
* @var string
*/
public $domain_onboarding_file_name;
/**
* Onboarding file directory.
*
* @var string
*/
public $domain_onboarding_file_directory;
/**
* Domain name.
*
* @var string
*/
private $domain_name;
/**
* Error message.
*
* @var string
*/
public $error_messages;
/**
* Success message.
*
* @var string
*/
public $success_messages;
/**
* API settings
*
* @var WC_Revolut_Settings_API
*/
public $api_settings;
/**
* Revolut payment request setting.
*
* @var array
*/
public $revolut_payment_request_settings;
/**
* API client
*
* @var WC_Revolut_API_Client
*/
public $api_client;
/**
* Constructor
*/
public function __construct() {
$this->api_settings = revolut_wc()->api_settings;
$this->api_client = new WC_Revolut_API_Client( $this->api_settings, true );
add_action( 'admin_init', array( $this, 'maybe_onboard_apple_pay_merchant' ) );
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
add_action( 'add_option_woocommerce_revolut_settings', array( $this, 'on_revolut_options_update' ), 10, 2 );
add_action( 'update_option_woocommerce_revolut_settings', array( $this, 'on_revolut_options_update' ), 10, 2 );
add_action(
'add_option_woocommerce_revolut_payment_request_settings',
array( $this, 'maybe_onboard_apple_pay_merchant' ),
10,
2
);
add_action(
'update_option_woocommerce_revolut_payment_request_settings',
array( $this, 'on_revolut_payment_request_options_update' ),
10,
2
);
$this->domain_onboarding_file_name = 'apple-developer-merchantid-domain-association';
$this->domain_onboarding_file_directory = '.well-known';
$this->onboarding_file_dir = untrailingslashit( ABSPATH ) . '/' . $this->domain_onboarding_file_directory;
$this->onboarding_file_path = $this->onboarding_file_dir . '/' . $this->domain_onboarding_file_name;
$this->domain_onboarding_file_remote_link = 'https://assets.revolut.com/api-docs/merchant-api/files/apple-developer-merchantid-domain-association';
$this->revolut_payment_request_settings = get_option( 'woocommerce_revolut_payment_request_settings', array() );
$this->domain_name = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : str_replace(
array(
'https://',
'http://',
),
'',
get_site_url()
); // phpcs:ignore
$this->error_messages = array();
$this->success_messages = array();
}
/**
* Try to onboard after Revolut API settings updated.
*
* @param array $old_options old options.
* @param array $new_options new options.
*/
public function on_revolut_options_update( $old_options, $new_options ) {
$this->api_settings = revolut_wc()->api_settings;
$this->api_client = new WC_Revolut_API_Client( $this->api_settings, true );
$this->maybe_onboard_apple_pay_merchant();
}
/**
* Try to onboard after payment request settings updated.
*
* @param array $old_options old options.
* @param array $new_options new options.
*/
public function on_revolut_payment_request_options_update( $old_options, $new_options ) {
$this->revolut_payment_request_settings = $new_options;
$this->maybe_onboard_apple_pay_merchant();
}
/**
* Display messages.
*/
public function admin_notices() {
$page = $this->get_request_data( 'page' );
$section = $this->get_request_data( 'section' );
if ( ! empty( $page ) && ! empty( $section ) ) {
$is_revolut_section = 'wc-settings' === $page && in_array( $section, WC_REVOLUT_GATEWAYS, true );
if ( $is_revolut_section ) {
if ( ! empty( $this->error_messages ) ) {
$this->error_messages = array_unique( $this->error_messages );
foreach ( $this->error_messages as $message ) {
echo wp_kses_post( '<div class="error revolut-passphrase-message"><p>' . $message . '</p></div>' );
}
}
if ( ! empty( $this->success_messages ) ) {
foreach ( $this->success_messages as $message ) {
echo wp_kses_post( '<div style="border-left-color: green" class="error revolut-passphrase-message"><p>' . $message . '</p></div>' );
}
}
}
}
}
/**
* Check is shop needs onboarding.
*/
public function check_is_shop_needs_onboarding() {
if ( $this->check_is_already_onboarded() ) {
return false;
}
if ( ! $this->is_revolut_payment_request_enabled() ) {
return false;
}
if ( ! $this->check_is_api_key_configured() ) {
return false;
}
return true;
}
/**
* Check is api key configured.
*/
public function check_is_api_key_configured() {
return ! empty( $this->api_client->api_key ) && 'sandbox' !== $this->api_settings->get_option( 'mode' );
}
/**
* Check is already onboarded.
*/
public function check_is_already_onboarded() {
return $this->get_option( 'apple_pay_merchant_onboarded' ) === 'yes'
&& $this->get_option( 'apple_pay_merchant_onboarded_api_key' ) === $this->api_client->api_key
&& $this->domain_name === $this->get_option( 'apple_pay_merchant_onboarded_domain' );
}
/**
* Check is payment request enabled.
*/
public function is_revolut_payment_request_enabled() {
return 'yes' === $this->get_option( 'enabled', 'yes' );
}
/**
* Get configuration options.
*
* @param string $option option key.
* @param mixed $default default value.
*/
public function get_option( $option, $default = '' ) {
if ( isset( $this->revolut_payment_request_settings[ $option ] ) && ! empty( $this->revolut_payment_request_settings[ $option ] ) ) {
return $this->revolut_payment_request_settings[ $option ];
}
return $default;
}
/**
* Onboard Apple Pay merchant if required.
*/
public function maybe_onboard_apple_pay_merchant() {
$action = $this->get_post_request_data( 'action' );
if ( ! empty( $action ) && 'wc_revolut_onboard_applepay_domain' === $action ) {
return false; // skip for manual on-boarding.
}
if ( ! $this->check_is_shop_needs_onboarding() ) {
return false;
}
flush_rewrite_rules();
if ( ! $this->download_onboarding_file() ) {
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded_domain'] = '';
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded'] = 'no';
update_option(
'woocommerce_revolut_payment_request_settings',
$this->revolut_payment_request_settings
);
return false;
}
if ( ! $this->register_domain() ) {
$this->add_onboarding_error_message(
__(
'Can not on-boarding Apple Pay merchant',
'revolut-gateway-for-woocommerce'
)
);
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded_domain'] = '';
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded'] = 'no';
update_option(
'woocommerce_revolut_payment_request_settings',
$this->revolut_payment_request_settings
);
return false;
}
$this->remove_onboarding_file();
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded_domain'] = $this->domain_name;
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded_api_key'] = $this->api_client->api_key;
$this->revolut_payment_request_settings['apple_pay_merchant_onboarded'] = 'yes';
update_option( 'woocommerce_revolut_payment_request_settings', $this->revolut_payment_request_settings );
$this->add_onboarding_success_message(
__(
'Apple Pay merchant on-boarded successfully',
'revolut-gateway-for-woocommerce'
)
);
}
/**
* Register domain.
*/
public function register_domain() {
try {
$request_body = array(
'domain' => $this->domain_name,
);
$res = $this->api_client->post( '/apple-pay/domains/register', $request_body );
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
return false;
}
return true;
}
/**
* Download onboarding file.
*/
public function download_onboarding_file() {
try {
if ( ! file_exists( $this->onboarding_file_dir ) && ! mkdir( $this->onboarding_file_dir, 0755 ) ) {
$this->add_onboarding_error_message(
__(
'Can not on-boarding Apple Pay merchant: Can not create directory',
'woocommerce-gateway-revolut'
)
);
return false;
}
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
}
if ( ! file_put_contents( $this->onboarding_file_path, file_get_contents( $this->domain_onboarding_file_remote_link )) ) // phpcs:ignore
{
$this->add_onboarding_error_message(
__(
'Can not on-boarding Apple Pay merchant: Can not locate on-boarding file',
'revolut-gateway-for-woocommerce'
)
);
return false;
}
return true;
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
return false;
}
}
/**
* Remove onboarding file.
*/
public function remove_onboarding_file() {
if ( ! unlink( $this->onboarding_file_path ) ) {
$this->add_onboarding_error_message(
__(
'Can not remove on-boarding file',
'revolut-gateway-for-woocommerce'
)
);
}
}
/**
* Add error message.
*
* @param string $message message.
*/
public function add_onboarding_error_message( $message ) {
$this->error_messages[] = $message;
}
/**
* Add success message.
*
* @param string $message message.
*/
public function add_onboarding_success_message( $message ) {
$this->success_messages[] = $message;
}
/**
* Safe get posted data
*
* @param string $post_key request key.
*/
public function get_post_request_data( $post_key ) {
if ( ! isset( $_POST[ $post_key ] ) ) { // phpcs:ignore
return null;
}
return $this->recursive_sanitize_text_field( $_POST[ $post_key ]); // phpcs:ignore
}
/**
* Safe get request data
*
* @param string $get_key request key.
*/
public function get_request_data( $get_key ) {
if ( ! isset( $_GET[ $get_key ] ) ) { // phpcs:ignore
return null;
}
return $this->recursive_sanitize_text_field( $_GET[ $get_key ] ); // phpcs:ignore
}
/**
* Clear data.
*
* @param mixed $var data for cleaning.
*/
public function recursive_sanitize_text_field( $var ) {
if ( is_array( $var ) ) {
return array_map( array( $this, 'recursive_sanitize_text_field' ), $var );
} else {
return sanitize_text_field( wp_unslash( $var ) );
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Singleton class that handles class loading.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0.0
*/
defined( 'ABSPATH' ) || exit();
/**
* WC_Revolut_Manager class.
*/
class WC_Revolut_Manager {
/**
* Class instance.
*
* @var object
*/
public static $instance;
/**
* Get singleton class instance.
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
public function __construct() {
add_action( 'woocommerce_init', array( $this, 'woocommerce_dependencies' ) );
}
/**
* Include plugin dependencies.
*/
public function woocommerce_dependencies() {
// traits.
include_once REVOLUT_PATH . 'includes/traits/wc-revolut-settings-trait.php';
include_once REVOLUT_PATH . 'includes/traits/wc-revolut-logger-trait.php';
include_once REVOLUT_PATH . 'includes/traits/wc-revolut-helper-trait.php';
include_once REVOLUT_PATH . 'includes/traits/wc-revolut-express-checkout-helper-trait.php';
// load gateways.
include_once REVOLUT_PATH . 'includes/abstract/class-wc-payment-gateway-revolut.php';
include_once REVOLUT_PATH . 'includes/gateways/class-wc-gateway-revolut-cc.php';
include_once REVOLUT_PATH . 'includes/gateways/class-wc-gateway-revolut-pay.php';
require_once REVOLUT_PATH . 'includes/gateways/class-wc-gateway-revolut-payment-request.php';
// main classes.
include_once REVOLUT_PATH . 'includes/class-wc-revolut-privacy.php';
include_once REVOLUT_PATH . 'includes/class-wc-revolut-validate-checkout.php';
include_once REVOLUT_PATH . 'includes/class-wc-revolut-order-descriptor.php';
include_once REVOLUT_PATH . 'includes/class-wc-revolut-payment-ajax-controller.php';
include_once REVOLUT_PATH . 'includes/class-wc-revolut-apple-pay-onboarding.php';
include_once REVOLUT_PATH . 'includes/api/class-wc-revolut-api-client.php';
include_once REVOLUT_PATH . '/api/class-revolut-webhook-controller.php';
// settings.
include_once REVOLUT_PATH . 'includes/settings/class-wc-revolut-settings-api.php';
include_once REVOLUT_PATH . 'includes/settings/class-wc-revolut-advanced-settings.php';
include_once REVOLUT_PATH . '/includes/class-wc-revolut-payment-tokens.php';
// allow other plugins to provide their own settings class.
$setting_classes = apply_filters(
'wc_revolut_setting_classes',
array(
'api_settings' => 'WC_Revolut_Settings_API',
)
);
foreach ( $setting_classes as $id => $class_name ) {
if ( class_exists( $class_name ) ) {
$this->{$id} = new $class_name();
}
}
new WC_Revolut_Apple_Pay_OnBoarding();
new WC_Revolut_Payment_Ajax_Controller();
new WC_Gateway_Revolut_Payment_Request();
new WC_Gateway_Revolut_Pay();
new WC_Revolut_Advanced_Settings();
}
}
/**
* Returns the global instance of the WC_Revolut_Manager.
*
* @return WC_Revolut_Manager
* @since 2.0.0
*/
function revolut_wc() {
return WC_Revolut_Manager::instance();
}
// load singleton.
revolut_wc();

View File

@@ -0,0 +1,61 @@
<?php
/**
* Revolut order Descriptor.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0.0
*/
/**
* WC_Revolut_Order_Descriptor class.
*/
class WC_Revolut_Order_Descriptor {
use WC_Gateway_Revolut_Helper_Trait;
/**
* Order total amount.
*
* @var float
*/
public $amount;
/**
* Order currency.
*
* @var string
*/
public $currency;
/**
* Revolut customer id.
*
* @var string
*/
public $revolut_customer_id;
/**
* OrderDescriptor constructor.
*
* @param float $amount payment amount.
* @param string $currency currency.
* @param string $revolut_customer_id Revolut customer id.
*/
public function __construct( $amount, $currency, $revolut_customer_id ) {
if ( $this->check_is_get_data_submitted( 'pay_for_order' ) && ! empty( $this->get_request_data( 'key' ) ) ) {
global $wp;
$order = wc_get_order( wc_clean( $wp->query_vars['order-pay'] ) );
$amount = $order->get_total();
}
if ( is_add_payment_method_page() || $this->is_subs_change_payment() ) {
$amount = 0;
}
$this->amount = $this->get_revolut_order_total( $amount, $currency );
$this->currency = $currency;
$this->revolut_customer_id = $revolut_customer_id;
}
}

View File

@@ -0,0 +1,471 @@
<?php
/**
* Handles AJAX calls, related to Revolut Payment.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 3.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Revolut_Payment_Ajax_Controller class.
*/
class WC_Revolut_Payment_Ajax_Controller {
use WC_Gateway_Revolut_Helper_Trait;
/**
* API client
*
* @var WC_Revolut_API_Client
*/
public $api_client;
/**
* Constructor
*/
public function __construct() {
$this->api_settings = revolut_wc()->api_settings;
$this->api_client = new WC_Revolut_API_Client( $this->api_settings );
add_action( 'wc_ajax_wc_revolut_validate_checkout_fields', array( $this, 'wc_revolut_validate_checkout_fields' ) );
add_action( 'wc_ajax_wc_revolut_validate_order_pay_form', array( $this, 'wc_revolut_validate_order_pay_form' ) );
add_action( 'wc_ajax_wc_revolut_get_order_pay_billing_info', array( $this, 'wc_revolut_get_order_pay_billing_info' ) );
add_action( 'wc_ajax_wc_revolut_get_customer_info', array( $this, 'wc_revolut_get_customer_info' ) );
add_action( 'wc_ajax_wc_revolut_process_payment_result', array( $this, 'wc_revolut_process_payment_result' ) );
add_action( 'wc_ajax_revolut_payment_request_cancel_order', array( $this, 'revolut_payment_request_ajax_cancel_order' ) );
add_action( 'wc_ajax_revolut_payment_request_set_error_message', array( $this, 'revolut_payment_request_ajax_set_error_message' ) );
add_action( 'wc_ajax_revolut_payment_request_log_error', array( $this, 'revolut_payment_request_ajax_log_error' ) );
add_action( 'wc_ajax_revolut_payment_request_log_error', array( $this, 'revolut_payment_request_ajax_log_error' ) );
if ( is_admin() ) {
add_action( 'wp_ajax_wc_revolut_set_webhook', array( $this, 'wc_revolut_set_webhook' ) );
add_action( 'wp_ajax_wc_revolut_clear_records', array( $this, 'wc_revolut_clear_records' ) );
add_action(
'wp_ajax_wc_revolut_onboard_applepay_domain',
array( $this, 'wc_revolut_onboard_applepay_domain' )
);
}
}
/**
* Process Revolut Order
*
* @throws Exception Exception.
*/
public function wc_revolut_process_payment_result() {
try {
$wc_order_id = $this->get_posted_integer_data( 'wc_order_id' );
$selected_gateway = $this->get_post_request_data( 'revolut_gateway' );
if ( empty( $wc_order_id ) || empty( $selected_gateway ) || empty( $this->get_post_request_data( 'revolut_public_id' ) ) ) {
$this->log_error(
array(
'wc_order_id' => $wc_order_id,
'selected_gateway' => $selected_gateway,
'revolut_public_id' => $this->get_post_request_data( 'revolut_public_id' ),
)
);
$revolut_payment_error = $this->get_post_request_data( 'revolut_payment_error' );
if ( empty( $revolut_payment_error ) ) {
$revolut_payment_error = __( 'We are unable to process your order, please try again.', 'woocommerce' );
}
throw new Exception( $revolut_payment_error );
}
$revolut_gateway = new WC_Gateway_Revolut_CC();
if ( 'revolut_pay' === $selected_gateway ) {
$revolut_gateway = new WC_Gateway_Revolut_Pay();
} elseif ( 'revolut_payment_request' === $selected_gateway ) {
$revolut_gateway = new WC_Gateway_Revolut_Payment_Request();
}
$result = $revolut_gateway->process_payment( $wc_order_id );
} catch ( Exception $e ) {
$result = array(
'messages' => $e->getMessage(),
'result' => 'fail',
'redirect' => '',
);
}
try {
if ( ! empty( $wc_order_id ) && isset( $result['result'] ) && 'success' === $result['result'] ) {
$result['order_id'] = $wc_order_id;
apply_filters( 'woocommerce_payment_successful_result', $result, $wc_order_id );
}
} catch ( Exception $e ) {
// if hook was unsuccessful do not prevent order process.
$this->log_error( $e->getMessage() );
}
wp_send_json( $result );
}
/**
* Setup webhook
*
* @throws Exception Exception.
*/
public function wc_revolut_set_webhook() {
try {
if ( $this->check_is_post_data_submitted( 'apiKey' ) || empty( $this->get_post_request_data( 'apiKey' ) ) ) {
wp_die( false );
}
if ( ! $this->check_is_post_data_submitted( 'mode' ) || empty( $this->get_post_request_data( 'mode' ) ) ) {
wp_die( false );
}
$web_hook_url = get_site_url( null, '/wp-json/wc/v3/revolut', 'https' );
$body = array(
'url' => $web_hook_url,
'events' => array(
'ORDER_COMPLETED',
'ORDER_AUTHORISED',
),
);
$mode = $this->get_post_request_data( 'mode' );
if ( 'live' === $mode ) {
$this->api_client->api_url = $this->api_client->api_url_live;
} elseif ( 'sandbox' === $mode ) {
$this->api_client->api_url = $this->api_client->api_url_sandbox;
} elseif ( 'dev' === $mode ) {
$this->api_client->api_url = $this->api_client->api_url_dev;
}
$this->api_client->api_url .= '/api/1.0';
$this->api_client->api_key = $this->get_post_request_data( 'apiKey' );
$web_hook_url_list = $this->api_client->get( '/webhooks' );
if ( ! empty( $web_hook_url_list ) ) {
$web_hook_url_list = array_column( $web_hook_url_list, 'url' );
if ( in_array( $web_hook_url, $web_hook_url_list, true ) ) {
wp_send_json(
array(
'success' => true,
)
);
}
}
$response = $this->api_client->post( '/webhooks', $body );
if ( isset( $response['id'] ) && ! empty( $response['id'] ) ) {
wp_send_json(
array(
'success' => true,
)
);
}
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
wp_send_json(
array(
'success' => false,
'message' => $e->getMessage(),
)
);
}
wp_send_json(
array(
'success' => true,
)
);
}
/**
* Onboard Apple Pay domain
*
* @throws Exception Exception.
*/
public function wc_revolut_onboard_applepay_domain() {
try {
$domain_name = str_replace( array( 'https://', 'http://' ), '', get_site_url() );
$onboarding_file = untrailingslashit( ABSPATH ) . '/.well-known/apple-developer-merchantid-domain-association';
$is_exist = fopen( $onboarding_file, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
if ( ! $is_exist ) {
wp_send_json(
array(
'success' => false,
'message' => 'Can not find Apple Pay on-boarding file: ' . $onboarding_file,
)
);
}
$request_body = array(
'domain' => $domain_name,
);
$this->api_settings = revolut_wc()->api_settings;
$this->api_client = new WC_Revolut_API_Client( $this->api_settings, true );
$response = $this->api_client->post( '/apple-pay/domains/register', $request_body );
$revolut_payment_request_settings = get_option( 'woocommerce_revolut_payment_request_settings', array() );
$revolut_payment_request_settings['apple_pay_merchant_onboarded_domain'] = $domain_name;
$revolut_payment_request_settings['apple_pay_merchant_onboarded_api_key'] = $this->api_client->api_key;
$revolut_payment_request_settings['apple_pay_merchant_onboarded'] = 'yes';
update_option( 'woocommerce_revolut_payment_request_settings', $revolut_payment_request_settings );
wp_send_json(
array(
'success' => true,
'response' => $response,
)
);
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
wp_send_json(
array(
'success' => false,
'message' => $e->getMessage(),
)
);
}
wp_send_json(
array(
'success' => false,
'message' => 'Something went wrong.',
)
);
}
/**
* Clear unused order records
*
* @throws Exception Exception.
*/
public function wc_revolut_clear_records() {
try {
global $wpdb;
$result = $wpdb->query( $wpdb->prepare( 'DELETE FROM `' . $wpdb->prefix . 'wc_revolut_orders` WHERE wc_order_id is NUll or wc_order_id = "";' ) ); // phpcs:ignore
if ( ! $result && ! empty( $wpdb->last_error ) ) {
throw new Exception( $wpdb->last_error );
}
wp_send_json(
array(
'success' => true,
'result' => $result,
)
);
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
wp_send_json(
array(
'success' => false,
'message' => $e->getMessage(),
)
);
}
}
/**
* Validate checkout fields
*
* @throws Exception Exception.
*/
public function wc_revolut_validate_checkout_fields() {
try {
wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
if ( WC()->cart->is_empty() ) {
/* translators: %s: shop cart url */
throw new Exception( sprintf( __( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'woocommerce' ), esc_url( wc_get_page_permalink( 'shop' ) ) ) );
}
$validate_checkout = new WC_Revolut_Validate_Checkout();
$validate_checkout->validate_checkout_fields();
if ( 0 === wc_notice_count( 'error' ) ) {
wp_send_json(
array(
'result' => 'success',
)
);
}
$validate_checkout->return_ajax_failure_response();
} catch ( Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
$messages = wc_print_notices( true );
$response = array(
'result' => 'failure',
'messages' => isset( $messages ) ? $messages : '',
);
wp_send_json( $response );
}
}
/**
* Validate checkout fields
*
* @throws Exception Exception.
*/
public function wc_revolut_validate_order_pay_form() {
try {
$nonce_value = wc_get_var( $this->get_post_request_data( 'woocommerce-pay-nonce' ), $this->get_post_request_data( '_wpnonce' ) );
if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-pay' ) ) {
throw new Exception( __( 'Something went wrong.', 'woocommerce' ) );
}
$order_key = $this->get_post_request_data( 'wc_order_key' );
$order_id = $this->get_posted_integer_data( 'wc_order_id' );
$order = wc_get_order( $order_id );
if ( $order_id === $order->get_id() && hash_equals( $order->get_order_key(), $order_key ) && $order->needs_payment() ) {
do_action( 'woocommerce_before_pay_action', $order );
if ( ! empty( $this->get_posted_integer_data( 'terms-field' ) && empty( $this->get_post_request_data( 'terms' ) ) ) ) {
throw new Exception( __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' ) );
}
} else {
throw new Exception( __( 'Something went wrong.', 'woocommerce' ) );
}
wp_send_json(
array(
'result' => 'success',
)
);
} catch ( Exception $e ) {
$this->log_error( 'wc_revolut_validate_order_pay_form: ' . $e->getMessage() );
wc_add_notice( $e->getMessage(), 'error' );
wp_send_json(
array(
'result' => 'failure',
'messages' => wc_print_notices( true ),
)
);
}
}
/**
* Get billing info for manual order payments
*/
public function wc_revolut_get_order_pay_billing_info() {
check_ajax_referer( 'wc-revolut-get-billing-info', 'security' );
$order_id = $this->get_posted_integer_data( 'order_id' );
$order_key = $this->get_post_request_data( 'order_key' );
$order = wc_get_order( $order_id );
// validate order key.
if ( $order && $order_key === $order->get_order_key() ) {
$billing_address = $order->get_address( 'billing' );
$billing_info = array(
'name' => $billing_address['first_name'] . ' ' . $billing_address['last_name'],
'email' => $billing_address['email'],
'phone' => $billing_address['phone'],
'billingAddress' => array(
'countryCode' => $billing_address['country'],
'region' => $billing_address['state'],
'city' => $billing_address['city'],
'streetLine1' => $billing_address['address_1'],
'streetLine2' => $billing_address['address_2'],
'postcode' => $billing_address['postcode'],
),
);
wp_send_json( $billing_info );
}
wp_send_json( array() );
}
/**
* Get billing info for payment method save
*/
public function wc_revolut_get_customer_info() {
check_ajax_referer( 'wc-revolut-get-customer-info', 'security' );
$customer_id = get_current_user_id();
$customer = new WC_Customer( $customer_id );
// validate order key.
if ( $customer_id ) {
$billing_info = array(
'name' => $customer->get_first_name() . ' ' . $customer->get_last_name(),
'email' => $customer->get_email(),
'phone' => $customer->get_billing_phone(),
);
wp_send_json( $billing_info );
} else {
wp_send_json(
array(
'error' => true,
'msg' => 'Can not find customer address',
)
);
}
wp_die();
}
/**
* Cancel api order
*/
public function revolut_payment_request_ajax_cancel_order() {
check_ajax_referer( 'wc-revolut-cancel-order', 'security' );
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
$revolut_order_id = $this->get_revolut_order_by_public_id( $revolut_public_id );
try {
$revolut_gateway = new WC_Gateway_Revolut_CC();
$revolut_gateway->action_revolut_order( $revolut_order_id, 'cancel' );
$revolut_gateway->clear_temp_session( $revolut_order_id );
$revolut_public_id = $this->create_revolut_order( $revolut_gateway->get_revolut_order_descriptor(), true );
$revolut_gateway->set_revolut_express_checkout_public_id( $revolut_public_id );
wp_send_json(
array(
'success' => true,
'revolut_public_id' => $revolut_public_id,
)
);
} catch ( Exception $e ) {
wp_send_json( array( 'success' => false ) );
$this->log_error( $e );
}
}
/**
* Set error message
*/
public function revolut_payment_request_ajax_set_error_message() {
$error_message = $this->get_post_request_data( 'revolut_payment_request_error' );
if ( empty( $error_message ) ) {
$error_message = __( 'Something went wrong', 'revolut-gateway-for-woocommerce' );
}
wc_add_notice( $error_message, 'error' );
}
/**
* Log error message
*/
public function revolut_payment_request_ajax_log_error() {
$error_message = $this->get_post_request_data( 'revolut_payment_request_error' );
$this->log_error( $error_message );
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Handles and process WC payment tokens API.
* Seen in checkout page and my account->add payment method page.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Revolut_Payment_Tokens class.
*/
class WC_Revolut_Payment_Tokens {
/**
* Constructor
*/
public function __construct() {
add_action( 'woocommerce_payment_token_deleted', array( $this, 'woocommerce_payment_token_deleted' ), 10, 2 );
add_filter( 'woocommerce_payment_methods_list_item', array( $this, 'get_account_saved_payment_methods_list_item' ), 10, 2 );
}
/**
* Get saved payment method item.
*
* @param array $item list item.
* @param WC_Payment_Token $payment_token payment token.
*/
public function get_account_saved_payment_methods_list_item( $item, $payment_token ) {
return $item;
}
/**
* Delete payment token on API
*
* @param int $token_id id token.
* @param WC_Payment_Token $token payment token.
*/
public function woocommerce_payment_token_deleted( $token_id, $token ) {
$gateway_revolut = new WC_Gateway_Revolut_CC();
if ( $token->get_gateway_id() === $gateway_revolut->id ) {
$revolut_customer_id = $gateway_revolut->get_revolut_customer_id( get_current_user_id() );
if ( empty( $revolut_customer_id ) ) {
if ( is_account_page() ) {
wc_add_notice( 'Can not find customer ID', 'error' );
}
}
$payment_method_id = $token->get_token();
try {
$gateway_revolut->api_client->delete( "/customers/$revolut_customer_id/payment-methods/$payment_method_id" );
} catch ( Exception $e ) {
if ( is_account_page() ) {
wc_add_notice( $e->getMessage(), 'error' );
}
}
}
}
}
new WC_Revolut_Payment_Tokens();

View File

@@ -0,0 +1,177 @@
<?php
/**
* Revolut privacy class.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0.0
*/
if ( ! class_exists( 'WC_Abstract_Privacy' ) ) {
return;
}
/**
* WC_Gateway_Revolut_Privacy class.
*/
class WC_Revolut_Privacy extends WC_Abstract_Privacy {
/**
* Constructor
*/
public function __construct() {
parent::__construct( __( 'Revolut', 'revolut-gateway-for-woocommerce' ) );
$this->add_exporter(
'revolut-gateway-for-woocommerce-order-data',
__( 'WooCommerce Revolut Order Data', 'revolut-gateway-for-woocommerce' ),
array(
$this,
'order_data_exporter',
)
);
$this->add_eraser(
'revolut-gateway-for-woocommerce-order-data',
__( 'WooCommerce Revolut Data', 'revolut-gateway-for-woocommerce' ),
array(
$this,
'order_data_eraser',
)
);
}
/**
* Returns a list of orders that are using one of Revolut's payment methods.
*
* @param string $email_address Email address.
* @param int $page page.
*
* @return array WP_Post
*/
protected function get_revolut_orders( $email_address, $page ) {
$user = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
$order_query = array(
'payment_method' => 'revolut',
'limit' => 10,
'page' => $page,
);
if ( $user instanceof WP_User ) {
$order_query['customer_id'] = (int) $user->ID;
} else {
$order_query['billing_email'] = $email_address;
}
return wc_get_orders( $order_query );
}
/**
* Get privacy message
*
* @return string
*/
public function get_privacy_message() {
/* translators:%1s: %$2s: */
return wpautop( sprintf( __( 'By using this extension, you may be storing personal data or sharing data with an external service. %1$sLearn more about how this works, including what you may want to include in your privacy policy.%2$s', 'revolut-gateway-for-woocommerce' ), '<a href="https://docs.woocommerce.com/document/privacy-payments/#revolut-gateway-for-woocommerce" target="_blank">', '</a>' ) );
}
/**
* Handle exporting data for Orders.
*
* @param string $email_address E-mail address to export.
* @param int $page Pagination of data.
*
* @return array
*/
public function order_data_exporter( $email_address, $page = 1 ) {
$done = false;
$data_to_export = array();
$orders = $this->get_revolut_orders( $email_address, (int) $page );
$done = true;
if ( 0 < count( $orders ) ) {
foreach ( $orders as $order ) {
$data_to_export[] = array(
'group_id' => 'woocommerce_orders',
'group_label' => __( 'Orders', 'revolut-gateway-for-woocommerce' ),
'item_id' => 'order-' . $order->get_id(),
'data' => array(
array(
'name' => __( 'Revolut token', 'revolut-gateway-for-woocommerce' ),
'value' => get_post_meta( $order->get_id(), '_revolut_pre_order_token', true ),
),
),
);
}
$done = 10 > count( $orders );
}
return array(
'data' => $data_to_export,
'done' => $done,
);
}
/**
* Finds and erases order data by email address.
*
* @param string $email_address The user email address.
* @param int $page Page.
*
* @return array An array of personal data in name value pairs
*/
public function order_data_eraser( $email_address, $page ) {
$orders = $this->get_revolut_orders( $email_address, (int) $page );
$items_removed = false;
$items_retained = false;
$messages = array();
foreach ( (array) $orders as $order ) {
$order = wc_get_order( $order->get_id() );
list($removed, $retained, $msgs) = $this->maybe_handle_order( $order );
$items_removed |= $removed;
$items_retained |= $retained;
$messages = array_merge( $messages, $msgs );
}
// Tell core if we have more orders to work on still.
$done = count( $orders ) < 10;
return array(
'items_removed' => $items_removed,
'items_retained' => $items_retained,
'messages' => $messages,
'done' => $done,
);
}
/**
* Handle eraser of data tied to Orders
*
* @param WC_Order $order WooCommerce Order.
*
* @return array
*/
protected function maybe_handle_order( $order ) {
$order_id = $order->get_id();
$revolut_token = get_post_meta( $order_id, '_revolut_pre_order_token', true );
if ( empty( $revolut_token ) ) {
return array( false, false, array() );
}
delete_post_meta( $order_id, '_revolut_pre_order_token' );
return array( true, false, array( __( 'Revolut Order Data Erased.', 'revolut-gateway-for-woocommerce' ) ) );
}
}
new WC_Revolut_Privacy();

View File

@@ -0,0 +1,47 @@
<?php
/**
* Validate checkout fields.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Revolut_Validate_Checkout class.
*/
class WC_Revolut_Validate_Checkout extends WC_Checkout {
/**
* Validate Checkout fields.
*/
public function validate_checkout_fields() {
$errors = new WP_Error();
$posted_data = $this->get_posted_data();
// Update session for customer and totals.
$this->update_session( $posted_data );
// Validate posted data and cart items before proceeding.
$this->validate_checkout( $posted_data, $errors );
foreach ( $errors->errors as $code => $messages ) {
$data = $errors->get_error_data( $code );
foreach ( $messages as $message ) {
wc_add_notice( $message, 'error', $data );
}
}
}
/**
* Return failure response.
*/
public function return_ajax_failure_response() {
$this->send_ajax_failure_response();
}
}

View File

@@ -0,0 +1,676 @@
<?php
/**
* Revolut Credit Card Gateway
*
* Provides a Revolut Payment Gateway to accept credit card payments.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
*/
/**
* WC_Gateway_Revolut_CC class.
*/
class WC_Gateway_Revolut_CC extends WC_Payment_Gateway_Revolut {
const GATEWAY_ID = 'revolut_cc';
/**
* Constructor
*/
public function __construct() {
$this->id = self::GATEWAY_ID;
$this->method_title = __( 'Revolut Gateway - Credit/Debit Cards', 'revolut-gateway-for-woocommerce' );
$this->tab_title = __( 'Credit/Debit Cards', 'revolut-gateway-for-woocommerce' );
$this->default_title = __( 'Pay with card', 'revolut-gateway-for-woocommerce' );
/* translators:%1s: %$2s: */
$this->method_description = sprintf( __( 'Accept card payments easily and securely via %1$sRevolut%2$s.', 'revolut-gateway-for-woocommerce' ), '<a href="https://www.revolut.com/business/online-payments">', '</a>' );
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->save_payment_method_for = 'merchant';
parent::__construct();
if ( get_option( 'woocommerce_revolut_cc_settings' ) === false ) {
$this->add_default_options();
}
$this->init_scripts();
$this->revolut_saved_cards = 'yes' === $this->get_option( 'revolut_saved_cards' );
add_action( 'wp_enqueue_scripts', array( $this, 'load_payment_scripts' ) );
add_filter( 'wc_revolut_settings_nav_tabs', array( $this, 'admin_nav_tab' ), 4 );
if ( class_exists( 'WC_Subscriptions_Order' ) ) {
add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
add_action( 'woocommerce_subscriptions_change_payment_before_submit', array( $this, 'differentiate_change_payment_method_form' ) );
add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 );
// display the credit card used for a subscription in the "My Subscriptions" table.
add_filter( 'woocommerce_my_subscriptions_payment_method', array( $this, 'maybe_render_subscription_payment_method' ), 10, 2 );
add_action( 'woocommerce_subscription_failing_payment_method_updated_' . $this->id, array( $this, 'update_failing_payment_method' ), 10, 2 );
add_action( 'woocommerce_subscription_token_changed', array( $this, 'update_changed_subscription_token' ), 10, 2 );
}
}
/**
* Load required payment scripts.
*/
public function load_payment_scripts() {
$this->tokenization_script();
}
/**
* Supported functionality
*/
public function init_supports() {
parent::init_supports();
$this->supports = array(
'refunds',
'tokenization',
'add_payment_method',
'subscriptions',
'subscription_cancellation',
'subscription_suspension',
'subscription_reactivation',
'subscription_amount_changes',
'subscription_date_changes',
'subscription_payment_method_change',
'subscription_payment_method_change_customer',
'subscription_payment_method_change_admin',
'multiple_subscriptions',
'pre-orders',
);
}
/**
* Initialize Gateway Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Enable ', 'revolut-gateway-for-woocommerce' ) . $this->method_title,
'type' => 'checkbox',
'description' => __( 'This controls whether or not this gateway is enabled within WooCommerce.', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'title' => array(
'title' => __( 'Title', 'revolut-gateway-for-woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title that the user sees during checkout.', 'revolut-gateway-for-woocommerce' ),
'default' => $this->default_title,
'desc_tip' => true,
),
'card_widget_type' => array(
'title' => __( 'Card widget type', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the card widget type.', 'revolut-gateway-for-woocommerce' ),
'default' => 'card_field',
'desc_tip' => true,
'options' => array(
'card_field' => __( 'Card field', 'revolut-gateway-for-woocommerce' ),
'popup' => __( 'Popup', 'revolut-gateway-for-woocommerce' ),
),
),
'revolut_saved_cards' => array(
'title' => __( 'Save Cards', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Enable Payment with Saved Cards', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If enabled, users will be able to save their cards and use them to checkout in subsequent purchases. Card details are stored securely on Revolut servers', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'enable_cardholder_name' => array(
'title' => __( 'Cardholder\'s Name Field', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Enable', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'description' => __( 'When enabled, input for Cardholder\'s Name will be activated.', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
),
'styling_title' => array(
'title' => __( 'Card widget style', 'revolut-gateway-for-woocommerce' ),
'type' => 'title',
),
'widget_styling' => array(
'title' => __( 'Customize card widget style', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Enable', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'By enabling this option you can customize the style of the Revolut card widget', 'revolut-gateway-for-woocommerce' ),
'default' => 'no',
'class' => 'revolut_styling_option_enable',
'desc_tip' => true,
),
'widget_background_color' => array(
'title' => __( 'Card widget background color', 'revolut-gateway-for-woocommerce' ),
'type' => 'color',
'description' => __( 'This controls the background color of the Revolut card widget', 'revolut-gateway-for-woocommerce' ),
'default' => WC_REVOLUT_CARD_WIDGET_BG_COLOR,
'class' => 'revolut_styling_option',
'desc_tip' => true,
),
'widget_text_color' => array(
'title' => __( 'Card widget font color', 'revolut-gateway-for-woocommerce' ),
'type' => 'color',
'description' => __( 'This controls the text color of the Revolut Card widget', 'revolut-gateway-for-woocommerce' ),
'default' => WC_REVOLUT_CARD_WIDGET_TEXT_COLOR,
'class' => 'revolut_styling_option',
'desc_tip' => true,
),
'revolut_logo_color' => array(
'title' => __( 'Revolut logo theme', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'default' => '#7a7a7a',
'class' => 'revolut_styling_option',
'options' => array(
'#7a7a7a' => __( 'Dark', 'revolut-gateway-for-woocommerce' ),
'#cccccc' => __( 'Light', 'revolut-gateway-for-woocommerce' ),
),
'description' => __( 'This controls the color of the Revolut', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
),
'restore_button' => array(
'title' => '',
'type' => 'text',
'description' => '<button class="revolut_style_restore revolut_styling_option setup-webhook" style="min-height: 30px;"><span id="span-for-active-button-sandbox">Restore defaults</span></button>',
),
);
}
/**
* Scheduled_subscription_payment function.
*
* @param float $amount_to_charge The amount to charge.
* @param WC_Order $renewal_order WC_Order order.
*
* @throws Exception Exception.
*/
public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
try {
$wc_order_id = $renewal_order->get_id();
$payment_token_id = $renewal_order->get_meta( '_payment_token_id', true );
if ( empty( $payment_token_id ) ) {
throw new Exception( "Subscription order payment token is missing: $payment_token_id" );
}
$wc_customer_id = $renewal_order->get_meta( '_wc_customer_id', true );
if ( empty( $wc_customer_id ) ) {
throw new Exception( "Subscription customer ID is missing: $payment_token_id" );
}
$revolut_customer_id = $this->get_revolut_customer_id( $wc_customer_id );
if ( empty( $revolut_customer_id ) ) {
throw new Exception( "Can not find Subscription Revolut customer ID: $revolut_customer_id" );
}
$descriptor = new WC_Revolut_Order_Descriptor( $amount_to_charge, $renewal_order->get_currency(), $revolut_customer_id );
$revolut_payment_public_id = $this->create_revolut_order( $descriptor );
// resolve revolut_public_id into revolut_order_id.
$revolut_order_id = $this->get_revolut_order_by_public_id( $revolut_payment_public_id );
if ( empty( $revolut_order_id ) ) {
throw new Exception( 'Can not find Revolut order ID' );
}
// update internal table to avoid piggybacking on already paid order.
$this->save_wc_order_id( $revolut_payment_public_id, $revolut_order_id, $wc_order_id );
// make the payment with saved card $payment_method_id.
$wc_token = WC_Payment_Tokens::get( $payment_token_id );
$this->pay_by_saved_method( $revolut_order_id, $wc_token );
// payment should be processed until this point, if not throw an error.
$this->check_payment_processed( $revolut_order_id );
// payment process began...
$renewal_order->update_status( 'on-hold' );
$renewal_order->add_order_note( 'Payment has been successfully authorized (Order ID: ' . $revolut_order_id . ').' );
// check payment result and update order status.
$this->handle_revolut_order_result( $renewal_order, $revolut_order_id );
$message = sprintf( 'Subscription charge successfully completed by Revolut (Order ID: %s)', $revolut_order_id );
$renewal_order->add_order_note( $message );
$renewal_order->set_transaction_id( $revolut_order_id );
WC_Subscriptions_Manager::process_subscription_payments_on_order( $renewal_order );
} catch ( Exception $e ) {
WC_Subscriptions_Manager::process_subscription_payment_failure_on_order( $renewal_order );
$renewal_order->update_status( 'failed', 'An error occurred while Payment processing: ' . $e->getMessage() );
$this->log_error( $e->getMessage() );
return false;
}
return true;
}
/**
* Process the payment and return the result.
*
* @param int $wc_order_id WooCommerce Order order id.
*
* @throws Exception Exception.
*/
public function process_payment( $wc_order_id ) {
if ( $this->has_subscription( $wc_order_id ) ) {
if ( $this->is_subs_change_payment() ) {
return $this->change_subs_payment_method( $wc_order_id );
}
// Regular payment with force customer enabled.
return parent::process_payment( $wc_order_id );
} else {
return parent::process_payment( $wc_order_id );
}
}
/**
* Render an input in the "Change payment method" form which does not appear in the "Pay for order" page
*/
public function differentiate_change_payment_method_form() {
echo wp_kses(
'<input type="hidden" id="wc-revolut-change-payment-method" />',
array(
'input' => array(
'id' => array(),
'type' => array(),
'name' => array(),
'value' => array(),
'checked' => array(),
'class' => array(),
'placeholder' => array(),
'style' => array(),
),
),
);
}
/**
* Process the payment method change for subscriptions.
*
* @param int $wc_order_id WooCommerce order id.
*
* @throws Exception Exception.
*/
public function change_subs_payment_method( $wc_order_id ) {
try {
$subscription = wc_get_order( $wc_order_id );
$revolut_payment_public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $revolut_payment_public_id ) ) {
throw new Exception( 'Missing revolut_public_id parameter' );
}
// resolve revolut_public_id into revolut_order_id.
$revolut_order_id = $this->get_revolut_order_by_public_id( $revolut_payment_public_id );
if ( empty( $revolut_order_id ) ) {
throw new Exception( 'Can not find Revolut order ID' );
}
if ( $this->is_using_saved_payment_method() ) {
$wc_token = $this->get_selected_payment_token();
} else {
$wc_token = $this->save_payment_method( $revolut_order_id );
if ( null === $wc_token ) {
throw new Exception( 'An error occurred while saving payment method' );
}
}
$this->save_payment_token_to_order( $subscription, $wc_token, get_current_user_id() );
$this->handle_add_payment_method( $subscription, $wc_token, get_current_user_id() );
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $subscription ),
);
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
wc_add_notice( $e->getMessage(), 'error' );
return false;
}
}
/**
* Updates all active subscriptions payment method.
*
* @param WC_Subscription $current_subscription WooCommerce Subscription.
* @param object $wc_token WooCommerce Payment Token.
* @param int $wc_customer_id WooCommerce Customer id.
* @return bool
*/
public function handle_add_payment_method( $current_subscription, $wc_token, $wc_customer_id ) {
// remove public ID after saving the card.
$this->unset_revolut_public_id();
if ( $this->update_all_subscriptions_payment_method() ) {
$all_subs = wcs_get_users_subscriptions();
if ( ! empty( $all_subs ) ) {
foreach ( $all_subs as $sub ) {
$this->update_payment_subscription_method( $sub, $wc_token, $wc_customer_id );
}
}
return true;
}
if ( null !== $current_subscription ) {
return $this->update_payment_subscription_method( $current_subscription, $wc_token, $wc_customer_id );
}
return true;
}
/**
* Updates active subscription payment method.
*
* @param WC_Subscription $subscription WooCommerce Subscription.
* @param object $wc_token WooCommerce Payment Token id.
* @param int $wc_customer_id WooCommerce Customer id.
* @return bool
*/
public function update_payment_subscription_method( $subscription, $wc_token, $wc_customer_id ) {
if ( $subscription->has_status( 'active' ) ) {
WC_Subscriptions_Change_Payment_Gateway::update_payment_method(
$subscription,
$this->id,
array(
'post_meta' => array(
'_payment_token' => array( 'value' => $wc_token->get_token() ),
'_payment_token_id' => array( 'value' => $wc_token->get_id() ),
'_wc_customer_id' => array( 'value' => $wc_customer_id ),
),
)
);
}
return true;
}
/**
* Check is $order_id a subscription.
*
* @param int $order_id WooCommerce Order id.
* @return boolean
*/
public function has_subscription( $order_id ) {
return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
}
/**
* Remove previous subscription information from resubscribe orders.
*
* @param WC_Order $resubscribe_order instance of a order object.
* @return void
*/
public function delete_resubscribe_meta( $resubscribe_order ) {
delete_post_meta( $resubscribe_order->get_id(), '_payment_token' );
delete_post_meta( $resubscribe_order->get_id(), '_payment_token_id' );
delete_post_meta( $resubscribe_order->get_id(), '_wc_customer_id' );
}
/**
* Render the payment method used for a subscription in the "My Subscriptions" table
*
* @param string $payment_method_to_display the default payment method text to display.
* @param WC_Subscription $subscription the subscription details.
* @return string the subscription payment method.
*/
public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
$customer_user = $subscription->get_customer_id();
// fail for other payment methods.
if ( $subscription->get_payment_method() !== $this->id || ! $customer_user ) {
return $payment_method_to_display;
}
$revolut_payment_token_id = get_post_meta( $subscription->get_id(), '_payment_token_id', true );
$payment_method_to_display = __( 'N/A', 'revolut-gateway-for-woocommerce' );
$wc_token = WC_Payment_Tokens::get( $revolut_payment_token_id );
if ( $wc_token ) {
/* translators:%1s: Card type %$2s: Card last four digits */
$payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'revolut-gateway-for-woocommerce' ), $wc_token->get_card_type(), $wc_token->get_last4() );
}
return $payment_method_to_display;
}
/**
* Update the customer_id for a subscription after using Revolut to complete a payment to make up for
* an automatic renewal payment which previously failed.
*
* @param WC_Subscription $subscription The subscription for which the failing payment method relates.
* @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
* @return void
*/
public function update_failing_payment_method( $subscription, $renewal_order ) {
update_post_meta( $subscription->get_id(), '_payment_token', $renewal_order->get_meta( '_payment_token' ) );
update_post_meta( $subscription->get_id(), '_payment_token_id', $renewal_order->get_meta( '_payment_token_id' ) );
update_post_meta( $subscription->get_id(), '_wc_customer_id', $renewal_order->get_meta( '_wc_customer_id' ) );
}
/**
* Update the subscription payment meta to change from an old payment token to a new one.
*
* @param WC_Subscription $subscription The subscription to update.
* @param WC_Payment_Token $new_token The new payment token.
* @return void
*/
public function update_changed_subscription_token( $subscription, $new_token ) {
if ( $new_token->get_gateway_id() === $this->id ) {
update_post_meta( $subscription->get_id(), '_payment_token', $new_token->get_token() );
update_post_meta( $subscription->get_id(), '_payment_token_id', $new_token->get_id() );
}
}
/**
* Display payment icons
*/
public function get_icon() {
$icons_str = '';
$icons_str .= '<img class="revolut-card-gateway-icon-amex" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/amex.svg" style="margin-left:2px" alt="Amex" />';
$icons_str .= '<img class="revolut-card-gateway-icon-visa" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/visa.svg" style="margin-left:2px" alt="Visa" />';
$icons_str .= '<img class="revolut-card-gateway-icon-mastercard" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/mastercard.svg" alt="MasterCard" />';
return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
}
/**
* Update all subscriptions payment methods
*
* @return bool
*/
public function update_all_subscriptions_payment_method() {
return $this->check_is_post_data_submitted( 'wc-' . $this->id . '-update-subs-payment-method-card' ) || $this->check_is_post_data_submitted( 'update_all_subscriptions_payment_method' );
}
/**
* Check if it is using saved payment method
*
* @return bool
*/
public function is_using_saved_payment_method() {
return ( $this->check_is_post_data_submitted( 'wc-' . $this->id . '-payment-token' ) && ! empty( $this->get_post_request_data( 'wc-' . $this->id . '-payment-token' ) && 'new' !== $this->get_post_request_data( 'wc-' . $this->id . '-payment-token' ) ) );
}
/**
* Grab selected payment token from Request
*
* @return string
* @throws Exception Exception.
*/
public function get_selected_payment_token() {
$wc_token_id = $this->get_posted_integer_data( 'wc-' . $this->id . '-payment-token' );
$wc_token = WC_Payment_Tokens::get( $wc_token_id );
$payment_method_id = $wc_token->get_token();
if ( empty( $payment_method_id ) || $wc_token->get_user_id() !== get_current_user_id() ) {
throw new Exception( 'Can not process payment token' );
}
return $wc_token;
}
/**
* Check if save payment method requested
*/
public function save_payment_method_requested() {
return $this->get_posted_integer_data( 'revolut_save_payment_method' );
}
/**
* Add public_id field and logo on card form
*
* @param String $public_id Revolut public id.
* @param String $merchant_public_key Revolut public key.
* @param String $display_tokenization Available saved card tokens.
*
* @return string
*/
public function generate_inline_revolut_form( $public_id, $merchant_public_key, $display_tokenization ) {
if ( ! in_array( get_woocommerce_currency(), $this->card_payments_currency_list, true ) ) {
return get_woocommerce_currency() . ' currency is not available for card payments';
}
$total = WC()->cart->get_total( '' );
$currency = get_woocommerce_currency();
$total = $this->get_revolut_order_total( $total, $currency );
$mode = $this->api_settings->get_option( 'mode' );
$hide_fieldset = $this->get_option( 'card_widget_type' ) === 'popup' || $this->get_request_data( 'pay_for_order' ) ? 'height:0px;padding:0' : '';
$shipping_total = $this->get_cart_total_shipping();
$hide_payment_method = ! empty( $hide_fieldset ) && ! $display_tokenization ? true : false;
$available_card_brands = $this->get_available_card_brands( $this->get_revolut_public_id() );
$display_banner = $this->api_settings->get_option( 'disable_banner' ) === 'yes' ? '<div id="revolut-upsell-banner"></div>' : '';
$cardholder_name_field = '';
if ( 'yes' === $this->get_option( 'enable_cardholder_name', 'yes' ) && ! $hide_fieldset ) {
$cardholder_name_field .= '<p class="form-row form-row-first validate-required" id="cardholder-name" data-priority="10" style="display: block;width:100%;float:none">';
$cardholder_name_field .= '<span class="woocommerce-input-wrapper"><input type="text" class="input-text" name="wc-revolut-cardholder-name" id="wc-revolut-cardholder-name" placeholder="Cardholder name" autocomplete="cardholder" required></span>';
$cardholder_name_field .= '</p>';
}
return '<fieldset id="wc-' . $this->id . '-cc-form" class="wc-credit-card-form wc-payment-form" style="background:transparent;' . $hide_fieldset . '"> ' . $cardholder_name_field . '
<div style="background: ' . $this->get_option( 'widget_background_color' ) . ';' . $hide_fieldset . '" id="woocommerce-revolut-card-element" data-available-card-brands="' . $available_card_brands . '" data-mode="' . $mode . '" data-shipping-total="' . $shipping_total . '" data-currency="' . $currency . '" data-total="' . $total . '" data-textcolor="' . $this->get_option( 'widget_text_color' ) . '" data-widget-type="' . $this->get_option( 'card_widget_type' ) . '" data-hide-payment-method="' . $hide_payment_method . '" data-locale="' . $this->get_lang_iso_code() . '" data-public-id="' . $public_id . '" data-merchant-public-key="' . $merchant_public_key . '" data-save-payment-for="' . $this->save_payment_method_for . '" data-payment-method-save-is-mandatory="' . $this->is_save_payment_method_mandatory() . '"></div>' . $this->getSvgImage() . $display_banner . '</fieldset>';
}
/**
* Check if cart contains subscription.
*/
public function cart_contains_subscription() {
try {
$cart_contains_subscription = WC_Subscriptions_Cart::cart_contains_subscription();
} catch ( Exception $e ) {
$cart_contains_subscription = false;
}
return $cart_contains_subscription;
}
/**
* Check is payment method available.
*/
public function is_available() {
if ( is_add_payment_method_page() && ! $this->revolut_saved_cards ) {
return false;
}
return 'yes' === $this->enabled;
}
/**
* Check currency support for card payments
*/
public function check_currency_support() {
if ( ! in_array( get_woocommerce_currency(), $this->card_payments_currency_list, true ) ) {
return false;
}
return true;
}
/**
* Check if saving payment method is mandatory
*/
public function is_save_payment_method_mandatory() {
if ( is_add_payment_method_page() ) {
return true;
}
if ( ! class_exists( 'WC_Subscriptions_Order' ) ) {
return false;
}
return $this->check_is_get_data_submitted( 'change_payment_method' ) || $this->cart_contains_subscription();
}
/**
* Displays a checkbox to allow users to save payment methods.
*/
public function save_payment_method_checkbox() {
if ( $this->get_option( 'card_widget_type' ) !== 'popup' && ! $this->is_save_payment_method_mandatory() && $this->revolut_saved_cards && $this->check_currency_support() && get_current_user_id() ) {
return '<p class="form-row woocommerce-SavedPaymentMethods-saveNew revolut-payment-method-save">
<input id="wc-' . $this->id . '-new-payment-method" name="wc-' . $this->id . '-new-payment-method" type="checkbox" value="' . $this->save_payment_method_for . '" style="width:auto;" />
<label for="wc-' . $this->id . '-new-payment-method">' . __( 'Save payment information to my account for future purchases.', 'revolut-gateway-for-woocommerce' ) . '</label>
</p>';
}
return '';
}
/**
* Displays a checkbox to allow users to update all subs payments with new payment.
*/
public function display_update_subs_payment_checkout() {
if ( ! class_exists( 'WC_Subscriptions_Order' ) ) {
return false;
}
if (
wcs_user_has_subscription( get_current_user_id(), '', array( 'active' ) ) &&
is_add_payment_method_page()
) {
$label = wp_kses_post( __( 'Update the Payment Method used for all of my active subscriptions.', 'revolut-gateway-for-woocommerce' ) );
$id = sprintf( 'wc-%1$s-update-subs-payment-method-card', $this->id );
woocommerce_form_field(
$id,
array(
'type' => 'checkbox',
'label' => $label,
'default' => false,
)
);
}
}
/**
* Revolut logo in SVG format.
*/
public function getSvgImage() {
return '<svg width="94" height="17" viewBox="0 0 94 17" fill="' . $this->get_option( 'revolut_logo_color' ) . '" xmlns="http://www.w3.org/2000/svg">
<path d="M1.68302 12V9.714H3.22202C4.49102 9.714 5.30102 8.895 5.30102 7.689C5.30102 6.519 4.50902 5.7 3.22202 5.7H0.900024V12H1.68302ZM1.68302 6.411H3.12302C3.96902 6.411 4.49102 6.879 4.49102 7.689C4.49102 8.535 3.99602 8.994 3.12302 8.994H1.68302V6.411Z" />
<path d="M7.92129 12.081C9.36129 12.081 10.0003 11.019 10.0003 9.705C10.0003 8.382 9.34329 7.32 7.92129 7.32C6.49929 7.32 5.84229 8.382 5.84229 9.705C5.84229 11.028 6.48129 12.081 7.92129 12.081ZM7.92129 11.406C6.99429 11.406 6.60729 10.695 6.60729 9.705C6.60729 8.724 6.98529 7.995 7.92129 7.995C8.85729 7.995 9.23529 8.715 9.23529 9.705C9.23529 10.695 8.85729 11.406 7.92129 11.406Z" />
<path d="M12.534 12L13.641 8.427H13.677L14.757 12H15.54L16.836 7.491V7.401H16.071L15.153 10.974H15.117L14.037 7.401H13.263L12.183 10.974H12.147L11.229 7.401H10.446V7.491L11.751 12H12.534Z" />
<path d="M19.374 12.081C20.355 12.081 21.165 11.514 21.291 10.668H20.535C20.409 11.154 19.977 11.406 19.374 11.406C18.519 11.406 18.078 10.803 18.078 9.93H21.354V9.561C21.354 8.283 20.661 7.32 19.365 7.32C17.943 7.32 17.286 8.373 17.286 9.705C17.286 11.055 18.015 12.081 19.374 12.081ZM18.087 9.3C18.114 8.58 18.501 7.995 19.365 7.995C20.184 7.995 20.571 8.571 20.571 9.3H18.087Z" />
<path d="M23.1878 12V9.768C23.1878 8.688 23.5838 8.076 24.4568 8.076H24.8168V7.347H24.4028C23.8358 7.347 23.3768 7.662 23.1878 8.175H23.1518L23.0888 7.401H22.4318V12H23.1878Z" />
<path d="M27.3109 12.081C28.2919 12.081 29.1019 11.514 29.2279 10.668H28.4719C28.3459 11.154 27.9139 11.406 27.3109 11.406C26.4559 11.406 26.0149 10.803 26.0149 9.93H29.2909V9.561C29.2909 8.283 28.5979 7.32 27.3019 7.32C25.8799 7.32 25.2229 8.373 25.2229 9.705C25.2229 11.055 25.9519 12.081 27.3109 12.081ZM26.0239 9.3C26.0509 8.58 26.4379 7.995 27.3019 7.995C28.1209 7.995 28.5079 8.571 28.5079 9.3H26.0239Z" />
<path d="M31.9617 12.081C32.6457 12.081 33.1047 11.766 33.3477 11.361H33.3837L33.4827 12H34.1397V5.34H33.3837V8.04H33.3477C33.1047 7.635 32.6457 7.32 31.9617 7.32C30.6657 7.32 30.0177 8.337 30.0087 9.696C30.0177 11.181 30.7557 12.081 31.9617 12.081ZM32.0877 11.406C31.1337 11.406 30.7737 10.623 30.7737 9.696C30.7737 8.778 31.1337 7.995 32.0877 7.995C33.0057 7.995 33.4017 8.787 33.4017 9.696C33.4017 10.614 33.0057 11.406 32.0877 11.406Z" />
<path d="M40.1847 12.081C41.3907 12.081 42.1287 11.181 42.1377 9.696C42.1287 8.337 41.4807 7.32 40.1847 7.32C39.5007 7.32 39.0417 7.635 38.7987 8.04H38.7627V5.34H38.0067V12H38.6637L38.7627 11.361H38.7987C39.0417 11.766 39.5007 12.081 40.1847 12.081ZM40.0587 11.406C39.1407 11.406 38.7447 10.614 38.7447 9.696C38.7447 8.787 39.1407 7.995 40.0587 7.995C41.0127 7.995 41.3727 8.778 41.3727 9.696C41.3727 10.623 41.0127 11.406 40.0587 11.406Z" />
<path d="M44.3326 14.061L46.8796 7.491V7.401H46.0875L44.7375 11.01H44.7015L43.3065 7.401H42.4965V7.491L44.3415 11.946L44.3505 11.964L43.5676 13.971V14.061H44.3326Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M59.0134 13.0369L56.793 9.12191C58.1976 8.60604 58.8939 7.70677 58.8939 6.39711C58.8954 4.79652 57.6336 3.77778 55.6244 3.77778H51.6981V13.0369H53.4318V9.28037H54.9032L57.0304 13.0369H59.0134ZM55.6244 5.31189C56.6488 5.31189 57.1613 5.72178 57.1613 6.52858C57.1613 7.33537 56.6483 7.74526 55.6244 7.74526H53.4318V5.31189H55.6244ZM82.044 13.0371V3.9604H80.3892V13.0371H82.044ZM78.5549 7.06465C77.8585 6.44281 77.018 6.13889 76.0461 6.13889C75.0876 6.13889 74.247 6.44331 73.5502 7.06465C72.8538 7.673 72.5 8.51977 72.5 9.60449C72.5 10.6892 72.8538 11.5355 73.5502 12.1574C74.2465 12.7657 75.0876 13.0701 76.0461 13.0701C77.018 13.0701 77.8585 12.7657 78.5549 12.1574C79.2646 11.5355 79.6185 10.6892 79.6185 9.60449C79.6185 8.51977 79.2641 7.673 78.5549 7.06465ZM74.7332 10.9931C74.366 10.6362 74.1811 10.1729 74.1811 9.60449C74.1811 9.03565 74.3645 8.57276 74.7332 8.22885C75.1005 7.87195 75.5346 7.69998 76.0456 7.69998C76.557 7.69998 77.0041 7.87195 77.3719 8.22885C77.752 8.57276 77.9368 9.03565 77.9368 9.60449C77.9368 10.1733 77.7535 10.6362 77.3719 10.9931C77.0046 11.3371 76.5576 11.509 76.0456 11.509C75.5346 11.509 75.1015 11.3371 74.7332 10.9931ZM87.3855 10.0976V6.47599H89.0443V10.3755C89.0443 11.8566 88.1001 13.2218 86.0384 13.2218H86.0255C83.9509 13.2218 83.0052 11.8861 83.0052 10.3755V6.47599H84.6631V10.0976C84.6631 10.9708 85.1191 11.5487 86.0255 11.5487C86.9172 11.5487 87.3855 10.9703 87.3855 10.0976ZM70.6804 6.47599L69.0255 10.9204L67.3707 6.47599H65.6107L68.2514 13.0368H69.8017L72.4418 6.47599H70.6804ZM64.7265 7.2166C65.3177 7.83844 65.6196 8.61874 65.6196 9.571H65.6191V10.1533H60.603C60.708 11.1586 61.4044 11.7934 62.3891 11.7934C63.1901 11.7934 63.7818 11.3965 64.1883 10.5897L65.4223 11.3041C64.8063 12.5872 63.7952 13.2221 62.3629 13.2221C61.4306 13.2221 60.6426 12.9046 59.9855 12.2563C59.3417 11.6085 59.0135 10.7752 59.0135 9.75644C59.0135 8.73771 59.3411 7.91792 59.9984 7.26959C60.6684 6.62175 61.4832 6.29084 62.4417 6.29084C63.3745 6.29084 64.1352 6.59476 64.7265 7.2166ZM64.0956 8.93616C63.9385 8.08937 63.3204 7.56051 62.3882 7.56051C61.5342 7.56051 60.8522 8.15586 60.602 8.93616H64.0956ZM89.9207 11.2769C89.9207 12.3511 90.7845 13.2214 91.8496 13.2214H92.9945V11.7398H92.142C91.8288 11.7398 91.5755 11.4848 91.5755 11.1699V7.82728H92.9945V6.47813H91.5755V4.70308H89.9207V11.2769Z" />
</svg>';
}
}

View File

@@ -0,0 +1,465 @@
<?php
/**
* Revolut Pay
*
* Provides a gateway to accept payments through Revolut Pay.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
/**
* WC_Gateway_Revolut_Pay class
*/
class WC_Gateway_Revolut_Pay extends WC_Payment_Gateway_Revolut {
const GATEWAY_ID = 'revolut_pay';
/**
* Constructor
*/
public function __construct() {
$this->id = self::GATEWAY_ID;
$this->method_title = __( 'Revolut Gateway - Revolut Pay', 'revolut-gateway-for-woocommerce' );
$this->tab_title = __( 'Revolut Pay', 'revolut-gateway-for-woocommerce' );
$this->default_title = __( 'Revolut Pay', 'revolut-gateway-for-woocommerce' );
/* translators:%1s: %$2s: */
$this->method_description = sprintf( __( 'Accept payments easily and securely via %1$sRevolut%2$s.', 'revolut-gateway-for-woocommerce' ), '<a href="https://www.revolut.com/business/online-payments">', '</a>' );
$this->title = __( 'Revolut Pay', 'revolut-gateway-for-woocommerce' );
$this->description = $this->get_option( 'description' );
parent::__construct();
if ( get_option( 'woocommerce_revolut_pay_settings' ) === false ) {
$this->add_default_options();
}
$this->activate_default_express_checkout();
if ( ! $this->is_revolut_cc_gateway_active() ) {
$this->init_scripts();
}
add_action( 'wp_loaded', array( $this, 'check_revolut_pay_payment_result' ) );
add_filter( 'wc_revolut_settings_nav_tabs', array( $this, 'admin_nav_tab' ), 3 );
add_action( 'wp_enqueue_scripts', array( $this, 'wc_revolut_pay_enqueue_scripts' ) );
add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_payment_request_button_html' ), 1 );
add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_payment_request_button_html' ), 1 );
add_action( 'wc_ajax_revolut_payment_request_load_order_data', array( $this, 'revolut_payment_request_ajax_load_order_data' ) );
add_action( 'wc_ajax_revolut_payment_request_get_express_checkout_params', array( $this, 'revolut_payment_request_ajax_get_express_checkout_params' ) );
}
/**
* Get express checkout params
*/
public function check_revolut_pay_payment_result() {
if ( empty( $this->get_request_data( '_rp_oid' ) ) ) {
return;
}
$public_id = $this->get_request_data( '_rp_oid' );
global $wpdb;
$wc_order_id = $wpdb->get_row( $wpdb->prepare( 'SELECT wc_order_id, HEX(order_id) as order_id FROM ' . $wpdb->prefix . "wc_revolut_orders WHERE public_id=UNHEX(REPLACE(%s, '-', ''))", array( $public_id ) ), ARRAY_A ); // db call ok; no-cache ok.
$revolut_order_id = $this->uuid_dashes( $wc_order_id['order_id'] );
if ( empty( $revolut_order_id ) || $this->is_pending_payment( $revolut_order_id ) ) {
return;
}
if ( empty( $wc_order_id ) || empty( $wc_order_id['wc_order_id'] ) ) {
// check if fast checkout.
if ( ! empty( $wc_order_id['order_id'] ) ) {
$temp_session = $wpdb->get_row( $wpdb->prepare( 'SELECT temp_session FROM ' . $wpdb->prefix . 'wc_revolut_temp_session WHERE order_id=%s', array( $this->uuid_dashes( $wc_order_id['order_id'] ) ) ), ARRAY_A ); // db call ok; no-cache ok.
if ( ! empty( $temp_session ) ) {
$this->log_error( 'order processing FC - public_id: ' . $public_id );
return $this->process_revolut_pay_fc_payment( $public_id );
}
}
return;
}
$wc_order_id = $wc_order_id['wc_order_id'];
$wc_order = wc_get_order( $wc_order_id );
if ( empty( $wc_order->get_id() ) ) {
return;
}
$this->log_error( 'order processing - public_id: ' . $public_id . ' - wc_order_id: ' . $wc_order_id );
$_POST['revolut_public_id'] = $public_id;
$_POST['revolut_pay_redirected'] = 1;
$this->process_payment( $wc_order_id );
}
/**
* Process Revolut Pay Fast Checkout payment
*
* @param string $revolut_public_id Revolut order public id.
* @throws Exception Exception.
*/
public function process_revolut_pay_fc_payment( $revolut_public_id ) {
try {
if ( ! empty( $this->get_request_data( '_rp_fr' ) ) ) {
wc_add_notice( $this->get_request_data( '_rp_fr' ), 'error' );
return;
}
$order_data = $this->load_order_data( $revolut_public_id );
$revolut_order_id = $this->get_revolut_order_by_public_id( $revolut_public_id );
if ( empty( $order_data ) ) {
return;
}
$address = $order_data['address_info'];
$selected_shipping_option = $order_data['selected_shipping_option'];
if ( WC()->cart->is_empty() ) {
$this->convert_revolut_order_metadata_into_wc_session( $revolut_order_id );
}
if ( WC()->cart->is_empty() ) {
throw new Exception( 'Cannot initialize cart' );
}
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
$wc_order_data = $this->format_wc_order_details(
$address,
WC()->cart->needs_shipping(),
self::GATEWAY_ID
);
$wc_order_data['shipping_method'] = array( $selected_shipping_option );
$wc_order_data['revolut_create_wc_order'] = 1;
$wc_order_data['revolut_pay_redirected'] = 1;
$wc_order_data['is_express_checkout'] = 1;
$wc_order_data['payment_method'] = self::GATEWAY_ID;
$wc_order_data['revolut_public_id'] = $revolut_public_id;
$_POST = $wc_order_data;
$_POST['_wpnonce'] = wp_create_nonce( 'woocommerce-process_checkout' );
$_REQUEST['_wpnonce'] = $_POST['_wpnonce']; // phpcs:ignore
WC()->checkout()->process_checkout();
} catch ( Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
}
}
/**
* Get express checkout params
*/
public function revolut_payment_request_ajax_get_express_checkout_params() {
check_ajax_referer( 'wc-revolut-get-express-checkout-params', 'security' );
try {
wp_send_json(
array(
'success' => true,
'revolut_public_id' => $this->create_express_checkout_public_id(),
'checkout_nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
)
);
} catch ( Exception $e ) {
wp_send_json( array( 'success' => false ) );
$this->log_error( $e );
}
}
/**
* Load express checkout information from api
*/
public function activate_default_express_checkout() {
try {
if ( 'yes' !== $this->get_option( 'revolut_pay_express_checkout_activate_default' ) && 'yes' === $this->get_option( 'enabled' ) && empty( $this->get_option( 'revolut_pay_button_locations' ) ) && $this->is_revolut_payment_request_gateway_active() ) {
$this->update_option( 'revolut_pay_button_locations', array( 'product', 'cart' ) );
}
$this->update_option( 'revolut_pay_express_checkout_activate_default', 'yes' );
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
}
}
/**
* Load express checkout information from api
*
* @param string $revolut_public_id Revolut order public id.
*/
public function load_order_data( $revolut_public_id ) {
try {
$order_id = $this->get_revolut_order_by_public_id( $revolut_public_id );
$revolut_order = $this->api_client->get( "/orders/{$order_id }" );
$customer_id = $revolut_order['customer_id'];
$shipping_address = $revolut_order['shipping_address'];
$customer = $this->api_client->get( '/customers/' . $customer_id );
$this->log_info( 'load_order_data' );
$this->log_info( $customer );
$this->log_info( $shipping_address );
$address_info['fullname'] = ! empty( $customer['full_name'] ) ? $customer['full_name'] : '';
$address_info['email'] = ! empty( $customer['email'] ) ? $customer['email'] : '';
$address_info['phone'] = ! empty( $customer['phone'] ) ? $customer['phone'] : '';
$shipping_address['recipient'] = $address_info['fullname'];
$shipping_address['phone'] = $address_info['phone'];
$shipping_address = $this->convert_revolut_address_to_express_checkout_address( $shipping_address );
$address_info['shippingAddress'] = $shipping_address;
$address_info['billingAddress'] = $address_info['shippingAddress'];
$selected_shipping_option = 0;
if ( ! empty( $revolut_order['delivery_method'] ) ) {
$selected_shipping_option = $revolut_order['delivery_method']['ref'];
$this->get_shipping_options( $shipping_address );
$this->update_shipping_method( array( $selected_shipping_option ) );
}
return array(
'address_info' => $address_info,
'selected_shipping_option' => $selected_shipping_option,
);
} catch ( Exception $e ) {
return array();
}
}
/**
* Load express checkout information from api
*/
public function revolut_payment_request_ajax_load_order_data() {
try {
check_ajax_referer( 'wc-revolut-load-order-data', 'security' );
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
wp_send_json(
$this->load_order_data( $revolut_public_id )
);
} catch ( Exception $e ) {
wp_send_json( array( 'success' => false ) );
$this->log_info( 'load_order_data_error:' );
$this->log_error( $e );
}
}
/**
* Display payment request button html
*/
public function display_payment_request_button_html() {
if ( 'yes' !== $this->enabled || ! $this->page_supports_payment_request_button( $this->get_option( 'revolut_pay_button_locations' ) ) || ! $this->is_shipping_required() ) {
return false;
}
if ( ! $this->is_revolut_payment_request_gateway_active() ) {
?>
<div class="wc-revolut-pay-express-checkout-instance" id="wc-revolut-pay-express-checkout-container" style="clear:both;padding-top:1.5em;">
<div id="revolut-pay-express-checkout-button"></div>
<p id="wc-revolut-pay-express-checkout-button-separator" style="text-align:center;margin-bottom:1.5em;">&mdash;&nbsp;<?php echo esc_html( __( 'OR', 'revolut-gateway-for-woocommerce' ) ); ?>
&nbsp;&mdash;</p>
</div>
<?php
return;
}
?>
<div class="wc-revolut-pay-express-checkout-instance" id="wc-revolut-pay-express-checkout-container" style="clear:both;padding-top:1.5em;">
<div id="revolut-pay-express-checkout-button"></div>
</div>
<?php
}
/**
* Add script to load card form
*/
public function wc_revolut_pay_enqueue_scripts() {
wp_localize_script(
'revolut-woocommerce',
'revolut_pay_button_style',
array(
'revolut_pay_button_theme' => $this->get_option( 'revolut_pay_button_theme' ),
'revolut_pay_button_size' => $this->get_option( 'revolut_pay_button_size' ),
'revolut_pay_button_radius' => $this->get_option( 'revolut_pay_button_radius' ),
'revolut_pay_origin_url' => str_replace( array( 'https://', 'http://' ), '', get_site_url() ),
)
);
if ( 'yes' !== $this->enabled || ! $this->page_supports_payment_request_button( $this->get_option( 'revolut_pay_button_locations' ) ) || ! $this->is_shipping_required() ) {
return false;
}
wp_register_script( 'revolut-core', $this->api_client->base_url . '/embed.js', false, WC_GATEWAY_REVOLUT_VERSION, true );
wp_register_script(
'revolut-woocommerce-payment-request',
plugins_url( 'assets/js/revolut-payment-request.js', WC_REVOLUT_MAIN_FILE ),
array(
'revolut-core',
'jquery',
),
WC_GATEWAY_REVOLUT_VERSION,
true
);
wp_localize_script(
'revolut-woocommerce-payment-request',
'wc_revolut_payment_request_params',
$this->get_wc_revolut_payment_request_params()
);
wp_enqueue_script( 'revolut-woocommerce-payment-request' );
}
/**
* Check if card payments is active.
*/
public function is_revolut_cc_gateway_active() {
$revolut_cc_gateway_options = get_option( 'woocommerce_revolut_cc_settings' );
return isset( $revolut_cc_gateway_options['enabled'] ) && 'yes' === $revolut_cc_gateway_options['enabled'];
}
/**
* Check if the request payments active.
*/
public function is_revolut_payment_request_gateway_active() {
$woocommerce_revolut_payment_request_settings = get_option( 'woocommerce_revolut_payment_request_settings' );
return isset( $woocommerce_revolut_payment_request_settings['enabled'] ) && 'yes' === $woocommerce_revolut_payment_request_settings['enabled'] && $this->api_settings->get_option( 'mode' ) !== 'sandbox';
}
/**
* Supported functionality
*/
public function init_supports() {
parent::init_supports();
$this->supports[] = 'refunds';
}
/**
* Initialize Gateway Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Enable ', 'revolut-gateway-for-woocommerce' ) . $this->method_title,
'type' => 'checkbox',
'description' => __( 'This controls whether or not this gateway is enabled within WooCommerce.', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'revolut_pay_button_locations' => array(
'title' => __( 'Revolut Pay Express Checkout', 'revolut-gateway-for-woocommerce' ),
'type' => 'multiselect',
'description' => __( 'Select where you would like Revolut Pay Button to be displayed as express checkout button', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'class' => 'wc-enhanced-select',
'options' => array(
'product' => __( 'Product', 'revolut-gateway-for-woocommerce' ),
'cart' => __( 'Cart', 'revolut-gateway-for-woocommerce' ),
),
'default' => array(),
'custom_attributes' => array(
'data-placeholder' => __( 'Select pages', 'revolut-gateway-for-woocommerce' ),
),
),
'revolut_pay_button_theme' => array(
'title' => __( 'Revolut Pay Button Theme', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Theme', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button theme you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'dark',
'desc_tip' => true,
'options' => array(
'dark' => __( 'Dark', 'revolut-gateway-for-woocommerce' ),
'light' => __( 'Light', 'revolut-gateway-for-woocommerce' ),
'light-outlined' => __( 'Light-Outline', 'revolut-gateway-for-woocommerce' ),
),
),
'revolut_pay_button_size' => array(
'title' => __( 'Revolut Pay Button Size', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Size', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button size you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'large',
'desc_tip' => true,
'options' => array(
'large' => __( 'Large', 'revolut-gateway-for-woocommerce' ),
'small' => __( 'Small', 'revolut-gateway-for-woocommerce' ),
),
),
'revolut_pay_button_radius' => array(
'title' => __( 'Revolut Pay Button Radius', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Radius', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button radius you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'none',
'desc_tip' => true,
'options' => array(
'small' => __( 'Small', 'revolut-gateway-for-woocommerce' ),
'large' => __( 'Large', 'revolut-gateway-for-woocommerce' ),
'none' => __( 'None', 'revolut-gateway-for-woocommerce' ),
),
),
);
}
/**
* Display Revolut Pay icon
*/
public function get_icon() {
$icons_str = '';
$icons_str .= '<img class="revolut-card-gateway-icon-amex" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/amex.svg" style="margin-left:2px" alt="Amex" />';
$icons_str .= '<img class="revolut-card-gateway-icon-visa" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/visa.svg" style="margin-left:2px" alt="Visa" />';
$icons_str .= '<img class="revolut-card-gateway-icon-mastercard" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/mastercard.svg" style="margin-left:2px" alt="MasterCard" />';
$icons_str .= '<img class="rev-pay-v2" src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/revolut.svg" alt="Revolut Pay" />';
return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
}
/**
* Add public_id field and logo on card form
*
* @param String $public_id Revolut public id.
* @param String $merchant_public_key Revolut public key.
* @param String $display_tokenization Available saved card tokens.
*
* @return string
*/
public function generate_inline_revolut_form( $public_id, $merchant_public_key, $display_tokenization ) {
$total = WC()->cart->get_total( '' );
if ( $this->check_is_get_data_submitted( 'pay_for_order' ) && ! empty( $this->get_request_data( 'key' ) ) ) {
global $wp;
$order = wc_get_order( wc_clean( $wp->query_vars['order-pay'] ) );
$total = $order->get_total();
}
$currency = get_woocommerce_currency();
$total = $this->get_revolut_order_total( $total, $currency );
$mode = $this->api_settings->get_option( 'mode' );
$shipping_total = $this->get_cart_total_shipping();
$mobile_redirect_url = wc_get_checkout_url();
$available_card_brands = $this->get_available_card_brands( $this->get_revolut_public_id() );
$revolut_pay_v2_class_indicator = '';
if ( ! empty( $merchant_public_key ) ) {
$revolut_pay_v2_class_indicator = 'revolut-pay-v2';
}
return '<div id="woocommerce-revolut-pay-element" class="revolut-pay ' . $revolut_pay_v2_class_indicator . '" data-available-card-brands="' . $available_card_brands . '" data-redirect-url = "' . $mobile_redirect_url . '" data-mode="' . $mode . '" data-shipping-total="' . $shipping_total . '" data-currency="' . $currency . '" data-total="' . $total . '" data-textcolor="" data-locale="' . $this->get_lang_iso_code() . '" data-public-id="' . $public_id . '" data-merchant-public-key="' . $merchant_public_key . '"></div>
<input type="hidden" id="wc_' . $this->id . '_payment_nonce" name="wc_' . $this->id . '_payment_nonce" />';
}
}

View File

@@ -0,0 +1,634 @@
<?php
/**
* Revolut Payment Request
*
* Provides a gateway to accept payments through Apple and Google Pay.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 3.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Gateway_Revolut_Payment_Request class.
*/
class WC_Gateway_Revolut_Payment_Request extends WC_Payment_Gateway_Revolut {
const GATEWAY_ID = 'revolut_payment_request';
/**
* Constructor
*/
public function __construct() {
$this->id = self::GATEWAY_ID;
$this->method_title = __( 'Apple Pay / Google Pay', 'revolut-gateway-for-woocommerce' );
/* translators:%1s: %$2s: */
$this->method_description = sprintf( __( 'Accept Apple and Google payments easily and securely via %1$sRevolut%2$s.', 'revolut-gateway-for-woocommerce' ), '<a href="https://www.revolut.com/business/online-payments">', '</a>' );
$this->tab_title = __( 'Apple Pay / Google Pay', 'revolut-gateway-for-woocommerce' );
$this->title = __( 'Digital Wallet (ApplePay/GooglePay)', 'revolut-gateway-for-woocommerce' );
parent::__construct();
add_filter( 'wc_revolut_settings_nav_tabs', array( $this, 'admin_nav_tab' ), 2 );
add_action( 'wc_ajax_revolut_payment_request_add_to_cart', array( $this, 'revolut_payment_request_ajax_add_to_cart' ) );
add_action( 'wc_ajax_revolut_payment_request_get_shipping_options', array( $this, 'revolut_payment_request_ajax_get_shipping_options' ) );
add_action( 'wc_ajax_revolut_payment_request_update_shipping_method', array( $this, 'revolut_payment_request_ajax_update_shipping_method' ) );
add_action( 'wc_ajax_revolut_payment_request_update_payment_total', array( $this, 'revolut_payment_request_update_revolut_order_with_cart_total' ) );
add_action( 'wc_ajax_revolut_payment_request_create_order', array( $this, 'revolut_payment_request_ajax_create_order' ) );
add_action( 'wc_ajax_revolut_payment_request_get_payment_request_params', array( $this, 'revolut_payment_request_ajax_get_payment_request_params' ) );
if ( 'yes' !== $this->enabled || $this->api_settings->get_option( 'mode' ) === 'sandbox' ) {
return;
}
add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_payment_request_button_html' ), 2 );
add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_payment_request_button_html' ), 2 );
add_action( 'wp_enqueue_scripts', array( $this, 'revolut_enqueue_payment_request_scripts' ) );
}
/**
* Get express checkout params
*/
public function revolut_payment_request_ajax_get_payment_request_params() {
check_ajax_referer( 'wc-revolut-get-payment-request-params', 'security' );
try {
wp_send_json(
array(
'success' => true,
'revolut_public_id' => $this->create_express_checkout_public_id(),
'checkout_nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
)
);
} catch ( Exception $e ) {
wp_send_json( array( 'success' => false ) );
$this->log_error( $e );
}
}
/**
* Add required scripts
*/
public function revolut_enqueue_payment_request_scripts() {
try {
wp_localize_script(
'revolut-woocommerce',
'revolut_payment_request_button_style',
array(
'payment_request_button_title' => $this->get_option( 'title' ),
'payment_request_button_type' => $this->get_option( 'payment_request_button_type' ),
'payment_request_button_theme' => $this->get_option( 'payment_request_button_theme' ),
'payment_request_button_radius' => $this->get_option( 'payment_request_button_radius' ),
'payment_request_button_size' => $this->get_option( 'payment_request_button_size' ),
)
);
if ( ! $this->page_supports_payment_request_button( $this->get_option( 'payment_request_button_locations' ) ) ) {
return false;
}
wp_register_script( 'revolut-core', $this->api_client->base_url . '/embed.js', false, WC_GATEWAY_REVOLUT_VERSION, true );
wp_register_script(
'revolut-woocommerce-payment-request',
plugins_url( 'assets/js/revolut-payment-request.js', WC_REVOLUT_MAIN_FILE ),
array(
'revolut-core',
'jquery',
),
WC_GATEWAY_REVOLUT_VERSION,
true
);
wp_localize_script(
'revolut-woocommerce-payment-request',
'wc_revolut_payment_request_params',
$this->get_wc_revolut_payment_request_params()
);
wp_enqueue_script( 'revolut-woocommerce-payment-request' );
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
}
}
/**
* Check if the Revolut Pay Fast checkout payments active.
*/
public function is_revolut_pay_fast_checkout_active() {
$revolut_cc_gateway_options = get_option( 'woocommerce_revolut_pay_settings' );
return isset( $revolut_cc_gateway_options['revolut_pay_button_locations'] ) && $this->page_supports_payment_request_button( $revolut_cc_gateway_options['revolut_pay_button_locations'] ) && $this->is_shipping_required();
}
/**
* Update Revolut order with cart total amount
*
* @param string $revolut_public_id Revolut public id.
*
* @param bool $is_revolut_pay indicator.
*
* @throws Exception Exception.
*/
public function update_revolut_order_with_cart_total( $revolut_public_id, $is_revolut_pay = false ) {
$revolut_order_id = $this->get_revolut_order_by_public_id( $revolut_public_id );
$revolut_order = $this->api_client->get( "/orders/$revolut_order_id" );
$revolut_order_shipping_total = $this->get_revolut_order_total_shipping( $revolut_order );
$cart_subtotal = round( (float) ( WC()->cart->get_subtotal() + WC()->cart->get_subtotal_tax() + $revolut_order_shipping_total ), 2 );
$this->log_info(
array(
'is_revolut_pay' => $is_revolut_pay,
'cart_total_without_shipping' => $cart_subtotal,
'cart_total' => WC()->cart->get_total( '' ),
'wc_shipping_total' => WC()->cart->get_shipping_total(),
'revolut_shipping_total' => $revolut_order_shipping_total,
)
);
$descriptor = new WC_Revolut_Order_Descriptor( $is_revolut_pay ? $cart_subtotal : WC()->cart->get_total( '' ), get_woocommerce_currency(), null );
$public_id = $this->update_revolut_order( $descriptor, $revolut_public_id, true );
if ( $public_id !== $revolut_public_id ) {
throw new Exception( 'Can not update the Order' );
}
$revolut_order_id = $this->get_revolut_order_by_public_id( $public_id );
$revolut_order = $this->api_client->get( "/orders/$revolut_order_id" );
$this->log_info( 'revolut_order' );
$this->log_info( $revolut_order );
return $this->get_revolut_order_amount( $revolut_order );
}
/**
* Check is payment method available
*/
public function is_available() {
if ( ( 'yes' === $this->enabled && is_product() ) || ( $this->check_is_post_data_submitted( 'payment_method' ) && $this->get_post_request_data( 'payment_method' ) === $this->id ) ) {
return true;
}
$payment_request_button_locations = $this->get_option( 'payment_request_button_locations' );
if ( empty( $payment_request_button_locations ) ) {
$payment_request_button_locations = array();
}
if ( is_checkout() ) {
return 'yes' === $this->enabled && in_array( 'checkout', $payment_request_button_locations, true ) && ! $this->api_settings->is_sandbox();
}
return false;
}
/**
* Initialize Gateway Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'revolut-gateway-for-woocommerce' ),
'label' => sprintf(
/* translators:%1s: %$2s: %3$s: %4$s: */
__( 'Enable Payment Request Buttons. (Apple Pay/Google Pay) %1$sBy using Apple Pay, you agree with %2$sApple\'s%3$s terms of service. (Apple Pay domain verification is performed automatically in live mode.) %4$s', 'revolut-gateway-for-woocommerce' ),
'<br />',
'<a href="https://www.revolut.com/legal/payment-terms-applepay" target="_blank">',
'</a>',
$this->check_authentication_required() ? '<br /> <p style="color:red">' . __( 'Payment with Apple/Google Pay buttons wont be possible for guest users until Guest checkout is enabled', 'revolut-gateway-for-woocommerce' ) . '</p>' : ''
),
'type' => 'checkbox',
'description' => __( 'If enabled, users will be able to pay using Apple Pay (Safari & iOS), Google Pay (Chrome & Android) or native W3C Payment Requests if supported by the browser.', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'title' => array(
'title' => __( 'Title', 'revolut-gateway-for-woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title that the user sees during checkout. Plugin will add the button\'s name (Apple Pay or Google Pay) before this title.', 'revolut-gateway-for-woocommerce' ),
'default' => '(via Revolut)',
'desc_tip' => true,
),
'payment_request_button_type' => array(
'title' => __( 'Payment Request Button Action', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Action', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button type you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'buy',
'desc_tip' => true,
'options' => array(
'buy' => __( 'Buy', 'revolut-gateway-for-woocommerce' ),
'donate' => __( 'Donate', 'revolut-gateway-for-woocommerce' ),
'pay' => __( 'Pay', 'revolut-gateway-for-woocommerce' ),
),
),
'payment_request_button_theme' => array(
'title' => __( 'Payment Request Button Theme', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Theme', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button theme you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'dark',
'desc_tip' => true,
'options' => array(
'dark' => __( 'Dark', 'revolut-gateway-for-woocommerce' ),
'light' => __( 'Light', 'revolut-gateway-for-woocommerce' ),
'light-outlined' => __( 'Light-Outline', 'revolut-gateway-for-woocommerce' ),
),
),
'payment_request_button_radius' => array(
'title' => __( 'Payment Request Button Radius', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Radius', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button radius you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'none',
'desc_tip' => true,
'options' => array(
'none' => __( 'None', 'revolut-gateway-for-woocommerce' ),
'small' => __( 'Small', 'revolut-gateway-for-woocommerce' ),
'large' => __( 'Large', 'revolut-gateway-for-woocommerce' ),
),
),
'payment_request_button_size' => array(
'title' => __( 'Payment Request Button Size', 'revolut-gateway-for-woocommerce' ),
'label' => __( 'Button Size', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select the button size you would like to show.', 'revolut-gateway-for-woocommerce' ),
'default' => 'large',
'desc_tip' => true,
'options' => array(
'small' => __( 'Small', 'revolut-gateway-for-woocommerce' ),
'large' => __( 'Large', 'revolut-gateway-for-woocommerce' ),
),
),
'payment_request_button_locations' => array(
'title' => __( 'Payment Request Button Locations', 'revolut-gateway-for-woocommerce' ),
'type' => 'multiselect',
'description' => __( 'Select where you would like Payment Request Buttons to be displayed', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'class' => 'wc-enhanced-select',
'options' => array(
'product' => __( 'Product', 'revolut-gateway-for-woocommerce' ),
'cart' => __( 'Cart', 'revolut-gateway-for-woocommerce' ),
'checkout' => __( 'Checkout', 'revolut-gateway-for-woocommerce' ),
),
'default' => array( 'product', 'cart' ),
'custom_attributes' => array(
'data-placeholder' => __( 'Select pages', 'revolut-gateway-for-woocommerce' ),
),
),
);
if ( $this->get_option( 'apple_pay_merchant_onboarded' ) === 'no' ) {
$this->form_fields['onboard_applepay'] = array(
'title' => __( 'Onboard shop domain for Apple Pay', 'revolut-gateway-for-woocommerce' ),
'type' => 'text',
'description' => '<button class="setup-applepay-domain" style="min-height: 30px;">Setup</button>
<p class="setup-applepay-domain-error" style="color:red;display: none"></p>
<p>Seems that there is a problem automatically onboarding your website for Apple Pay. You can also set this up manually by downloading this <a href="https://assets.revolut.com/api-docs/merchant-api/files/domain_validation_file_prod">file</a> and adding it to the root folder of your shop with the following uri <code>/.well-known/apple-developer-merchantid-domain-association</code>. To learn more about how to do this, visit our <a href="https://developer.revolut.com/docs/accept-payments/plugins/woocommerce/features#set-up-apple-pay-manually" target="_blank">documentation</a></p>',
);
}
}
/**
* Supported functionality
*/
public function init_supports() {
parent::init_supports();
$this->supports[] = 'refunds';
}
/**
* Display payment request button html
*/
public function display_payment_request_button_html() {
if ( ! $this->page_supports_payment_request_button( $this->get_option( 'payment_request_button_locations' ) ) ) {
return false;
}
if ( $this->is_revolut_pay_fast_checkout_active() ) {
?>
<div class="wc-revolut-payment-request-instance" id="wc-revolut-payment-request-container" style="clear:both;">
<div id="revolut-payment-request-button"></div>
<p id="wc-revolut-payment-request-button-separator" style="margin-top:1.5em;text-align:center;">&mdash;&nbsp;<?php echo esc_html( __( 'OR', 'revolut-gateway-for-woocommerce' ) ); ?>
&nbsp;&mdash;</p>
</div>
<?php
return;
}
?>
<div class="wc-revolut-payment-request-instance" id="wc-revolut-payment-request-container" style="clear:both;padding-top:1.5em;">
<div id="revolut-payment-request-button"></div>
<p id="wc-revolut-payment-request-button-separator" style="margin-top:1.5em;text-align:center;">&mdash;&nbsp;<?php echo esc_html( __( 'OR', 'revolut-gateway-for-woocommerce' ) ); ?>
&nbsp;&mdash;</p>
</div>
<?php
}
/**
* Ajax endpoint in order to create WooCommerce order
*/
public function revolut_payment_request_ajax_create_order() {
if ( WC()->cart->is_empty() ) {
wp_send_json_error( __( 'Empty cart', 'revolut-gateway-for-woocommerce' ) );
}
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
$errors = new WP_Error();
try {
$wc_order_data = $this->get_wc_order_details();
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
$errors->add( 'payment', __( 'Something went wrong', 'woocommerce' ) );
if ( ! empty( $this->get_post_request_data( 'revolut_payment_error' ) ) ) {
$errors->add( 'payment', $this->get_post_request_data( 'revolut_payment_error' ) );
$this->log_error( $this->get_post_request_data( 'revolut_payment_error' ) );
}
}
foreach ( $errors->errors as $code => $messages ) {
$data = $errors->get_error_data( $code );
foreach ( $messages as $message ) {
wc_add_notice( $message, 'error', $data );
}
}
if ( 0 === wc_notice_count( 'error' ) ) {
$_POST = array_merge( $_POST, $wc_order_data ); // phpcs:ignore
unset( $_POST['address_info'] ); // phpcs:ignore
$_POST['_wpnonce'] = wp_create_nonce( 'woocommerce-process_checkout' );
WC()->checkout()->process_checkout();
}
$messages = wc_print_notices( true );
wp_send_json(
array(
'result' => 'failure',
'messages' => $messages,
)
);
}
/**
* Get order details
*
* @throws Exception Exception.
*/
public function get_wc_order_details() {
$public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $public_id ) ) {
throw new Exception( 'Public ID is missing for the session' );
}
$order_id = $this->get_revolut_order_by_public_id( $public_id );
if ( empty( $order_id ) ) {
throw new Exception( 'Can not find revolut order id' );
}
$address_info = $this->get_post_request_data( 'address_info' );
if ( empty( $address_info ) ) {
throw new Exception( 'Address information is missing' );
}
return $this->format_wc_order_details(
$this->get_post_request_data( 'address_info' ),
$this->get_posted_integer_data( 'shipping_required' ),
$this->get_post_request_data( 'revolut_gateway' )
);
}
/**
* Ajax endpoint for adding product to cart.
*
* @throws Exception Exception.
*/
public function revolut_payment_request_ajax_add_to_cart() {
try {
check_ajax_referer( 'wc-revolut-pr-add-to-cart', 'security' );
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $revolut_public_id ) ) {
throw new Exception( 'Can not get required parameter' );
}
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
WC()->shipping->reset_shipping();
$product_id = $this->get_posted_integer_data( 'product_id' );
$is_revolut_pay = $this->get_posted_integer_data( 'is_revolut_pay' );
$qty = ! $this->check_is_post_data_submitted( 'qty' ) ? 1 : $this->get_posted_integer_data( 'qty' );
$product = wc_get_product( $product_id );
$product_type = $product->get_type();
$global_cart = WC()->cart;
if ( ! $this->get_posted_integer_data( 'add_to_cart' ) ) {
WC()->cart = clone WC()->cart;
}
WC()->cart->empty_cart();
if ( 'simple' === $product_type || 'subscription' === $product_type ) {
WC()->cart->add_to_cart( $product->get_id(), $qty );
} elseif ( $this->check_is_post_data_submitted( 'attributes' ) && ( 'variable' === $product_type || 'variable-subscription' === $product_type ) ) {
$attributes = $this->get_post_request_data( 'attributes' );
$data_store = WC_Data_Store::load( 'product' );
$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
}
WC()->shipping->reset_shipping();
WC()->cart->calculate_totals();
$total_amount = $this->update_revolut_order_with_cart_total( $revolut_public_id, $is_revolut_pay );
$is_cart_empty = ! WC()->cart->is_empty();
if ( ! $this->get_posted_integer_data( 'add_to_cart' ) ) {
WC()->cart = $global_cart;
}
$data['total']['amount'] = $total_amount;
$data['checkout_nonce'] = wp_create_nonce( 'woocommerce-process_checkout' );
$data['status'] = 'success';
$data['success'] = $is_cart_empty;
wp_send_json( $data );
} catch ( Exception $e ) {
$this->log_error( $e );
$data['status'] = 'fail';
$data['success'] = false;
wp_send_json( $data );
}
}
/**
* Ajax endpoint for listing shipping options
*
* @throws Exception Exception.
*/
public function revolut_payment_request_ajax_get_shipping_options() {
check_ajax_referer( 'wc-revolut-payment-request-shipping', 'security' );
try {
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $revolut_public_id ) ) {
throw new Exception( 'Can not get required parameter' );
}
$shipping_address = filter_input_array(
INPUT_POST,
array(
'country' => FILTER_SANITIZE_STRING,
'state' => FILTER_SANITIZE_STRING,
'postcode' => FILTER_SANITIZE_STRING,
'city' => FILTER_SANITIZE_STRING,
'address' => FILTER_SANITIZE_STRING,
'address_2' => FILTER_SANITIZE_STRING,
)
);
$shipping_options = $this->get_shipping_options( $shipping_address );
if ( count( $shipping_options ) > 0 ) {
$this->update_shipping_method( $shipping_options[0] );
}
$total_amount = $this->update_revolut_order_with_cart_total( $revolut_public_id );
$data['success'] = true;
$data['status'] = 'success';
$data['total']['amount'] = $total_amount;
$data['shippingOptions'] = $shipping_options;
wp_send_json( $data );
} catch ( Exception $e ) {
$this->log_error( $e );
$data['status'] = 'fail';
$data['success'] = false;
$data['total']['amount'] = 0;
$data['shippingOptions'] = array();
wp_send_json( $data );
}
}
/**
* Ajax endpoint for updating shipping options
*
* @throws Exception Exception.
*/
public function revolut_payment_request_ajax_update_shipping_method() {
check_ajax_referer( 'wc-revolut-update-shipping-method', 'security' );
try {
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $revolut_public_id ) ) {
throw new Exception( 'Can not get required parameter' );
}
$shipping_method = filter_input( INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$this->update_shipping_method( $shipping_method );
WC()->cart->calculate_totals();
$total_amount = $this->update_revolut_order_with_cart_total( $revolut_public_id );
$data['success'] = true;
$data['status'] = 'success';
$data['total']['amount'] = $total_amount;
wp_send_json( $data );
} catch ( Exception $e ) {
$this->log_error( $e );
$data['status'] = 'fail';
$data['success'] = false;
$data['total']['amount'] = 0;
wp_send_json( $data );
}
}
/**
* Ajax endpoint for updating shipping options
*
* @throws Exception Exception.
*/
public function revolut_payment_request_update_revolut_order_with_cart_total() {
check_ajax_referer( 'wc-revolut-update-order-total', 'security' );
try {
$revolut_public_id = $this->get_post_request_data( 'revolut_public_id' );
if ( empty( $revolut_public_id ) ) {
throw new Exception( 'Can not get required parameter' );
}
WC()->cart->calculate_totals();
$total_amount = $this->update_revolut_order_with_cart_total( $revolut_public_id, true );
$data['success'] = true;
$data['status'] = 'success';
$data['total']['amount'] = $total_amount;
wp_send_json( $data );
} catch ( Exception $e ) {
$this->log_error( $e );
$data['status'] = 'fail';
$data['success'] = false;
$data['total']['amount'] = 0;
wp_send_json( $data );
}
}
/**
* Display Revolut Pay icon
*/
public function get_icon() {
$icons_str = '';
$icons_str .= '<img src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/apple-pay-logo.svg" class="revolut-apple-pay-logo" style="max-width:50px;display:none" alt="Apple Pay" />';
$icons_str .= '<img src="' . WC_REVOLUT_PLUGIN_URL . '/assets/images/g-pay-logo.png" class="revolut-google-pay-logo" style="max-width:50px;display:none" alt="Google Pay" />';
return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
}
/**
* Add public_id field and logo on card form
*
* @param String $public_id Revolut public id.
* @param String $merchant_public_key Revolut public key.
* @param String $display_tokenization Available saved card tokens.
*
* @return string
*/
public function generate_inline_revolut_form( $public_id, $merchant_public_key, $display_tokenization ) {
$total = WC()->cart->get_total( '' );
$currency = get_woocommerce_currency();
$total = $this->get_revolut_order_total( $total, $currency );
$mode = $this->api_settings->get_option( 'mode' );
$shipping_total = $this->get_cart_total_shipping();
return '<div id="woocommerce-revolut-payment-request-element" class="revolut-payment-request" data-mode="' . $mode . '" data-shipping-total="' . $shipping_total . '" data-currency="' . $currency . '" data-total="' . $total . '" data-textcolor="" data-locale="' . $this->get_lang_iso_code() . '" data-public-id="' . $public_id . '" data-merchant-public-key="' . $merchant_public_key . '"></div>
<input type="hidden" id="wc_' . $this->id . '_payment_nonce" name="wc_' . $this->id . '_payment_nonce" />';
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* Revolut Api Settings
*
* Provides configuration for API settings
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
/**
* WC_Revolut_Settings_API class.
*/
class WC_Revolut_Advanced_Settings extends WC_Revolut_Settings_API {
/**
* Constructor
*/
public function __construct() {
$this->id = 'revolut_advanced_settings';
$this->tab_title = __( 'Advanced Settings', 'revolut-gateway-for-woocommerce' );
$this->init_form_fields();
$this->init_settings();
$this->hooks();
}
/**
* Add required filters
*/
public function hooks() {
add_action( 'woocommerce_settings_checkout', array( $this, 'admin_options' ) );
add_filter( 'wc_revolut_settings_nav_tabs', array( $this, 'admin_nav_tab' ), 10 );
add_action( 'woocommerce_update_options_checkout_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Displays configuration page with tabs
*/
public function admin_options() {
if ( $this->check_is_get_data_submitted( 'page' ) && $this->check_is_get_data_submitted( 'section' ) ) {
$is_revolut_api_section = 'wc-settings' === $this->get_request_data( 'page' ) && $this->id === $this->get_request_data( 'section' );
if ( $is_revolut_api_section ) {
echo wp_kses_post( '<table class="form-table">' );
$this->generate_settings_html( $this->get_form_fields(), true );
echo wp_kses_post( '</table>' );
}
}
}
/**
* Initialize Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'title' => array(
'type' => 'title',
'title' => __( 'Revolut Gateway - Advanced Settings', 'revolut-gateway-for-woocommerce' ),
),
'clear_unused_order_records' => array(
'title' => 'Clear unused orders now',
'type' => 'text',
'description' => '<button class="revolut_clear_unused_order_records" style="min-height: 30px;"><span id="span-for-active-button-sandbox">Clear</span></button><br><br><b>What is this?</b> The plugin creates a Revolut order every time a customer is attempting to pay. If you have fast checkout options active, this could mean that an order is created for every site visitor. If you have limited space on your database or you have a lot of site visitors, you might end up with a lot of unused orders. You can use this button to delete unused orders. WARNING: This could also delete orders that customers have not yet paid but have the intention to, so make sure that this is only used when there are no visitors on your website',
),
'consent_clear_unused_order_records' => array(
'title' => '',
'label' => __( 'By ticking this box I understand that unused order IDs stored in my websites database will be deleted. I understand as well that this is run at my own risk and could cause temporary issues with payments being failed.', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'no',
'class' => 'info_clear_unused_order_records',
),
);
}
}

View File

@@ -0,0 +1,483 @@
<?php
/**
* Revolut Api Settings
*
* Provides configuration for API settings
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
/**
* WC_Revolut_Settings_API class.
*/
class WC_Revolut_Settings_API extends WC_Settings_API {
use WC_Revolut_Settings_Trait;
/**
* Constructor
*/
public function __construct() {
$this->id = 'revolut';
$this->tab_title = __( 'API Settings', 'revolut-gateway-for-woocommerce' );
$this->init_form_fields();
$this->init_settings();
$this->hooks();
}
/**
* Add required filters
*/
public function hooks() {
add_filter( 'wc_revolut_settings_nav_tabs', array( $this, 'admin_nav_tab' ), 1 );
add_action( 'woocommerce_settings_checkout', array( $this, 'output_settings_nav' ) );
add_action( 'woocommerce_settings_checkout', array( $this, 'admin_options' ) );
add_action( 'admin_notices', array( $this, 'add_revolut_description' ) );
add_action( 'admin_notices', array( $this, 'check_api_key' ) );
add_action( 'admin_notices', array( $this, 'maybe_register_webhook' ) );
add_action( 'admin_notices', array( $this, 'maybe_register_synchronous_webhooks' ) );
add_action( 'woocommerce_update_options_checkout_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Initialize Settings Form Fields
*/
public function init_form_fields() {
$mode = $this->get_option( 'mode' );
$mode = empty( $mode ) ? 'sandbox' : $mode;
$api_key_sandbox = $this->get_option( 'api_key_sandbox' );
$api_key_dev = $this->get_option( 'api_key_dev' );
$api_key_live = $this->get_option( 'api_key' );
$this->form_fields = array(
'title' => array(
'type' => 'title',
'title' => __( 'Revolut Gateway - API Settings', 'revolut-gateway-for-woocommerce' ),
),
'mode' => array(
'title' => __( 'Select Mode', 'revolut-gateway-for-woocommerce' ),
'description' => __( 'Select mode between live mode and sandbox.', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'type' => 'select',
'default' => $mode,
'options' => array(
'sandbox' => __( 'Sandbox', 'revolut-gateway-for-woocommerce' ),
'live' => __( 'Live', 'revolut-gateway-for-woocommerce' ),
// phpcs:ignore
// 'dev' => __('Dev', 'revolut-gateway-for-woocommerce'),
),
),
'api_key_sandbox' => array(
'title' => __( 'Sandbox API secret key' ),
'description' => __( 'Sandbox API secret key from your Merchant settings on Revolut.', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'default' => $api_key_sandbox,
'type' => 'password',
'class' => 'enabled-sandbox',
),
// phpcs:ignore
// 'api_key_dev' => array(
// 'title' => __( 'API Key Dev' ),
// 'description' => __( 'API Key from your Merchant settings on Revolut.', 'revolut-gateway-for-woocommerce' ),
// 'desc_tip' => true,
// 'default' => $api_key_dev,
// 'type' => 'password',
// 'class' => 'enabled-sandbox',
// ),
'api_key' => array(
'title' => __( 'Production API secret key', 'revolut-gateway-for-woocommerce' ),
'type' => 'password',
'description' => __( 'Production API secret key from your Merchant settings on Revolut.', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'default' => $api_key_live,
'class' => 'enabled-live',
),
'payment_action' => array(
'title' => __( 'Payment Action', 'revolut-gateway-for-woocommerce' ),
'type' => 'select',
'default' => 'authorize_and_capture',
'options' => array(
'authorize' => __( 'Authorize Only', 'revolut-gateway-for-woocommerce' ),
'authorize_and_capture' => __( 'Authorize and Capture', 'revolut-gateway-for-woocommerce' ),
),
'description' => __(
'Select "Authorize Only" mode. This allows the payment to be captured up to 7 days after the user has placed the order (e.g. when the goods are shipped or received).
If not selected, Revolut will try to authorize and capture all payments.',
'revolut-gateway-for-woocommerce'
),
'desc_tip' => true,
),
'accept_capture' => array(
'title' => '',
'label' => __( 'Automatically capture order in Revolut', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Automatically try to capture orders when their status is changed.', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
),
'customise_capture_status' => array(
'title' => '',
'label' => __( 'Customize status to trigger capture.', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Default when checkbox not selected: Processing, Completed', 'revolut-gateway-for-woocommerce' ),
'default' => 'yes',
),
'selected_capture_status_list' => array(
'title' => '',
'type' => 'multiselect',
'description' => __( 'Order Status for triggering the payment capture on Revolut. Default: processing, completed', 'revolut-gateway-for-woocommerce' ),
'desc_tip' => true,
'class' => 'wc-enhanced-select',
'options' => wc_get_order_statuses(),
'default' => array(
'wc-processing' => 'Processing',
'wc-completed' => 'Completed',
),
'custom_attributes' => array(
'data-placeholder' => __( 'Select status', 'revolut-gateway-for-woocommerce' ),
),
),
'disable_banner' => array(
'title' => 'Banner Visibility',
'label' => __( 'Customers can get instructions to signup to Revolut and get rewarded.', 'revolut-gateway-for-woocommerce' ),
'type' => 'checkbox',
'description' => 'This will allow them to pay via Revolut Pay the next time they visit your store and checkout faster.',
'default' => 'yes',
),
);
}
/**
* Displays configuration page with tabs
*/
public function admin_options() {
if ( $this->check_is_get_data_submitted( 'page' ) && $this->check_is_get_data_submitted( 'section' ) ) {
$is_revolut_api_section = 'wc-settings' === $this->get_request_data( 'page' ) && 'revolut' === $this->get_request_data( 'section' );
if ( $is_revolut_api_section ) {
echo wp_kses_post( '<table class="form-table">' );
$this->generate_settings_html( $this->get_form_fields(), true );
echo wp_kses_post( '</table>' );
}
}
}
/**
* Output Revolut description.
*
* @since 2.0.0
*/
public function add_revolut_description() {
if ( $this->check_is_get_data_submitted( 'page' ) && $this->check_is_get_data_submitted( 'section' ) ) {
$is_revolut_section = 'wc-settings' === $this->get_request_data( 'page' ) && in_array( $this->get_request_data( 'section' ), WC_REVOLUT_GATEWAYS, true );
if ( $is_revolut_section ) {
if ( isset( $this->settings['api_key'] ) && empty( $this->settings['api_key'] ) ) {
?>
<div class="notice notice-info sf-notice-nux is-dismissible" xmlns="" id="revolut_notice">
<div class="notice-content">
<p>
Welcome to the <b>Revolut Gateway for Woocommerce plugin!</b>
</p>
<p>
To start accepting payments from your customers at great rates, you'll need to follow
three
simple steps:
</p>
<ul style="list-style-type: disc; margin-left: 50px;">
<li>
<a href="https://business.revolut.com/signup">Sign up for Revolut Business</a> if
you
don't
have an account already.
</li><li>
Once your Revolut Business account has been approved, <a target="_blank" href="https://business.revolut.com/merchant">apply
for a Merchant
Account</a>
</li>
<li>
<a target="_blank" href="https://business.revolut.com/settings/merchant-api">Get
your Production API key</a>
and
paste it in the corresponding field below
</li>
</ul>
<p>
<a target="_blank" href="https://www.revolut.com/business/online-payments">Find out
more</a> about why
accepting
payments through Revolut is the right decision for your business.
</p>
<p>
If you'd like to know more about how to configure this plugin for your needs, <a
target="_blank"
href="https://developer.revolut.com/docs/accept-payments/plugins/woocommerce/configuration">check
out
our documentation.</a>
</p>
</div>
</div>
<?php
} else {
?>
<script>
jQuery(document).ready(function ($) {
$("#revolut_notice").hide();
});
</script>
<?php
}
}
}
}
/**
* Add admin notice when set up failed
*/
public function check_api_key() {
if ( $this->check_is_get_data_submitted( 'page' ) && $this->check_is_get_data_submitted( 'section' ) ) {
$is_revolut_section = 'wc-settings' === $this->get_request_data( 'page' ) && in_array( $this->get_request_data( 'section' ), WC_REVOLUT_GATEWAYS, true );
if ( $is_revolut_section ) {
$api_key_sandbox = $this->get_option( 'api_key_sandbox' );
$api_key_live = $this->get_option( 'api_key' );
if ( empty( $api_key_sandbox ) && empty( $api_key_live ) ) {
$this->add_error_message( __( 'Revolut requires an API Key to work.', 'revolut-gateway-for-woocommerce' ) );
}
}
}
}
/**
* Setup Revolut webhook if not configured
*/
public function maybe_register_webhook() {
$api_client = new WC_Revolut_API_Client( $this );
if ( empty( $api_client->api_key ) ) {
return false;
}
if ( ! $this->check_is_shop_needs_webhook_setup() ) {
return false;
}
if ( ! $this->setup_revolut_webhook() ) {
return false;
}
$this->add_success_message( __( 'Webhook url successfully configured', 'revolut-gateway-for-woocommerce' ) );
}
/**
* Setup Revolut synchronous webhooks if not configured
*/
public function maybe_register_synchronous_webhooks() {
$api_client = new WC_Revolut_API_Client( $this );
if ( empty( $api_client->api_key ) ) {
return false;
}
if ( ! $this->setup_revolut_synchronous_webhook() ) {
/* translators:%1s: %$2s: */
$this->add_error_message( sprintf( __( 'Synchronous Webhook setup unsuccessful. Please make sure you are using the correct %1$sAPI key%2$s. If the problem persists, please reach out to support via our in-app chat.', 'revolut-gateway-for-woocommerce' ), '<a href="https://developer.revolut.com/docs/accept-payments/get-started/generate-the-api-key" target="_blank">', '</a>' ) );
return false;
}
}
/**
* Revolut location setup
*
* @throws Exception Exception.
*/
public function setup_revolut_location() {
$domain = get_site_url();
$location_name = str_replace( array( 'https://', 'http://' ), '', $domain );
$api_client = new WC_Revolut_API_Client( $this, true );
$locations = $api_client->get( '/locations' );
if ( ! empty( $locations ) ) {
foreach ( $locations as $location ) {
if ( isset( $location['name'] ) && $location['name'] === $domain && ! empty( $location['id'] ) ) {
return $location['id'];
}
}
}
$body = array(
'name' => $location_name,
'type' => 'online',
'details' => array(
'domain' => $domain,
),
);
$location = $api_client->post( '/locations', $body );
if ( ! isset( $location['id'] ) || empty( $location['id'] ) ) {
throw new Exception( 'Can not create location object.' );
}
return $location['id'];
}
/**
* Check is shop needs webhook setup
*/
public function check_is_shop_needs_webhook_setup() {
try {
$web_hook_url = get_site_url( null, '/wp-json/wc/v3/revolut', 'https' );
if ( $this->get_option( 'revolut_webhook_domain' ) === $web_hook_url ) {
return false;
}
$api_client = new WC_Revolut_API_Client( $this );
$web_hook_url_list = $api_client->get( '/webhooks' );
if ( ! empty( $web_hook_url_list ) ) {
$web_hook_url_list = array_column( $web_hook_url_list, 'url' );
if ( in_array( $web_hook_url, $web_hook_url_list, true ) ) {
return false;
}
}
} catch ( Exception $e ) {
$this->add_error_message( $e->getMessage() );
}
return true;
}
/**
* Revolut webhook setup
*/
public function setup_revolut_webhook() {
try {
$web_hook_url = get_site_url( null, '/wp-json/wc/v3/revolut', 'https' );
$body = array(
'url' => $web_hook_url,
'events' => array(
'ORDER_COMPLETED',
'ORDER_AUTHORISED',
),
);
$api_client = new WC_Revolut_API_Client( $this );
$response = $api_client->post( '/webhooks', $body );
if ( isset( $response['id'] ) && ! empty( $response['id'] ) ) {
$this->update_option( 'revolut_webhook_domain', $web_hook_url );
return true;
}
} catch ( Exception $e ) { // phpcs:ignore
// Prevent double logs. Exception logged previously.
}
return false;
}
/**
* Revolut webhook setup
*/
public function setup_revolut_synchronous_webhook() {
try {
$web_hook_url = get_site_url( null, '/wp-json/wc/v3/revolut', 'https' );
$location_id = $this->setup_revolut_location();
$mode = $this->get_option( 'mode' );
$mode = empty( $mode ) ? 'sandbox' : $mode;
if ( $this->get_option( 'revolut_pay_synchronous_webhook_domain_' . $mode . '_' . $location_id ) === $web_hook_url ) {
return true;
}
$body = array(
'url' => $web_hook_url,
'event_type' => 'fast_checkout.validate_address',
'location_id' => $location_id,
);
$api_client = new WC_Revolut_API_Client( $this, true );
$response = $api_client->post( '/synchronous-webhooks', $body );
if ( isset( $response['signing_key'] ) && ! empty( $response['signing_key'] ) ) {
$this->update_option( 'revolut_' . $mode . '_location_id', $location_id );
$this->update_option( 'revolut_pay_synchronous_webhook_domain_' . $mode . '_' . $location_id, $web_hook_url );
$this->update_option( 'revolut_pay_synchronous_webhook_domain_' . $mode . '_signing_key', $response['signing_key'] );
$this->add_success_message( __( 'Synchronous Webhook url successfully configured', 'revolut-gateway-for-woocommerce' ) );
return true;
}
$this->add_error_message( wp_json_encode( $response ) );
} catch ( Exception $e ) {
$this->add_error_message( $e->getMessage() );
}
return false;
}
/**
* Get Revolut Location
*/
public function get_revolut_location() {
$mode = empty( $this->get_option( 'mode' ) ) ? 'sandbox' : $this->get_option( 'mode' );
return $this->get_option( 'revolut_' . $mode . '_location_id' );
}
/**
* Display error message
*
* @param string $message display message.
*/
public function add_error_message( $message ) {
echo wp_kses_post( '<div class="error revolut-passphrase-message"><p>' . $message . '</p></div>' );
}
/**
* Display success message
*
* @param string $message display message.
*/
public function add_success_message( $message ) {
echo wp_kses_post( '<div style="border-left-color: green" class="error revolut-passphrase-message"><p>' . $message . '</p></div>' );
}
/**
* Check is data submitted for GET request.
*
* @param string $submit request key.
*/
public function check_is_get_data_submitted( $submit ) {
return isset( $_GET[ $submit ] ); // phpcs:ignore
}
/**
* Safe get request data
*
* @param string $get_key request key.
*/
public function get_request_data( $get_key ) {
if ( ! isset( $_GET[ $get_key ] ) ) { // phpcs:ignore
return null;
}
return $this->recursive_sanitize_text_field( $_GET[ $get_key ] ); // phpcs:ignore
}
/**
* Clear data.
*
* @param mixed $var data for cleaning.
*/
public function recursive_sanitize_text_field( $var ) {
if ( is_array( $var ) ) {
return array_map( array( $this, 'recursive_sanitize_text_field' ), $var );
} else {
return sanitize_text_field( wp_unslash( $var ) );
}
}
}

View File

@@ -0,0 +1,538 @@
<?php
/**
* Revolut Helper
*
* Helper class for required tools.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Gateway_Revolut_Helper_Trait trait.
*/
trait WC_Gateway_Revolut_Express_Checkout_Helper_Trait {
/**
* Free shipping.
*
* @var array
*/
public static $free_shipping = array(
array(
'id' => 'free_shipping',
'amount' => '0',
'description' => '',
'label' => 'SHIPPING',
),
);
/**
* Calculate shipping
*
* @param array $address customer address info.
*/
public function calculate_shipping( $address = array() ) {
$country = $address['country'];
$state = $this->convert_state_name_to_id( $address['country'], $address['state'] );
$postcode = $address['postcode'];
$city = $address['city'];
$address_1 = $address['address'];
$address_2 = $address['address_2'];
WC()->shipping->reset_shipping();
if ( $postcode && WC_Validation::is_postcode( $postcode, $country ) ) {
$postcode = wc_format_postcode( $postcode, $country );
}
if ( $country ) {
WC()->customer->set_location( $country, $state, $postcode, $city );
WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
} else {
WC()->customer->set_billing_address_to_base();
WC()->customer->set_shipping_address_to_base();
}
WC()->customer->set_calculated_shipping( true );
WC()->customer->save();
$packages = array();
$package = array();
$package['contents'] = WC()->cart->get_cart();
$package['contents_cost'] = 0;
$package['applied_coupons'] = WC()->cart->applied_coupons;
$package['user']['ID'] = get_current_user_id();
$package['destination']['country'] = $country;
$package['destination']['state'] = $state;
$package['destination']['postcode'] = $postcode;
$package['destination']['city'] = $city;
$package['destination']['address'] = $address_1;
$package['destination']['address_2'] = $address_2;
foreach ( WC()->cart->get_cart() as $item ) {
if ( $item['data']->needs_shipping() ) {
if ( isset( $item['line_total'] ) ) {
$package['contents_cost'] += $item['line_total'];
}
}
}
$packages[0] = $package;
$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
WC()->shipping->calculate_shipping( $packages );
}
/**
* Get shipping options
*
* @param array $shipping_address customer address info.
*/
public function get_shipping_options( $shipping_address ) {
$shipping_options = array();
$GLOBALS['wp']->query_vars['rest_route'] = 'wc/store/v3/cart';
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
$this->calculate_shipping( $shipping_address );
WC()->cart->calculate_totals();
$packages = WC()->shipping->get_packages();
if ( empty( $packages ) && ! WC()->customer->has_calculated_shipping() ) {
return $shipping_options;
}
$package = $packages[0];
if ( empty( $package ) || empty( $package['rates'] ) ) {
return $shipping_options;
}
foreach ( $package['rates'] as $rate ) {
$shipping_cost = (float) $rate->get_cost() + (float) $rate->get_shipping_tax();
$shipping_cost = wc_format_decimal( $shipping_cost, wc_get_price_decimals() );
$shipping_options[] = array(
'id' => $rate->id,
'label' => $rate->label . ' ' . $shipping_cost . ' ' . get_woocommerce_currency(),
'description' => '',
'amount' => $shipping_cost * 100,
);
}
if ( isset( $shipping_options[0] ) ) {
if ( isset( $chosen_shipping_methods[0] ) ) {
$chosen_method_id = $chosen_shipping_methods[0];
$compare_shipping_options = function ( $a, $b ) use ( $chosen_method_id ) {
if ( $a['id'] === $chosen_method_id ) {
return -1;
}
if ( $b['id'] === $chosen_method_id ) {
return 1;
}
return 0;
};
usort( $shipping_options, $compare_shipping_options );
}
$first_shipping_method_id = $shipping_options[0]['id'];
$this->update_shipping_method( array( $first_shipping_method_id ) );
}
WC()->cart->calculate_totals();
return $shipping_options;
}
/**
* Update shipping method in WC cart.
*
* @param array $shipping_methods shipping method list.
*/
public function update_shipping_method( $shipping_methods ) {
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( is_array( $shipping_methods ) ) {
foreach ( $shipping_methods as $i => $value ) {
$chosen_shipping_methods[ $i ] = wc_clean( $value );
}
}
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
WC()->cart->calculate_totals();
}
/**
* Get Required parameters for Revolut order.
*/
public function get_revolut_order_descriptor() {
if ( $this->is_product() ) {
$product = $this->get_product_data();
return new WC_Revolut_Order_Descriptor( $product['productPrice'], get_woocommerce_currency(), null );
}
return new WC_Revolut_Order_Descriptor( WC()->cart->get_total( '' ), get_woocommerce_currency(), null );
}
/**
* Check is product page.
*/
public function is_product() {
if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) {
$product = $this->get_product();
if ( 'subscription' !== $product->get_type() ) {
return true;
}
}
return false;
}
/**
* Get product data.
*/
public function get_product_data() {
if ( ! $this->is_product() ) {
return false;
}
$product = $this->get_product();
if ( 'variable' === $product->get_type() ) {
$variation_attributes = $product->get_variation_attributes();
$attributes = array();
foreach ( $variation_attributes as $attribute_name => $attribute_values ) {
$attribute_key = 'attribute_' . sanitize_title( $attribute_name );
if ( $this->check_is_get_data_submitted( $attribute_key ) ) {
$attributes[ $attribute_key ] = sanitize_text_field( wp_unslash( $_GET[ $attribute_key ] ) ); // phpcs:ignore
} else {
$attributes[ $attribute_key ] = $product->get_variation_default_attribute( $attribute_name );
}
}
$data_store = WC_Data_Store::load( 'product' );
$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
if ( ! empty( $variation_id ) ) {
$product = wc_get_product( $variation_id );
}
}
$data = array();
$data['productPrice'] = $this->get_product_price( $product );
$data['shipping_required'] = ( wc_shipping_enabled() && $product->needs_shipping() && 0 !== wc_get_shipping_method_count( true ) );
return $data;
}
/**
* Get product price
*
* @param object $product WooCommerce product.
*/
public function get_product_price( $product ) {
$product_price = $product->get_price();
if ( 'subscription' === $product->get_type() && class_exists( 'WC_Subscriptions_Product' ) ) {
$product_price = $product->get_price() + WC_Subscriptions_Product::get_sign_up_fee( $product );
}
return $product_price;
}
/**
* Get product object.
*/
public function get_product() {
global $post;
if ( is_product() ) {
return wc_get_product( $post->ID );
} elseif ( wc_post_content_has_shortcode( 'product_page' ) ) {
// Get id from product_page shortcode.
preg_match( '/\[product_page id="(?<id>\d+)"\]/', $post->post_content, $shortcode_match );
if ( ! isset( $shortcode_match['id'] ) ) {
return false;
}
return wc_get_product( $shortcode_match['id'] );
}
return false;
}
/**
* Get redirect URL.
*/
public function get_redirect_url() {
global $post;
if ( is_product() ) {
return get_permalink( $post->ID );
} elseif ( wc_post_content_has_shortcode( 'product_page' ) ) {
// Get id from product_page shortcode.
preg_match( '/\[product_page id="(?<id>\d+)"\]/', $post->post_content, $shortcode_match );
if ( isset( $shortcode_match['id'] ) ) {
return get_permalink( $shortcode_match['id'] );
}
}
if ( is_cart() ) {
return wc_get_cart_url();
}
return wc_get_checkout_url();
}
/**
* Get parameters
*/
public function get_wc_revolut_payment_request_params() {
try {
$revolut_public_id = $this->create_express_checkout_public_id();
$total = WC()->cart->get_total( '' );
$currency = get_woocommerce_currency();
$total = $this->get_revolut_order_total( $total, $currency );
$revolut_payment_request_settings = get_option( 'woocommerce_revolut_payment_request_settings', array() );
$revolut_pay_settings = get_option( 'woocommerce_revolut_pay_settings', array() );
return array(
'total' => $total,
'currency' => $currency,
'locale' => $this->get_lang_iso_code(),
'publicToken' => $this->get_merchant_public_api_key(),
'ajax_url' => WC_AJAX::get_endpoint( '%%wc_revolut_gateway_ajax_endpoint%%' ),
'revolut_public_id' => $revolut_public_id,
'revolut_pay_origin_url' => str_replace( array( 'https://', 'http://' ), '', get_site_url() ),
'revolut_pay_button_theme' => ! empty( $revolut_pay_settings['revolut_pay_button_theme'] ) ? $revolut_pay_settings['revolut_pay_button_theme'] : '',
'revolut_pay_button_size' => ! empty( $revolut_pay_settings['revolut_pay_button_size'] ) ? $revolut_pay_settings['revolut_pay_button_size'] : '',
'revolut_pay_button_radius' => ! empty( $revolut_pay_settings['revolut_pay_button_radius'] ) ? $revolut_pay_settings['revolut_pay_button_radius'] : '',
'payment_request_button_type' => ! empty( $revolut_payment_request_settings['payment_request_button_type'] ) ? $revolut_payment_request_settings['payment_request_button_type'] : '',
'payment_request_button_theme' => ! empty( $revolut_payment_request_settings['payment_request_button_theme'] ) ? $revolut_payment_request_settings['payment_request_button_theme'] : '',
'payment_request_button_radius' => ! empty( $revolut_payment_request_settings['payment_request_button_radius'] ) ? $revolut_payment_request_settings['payment_request_button_radius'] : '',
'payment_request_button_size' => ! empty( $revolut_payment_request_settings['payment_request_button_size'] ) ? $revolut_payment_request_settings['payment_request_button_size'] : '',
'shipping_options' => array(),
'nonce' => array(
'payment' => wp_create_nonce( 'wc-revolut-payment-request' ),
'shipping' => wp_create_nonce( 'wc-revolut-payment-request-shipping' ),
'update_shipping' => wp_create_nonce( 'wc-revolut-update-shipping-method' ),
'update_order_total' => wp_create_nonce( 'wc-revolut-update-order-total' ),
'load_order_data' => wp_create_nonce( 'wc-revolut-load-order-data' ),
'cancel_order' => wp_create_nonce( 'wc-revolut-cancel-order' ),
'get_express_checkout_params' => wp_create_nonce( 'wc-revolut-get-express-checkout-params' ),
'get_payment_request_params' => wp_create_nonce( 'wc-revolut-get-payment-request-params' ),
'checkout' => wp_create_nonce( 'woocommerce-process_checkout' ),
'add_to_cart' => wp_create_nonce( 'wc-revolut-pr-add-to-cart' ),
'get_selected_product_data' => wp_create_nonce( 'wc-revolut-get-selected-product-data' ),
'log_errors' => wp_create_nonce( 'wc-revolut-log-errors' ),
'clear_cart' => wp_create_nonce( 'wc-revolut-clear-cart' ),
),
'is_product_page' => $this->is_product(),
'redirect_url' => $this->get_redirect_url(),
'is_cart_page' => is_cart(),
'product' => $this->get_product_data(),
'shipping_required' => $this->is_shipping_required(),
'free_shipping_option' => self::$free_shipping,
'error_messages' => array(
'checkout_general' => __( 'Something went wrong while processing the order. Please try again', 'revolut-gateway-for-woocommerce' ),
'cart_create_failed' => __( 'An error occurred while creating WooCommerce cart', 'revolut-gateway-for-woocommerce' ),
),
);
} catch ( Exception $e ) {
$this->log_error( $e->getMessage() );
}
}
/**
* Create Revolut order for express checkout.
*/
public function create_express_checkout_public_id() {
$revolut_public_id = $this->get_revolut_express_checkout_public_id();
$descriptor = $this->get_revolut_order_descriptor();
if ( null === $revolut_public_id ) {
$revolut_public_id = $this->create_revolut_order( $descriptor, true );
$this->set_revolut_express_checkout_public_id( $revolut_public_id );
} else {
$revolut_public_id = $this->update_revolut_order( $descriptor, $revolut_public_id, true );
$this->set_revolut_express_checkout_public_id( $revolut_public_id );
}
return $revolut_public_id;
}
/**
* Check if shipping required for the order.
*/
public function is_shipping_required() {
if ( $this->is_product() ) {
$product = $this->get_product_data();
return isset( $product['shipping_required'] ) ? $product['shipping_required'] : false;
}
if ( is_cart() ) {
return ! is_null( WC()->cart ) && WC()->cart->needs_shipping();
}
return false;
}
/**
* Check is page supports payment request button
*
* @param array $payment_request_button_locations configuration.
*/
public function page_supports_payment_request_button( $payment_request_button_locations ) {
if ( empty( $payment_request_button_locations ) ) {
$payment_request_button_locations = array();
}
if ( ! is_cart() && ! $this->is_product() ) {
return false;
}
if ( $this->is_product() && ! in_array( 'product', $payment_request_button_locations, true ) ) {
return false;
}
if ( is_cart() && ! in_array( 'cart', $payment_request_button_locations, true ) ) {
return false;
}
if ( $this->is_subscription_product() ) {
return false;
}
if ( ! $this->check_order_creation_possible() ) {
return false;
}
return true;
}
/**
* Check is subscription product.
*/
public function is_subscription_product() {
if ( ! class_exists( 'WC_Subscriptions_Product' ) ) {
return false;
}
if ( $this->is_product() ) {
$product = $this->get_product();
if ( WC_Subscriptions_Product::is_subscription( $product ) ) {
return true;
}
}
if ( is_cart() ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( WC_Subscriptions_Product::is_subscription( $_product ) ) {
return true;
}
}
}
return false;
}
/**
* Check is creating WooCommerce order possible.
*/
public function check_order_creation_possible() {
if ( is_user_logged_in() || ! $this->check_authentication_required() ) {
return true;
}
return false;
}
/**
* Covert Revolut address type into the required form of express checkout.
*
* @param array $address from api.
*/
public function convert_revolut_address_to_express_checkout_address( $address ) {
$address['country'] = $address['country_code'];
$address['addressLine'] = array( $address['street_line_1'], '' );
$address['address'] = $address['street_line_1'];
$address['address_2'] = '';
$address['postalCode'] = $address['postcode'];
$address['region'] = ! empty( $address['region'] ) ? $address['region'] : '';
$address['state'] = $address['region'];
return $address;
}
/**
* Check if authentication is required.
*/
public function check_authentication_required() {
if ( 'no' === get_option( 'woocommerce_enable_guest_checkout', 'yes' ) && ! $this->check_account_creation_possible() ) {
return true;
}
return false;
}
/**
* Check if account creation possible
*/
public function check_account_creation_possible() {
return (
'yes' === get_option( 'woocommerce_enable_signup_and_login_from_checkout', 'no' ) &&
'yes' === get_option( 'woocommerce_registration_generate_username', 'yes' ) &&
'yes' === get_option( 'woocommerce_registration_generate_password', 'yes' )
);
}
/**
* Load sate id by name if the api sends state name.
*
* @param string $country_id id country.
* @param string $state_name state name.
*/
public function convert_state_name_to_id( $country_id, $state_name ) {
$wc_states = WC()->countries->get_states( $country_id );
if ( empty( $wc_states ) || empty( $state_name ) ) {
return $state_name;
}
foreach ( $wc_states as $state_id => $wc_state_name ) {
if ( strtolower( $wc_state_name ) === strtolower( $state_name ) || strtolower( $state_id ) === strtolower( $state_name ) ) {
return $state_id;
}
}
// if the standard search fails search county by removing Co prefix for Irish states.
if ( 'IE' === $country_id ) {
$state_name = str_replace( 'Co. ', '', $state_name );
foreach ( $wc_states as $state_id => $wc_state_name ) {
if ( strtolower( $wc_state_name ) === strtolower( $state_name ) || strtolower( $state_id ) === strtolower( $state_name ) ) {
return $state_id;
}
}
}
return $state_name;
}
}

View File

@@ -0,0 +1,948 @@
<?php
/**
* Revolut Helper
*
* Helper class for required tools.
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Gateway_Revolut_Helper_Trait trait.
*/
trait WC_Gateway_Revolut_Helper_Trait {
use WC_Revolut_Settings_Trait;
use WC_Revolut_Logger_Trait;
/**
* Create Revolut Order
*
* @param WC_Revolut_Order_Descriptor $order_descriptor Revolut Order Descriptor.
*
* @param bool $is_express_checkout indicator.
*
* @return mixed
* @throws Exception Exception.
*/
public function create_revolut_order( WC_Revolut_Order_Descriptor $order_descriptor, $is_express_checkout = false ) {
$capture = 'authorize' === $this->api_settings->get_option( 'payment_action' ) || $is_express_checkout ? 'manual' : 'automatic';
$body = array(
'amount' => $order_descriptor->amount,
'currency' => $order_descriptor->currency,
'capture_mode' => $capture,
);
if ( ! empty( $order_descriptor->revolut_customer_id ) ) {
$body['customer'] = array( 'id' => $order_descriptor->revolut_customer_id );
}
if ( $is_express_checkout ) {
$body['cancel_authorised_after'] = WC_REVOLUT_AUTO_CANCEL_TIMEOUT;
$location_id = $this->api_settings->get_revolut_location();
if ( $location_id ) {
$body['location_id'] = $location_id;
}
}
$json = $this->api_client->post( '/orders', $body, false, true );
if ( isset( $json['token'] ) ) {
$json['public_id'] = $json['token'];
}
if ( empty( $json['id'] ) || empty( $json['public_id'] ) ) {
throw new Exception( 'Something went wrong: ' . wp_json_encode( $json, JSON_PRETTY_PRINT ) );
}
global $wpdb;
$result = $wpdb->query(
$wpdb->prepare(
'INSERT INTO ' . $wpdb->prefix . "wc_revolut_orders (order_id, public_id)
VALUES (UNHEX(REPLACE(%s, '-', '')), UNHEX(REPLACE(%s, '-', '')))",
array(
$json['id'],
$json['public_id'],
)
)
); // db call ok; no-cache ok.
if ( 1 !== $result ) {
throw new Exception( 'Can not save Revolut order record on DB:' . $wpdb->last_error );
}
if ( $is_express_checkout ) {
$this->add_or_update_temp_session( $json['id'] );
}
return $json['public_id'];
}
/**
* Retrieve the revolut customer's id.
*
* @param string $billing_phone holds customer email address.
* @param string $billing_email holds customer billing phone.
* @throws Exception Exception.
*/
public function get_or_create_revolut_customer( $billing_phone = '', $billing_email = '' ) {
if ( empty( $billing_email ) || empty( $billing_phone ) ) {
$wc_customer = WC()->customer;
$billing_email = $wc_customer->get_billing_email();
$billing_phone = $wc_customer->get_billing_phone();
}
if ( empty( $billing_email ) || empty( $billing_phone ) ) {
return;
}
$revolut_customer_id = $this->get_revolut_customer_id();
if ( empty( $revolut_customer_id ) ) {
$revolut_customer_id = $this->create_revolut_customer( $billing_phone, $billing_email );
return $revolut_customer_id;
}
return $revolut_customer_id;
}
/**
* Update the revolut customer's phone.
*
* @param string $revolut_customer_id customer_id.
* @param string $billing_phone billing phone number.
* @throws Exception Exception.
* @return void|null
*/
public function update_revolut_customer( $revolut_customer_id, $billing_phone ) {
if ( empty( $revolut_customer_id ) || empty( $billing_phone ) ) {
return null;
}
$this->api_client->patch( "/customers/$revolut_customer_id", array( 'phone' => $billing_phone ) );
}
/**
* Create revolut customer.
*
* @return $revolut_customer_id revolut customer id.
* @param string $billing_phone holds customer billing phone.
* @param string $billing_email holds customer email address.
* @throws Exception Exception.
*/
public function create_revolut_customer( $billing_phone, $billing_email ) {
try {
if ( empty( $billing_phone ) || empty( $billing_email ) ) {
return;
}
$revolut_customer_id = null;
$body = array(
'phone' => $billing_phone,
'email' => $billing_email,
);
$revolut_customer = $this->api_client->get( '/customers?term=' . $billing_email );
$revolut_customer_id = ! empty( $revolut_customer[0]['id'] ) ? $revolut_customer[0]['id'] : '';
if ( ! $revolut_customer_id ) {
$revolut_customer = $this->api_client->post( '/customers', $body );
$revolut_customer_id = ! empty( $revolut_customer['id'] ) ? $revolut_customer['id'] : '';
}
if ( ! $revolut_customer_id ) {
return $revolut_customer_id;
}
$this->insert_revolut_customer_id( $revolut_customer_id );
$this->update_revolut_customer( $revolut_customer_id, $billing_phone );
return $revolut_customer_id;
} catch ( Exception $e ) {
$this->log_error( 'create_revolut_customer: ' . $e->getMessage() );
}
}
/**
* Save Revolut customer id.
*
* @param string $revolut_customer_id Revolut customer id.
*
* @throws Exception Exception.
*/
protected function insert_revolut_customer_id( $revolut_customer_id ) {
if ( empty( get_current_user_id() ) ) {
return;
}
global $wpdb;
$revolut_customer_id = "{$this->api_client->mode}_$revolut_customer_id";
$wpdb->query(
$wpdb->prepare(
"INSERT INTO {$wpdb->prefix}wc_revolut_customer (wc_customer_id, revolut_customer_id)
VALUES (%d, %s) ON DUPLICATE KEY UPDATE wc_customer_id = VALUES(wc_customer_id)",
array( get_current_user_id(), $revolut_customer_id )
)
); // db call ok; no-cache ok.
}
/**
* Update Revolut Order.
*
* @param WC_Revolut_Order_Descriptor $order_descriptor Revolut Order Descriptor.
* @param String $public_id Revolut public id.
* @param Bool $is_revpay_express_checkout is revpay express checkout.
*
* @return mixed
* @throws Exception Exception.
*/
public function update_revolut_order( WC_Revolut_Order_Descriptor $order_descriptor, $public_id, $is_revpay_express_checkout = false ) {
$order_id = $this->get_revolut_order_by_public_id( $public_id );
$body = array(
'amount' => $order_descriptor->amount,
'currency' => $order_descriptor->currency,
'customer_id' => $order_descriptor->revolut_customer_id,
);
if ( empty( $order_id ) ) {
return $this->create_revolut_order( $order_descriptor, $is_revpay_express_checkout );
}
$revolut_order = $this->api_client->get( "/orders/$order_id" );
if ( ! isset( $revolut_order['public_id'] ) || ! isset( $revolut_order['id'] ) || 'PENDING' !== $revolut_order['state'] ) {
return $this->create_revolut_order( $order_descriptor, $is_revpay_express_checkout );
}
$revolut_order = $this->api_client->patch( "/orders/$order_id", $body );
if ( ! isset( $revolut_order['public_id'] ) || ! isset( $revolut_order['id'] ) ) {
return $this->create_revolut_order( $order_descriptor, $is_revpay_express_checkout );
}
if ( $is_revpay_express_checkout ) {
$this->add_or_update_temp_session( $revolut_order['id'] );
}
return $revolut_order['public_id'];
}
/**
* Convert saved customer session into current session.
*
* @param string $id_revolut_order Revolut order id.
*
* @return void.
*/
public function convert_revolut_order_metadata_into_wc_session( $id_revolut_order ) {
WC()->initialize_session();
WC()->initialize_cart();
global $wpdb;
$temp_session = $wpdb->get_row( $wpdb->prepare( 'SELECT temp_session FROM ' . $wpdb->prefix . 'wc_revolut_temp_session WHERE order_id=%s', array( $id_revolut_order ) ), ARRAY_A ); // db call ok; no-cache ok.
$this->log_info( 'start convert_revolut_order_metadata_into_wc_session temp_session:' );
$this->log_info( $temp_session['temp_session'] );
$wc_order_metadata = json_decode( $temp_session['temp_session'], true );
$id_wc_customer = (int) $wc_order_metadata['id_customer'];
if ( $id_wc_customer ) {
wp_set_current_user( $id_wc_customer );
}
WC()->session->set( 'cart', $wc_order_metadata['cart'] );
WC()->session->set( 'cart_totals', $wc_order_metadata['cart_totals'] );
WC()->session->set( 'applied_coupons', $wc_order_metadata['applied_coupons'] );
WC()->session->set( 'coupon_discount_totals', $wc_order_metadata['coupon_discount_totals'] );
WC()->session->set( 'coupon_discount_tax_totals', $wc_order_metadata['coupon_discount_tax_totals'] );
WC()->session->set( 'get_removed_cart_contents', $wc_order_metadata['get_removed_cart_contents'] );
$session = new WC_Cart_Session( WC()->cart );
$session->get_cart_from_session();
}
/**
* Get order details
*
* @param array $address Customer address.
* @param bool $shipping_required is shipping option required for the current order.
* @param string $gateway selected payment gateway.
* @throws Exception Exception.
*/
public function format_wc_order_details( $address, $shipping_required, $gateway ) {
if ( empty( $address['billingAddress'] ) ) {
throw new Exception( 'Billing address is missing' );
}
if ( $shipping_required && empty( $address['shippingAddress'] ) ) {
throw new Exception( 'Shipping address is missing' );
}
if ( empty( $address['email'] ) ) {
throw new Exception( 'User Email information is missing' );
}
$revolut_billing_address = $address['billingAddress'];
$revolut_customer_email = $address['email'];
$revolut_customer_full_name = ! empty( $revolut_billing_address['recipient'] ) ? $revolut_billing_address['recipient'] : '';
$revolut_customer_billing_phone = ! empty( $revolut_billing_address['phone'] ) ? $revolut_billing_address['phone'] : '';
$revolut_customer_shipping_phone = '';
$wc_shipping_address = array();
list($billing_firstname, $billing_lastname) = $this->parse_customer_name( $revolut_customer_full_name );
if ( isset( $address['shippingAddress'] ) && ! empty( $address['shippingAddress'] ) ) {
$revolut_shipping_address = $address['shippingAddress'];
$revolut_customer_shipping_phone = ! empty( $revolut_shipping_address['phone'] ) ? $revolut_shipping_address['phone'] : '';
$revolut_customer_shipping_full_name = ! empty( $revolut_shipping_address['recipient'] ) ? $revolut_shipping_address['recipient'] : '';
$shipping_firstname = $billing_firstname;
$shipping_lastname = $billing_lastname;
if ( ! empty( $revolut_customer_shipping_full_name ) ) {
list($shipping_firstname, $shipping_lastname) = $this->parse_customer_name( $revolut_customer_shipping_full_name );
}
if ( empty( $revolut_customer_shipping_phone ) && ! empty( $revolut_customer_billing_phone ) ) {
$revolut_customer_shipping_phone = $revolut_customer_billing_phone;
}
$wc_shipping_address = $this->get_wc_shipping_address( $revolut_shipping_address, $revolut_customer_email, $revolut_customer_shipping_phone, $shipping_firstname, $shipping_lastname );
}
if ( empty( $revolut_customer_billing_phone ) && ! empty( $revolut_customer_shipping_phone ) ) {
$revolut_customer_billing_phone = $revolut_customer_shipping_phone;
}
$wc_billing_address = $this->get_wc_billing_address( $revolut_billing_address, $revolut_customer_email, $revolut_customer_billing_phone, $billing_firstname, $billing_lastname );
if ( $shipping_required ) {
$wc_order_data = array_merge( $wc_billing_address, $wc_shipping_address );
} else {
$wc_order_data = $wc_billing_address;
}
$wc_order_data['ship_to_different_address'] = $shipping_required;
$wc_order_data['revolut_pay_express_checkout'] = 'revolut_pay' === $gateway;
$wc_order_data['terms'] = 1;
$wc_order_data['order_comments'] = '';
return $wc_order_data;
}
/**
* Get first and lastname from customer full name string.
*
* @param string $full_name Customer full name.
*/
public function parse_customer_name( $full_name ) {
$full_name_list = explode( ' ', $full_name );
if ( count( $full_name_list ) > 1 ) {
$lastname = array_pop( $full_name_list );
$firstname = implode( ' ', $full_name_list );
return array( $firstname, $lastname );
}
$firstname = $full_name;
$lastname = 'undefined';
return array( $firstname, $lastname );
}
/**
* Create billing address for order.
*
* @param array $shipping_address Shipping address.
* @param string $revolut_customer_email Email.
* @param string $revolut_customer_phone Phone.
* @param string $firstname Firstname.
* @param string $lastname Lastname.
*/
public function get_wc_shipping_address( $shipping_address, $revolut_customer_email, $revolut_customer_phone, $firstname, $lastname ) {
if ( isset( $shipping_address['country'] ) ) {
$shipping_address['country'] = strtoupper( $shipping_address['country'] );
}
$address['shipping_first_name'] = $firstname;
$address['shipping_last_name'] = $lastname;
$address['shipping_email'] = $revolut_customer_email;
$address['shipping_phone'] = $revolut_customer_phone;
$address['shipping_country'] = ! empty( $shipping_address['country'] ) ? $shipping_address['country'] : '';
$address['shipping_address_1'] = ! empty( $shipping_address['addressLine'][0] ) ? $shipping_address['addressLine'][0] : '';
$address['shipping_address_2'] = ! empty( $shipping_address['addressLine'][1] ) ? $shipping_address['addressLine'][1] : '';
$address['shipping_city'] = ! empty( $shipping_address['city'] ) ? $shipping_address['city'] : '';
$address['shipping_state'] = ! empty( $shipping_address['region'] ) ? $this->convert_state_name_to_id( $shipping_address['country'], $shipping_address['region'] ) : '';
$address['shipping_postcode'] = ! empty( $shipping_address['postalCode'] ) ? $shipping_address['postalCode'] : '';
$address['shipping_company'] = '';
return $address;
}
/**
* Create billing address for order.
*
* @param array $billing_address Billing address.
* @param string $revolut_customer_email Email.
* @param string $revolut_customer_phone Phone.
* @param string $firstname Firstname.
* @param string $lastname Lastname.
*/
public function get_wc_billing_address( $billing_address, $revolut_customer_email, $revolut_customer_phone, $firstname, $lastname ) {
if ( isset( $billing_address['country'] ) ) {
$billing_address['country'] = strtoupper( $billing_address['country'] );
}
$address = array();
$address['billing_first_name'] = $firstname;
$address['billing_last_name'] = $lastname;
$address['billing_email'] = $revolut_customer_email;
$address['billing_phone'] = $revolut_customer_phone;
$address['billing_country'] = ! empty( $billing_address['country'] ) ? $billing_address['country'] : '';
$address['billing_address_1'] = ! empty( $billing_address['addressLine'][0] ) ? $billing_address['addressLine'][0] : '';
$address['billing_address_2'] = ! empty( $billing_address['addressLine'][1] ) ? $billing_address['addressLine'][1] : '';
$address['billing_city'] = ! empty( $billing_address['city'] ) ? $billing_address['city'] : '';
$address['billing_state'] = ! empty( $billing_address['region'] ) ? $this->convert_state_name_to_id( $billing_address['country'], $billing_address['region'] ) : '';
$address['billing_postcode'] = ! empty( $billing_address['postalCode'] ) ? $billing_address['postalCode'] : '';
$address['billing_company'] = '';
return $address;
}
/**
* Check if payment is pending.
*
* @param string $revolut_order_id Revolut order id.
*/
protected function is_pending_payment( $revolut_order_id ) {
$revolut_order = $this->api_client->get( '/orders/' . $revolut_order_id );
return ! isset( $revolut_order['state'] ) || ( isset( $revolut_order['state'] ) && 'PENDING' === $revolut_order['state'] );
}
/**
* Save or Update customer session temporarily.
*
* @param string $revolut_order_id Revolut order id.
*
* @throws Exception Exception.
*/
public function add_or_update_temp_session( $revolut_order_id ) {
$order_metadata['id_customer'] = get_current_user_id();
$order_metadata['cart'] = WC()->cart->get_cart_for_session();
$order_metadata['cart_totals'] = WC()->cart->get_totals();
$order_metadata['applied_coupons'] = WC()->cart->get_applied_coupons();
$order_metadata['coupon_discount_totals'] = WC()->cart->get_coupon_discount_totals();
$order_metadata['coupon_discount_tax_totals'] = WC()->cart->get_coupon_discount_tax_totals();
$order_metadata['get_removed_cart_contents'] = WC()->cart->get_removed_cart_contents();
$temp_session = wp_json_encode( $order_metadata );
global $wpdb;
$wpdb->query(
$wpdb->prepare(
'INSERT INTO ' . $wpdb->prefix . 'wc_revolut_temp_session (order_id, temp_session)
VALUES (%s, %s) ON DUPLICATE KEY UPDATE temp_session = VALUES(temp_session)',
array(
$revolut_order_id,
$temp_session,
)
)
); // db call ok; no-cache ok.
}
/**
* Get Revolut customer id.
*
* @param int $wc_customer_id WooCommerce customer id.
*/
public function get_revolut_customer_id( $wc_customer_id = false ) {
if ( ! $wc_customer_id ) {
$wc_customer_id = get_current_user_id();
}
if ( empty( $wc_customer_id ) ) {
return null;
}
global $wpdb;
$revolut_customer_id = $wpdb->get_col( $wpdb->prepare( 'SELECT revolut_customer_id FROM ' . $wpdb->prefix . 'wc_revolut_customer WHERE wc_customer_id=%s', array( $wc_customer_id ) ) ); // db call ok; no-cache ok.
$revolut_customer_id = reset( $revolut_customer_id );
if ( empty( $revolut_customer_id ) ) {
$revolut_customer_id = null;
}
$revolut_customer_id_with_mode = explode( '_', $revolut_customer_id );
if ( count( $revolut_customer_id_with_mode ) > 1 ) {
list( $api_mode, $revolut_customer_id ) = $revolut_customer_id_with_mode;
if ( $api_mode !== $this->api_client->mode ) {
$this->delete_customer_record( $wc_customer_id );
return null;
}
}
// verify customer id through api.
$revolut_customer = $this->api_client->get( '/customers/' . $revolut_customer_id );
if ( empty( $revolut_customer['id'] ) ) {
$this->delete_customer_record( $wc_customer_id );
return null;
}
return $revolut_customer_id;
}
/**
* Remove customer db record
*
* @param string $wc_customer_id customer id.
*/
public function delete_customer_record( $wc_customer_id ) {
global $wpdb;
$wpdb->delete( // phpcs:ignore
$wpdb->prefix . 'wc_revolut_customer',
array(
'wc_customer_id' => $wc_customer_id,
)
);
}
/**
* Update Revolut Order Total
*
* @param float $order_total Order total.
* @param string $currency Order currency.
* @param string $public_id Order public id.
*
* @return bool
* @throws Exception Exception.
*/
public function update_revolut_order_total( $order_total, $currency, $public_id ) {
$order_id = $this->get_revolut_order_by_public_id( $public_id );
$order_total = round( $order_total, 2 );
$revolut_order_total = $this->get_revolut_order_total( $order_total, $currency );
$body = array(
'amount' => $revolut_order_total,
'currency' => $currency,
);
if ( empty( $order_id ) ) {
return false;
}
$revolut_order = $this->api_client->get( "/orders/$order_id" );
if ( ! isset( $revolut_order['public_id'] ) || ! isset( $revolut_order['id'] ) || 'PENDING' !== $revolut_order['state'] ) {
return false;
}
$revolut_order = $this->api_client->patch( "/orders/$order_id", $body );
if ( ! isset( $revolut_order['public_id'] ) || ! isset( $revolut_order['id'] ) ) {
return false;
}
return true;
}
/**
* Fetch Revolut order by public id
*
* @param String $public_id Revolut public id.
*
* @return string|null
*/
public function get_revolut_order_by_public_id( $public_id ) {
global $wpdb;
// resolve into order_id.
return $this->uuid_dashes(
$wpdb->get_col( // phpcs:ignore
$wpdb->prepare(
'SELECT HEX(order_id) FROM ' . $wpdb->prefix . 'wc_revolut_orders
WHERE public_id=UNHEX(REPLACE(%s, "-", ""))',
array( $public_id )
)
)
);
}
/**
* Load Merchant Public Key from API.
*
* @return string
*/
public function get_merchant_public_api_key() {
try {
$merchant_public_key = $this->get_revolut_merchant_public_key();
if ( ! empty( $merchant_public_key ) ) {
return $merchant_public_key;
}
$merchant_public_key = $this->api_client->get( WC_GATEWAY_PUBLIC_KEY_ENDPOINT, true );
$merchant_public_key = isset( $merchant_public_key['public_key'] ) ? $merchant_public_key['public_key'] : '';
if ( empty( $merchant_public_key ) ) {
return '';
}
$this->set_revolut_merchant_public_key( $merchant_public_key );
return $merchant_public_key;
} catch ( Exception $e ) {
return '';
}
}
/**
* Check Merchant Account features.
*
* @return bool
*/
public function check_feature_support() {
try {
$this->api_client->set_public_key( $this->get_revolut_merchant_public_key() );
$merchant_features = $this->api_client->get( '/public/merchant', true );
return isset( $merchant_features['features'] ) && is_array( $merchant_features['features'] ) && in_array(
WC_GATEWAY_REVPAY_INDEX,
$merchant_features['features'],
true
);
} catch ( Exception $e ) {
return false;
}
}
/**
* Checks if page is pay for order and change subs payment page.
*
* @return bool
*/
public function is_subs_change_payment() {
return ( isset( $_GET['pay_for_order'] ) && isset( $_GET['change_payment_method'] ) ); // phpcs:ignore
}
/**
* Unset Revolut public_id
*/
protected function unset_revolut_public_id() {
WC()->session->__unset( "{$this->api_client->mode}_revolut_public_id" );
}
/**
* Unset Revolut public_id
*/
protected function unset_revolut_express_checkout_public_id() {
WC()->session->__unset( "{$this->api_client->mode}_revolut_express_checkout_public_id" );
}
/**
* Set Revolut public_id
*
* @param string $value Revolut public id.
*/
protected function set_revolut_public_id( $value ) {
WC()->session->set( "{$this->api_client->mode}_revolut_public_id", $value );
}
/**
* Set Revolut public_id
*
* @param string $value Revolut public id.
*/
public function set_revolut_express_checkout_public_id( $value ) {
WC()->session->set( "{$this->api_client->mode}_revolut_express_checkout_public_id", $value );
}
/**
* Get Revolut public_id
*
* @return array|string|null
*/
protected function get_revolut_public_id() {
$public_id = WC()->session->get( "{$this->api_client->mode}_revolut_public_id" );
if ( empty( $public_id ) ) {
return null;
}
$order_id = $this->get_revolut_order_by_public_id( $public_id );
if ( empty( $order_id ) ) {
return null;
}
return $public_id;
}
/**
* Get Revolut public_id
*
* @return array|string|null
*/
protected function get_revolut_express_checkout_public_id() {
return WC()->session->get( "{$this->api_client->mode}_revolut_express_checkout_public_id" );
}
/**
* Get Revolut Merchant Public Key
*
* @return array|string|null
*/
protected function get_revolut_merchant_public_key() {
return WC()->session->get( "{$this->api_client->mode}_revolut_merchant_public_key" );
}
/**
* Set Revolut Merchant Public Key
*
* @param string $value Revolut Merchant public Key.
*/
protected function set_revolut_merchant_public_key( $value ) {
WC()->session->set( "{$this->api_client->mode}_revolut_merchant_public_key", $value );
}
/**
* Replace dashes
*
* @param mixed $uuid uuid.
*
* @return string|string[]|null
*/
protected function uuid_dashes( $uuid ) {
if ( is_array( $uuid ) ) {
if ( isset( $uuid[0] ) ) {
$uuid = $uuid[0];
}
}
$result = preg_replace( '/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/i', '$1-$2-$3-$4-$5', $uuid );
return $result;
}
/**
* Check if is not minor currency
*
* @param string $currency currency.
*
* @return bool
*/
public function is_zero_decimal( $currency ) {
return 'jpy' === strtolower( $currency );
}
/**
* Get order total for Api.
*
* @param float $order_total order total amount.
* @param string $currency currency.
*/
public function get_revolut_order_total( $order_total, $currency ) {
$order_total = round( (float) $order_total, 2 );
if ( ! $this->is_zero_decimal( $currency ) ) {
$order_total = round( $order_total * 100 );
}
return (int) $order_total;
}
/**
* Get order total for WC order.
*
* @param float $revolut_order_total order total amount.
* @param string $currency currency.
*/
public function get_wc_order_total( $revolut_order_total, $currency ) {
$order_total = $revolut_order_total;
if ( ! $this->is_zero_decimal( $currency ) ) {
$order_total = round( $order_total / 100, 2 );
}
return $order_total;
}
/**
* Get total amount value from Revolut order.
*
* @param array $revolut_order Revolut order.
*/
public function get_revolut_order_amount( $revolut_order ) {
return isset( $revolut_order['order_amount'] ) && isset( $revolut_order['order_amount']['value'] ) ? (int) $revolut_order['order_amount']['value'] : 0;
}
/**
* Get shipping amount value from Revolut order.
*
* @param array $revolut_order Revolut order.
*/
public function get_revolut_order_total_shipping( $revolut_order ) {
$shipping_total = isset( $revolut_order['delivery_method'] ) && isset( $revolut_order['delivery_method']['amount'] ) ? (int) $revolut_order['delivery_method']['amount'] : 0;
$currency = $this->get_revolut_order_currency( $revolut_order );
if ( $shipping_total ) {
return $this->get_wc_order_total( $shipping_total, $currency );
}
return 0;
}
/**
* Get currency from Revolut order.
*
* @param array $revolut_order Revolut order.
*/
public function get_revolut_order_currency( $revolut_order ) {
return isset( $revolut_order['order_amount'] ) && isset( $revolut_order['order_amount']['currency'] ) ? $revolut_order['order_amount']['currency'] : '';
}
/**
* Get total shipping price.
*/
public function get_cart_total_shipping() {
$cart_totals = WC()->session->get( 'cart_totals' );
$shipping_total = 0;
if ( ! empty( $cart_totals ) && is_array( $cart_totals ) && in_array( 'shipping_total', array_keys( $cart_totals ), true ) ) {
$shipping_total = $cart_totals['shipping_total'];
}
return $this->get_revolut_order_total( $shipping_total, get_woocommerce_currency() );
}
/**
* Check is data submitted for GET request.
*
* @param string $submit request key.
*/
public function check_is_get_data_submitted( $submit ) {
return isset( $_GET[ $submit ] ); // phpcs:ignore
}
/**
* Check is data submitted for POST request.
*
* @param string $submit request key.
*/
public function check_is_post_data_submitted( $submit ) {
return isset( $_POST[ $submit ] ); // phpcs:ignore
}
/**
* Safe get posted integer data
*
* @param string $post_key request key.
*/
public function get_posted_integer_data( $post_key ) {
if ( ! isset( $_POST[ $post_key ] ) ) { // phpcs:ignore
return 0;
}
return (int) $_POST[ $post_key ]; // phpcs:ignore
}
/**
* Safe get posted data
*
* @param string $post_key request key.
*/
public function get_post_request_data( $post_key ) {
if ( ! isset( $_POST[ $post_key ] ) ) { // phpcs:ignore
return null;
}
return $this->recursive_sanitize_text_field( $_POST[ $post_key ]); // phpcs:ignore
}
/**
* Safe get request data
*
* @param string $get_key request key.
*/
public function get_request_data( $get_key ) {
if ( ! isset( $_GET[ $get_key ] ) ) { // phpcs:ignore
return null;
}
return $this->recursive_sanitize_text_field( $_GET[ $get_key ] ); // phpcs:ignore
}
/**
* Clear data.
*
* @param mixed $var data for cleaning.
*/
public function recursive_sanitize_text_field( $var ) {
if ( is_array( $var ) ) {
return array_map( array( $this, 'recursive_sanitize_text_field' ), $var );
} else {
return sanitize_text_field( wp_unslash( $var ) );
}
}
/**
* Get two-digit language iso code.
*/
public function get_lang_iso_code() {
return substr( get_locale(), 0, 2 );
}
/**
* Check order status
*
* @param String $order_status data for checking.
*/
public function check_is_order_has_capture_status( $order_status ) {
if ( 'authorize' !== $this->api_settings->get_option( 'payment_action' ) ) {
return false;
}
if ( 'yes' !== $this->api_settings->get_option( 'accept_capture' ) ) {
return false;
}
$order_status = ( 0 !== strpos( $order_status, 'wc-' ) ) ? 'wc-' . $order_status : $order_status;
$selected_capture_status_list = $this->api_settings->get_option( 'selected_capture_status_list' );
$customize_capture_status = $this->api_settings->get_option( 'customise_capture_status' );
if ( empty( $selected_capture_status_list ) || 'no' === $customize_capture_status ) {
$selected_capture_status_list = array( 'wc-processing', 'wc-completed' );
}
return in_array( $order_status, $selected_capture_status_list, true );
}
/**
* Check order status
*
* @param String $public_id data.
*/
public function get_available_card_brands( $public_id ) {
try {
$order_details = $this->api_client->get( "/orders/token/{$public_id}", false, true );
if ( ! isset( $order_details['availableCardBrands'] ) || empty( $order_details['availableCardBrands'] ) ) {
return '';
}
return implode( ',', array_map( 'strtolower', $order_details['availableCardBrands'] ) );
} catch ( Exception $e ) {
$this->log_error( 'get_available_card_brands: ' . $e->getMessage() );
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Logger trait
*
* Provides shared logic for logging errors
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
defined( 'ABSPATH' ) || exit();
if ( ! class_exists( 'WC_Logger' ) ) {
return;
}
/**
* WC_Revolut_Logger_Trait trait.
*/
trait WC_Revolut_Logger_Trait {
/**
* Logger status
*
* @var boolean
*/
protected $enable_logging = true;
/**
* Return error message
*
* @param String $message Log message.
*/
public function log_error( $message ) {
$logger = wc_get_logger();
$context = array( 'source' => 'revolut-gateway-for-woocommerce' );
if ( is_array( $message ) ) {
$message = wp_json_encode( $message );
}
$logger->error( $message, $context );
}
/**
* Return error message
*
* @param String $message Log message.
*/
public function log_info( $message ) {
$logger = wc_get_logger();
$context = array( 'source' => 'revolut-gateway-for-woocommerce' );
if ( is_array( $message ) ) {
$message = wp_json_encode( $message );
}
$logger->info( $message, $context );
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Settings trait
*
* Provides shared logic for navigation tab settings
*
* @package WooCommerce
* @category Payment Gateways
* @author Revolut
* @since 2.0
*/
defined( 'ABSPATH' ) || exit();
/**
* WC_Revolut_Settings_Trait trait.
*/
trait WC_Revolut_Settings_Trait {
/**
* Tab title
*
* @var string
*/
protected $tab_title;
/**
* API settings
*
* @var WC_Revolut_Settings_API
*/
protected $api_settings;
/**
* Add setting tab to admin configuration.
*
* @param array $tabs setting tabs.
*/
public function admin_nav_tab( $tabs ) {
$tabs[ $this->id ] = $this->tab_title;
return $tabs;
}
/**
* Display navigation for settings page
*/
public function output_settings_nav() {
if ( $this->check_is_get_data_submitted( 'page' ) && $this->check_is_get_data_submitted( 'section' ) ) {
$is_revolut_section = 'wc-settings' === $this->get_request_data( 'page' ) && in_array( $this->get_request_data( 'section' ), WC_REVOLUT_GATEWAYS, true ) || $this->get_request_data( 'section' ) === 'revolut_advanced_settings';
if ( $is_revolut_section ) {
include REVOLUT_PATH . 'templates/html-settings-nav.php';
}
}
}
/**
* Check API mode
*/
public function is_sandbox() {
return 'sandbox' === $this->get_option( 'mode' );
}
}