2020-04-02 08:38:00 +03:00
|
|
|
|
<?php
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* The PayPal Payment Gateway
|
|
|
|
|
*
|
|
|
|
|
* @package Inpsyde\PayPalCommerce\WcGateway\Gateway
|
|
|
|
|
*/
|
2020-04-13 14:25:20 +03:00
|
|
|
|
|
2020-04-02 08:38:00 +03:00
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Inpsyde\PayPalCommerce\WcGateway\Gateway;
|
|
|
|
|
|
2020-08-26 09:43:48 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
2020-04-02 08:38:00 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\Session\SessionHandler;
|
2020-04-13 22:30:57 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
2020-04-28 12:31:12 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
2020-04-28 13:32:48 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
2020-09-02 12:21:13 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\SectionsRenderer;
|
2020-07-02 09:37:07 +03:00
|
|
|
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
2020-07-02 14:30:03 +03:00
|
|
|
|
use Psr\Container\ContainerInterface;
|
2020-04-02 08:38:00 +03:00
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* Class PayPalGateway
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
class PayPalGateway extends \WC_Payment_Gateway {
|
|
|
|
|
|
|
|
|
|
public const ID = 'ppcp-gateway';
|
|
|
|
|
public const CAPTURED_META_KEY = '_ppcp_paypal_captured';
|
|
|
|
|
public const INTENT_META_KEY = '_ppcp_paypal_intent';
|
|
|
|
|
public const ORDER_ID_META_KEY = '_ppcp_paypal_order_id';
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* The Settings Renderer.
|
|
|
|
|
*
|
|
|
|
|
* @var SettingsRenderer
|
|
|
|
|
*/
|
|
|
|
|
protected $settings_renderer;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The processor for authorized payments.
|
|
|
|
|
*
|
|
|
|
|
* @var AuthorizedPaymentsProcessor
|
|
|
|
|
*/
|
|
|
|
|
protected $authorized_payments;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The Authorized Order Action Notice.
|
|
|
|
|
*
|
|
|
|
|
* @var AuthorizeOrderActionNotice
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
protected $notice;
|
2020-08-28 08:13:45 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The processor for orders.
|
|
|
|
|
*
|
|
|
|
|
* @var OrderProcessor
|
|
|
|
|
*/
|
|
|
|
|
protected $order_processor;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The settings.
|
|
|
|
|
*
|
|
|
|
|
* @var ContainerInterface
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
protected $config;
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* The Session Handler.
|
|
|
|
|
*
|
|
|
|
|
* @var SessionHandler
|
|
|
|
|
*/
|
|
|
|
|
protected $session_handler;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* PayPalGateway constructor.
|
|
|
|
|
*
|
|
|
|
|
* @param SettingsRenderer $settings_renderer The Settings Renderer.
|
|
|
|
|
* @param OrderProcessor $order_processor The Order Processor.
|
|
|
|
|
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor.
|
|
|
|
|
* @param AuthorizeOrderActionNotice $notice The Order Action Notice object.
|
|
|
|
|
* @param ContainerInterface $config The settings.
|
|
|
|
|
* @param SessionHandler $session_handler The Session Handler.
|
|
|
|
|
*/
|
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,
|
|
|
|
|
AuthorizedPaymentsProcessor $authorized_payments_processor,
|
2020-08-27 11:08:36 +03:00
|
|
|
|
AuthorizeOrderActionNotice $notice,
|
|
|
|
|
ContainerInterface $config,
|
2020-08-28 08:13:45 +03:00
|
|
|
|
SessionHandler $session_handler
|
2020-08-27 11:08:36 +03:00
|
|
|
|
) {
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$this->id = self::ID;
|
|
|
|
|
$this->order_processor = $order_processor;
|
|
|
|
|
$this->authorized_payments = $authorized_payments_processor;
|
|
|
|
|
$this->notice = $notice;
|
|
|
|
|
$this->settings_renderer = $settings_renderer;
|
|
|
|
|
$this->config = $config;
|
|
|
|
|
$this->session_handler = $session_handler;
|
2020-08-27 11:08:36 +03:00
|
|
|
|
if (
|
|
|
|
|
defined( 'PPCP_FLAG_SUBSCRIPTION' )
|
|
|
|
|
&& PPCP_FLAG_SUBSCRIPTION
|
|
|
|
|
&& $this->config->has( 'vault_enabled' )
|
|
|
|
|
&& $this->config->get( 'vault_enabled' )
|
|
|
|
|
) {
|
|
|
|
|
$this->supports = array(
|
|
|
|
|
'products',
|
|
|
|
|
'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-09-02 12:21:13 +03:00
|
|
|
|
$this->method_title = $this->define_method_title();
|
|
|
|
|
$this->method_description = $this->define_method_description();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
$this->title = $this->config->has( 'title' ) ?
|
|
|
|
|
$this->config->get( 'title' ) : $this->method_title;
|
|
|
|
|
$this->description = $this->config->has( 'description' ) ?
|
|
|
|
|
$this->config->get( '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',
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* Whether the Gateway needs to be setup.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
public function needs_setup(): bool {
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* Initializes the form fields.
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
public function init_form_fields() {
|
|
|
|
|
$this->form_fields = array(
|
|
|
|
|
'enabled' => array(
|
2020-09-03 08:17:45 +03:00
|
|
|
|
'title' => __( 'Enable/Disable', 'paypal-for-woocommerce' ),
|
|
|
|
|
'type' => 'checkbox',
|
|
|
|
|
'desc_tip' => true,
|
|
|
|
|
'description' => __( 'In order to use PayPal or PayPal Card Processing, you need to enable the Gateway.', 'paypal-for-woocommerce' ),
|
|
|
|
|
'label' => __( 'Enable the PayPal Gateway', 'paypal-for-woocommerce' ),
|
|
|
|
|
'default' => 'no',
|
2020-08-27 11:08:36 +03:00
|
|
|
|
),
|
|
|
|
|
'ppcp' => array(
|
|
|
|
|
'type' => 'ppcp',
|
|
|
|
|
),
|
|
|
|
|
);
|
2020-09-02 13:02:31 +03:00
|
|
|
|
if ( $this->is_credit_card_tab() ) {
|
|
|
|
|
unset( $this->form_fields['enabled'] );
|
|
|
|
|
}
|
2020-08-27 11:08:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
2020-09-03 07:05:50 +03:00
|
|
|
|
* Process a payment for an WooCommerce order.
|
2020-08-28 08:13:45 +03:00
|
|
|
|
*
|
2020-09-03 07:05:50 +03:00
|
|
|
|
* @param int $order_id The WooCommerce order id.
|
2020-08-28 08:13:45 +03:00
|
|
|
|
*
|
|
|
|
|
* @return array|null
|
|
|
|
|
*/
|
|
|
|
|
public function process_payment( $order_id ): ?array {
|
2020-08-27 11:08:36 +03:00
|
|
|
|
global $woocommerce;
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$wc_order = wc_get_order( $order_id );
|
|
|
|
|
if ( ! is_a( $wc_order, \WC_Order::class ) ) {
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* If the WC_Order is payed through the approved webhook.
|
|
|
|
|
*/
|
2020-08-25 15:13:56 +03:00
|
|
|
|
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
2020-08-28 08:13:45 +03:00
|
|
|
|
if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) {
|
2020-09-02 08:43:41 +03:00
|
|
|
|
$this->session_handler->destroy_session_data();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return array(
|
|
|
|
|
'result' => 'success',
|
2020-08-28 08:13:45 +03:00
|
|
|
|
'redirect' => $this->get_return_url( $wc_order ),
|
2020-08-27 11:08:36 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
2020-08-25 15:13:56 +03:00
|
|
|
|
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
2020-08-25 11:58:02 +03:00
|
|
|
|
|
2020-08-27 11:08:36 +03:00
|
|
|
|
try {
|
2020-08-28 08:13:45 +03:00
|
|
|
|
if ( $this->order_processor->process( $wc_order, $woocommerce ) ) {
|
2020-09-02 08:43:41 +03:00
|
|
|
|
$this->session_handler->destroy_session_data();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return array(
|
|
|
|
|
'result' => 'success',
|
2020-08-28 08:13:45 +03:00
|
|
|
|
'redirect' => $this->get_return_url( $wc_order ),
|
2020-08-27 11:08:36 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch ( PayPalApiException $error ) {
|
2020-09-01 09:00:45 +03:00
|
|
|
|
if ( $error->has_detail( 'INSTRUMENT_DECLINED' ) ) {
|
2020-09-02 08:43:41 +03:00
|
|
|
|
$this->session_handler->increment_insufficient_funding_tries();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
$host = $this->config->has( 'sandbox_on' ) && $this->config->get( 'sandbox_on' ) ?
|
|
|
|
|
'https://www.sandbox.paypal.com/' : 'https://www.paypal.com/';
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$url = $host . 'checkoutnow?token=' . $this->session_handler->order()->id();
|
2020-09-02 08:43:41 +03:00
|
|
|
|
if ( $this->session_handler->insufficient_funding_tries() >= 3 ) {
|
|
|
|
|
$this->session_handler->destroy_session_data();
|
|
|
|
|
wc_add_notice(
|
|
|
|
|
__( 'Please use a different payment method.', 'paypal-for-woocommerce' ),
|
|
|
|
|
'error'
|
|
|
|
|
);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return array(
|
|
|
|
|
'result' => 'success',
|
|
|
|
|
'redirect' => $url,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-01 10:12:54 +03:00
|
|
|
|
$this->session_handler->destroy_session_data();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
}
|
2020-08-31 13:03:16 +03:00
|
|
|
|
wc_add_notice(
|
|
|
|
|
$this->order_processor->last_error(),
|
|
|
|
|
'error'
|
|
|
|
|
);
|
2020-08-27 11:08:36 +03:00
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
2020-09-03 07:05:50 +03:00
|
|
|
|
* Captures an authorized payment for an WooCommerce order.
|
2020-08-28 08:13:45 +03:00
|
|
|
|
*
|
2020-09-03 07:05:50 +03:00
|
|
|
|
* @param \WC_Order $wc_order The WooCommerce order.
|
2020-08-28 08:13:45 +03:00
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function capture_authorized_payment( \WC_Order $wc_order ): bool {
|
|
|
|
|
$is_processed = $this->authorized_payments->process( $wc_order );
|
|
|
|
|
$this->render_authorization_message_for_status( $this->authorized_payments->last_status() );
|
2020-08-27 11:08:36 +03:00
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
if ( $is_processed ) {
|
|
|
|
|
$wc_order->add_order_note(
|
2020-09-01 11:40:13 +03:00
|
|
|
|
__( 'Payment successfully captured.', 'paypal-for-woocommerce' )
|
2020-08-27 11:08:36 +03:00
|
|
|
|
);
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$wc_order->set_status( 'processing' );
|
|
|
|
|
$wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
|
|
|
|
|
$wc_order->save();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
if ( $this->authorized_payments->last_status() === AuthorizedPaymentsProcessor::ALREADY_CAPTURED ) {
|
|
|
|
|
if ( $wc_order->get_status() === 'on-hold' ) {
|
|
|
|
|
$wc_order->add_order_note(
|
2020-09-01 11:40:13 +03:00
|
|
|
|
__( 'Payment successfully captured.', 'paypal-for-woocommerce' )
|
2020-08-27 11:08:36 +03:00
|
|
|
|
);
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$wc_order->set_status( 'processing' );
|
2020-08-27 11:08:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
|
|
|
|
|
$wc_order->save();
|
2020-08-27 11:08:36 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* Displays the notice for a status.
|
|
|
|
|
*
|
|
|
|
|
* @param string $status The status.
|
|
|
|
|
*/
|
|
|
|
|
private function render_authorization_message_for_status( string $status ) {
|
2020-08-27 11:08:36 +03:00
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$message_mapping = array(
|
2020-08-27 11:08:36 +03:00
|
|
|
|
AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
|
|
|
|
|
AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
|
|
|
|
|
AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
|
|
|
|
|
AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
|
|
|
|
|
);
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$display_message = ( isset( $message_mapping[ $status ] ) ) ?
|
|
|
|
|
$message_mapping[ $status ]
|
2020-08-27 11:08:36 +03:00
|
|
|
|
: AuthorizeOrderActionNotice::FAILED;
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$this->notice->display_message( $display_message );
|
2020-08-27 11:08:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-28 08:13:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* Renders the settings.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2020-08-27 11:08:36 +03:00
|
|
|
|
public function generate_ppcp_html(): string {
|
|
|
|
|
|
|
|
|
|
ob_start();
|
2020-08-28 08:13:45 +03:00
|
|
|
|
$this->settings_renderer->render( false );
|
2020-08-27 11:08:36 +03:00
|
|
|
|
$content = ob_get_contents();
|
|
|
|
|
ob_end_clean();
|
|
|
|
|
return $content;
|
|
|
|
|
}
|
2020-09-02 12:21:13 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Defines the method title. If we are on the credit card tab in the settings, we want to change this.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
private function define_method_title(): string {
|
2020-09-02 13:02:31 +03:00
|
|
|
|
if ( $this->is_credit_card_tab() ) {
|
2020-09-02 12:41:10 +03:00
|
|
|
|
return __( 'PayPal Card Processing', 'paypal-for-woocommerce' );
|
2020-09-02 12:21:13 +03:00
|
|
|
|
}
|
2020-09-03 07:16:52 +03:00
|
|
|
|
if ( $this->is_paypal_tab() ) {
|
|
|
|
|
return __( 'PayPal Checkout', 'paypal-for-woocommerce' );
|
|
|
|
|
}
|
2020-09-03 06:58:47 +03:00
|
|
|
|
return __( 'PayPal', 'paypal-for-woocommerce' );
|
2020-09-02 12:21:13 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Defines the method description. If we are on the credit card tab in the settings, we want to change this.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
private function define_method_description(): string {
|
2020-09-02 13:02:31 +03:00
|
|
|
|
if ( $this->is_credit_card_tab() ) {
|
2020-09-02 12:21:13 +03:00
|
|
|
|
return __(
|
|
|
|
|
'Accept debit and credit cards, and local payment methods with PayPal’s latest solution.',
|
|
|
|
|
'paypal-for-woocommerce'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return __(
|
|
|
|
|
'Accept PayPal, PayPal Credit and alternative payment types with PayPal’s latest solution.',
|
|
|
|
|
'paypal-for-woocommerce'
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-09-02 13:02:31 +03:00
|
|
|
|
|
|
|
|
|
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
2020-09-02 14:53:30 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines, whether the current session is on the credit card tab in the admin settings.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
private function is_credit_card_tab() : bool {
|
2020-09-03 07:16:52 +03:00
|
|
|
|
return is_admin()
|
|
|
|
|
&& isset( $_GET[ SectionsRenderer::KEY ] )
|
|
|
|
|
&& CreditCardGateway::ID === sanitize_text_field( wp_unslash( $_GET[ SectionsRenderer::KEY ] ) );
|
|
|
|
|
|
|
|
|
|
}
|
2020-09-02 13:02:31 +03:00
|
|
|
|
|
2020-09-03 07:16:52 +03:00
|
|
|
|
/**
|
|
|
|
|
* Whether we are on the PayPal settings tab.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
private function is_paypal_tab() : bool {
|
|
|
|
|
return ! $this->is_credit_card_tab()
|
|
|
|
|
&& is_admin()
|
|
|
|
|
&& isset( $_GET['section'] )
|
|
|
|
|
&& self::ID === sanitize_text_field( wp_unslash( $_GET['section'] ) );
|
2020-09-02 13:02:31 +03:00
|
|
|
|
}
|
2020-09-02 12:41:10 +03:00
|
|
|
|
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
2020-04-06 11:16:18 +03:00
|
|
|
|
}
|