woocommerce-paypal-payments/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php

519 lines
13 KiB
PHP
Raw Normal View History

2020-08-18 09:04:58 +03:00
<?php
2020-08-28 08:13:45 +03:00
/**
* The Credit card gateway.
*
2020-09-11 14:11:10 +03:00
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
2020-08-28 08:13:45 +03:00
*/
2020-08-19 05:39:45 +03:00
2020-08-18 09:04:58 +03:00
declare(strict_types=1);
2020-09-11 14:11:10 +03:00
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
2020-08-18 09:04:58 +03:00
2022-07-13 09:51:23 +03:00
use Exception;
use Psr\Log\LoggerInterface;
use WC_Customer;
2022-07-13 09:51:23 +03:00
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
2022-07-13 09:51:23 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
2020-09-28 11:47:24 +03:00
use WooCommerce\PayPalCommerce\Onboarding\State;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\Session\SessionHandler;
2021-05-19 15:39:33 +02:00
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use WooCommerce\PayPalCommerce\Vaulting\VaultedCreditCardHandler;
2022-07-13 09:51:23 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
2020-09-28 11:47:24 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
2020-08-18 09:04:58 +03:00
2020-08-28 08:13:45 +03:00
/**
* Class CreditCardGateway
*/
class CreditCardGateway extends \WC_Payment_Gateway_CC {
use ProcessPaymentTrait, GatewaySettingsRendererTrait;
2020-08-27 11:08:36 +03:00
2020-09-11 13:38:02 +03:00
const ID = 'ppcp-credit-card-gateway';
2020-08-27 11:08:36 +03:00
2021-02-22 13:47:32 +02:00
/**
* The Settings Renderer.
2021-02-22 13:47:32 +02:00
*
* @var SettingsRenderer
2021-02-22 13:47:32 +02:00
*/
protected $settings_renderer;
2021-05-19 15:39:33 +02:00
/**
* The processor for orders.
2021-05-19 15:39:33 +02:00
*
* @var OrderProcessor
2021-05-19 15:39:33 +02:00
*/
protected $order_processor;
2021-05-19 15:39:33 +02:00
/**
* The settings.
*
* @var ContainerInterface
*/
protected $config;
/**
2022-08-12 10:56:25 +02:00
* The vaulted credit card handler.
*
* @var VaultedCreditCardHandler
*/
protected $vaulted_credit_card_handler;
2020-08-28 08:13:45 +03:00
/**
* The URL to the module.
*
* @var string
*/
private $module_url;
/**
* The Session Handler.
*
* @var SessionHandler
*/
protected $session_handler;
2020-09-28 11:47:24 +03:00
/**
* The refund processor.
*
* @var RefundProcessor
*/
private $refund_processor;
/**
* The state.
*
* @var State
*/
protected $state;
/**
* Service to get transaction url for an order.
*
* @var TransactionUrlProvider
*/
protected $transaction_url_provider;
2021-03-25 16:57:42 +01:00
/**
* The payment token repository.
*
* @var PaymentTokenRepository
*/
private $payment_token_repository;
/**
* The subscription helper.
*
* @var SubscriptionHelper
*/
protected $subscription_helper;
/**
* The logger.
*
* @var LoggerInterface
*/
protected $logger;
/**
* The payments endpoint
*
* @var PaymentsEndpoint
*/
protected $payments_endpoint;
2021-03-25 16:57:42 +01:00
/**
2020-08-28 08:13:45 +03:00
* CreditCardGateway constructor.
*
2022-08-12 10:56:25 +02:00
* @param SettingsRenderer $settings_renderer The Settings Renderer.
* @param OrderProcessor $order_processor The Order processor.
* @param ContainerInterface $config The settings.
* @param string $module_url The URL to the module.
* @param SessionHandler $session_handler The Session Handler.
* @param RefundProcessor $refund_processor The refund processor.
* @param State $state The state.
* @param TransactionUrlProvider $transaction_url_provider Service able to provide view transaction url base.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param LoggerInterface $logger The logger.
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
* @param VaultedCreditCardHandler $vaulted_credit_card_handler The vaulted credit card handler.
2020-08-28 08:13:45 +03:00
*/
2020-08-27 11:08:36 +03:00
public function __construct(
2020-08-28 08:13:45 +03:00
SettingsRenderer $settings_renderer,
OrderProcessor $order_processor,
2020-08-27 11:08:36 +03:00
ContainerInterface $config,
2020-08-28 08:13:45 +03:00
string $module_url,
2020-09-28 11:47:24 +03:00
SessionHandler $session_handler,
RefundProcessor $refund_processor,
2021-04-01 14:04:04 +03:00
State $state,
TransactionUrlProvider $transaction_url_provider,
SubscriptionHelper $subscription_helper,
LoggerInterface $logger,
PaymentsEndpoint $payments_endpoint,
VaultedCreditCardHandler $vaulted_credit_card_handler
2020-08-27 11:08:36 +03:00
) {
2022-08-12 10:56:25 +02:00
$this->id = self::ID;
$this->settings_renderer = $settings_renderer;
$this->order_processor = $order_processor;
$this->config = $config;
$this->module_url = $module_url;
$this->session_handler = $session_handler;
$this->refund_processor = $refund_processor;
$this->state = $state;
$this->transaction_url_provider = $transaction_url_provider;
$this->subscription_helper = $subscription_helper;
$this->logger = $logger;
$this->payments_endpoint = $payments_endpoint;
$this->vaulted_credit_card_handler = $vaulted_credit_card_handler;
2020-09-28 11:47:24 +03:00
if ( $state->current_state() === State::STATE_ONBOARDED ) {
$this->supports = array( 'refunds' );
}
if ( $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' ) ) {
2020-08-27 11:08:36 +03:00
$this->supports = array(
2020-09-28 11:47:24 +03:00
'refunds',
2020-08-27 11:08:36 +03:00
'products',
);
if (
( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) )
|| ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' )
) {
array_push(
$this->supports,
'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'
);
}
2020-08-27 11:08:36 +03:00
}
$this->method_title = __(
2022-10-07 12:08:07 +02:00
'Advanced Card Processing',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
);
$this->method_description = __(
2020-09-02 12:21:37 +03:00
'Accept debit and credit cards, and local payment methods with PayPals latest solution.',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
);
$this->title = $this->config->has( 'dcc_gateway_title' ) ?
$this->config->get( 'dcc_gateway_title' ) : $this->method_title;
$this->description = $this->config->has( 'dcc_gateway_description' ) ?
$this->config->get( 'dcc_gateway_description' ) : $this->method_description;
$this->init_form_fields();
$this->init_settings();
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
2021-03-25 16:57:42 +01:00
}
2020-08-27 11:08:36 +03:00
2020-08-28 08:13:45 +03:00
/**
* Initialize the form fields.
*/
2020-08-27 11:08:36 +03:00
public function init_form_fields() {
$this->form_fields = array(
'ppcp' => array(
2020-08-27 11:08:36 +03:00
'type' => 'ppcp',
),
);
}
2021-04-20 16:34:51 +03:00
/**
* Render the credit card fields.
*/
2021-04-20 16:43:08 +03:00
public function form() {
add_action( 'gettext', array( $this, 'replace_credit_card_cvv_label' ), 10, 3 );
2021-04-20 16:34:51 +03:00
parent::form();
2021-04-20 16:43:08 +03:00
remove_action( 'gettext', 'replace_credit_card_cvv_label' );
2021-04-20 16:34:51 +03:00
}
2021-04-20 16:43:08 +03:00
2021-04-20 16:34:51 +03:00
/**
* Replace WooCommerce credit card field label.
*
2021-04-20 16:51:48 +03:00
* @param string $translation Translated text.
* @param string $text Original text to translate.
* @param string $domain Text domain.
2021-04-20 16:34:51 +03:00
*
* @return string Translated field.
*/
2021-04-20 16:43:08 +03:00
public function replace_credit_card_cvv_label( string $translation, string $text, string $domain ): string {
if ( 'woocommerce' !== $domain || 'Card code' !== $text ) {
2021-04-20 16:34:51 +03:00
return $translation;
}
2021-04-20 16:43:08 +03:00
return __( 'CVV', 'woocommerce-paypal-payments' );
2021-04-20 16:34:51 +03:00
}
2020-08-28 08:13:45 +03:00
/**
* Returns the icons of the gateway.
2020-08-28 08:13:45 +03:00
*
* @return string
*/
public function get_icon() {
$icon = parent::get_icon();
2020-08-27 11:08:36 +03:00
$icons = $this->config->has( 'card_icons' ) ? (array) $this->config->get( 'card_icons' ) : array();
if ( empty( $icons ) ) {
return $icon;
2020-08-27 11:08:36 +03:00
}
2020-08-28 08:13:45 +03:00
$title_options = $this->card_labels();
$images = array_map(
function ( string $type ) use ( $title_options ): string {
$striped_dark = str_replace( '-dark', '', $type );
2020-08-27 11:08:36 +03:00
return '<img
title="' . esc_attr( $title_options[ $striped_dark ] ) . '"
2021-08-03 13:00:28 +02:00
src="' . esc_url( $this->module_url ) . 'assets/images/' . esc_attr( $type ) . '.svg"
2020-08-21 09:53:32 +03:00
class="ppcp-card-icon"
> ';
2020-08-27 11:08:36 +03:00
},
$icons
);
return implode( '', $images );
2020-08-27 11:08:36 +03:00
}
2020-08-28 08:13:45 +03:00
/**
* Returns an array of credit card names.
*
* @return array
*/
private function card_labels(): array {
2020-08-27 11:08:36 +03:00
return array(
'visa' => _x(
'Visa',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'mastercard' => _x(
'Mastercard',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'amex' => _x(
'American Express',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'discover' => _x(
'Discover',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'jcb' => _x(
'JCB',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'elo' => _x(
'Elo',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
'hiper' => _x(
'Hiper',
'Name of credit card',
'woocommerce-paypal-payments'
2020-08-27 11:08:36 +03:00
),
);
}
2020-09-02 14:53:30 +03:00
/**
* Whether the gateway is available or not.
*
* @return bool
*/
public function is_available() : bool {
return $this->is_enabled();
}
2020-09-28 11:47:24 +03:00
2022-07-13 09:51:23 +03:00
/**
* Process payment for a WooCommerce order.
*
* @param int $order_id The WooCommerce order id.
*
* @return array
*/
public function process_payment( $order_id ) {
$wc_order = wc_get_order( $order_id );
if ( ! is_a( $wc_order, WC_Order::class ) ) {
return $this->handle_payment_failure(
null,
new GatewayGenericException( new Exception( 'WC order was not found.' ) )
);
}
/**
* If customer has chosen a saved credit card payment.
*/
// phpcs:ignore WordPress.Security.NonceVerification.Missing
2022-10-18 15:59:11 +02:00
$saved_credit_card = wc_clean( wp_unslash( $_POST['saved_credit_card'] ?? '' ) );
2022-08-12 10:56:25 +02:00
if ( $saved_credit_card ) {
2022-07-13 09:51:23 +03:00
try {
$wc_order = $this->vaulted_credit_card_handler->handle_payment(
$saved_credit_card,
$wc_order
2022-07-13 09:51:23 +03:00
);
return $this->handle_payment_success( $wc_order );
2022-08-12 10:56:25 +02:00
} catch ( RuntimeException $error ) {
return $this->handle_payment_failure( $wc_order, $error );
2022-07-13 09:51:23 +03:00
}
}
/**
* If the WC_Order is paid through the approved webhook.
*/
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) {
return $this->handle_payment_success( $wc_order );
}
//phpcs:enable WordPress.Security.NonceVerification.Recommended
try {
if ( ! $this->order_processor->process( $wc_order ) ) {
return $this->handle_payment_failure(
$wc_order,
new Exception(
$this->order_processor->last_error()
)
);
}
if ( $this->subscription_helper->has_subscription( $order_id ) ) {
2022-07-13 15:20:39 +03:00
$this->schedule_saved_payment_check( $order_id, $wc_order->get_customer_id() );
2022-07-13 09:51:23 +03:00
}
return $this->handle_payment_success( $wc_order );
} catch ( PayPalApiException $error ) {
return $this->handle_payment_failure(
$wc_order,
new Exception(
Messages::generic_payment_error_message() . ' ' . $error->getMessage(),
$error->getCode(),
$error
)
);
} catch ( RuntimeException $error ) {
return $this->handle_payment_failure( $wc_order, $error );
}
}
2020-09-28 11:47:24 +03:00
/**
* Process refund.
*
* If the gateway declares 'refunds' support, this will allow it to refund.
* a passed in amount.
*
* @param int $order_id Order ID.
* @param float $amount Refund amount.
* @param string $reason Refund reason.
* @return boolean True or false based on success, or a WP_Error object.
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) {
$order = wc_get_order( $order_id );
if ( ! is_a( $order, \WC_Order::class ) ) {
return false;
}
return $this->refund_processor->process( $order, (float) $amount, (string) $reason );
}
2021-02-22 13:47:32 +02:00
/**
* Set the class property then call parent function.
*
* @param \WC_Order $order WC Order to get transaction url for.
*
* @inheritDoc
*/
public function get_transaction_url( $order ): string {
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
2021-02-22 13:47:32 +02:00
return parent::get_transaction_url( $order );
}
/**
* Initialize settings for WC.
*
* @return void
*/
public function init_settings() {
parent::init_settings();
// looks like in some cases WC uses this field instead of get_option.
$this->enabled = $this->is_enabled() ? 'yes' : '';
}
/**
* Get the option value for WC.
*
* @param string $key The option key.
* @param mixed $empty_value Value when empty.
* @return mixed
*/
public function get_option( $key, $empty_value = null ) {
if ( 'enabled' === $key ) {
return $this->is_enabled();
}
return parent::get_option( $key, $empty_value );
}
/**
* Handle update of WC settings.
*
* @param string $key The option key.
* @param string $value The option value.
* @return bool was anything saved?
*/
public function update_option( $key, $value = '' ) {
$ret = parent::update_option( $key, $value );
if ( 'enabled' === $key ) {
$this->config->set( 'dcc_enabled', 'yes' === $value );
$this->config->persist();
return true;
}
return $ret;
}
/**
* Returns if the gateway is enabled.
*
* @return bool
*/
private function is_enabled(): bool {
return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' );
}
2021-10-06 17:44:41 +03:00
/**
2022-07-13 10:02:15 +03:00
* Returns the settings renderer.
2021-10-06 17:44:41 +03:00
*
2022-07-13 10:02:15 +03:00
* @return SettingsRenderer
2021-10-06 17:44:41 +03:00
*/
2022-07-13 10:02:15 +03:00
protected function settings_renderer(): SettingsRenderer {
return $this->settings_renderer;
2021-10-06 17:44:41 +03:00
}
2020-08-19 05:39:45 +03:00
}