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' ), '', '' );
$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' => '',
),
);
}
/**
* 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(
'',
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 .= '';
$icons_str .= '
';
$icons_str .= '
';
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' ? '
'; $cardholder_name_field .= ''; $cardholder_name_field .= '
'; } return ''; } /** * 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 ''; } 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 ''; } }