This commit is contained in:
David Remer 2020-08-27 11:08:36 +03:00
parent 61d24eede6
commit cfbd3ae428
52 changed files with 3985 additions and 4147 deletions

View file

@ -7,88 +7,81 @@ namespace Inpsyde\PayPalCommerce\WcGateway\Admin;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
class OrderTablePaymentStatusColumn
{
private const COLUMN_KEY = 'ppcp_payment_status';
private const INTENT = 'authorize';
private const AFTER_COLUMN_KEY = 'order_status';
private $settings;
class OrderTablePaymentStatusColumn {
public function __construct(Settings $settings)
{
$this->settings = $settings;
}
private const COLUMN_KEY = 'ppcp_payment_status';
private const INTENT = 'authorize';
private const AFTER_COLUMN_KEY = 'order_status';
private $settings;
public function register(array $columns): array
{
if (! $this->settings->has('intent') || $this->settings->get('intent') !== self::INTENT) {
return $columns;
}
public function __construct( Settings $settings ) {
$this->settings = $settings;
}
$statusColumnPosition = array_search(self::AFTER_COLUMN_KEY, array_keys($columns), true);
$toInsertPosition = false === $statusColumnPosition ? count($columns) : $statusColumnPosition + 1;
public function register( array $columns ): array {
if ( ! $this->settings->has( 'intent' ) || $this->settings->get( 'intent' ) !== self::INTENT ) {
return $columns;
}
$columns = array_merge(
array_slice($columns, 0, $toInsertPosition),
[
self::COLUMN_KEY => __('Payment Captured', 'woocommerce-paypal-commerce-gateway'),
],
array_slice($columns, $toInsertPosition)
);
$statusColumnPosition = array_search( self::AFTER_COLUMN_KEY, array_keys( $columns ), true );
$toInsertPosition = false === $statusColumnPosition ? count( $columns ) : $statusColumnPosition + 1;
return $columns;
}
$columns = array_merge(
array_slice( $columns, 0, $toInsertPosition ),
array(
self::COLUMN_KEY => __( 'Payment Captured', 'woocommerce-paypal-commerce-gateway' ),
),
array_slice( $columns, $toInsertPosition )
);
public function render(string $column, int $wcOrderId)
{
if (! $this->settings->has('intent') || $this->settings->get('intent') !== self::INTENT) {
return;
}
return $columns;
}
if (self::COLUMN_KEY !== $column) {
return;
}
public function render( string $column, int $wcOrderId ) {
if ( ! $this->settings->has( 'intent' ) || $this->settings->get( 'intent' ) !== self::INTENT ) {
return;
}
$wcOrder = wc_get_order($wcOrderId);
if ( self::COLUMN_KEY !== $column ) {
return;
}
if (! is_a($wcOrder, \WC_Order::class) || ! $this->renderForOrder($wcOrder)) {
return;
}
$wcOrder = wc_get_order( $wcOrderId );
if ($this->isCaptured($wcOrder)) {
$this->renderCompletedStatus();
return;
}
if ( ! is_a( $wcOrder, \WC_Order::class ) || ! $this->renderForOrder( $wcOrder ) ) {
return;
}
$this->renderIncompletedStatus();
}
if ( $this->isCaptured( $wcOrder ) ) {
$this->renderCompletedStatus();
return;
}
private function renderForOrder(\WC_Order $order): bool
{
return !empty($order->get_meta(PayPalGateway::CAPTURED_META_KEY));
}
$this->renderIncompletedStatus();
}
private function isCaptured(\WC_Order $wcOrder): bool
{
$captured = $wcOrder->get_meta(PayPalGateway::CAPTURED_META_KEY);
return wc_string_to_bool($captured);
}
private function renderForOrder( \WC_Order $order ): bool {
return ! empty( $order->get_meta( PayPalGateway::CAPTURED_META_KEY ) );
}
private function renderCompletedStatus()
{
printf(
'<span class="dashicons dashicons-yes">
private function isCaptured( \WC_Order $wcOrder ): bool {
$captured = $wcOrder->get_meta( PayPalGateway::CAPTURED_META_KEY );
return wc_string_to_bool( $captured );
}
private function renderCompletedStatus() {
printf(
'<span class="dashicons dashicons-yes">
<span class="screen-reader-text">%s</span>
</span>',
esc_html__('Payment captured', 'woocommerce-paypal-commerce-gateway')
);
}
esc_html__( 'Payment captured', 'woocommerce-paypal-commerce-gateway' )
);
}
private function renderIncompletedStatus()
{
printf(
'<mark class="onbackorder">%s</mark>',
esc_html__('Not captured', 'woocommerce-paypal-commerce-gateway')
);
}
private function renderIncompletedStatus() {
printf(
'<mark class="onbackorder">%s</mark>',
esc_html__( 'Not captured', 'woocommerce-paypal-commerce-gateway' )
);
}
}

View file

@ -6,33 +6,32 @@ namespace Inpsyde\PayPalCommerce\WcGateway\Admin;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
class PaymentStatusOrderDetail
{
public function render(int $wcOrderId)
{
$wcOrder = new \WC_Order($wcOrderId);
$intent = $wcOrder->get_meta(PayPalGateway::INTENT_META_KEY);
$captured = $wcOrder->get_meta(PayPalGateway::CAPTURED_META_KEY);
class PaymentStatusOrderDetail {
if (strcasecmp($intent, 'AUTHORIZE') !== 0) {
return;
}
public function render( int $wcOrderId ) {
$wcOrder = new \WC_Order( $wcOrderId );
$intent = $wcOrder->get_meta( PayPalGateway::INTENT_META_KEY );
$captured = $wcOrder->get_meta( PayPalGateway::CAPTURED_META_KEY );
if (!empty($captured) && wc_string_to_bool($captured)) {
return;
}
if ( strcasecmp( $intent, 'AUTHORIZE' ) !== 0 ) {
return;
}
printf(
if ( ! empty( $captured ) && wc_string_to_bool( $captured ) ) {
return;
}
printf(
// @phpcs:ignore Inpsyde.CodeQuality.LineLength.TooLong
'<li class="wide"><p><mark class="order-status status-on-hold"><span>%1$s</span></mark></p><p>%2$s</p></li>',
esc_html__(
'Not captured',
'woocommerce-paypal-commerce-gateway'
),
esc_html__(
'To capture the payment select capture action from the list below.',
'woocommerce-paypal-commerce-gateway'
),
);
}
'<li class="wide"><p><mark class="order-status status-on-hold"><span>%1$s</span></mark></p><p>%2$s</p></li>',
esc_html__(
'Not captured',
'woocommerce-paypal-commerce-gateway'
),
esc_html__(
'To capture the payment select capture action from the list below.',
'woocommerce-paypal-commerce-gateway'
),
);
}
}

View file

@ -11,118 +11,114 @@ use Inpsyde\PayPalCommerce\Session\SessionHandler;
* Service that fills checkout address fields
* with address selected via PayPal
*/
class CheckoutPayPalAddressPreset
{
class CheckoutPayPalAddressPreset {
private $shippingCache = [];
/**
* @var SessionHandler
*/
private $sessionHandler;
private $shippingCache = array();
/**
* @param SessionHandler $sessionHandler
*/
public function __construct(SessionHandler $sessionHandler)
{
$this->sessionHandler = $sessionHandler;
}
/**
* @var SessionHandler
*/
private $sessionHandler;
/**
* @wp-hook woocommerce_checkout_get_value
* @param string|null
* @param string $fieldId
*
* @return string|null
*/
public function filterCheckoutFiled($defaultValue, $fieldId): ?string
{
if (! is_string($defaultValue)) {
$defaultValue = null;
}
/**
* @param SessionHandler $sessionHandler
*/
public function __construct( SessionHandler $sessionHandler ) {
$this->sessionHandler = $sessionHandler;
}
if (!is_string($fieldId)) {
return $defaultValue;
}
/**
* @wp-hook woocommerce_checkout_get_value
* @param string|null
* @param string $fieldId
*
* @return string|null
*/
public function filterCheckoutFiled( $defaultValue, $fieldId ): ?string {
if ( ! is_string( $defaultValue ) ) {
$defaultValue = null;
}
return $this->readPresetForField($fieldId) ?? $defaultValue;
}
if ( ! is_string( $fieldId ) ) {
return $defaultValue;
}
private function readPresetForField(string $fieldId): ?string
{
$order = $this->sessionHandler->order();
if (! $order) {
return null;
}
return $this->readPresetForField( $fieldId ) ?? $defaultValue;
}
$shipping = $this->readShippingFromOrder();
$payer = $order->payer();
private function readPresetForField( string $fieldId ): ?string {
$order = $this->sessionHandler->order();
if ( ! $order ) {
return null;
}
$addressMap = [
'billing_address_1' => 'addressLine1',
'billing_address_2' => 'addressLine2',
'billing_postcode' => 'postalCode',
'billing_country' => 'countryCode',
'billing_city' => 'adminArea2',
'billing_state' => 'adminArea1',
];
$payerNameMap = [
'billing_last_name' => 'surname',
'billing_first_name' => 'givenName',
];
$payerMap = [
'billing_email' => 'emailAddress',
];
$payerPhoneMap = [
'billing_phone' => 'nationalNumber',
];
$shipping = $this->readShippingFromOrder();
$payer = $order->payer();
if (array_key_exists($fieldId, $addressMap) && $shipping) {
return $shipping->address()->{$addressMap[$fieldId]}() ?: null;
}
$addressMap = array(
'billing_address_1' => 'addressLine1',
'billing_address_2' => 'addressLine2',
'billing_postcode' => 'postalCode',
'billing_country' => 'countryCode',
'billing_city' => 'adminArea2',
'billing_state' => 'adminArea1',
);
$payerNameMap = array(
'billing_last_name' => 'surname',
'billing_first_name' => 'givenName',
);
$payerMap = array(
'billing_email' => 'emailAddress',
);
$payerPhoneMap = array(
'billing_phone' => 'nationalNumber',
);
if (array_key_exists($fieldId, $payerNameMap) && $payer) {
return $payer->name()->{$payerNameMap[$fieldId]}() ?: null;
}
if ( array_key_exists( $fieldId, $addressMap ) && $shipping ) {
return $shipping->address()->{$addressMap[ $fieldId ]}() ?: null;
}
if (array_key_exists($fieldId, $payerMap) && $payer) {
return $payer->{$payerMap[$fieldId]}() ?: null;
}
if ( array_key_exists( $fieldId, $payerNameMap ) && $payer ) {
return $payer->name()->{$payerNameMap[ $fieldId ]}() ?: null;
}
if (
array_key_exists($fieldId, $payerPhoneMap)
&& $payer
&& $payer->phone()
&& $payer->phone()->phone()
) {
return $payer->phone()->phone()->{$payerPhoneMap[$fieldId]}() ?: null;
}
if ( array_key_exists( $fieldId, $payerMap ) && $payer ) {
return $payer->{$payerMap[ $fieldId ]}() ?: null;
}
return null;
}
if (
array_key_exists( $fieldId, $payerPhoneMap )
&& $payer
&& $payer->phone()
&& $payer->phone()->phone()
) {
return $payer->phone()->phone()->{$payerPhoneMap[ $fieldId ]}() ?: null;
}
private function readShippingFromOrder(): ?Shipping
{
$order = $this->sessionHandler->order();
if (! $order) {
return null;
}
return null;
}
if (array_key_exists($order->id(), $this->shippingCache)) {
return $this->shippingCache[$order->id()];
}
private function readShippingFromOrder(): ?Shipping {
$order = $this->sessionHandler->order();
if ( ! $order ) {
return null;
}
$shipping = null;
foreach ($this->sessionHandler->order()->purchaseUnits() as $unit) {
$shipping = $unit->shipping();
if ($shipping) {
break;
}
}
if ( array_key_exists( $order->id(), $this->shippingCache ) ) {
return $this->shippingCache[ $order->id() ];
}
$this->shippingCache[$order->id()] = $shipping;
$shipping = null;
foreach ( $this->sessionHandler->order()->purchaseUnits() as $unit ) {
$shipping = $unit->shipping();
if ( $shipping ) {
break;
}
}
return $shipping;
}
$this->shippingCache[ $order->id() ] = $shipping;
return $shipping;
}
}

View file

@ -9,62 +9,59 @@ use Inpsyde\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Psr\Container\ContainerInterface;
class DisableGateways
{
class DisableGateways {
private $sessionHandler;
private $settings;
public function __construct(
SessionHandler $sessionHandler,
ContainerInterface $settings
) {
$this->sessionHandler = $sessionHandler;
$this->settings = $settings;
}
private $sessionHandler;
private $settings;
public function __construct(
SessionHandler $sessionHandler,
ContainerInterface $settings
) {
public function handler(array $methods): array
{
if (! isset($methods[PayPalGateway::ID]) && ! isset($methods[CreditCardGateway::ID])) {
return $methods;
}
if (
! $this->settings->has('merchant_email')
|| ! is_email($this->settings->get('merchant_email'))
) {
unset($methods[PayPalGateway::ID]);
unset($methods[CreditCardGateway::ID]);
return $methods;
}
$this->sessionHandler = $sessionHandler;
$this->settings = $settings;
}
if (! $this->settings->has('client_id') || empty($this->settings->get('client_id'))) {
unset($methods[CreditCardGateway::ID]);
}
public function handler( array $methods ): array {
if ( ! isset( $methods[ PayPalGateway::ID ] ) && ! isset( $methods[ CreditCardGateway::ID ] ) ) {
return $methods;
}
if (
! $this->settings->has( 'merchant_email' )
|| ! is_email( $this->settings->get( 'merchant_email' ) )
) {
unset( $methods[ PayPalGateway::ID ] );
unset( $methods[ CreditCardGateway::ID ] );
return $methods;
}
if (! $this->needsToDisableGateways()) {
return $methods;
}
if ( ! $this->settings->has( 'client_id' ) || empty( $this->settings->get( 'client_id' ) ) ) {
unset( $methods[ CreditCardGateway::ID ] );
}
if ($this->isCreditCard()) {
return [CreditCardGateway::ID => $methods[CreditCardGateway::ID]];
}
return [PayPalGateway::ID => $methods[PayPalGateway::ID]];
}
if ( ! $this->needsToDisableGateways() ) {
return $methods;
}
private function needsToDisableGateways(): bool
{
return $this->sessionHandler->order() !== null;
}
if ( $this->isCreditCard() ) {
return array( CreditCardGateway::ID => $methods[ CreditCardGateway::ID ] );
}
return array( PayPalGateway::ID => $methods[ PayPalGateway::ID ] );
}
private function isCreditCard(): bool
{
$order = $this->sessionHandler->order();
if (! $order) {
return false;
}
if (! $order->paymentSource() || ! $order->paymentSource()->card()) {
return false;
}
return true;
}
private function needsToDisableGateways(): bool {
return $this->sessionHandler->order() !== null;
}
private function isCreditCard(): bool {
$order = $this->sessionHandler->order();
if ( ! $order ) {
return false;
}
if ( ! $order->paymentSource() || ! $order->paymentSource()->card() ) {
return false;
}
return true;
}
}

View file

@ -3,52 +3,50 @@ declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\WcGateway\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Inpsyde\PayPalCommerce\Webhooks\Handler\PrefixTrait;
class ReturnUrlEndpoint
{
use PrefixTrait;
public const ENDPOINT = 'ppc-return-url';
class ReturnUrlEndpoint {
private $gateway;
private $orderEndpoint;
public function __construct(PayPalGateway $gateway, OrderEndpoint $orderEndpoint, string $prefix)
{
$this->gateway = $gateway;
$this->orderEndpoint = $orderEndpoint;
$this->prefix = $prefix;
}
use PrefixTrait;
public const ENDPOINT = 'ppc-return-url';
public function handleRequest() {
private $gateway;
private $orderEndpoint;
public function __construct( PayPalGateway $gateway, OrderEndpoint $orderEndpoint, string $prefix ) {
$this->gateway = $gateway;
$this->orderEndpoint = $orderEndpoint;
$this->prefix = $prefix;
}
if (! isset($_GET['token'])) {
exit;
}
$token = sanitize_text_field(wp_unslash($_GET['token']));
$order = $this->orderEndpoint->order($token);
if (! $order) {
exit;
}
public function handleRequest() {
$wcOrderId = $this->sanitizeCustomId($order->purchaseUnits()[0]->customId());
if (! $wcOrderId) {
exit;
}
if ( ! isset( $_GET['token'] ) ) {
exit;
}
$token = sanitize_text_field( wp_unslash( $_GET['token'] ) );
$order = $this->orderEndpoint->order( $token );
if ( ! $order ) {
exit;
}
$wcOrder = wc_get_order($wcOrderId);
if (! $wcOrder) {
exit;
}
$wcOrderId = $this->sanitizeCustomId( $order->purchaseUnits()[0]->customId() );
if ( ! $wcOrderId ) {
exit;
}
$success = $this->gateway->process_payment($wcOrderId);
if (isset($success['result']) && $success['result'] === 'success') {
wp_redirect($success['redirect']);
exit;
}
wp_redirect(wc_get_checkout_url());
exit;
}
$wcOrder = wc_get_order( $wcOrderId );
if ( ! $wcOrder ) {
exit;
}
$success = $this->gateway->process_payment( $wcOrderId );
if ( isset( $success['result'] ) && $success['result'] === 'success' ) {
wp_redirect( $success['redirect'] );
exit;
}
wp_redirect( wc_get_checkout_url() );
exit;
}
}

View file

@ -7,7 +7,7 @@ namespace Inpsyde\PayPalCommerce\WcGateway\Exception;
use Exception;
use Psr\Container\NotFoundExceptionInterface;
class NotFoundException extends Exception implements NotFoundExceptionInterface
{
class NotFoundException extends Exception implements NotFoundExceptionInterface {
}

View file

@ -15,165 +15,161 @@ use Psr\Container\ContainerInterface;
//phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
//phpcs:disable Inpsyde.CodeQuality.NoAccessors.NoGetter
//phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType
class CreditCardGateway extends PayPalGateway
{
public const ID = 'ppcp-credit-card-gateway';
class CreditCardGateway extends PayPalGateway {
private $moduleUrl;
public function __construct(
SettingsRenderer $settingsRenderer,
OrderProcessor $orderProcessor,
AuthorizedPaymentsProcessor $authorizedPayments,
AuthorizeOrderActionNotice $notice,
ContainerInterface $config,
string $moduleUrl,
SessionHandler $sessionHandler
) {
public const ID = 'ppcp-credit-card-gateway';
$this->id = self::ID;
$this->orderProcessor = $orderProcessor;
$this->authorizedPayments = $authorizedPayments;
$this->notice = $notice;
$this->settingsRenderer = $settingsRenderer;
$this->config = $config;
$this->sessionHandler = $sessionHandler;
if (
defined('PPCP_FLAG_SUBSCRIPTION')
&& PPCP_FLAG_SUBSCRIPTION
&& $this->config->has('vault_enabled')
&& $this->config->get('vault_enabled')
) {
$this->supports = [
'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',
];
}
private $moduleUrl;
public function __construct(
SettingsRenderer $settingsRenderer,
OrderProcessor $orderProcessor,
AuthorizedPaymentsProcessor $authorizedPayments,
AuthorizeOrderActionNotice $notice,
ContainerInterface $config,
string $moduleUrl,
SessionHandler $sessionHandler
) {
$this->method_title = __(
'PayPal Credit Card Processing',
'woocommerce-paypal-commerce-gateway'
);
$this->method_description = __(
'Provide your customers with the option to pay with credit card.',
'woocommerce-paypal-commerce-gateway'
);
$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->id = self::ID;
$this->orderProcessor = $orderProcessor;
$this->authorizedPayments = $authorizedPayments;
$this->notice = $notice;
$this->settingsRenderer = $settingsRenderer;
$this->config = $config;
$this->sessionHandler = $sessionHandler;
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',
);
}
$this->init_form_fields();
$this->init_settings();
$this->method_title = __(
'PayPal Credit Card Processing',
'woocommerce-paypal-commerce-gateway'
);
$this->method_description = __(
'Provide your customers with the option to pay with credit card.',
'woocommerce-paypal-commerce-gateway'
);
$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;
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
[
$this,
'process_admin_options',
]
);
$this->init_form_fields();
$this->init_settings();
$this->moduleUrl = $moduleUrl;
}
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
public function init_form_fields()
{
$this->form_fields = [
'enabled' => [
'title' => __('Enable/Disable', 'woocommerce-paypal-commerce-gateway'),
'type' => 'checkbox',
'label' => __('Enable Credit Card Payments', 'woocommerce-paypal-commerce-gateway'),
'default' => 'no',
],
'ppcp' => [
'type' => 'ppcp',
],
];
}
$this->moduleUrl = $moduleUrl;
}
public function generate_ppcp_html(): string
{
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-paypal-commerce-gateway' ),
'type' => 'checkbox',
'label' => __( 'Enable Credit Card Payments', 'woocommerce-paypal-commerce-gateway' ),
'default' => 'no',
),
'ppcp' => array(
'type' => 'ppcp',
),
);
}
ob_start();
$this->settingsRenderer->render(true);
$content = ob_get_contents();
ob_end_clean();
return $content;
}
public function generate_ppcp_html(): string {
public function get_title()
{
ob_start();
$this->settingsRenderer->render( true );
$content = ob_get_contents();
ob_end_clean();
return $content;
}
if (is_admin()) {
return parent::get_title();
}
$title = parent::get_title();
$icons = $this->config->has('card_icons') ? (array) $this->config->get('card_icons') : [];
if (empty($icons)) {
return $title;
}
public function get_title() {
$titleOptions = $this->cardLabels();
$images = array_map(
function (string $type) use ($titleOptions): string {
return '<img
title="' . esc_attr($titleOptions[$type]) . '"
src="' . esc_url($this->moduleUrl) . '/assets/images/' . esc_attr($type) . '.svg"
if ( is_admin() ) {
return parent::get_title();
}
$title = parent::get_title();
$icons = $this->config->has( 'card_icons' ) ? (array) $this->config->get( 'card_icons' ) : array();
if ( empty( $icons ) ) {
return $title;
}
$titleOptions = $this->cardLabels();
$images = array_map(
function ( string $type ) use ( $titleOptions ): string {
return '<img
title="' . esc_attr( $titleOptions[ $type ] ) . '"
src="' . esc_url( $this->moduleUrl ) . '/assets/images/' . esc_attr( $type ) . '.svg"
class="ppcp-card-icon"
> ';
},
$icons
);
return $title . implode('', $images);
}
},
$icons
);
return $title . implode( '', $images );
}
private function cardLabels(): array
{
return [
'visa' => _x(
'Visa',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'mastercard' => _x(
'Mastercard',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'amex' => _x(
'American Express',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'discover' => _x(
'Discover',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'jcb' => _x(
'JCB',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'elo' => _x(
'Elo',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'hiper' => _x(
'Hiper',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
];
}
private function cardLabels(): array {
return array(
'visa' => _x(
'Visa',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'mastercard' => _x(
'Mastercard',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'amex' => _x(
'American Express',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'discover' => _x(
'Discover',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'jcb' => _x(
'JCB',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'elo' => _x(
'Elo',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
'hiper' => _x(
'Hiper',
'Name of credit card',
'woocommerce-paypal-commerce-gateway'
),
);
}
}

View file

@ -23,206 +23,200 @@ use Psr\Container\ContainerInterface;
//phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
//phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
class PayPalGateway extends \WC_Payment_Gateway
{
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';
protected $settingsRenderer;
protected $authorizedPayments;
protected $notice;
protected $orderProcessor;
protected $config;
protected $sessionHandler;
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';
public function __construct(
SettingsRenderer $settingsRenderer,
OrderProcessor $orderProcessor,
AuthorizedPaymentsProcessor $authorizedPayments,
AuthorizeOrderActionNotice $notice,
ContainerInterface $config,
SessionHandler $sessionHandler
) {
protected $settingsRenderer;
protected $authorizedPayments;
protected $notice;
protected $orderProcessor;
protected $config;
protected $sessionHandler;
$this->id = self::ID;
$this->orderProcessor = $orderProcessor;
$this->authorizedPayments = $authorizedPayments;
$this->notice = $notice;
$this->settingsRenderer = $settingsRenderer;
$this->config = $config;
$this->sessionHandler = $sessionHandler;
if (
defined('PPCP_FLAG_SUBSCRIPTION')
&& PPCP_FLAG_SUBSCRIPTION
&& $this->config->has('vault_enabled')
&& $this->config->get('vault_enabled')
) {
$this->supports = [
'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',
];
}
public function __construct(
SettingsRenderer $settingsRenderer,
OrderProcessor $orderProcessor,
AuthorizedPaymentsProcessor $authorizedPayments,
AuthorizeOrderActionNotice $notice,
ContainerInterface $config,
SessionHandler $sessionHandler
) {
$this->method_title = __('PayPal', 'woocommerce-paypal-commerce-gateway');
$this->method_description = __(
'Provide your customers with the PayPal payment option.',
'woocommerce-paypal-commerce-gateway'
);
$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->id = self::ID;
$this->orderProcessor = $orderProcessor;
$this->authorizedPayments = $authorizedPayments;
$this->notice = $notice;
$this->settingsRenderer = $settingsRenderer;
$this->config = $config;
$this->sessionHandler = $sessionHandler;
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',
);
}
$this->init_form_fields();
$this->init_settings();
$this->method_title = __( 'PayPal', 'woocommerce-paypal-commerce-gateway' );
$this->method_description = __(
'Provide your customers with the PayPal payment option.',
'woocommerce-paypal-commerce-gateway'
);
$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;
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
[
$this,
'process_admin_options',
]
);
}
$this->init_form_fields();
$this->init_settings();
public function needs_setup(): bool
{
add_action(
'woocommerce_update_options_payment_gateways_' . $this->id,
array(
$this,
'process_admin_options',
)
);
}
return true;
}
public function needs_setup(): bool {
public function init_form_fields()
{
$this->form_fields = [
'enabled' => [
'title' => __('Enable/Disable', 'woocommerce-paypal-commerce-gateway'),
'type' => 'checkbox',
'label' => __('Enable PayPal Payments', 'woocommerce-paypal-commerce-gateway'),
'default' => 'no',
],
'ppcp' => [
'type' => 'ppcp',
],
];
}
return true;
}
public function process_payment($orderId): ?array
{
global $woocommerce;
$wcOrder = wc_get_order($orderId);
if (! is_a($wcOrder, \WC_Order::class)) {
return null;
}
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-paypal-commerce-gateway' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal Payments', 'woocommerce-paypal-commerce-gateway' ),
'default' => 'no',
),
'ppcp' => array(
'type' => 'ppcp',
),
);
}
/**
* If the WC_Order is payed through the approved webhook.
*/
public function process_payment( $orderId ): ?array {
global $woocommerce;
$wcOrder = wc_get_order( $orderId );
if ( ! is_a( $wcOrder, \WC_Order::class ) ) {
return null;
}
/**
* If the WC_Order is payed through the approved webhook.
*/
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if (isset($_REQUEST['ppcp-resume-order']) && $wcOrder->has_status('processing')) {
return [
'result' => 'success',
'redirect' => $this->get_return_url($wcOrder),
];
}
if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wcOrder->has_status( 'processing' ) ) {
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wcOrder ),
);
}
//phpcs:enable WordPress.Security.NonceVerification.Recommended
try {
if ($this->orderProcessor->process($wcOrder, $woocommerce)) {
return [
'result' => 'success',
'redirect' => $this->get_return_url($wcOrder),
];
}
} catch (PayPalApiException $error) {
if ($error->hasDetail('INSTRUMENT_DECLINED')) {
$host = $this->config->has('sandbox_on') && $this->config->get('sandbox_on') ?
'https://www.sandbox.paypal.com/' : 'https://www.paypal.com/';
$url = $host . 'checkoutnow?token=' . $this->sessionHandler->order()->id();
try {
if ( $this->orderProcessor->process( $wcOrder, $woocommerce ) ) {
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wcOrder ),
);
}
} catch ( PayPalApiException $error ) {
if ( $error->hasDetail( 'INSTRUMENT_DECLINED' ) ) {
$host = $this->config->has( 'sandbox_on' ) && $this->config->get( 'sandbox_on' ) ?
'https://www.sandbox.paypal.com/' : 'https://www.paypal.com/';
$url = $host . 'checkoutnow?token=' . $this->sessionHandler->order()->id();
return [
'result' => 'success',
'redirect' => $url,
];
}
return array(
'result' => 'success',
'redirect' => $url,
);
}
$this->sessionHandler->destroySessionData();
wc_add_notice(
__(
'Something went wrong. Please try again.',
'woocommerce-paypal-commerce-gateway'
),
'error'
);
}
$this->sessionHandler->destroySessionData();
wc_add_notice(
__(
'Something went wrong. Please try again.',
'woocommerce-paypal-commerce-gateway'
),
'error'
);
}
return null;
}
return null;
}
public function captureAuthorizedPayment(\WC_Order $wcOrder): bool
{
$isProcessed = $this->authorizedPayments->process($wcOrder);
$this->renderAuthorizationMessageForStatus($this->authorizedPayments->lastStatus());
public function captureAuthorizedPayment( \WC_Order $wcOrder ): bool {
$isProcessed = $this->authorizedPayments->process( $wcOrder );
$this->renderAuthorizationMessageForStatus( $this->authorizedPayments->lastStatus() );
if ($isProcessed) {
$wcOrder->add_order_note(
__('Payment successfully captured.', 'woocommerce-paypal-commerce-gateway')
);
if ( $isProcessed ) {
$wcOrder->add_order_note(
__( 'Payment successfully captured.', 'woocommerce-paypal-commerce-gateway' )
);
$wcOrder->set_status('processing');
$wcOrder->update_meta_data(self::CAPTURED_META_KEY, 'true');
$wcOrder->save();
return true;
}
$wcOrder->set_status( 'processing' );
$wcOrder->update_meta_data( self::CAPTURED_META_KEY, 'true' );
$wcOrder->save();
return true;
}
if ($this->authorizedPayments->lastStatus() === AuthorizedPaymentsProcessor::ALREADY_CAPTURED) {
if ($wcOrder->get_status() === 'on-hold') {
$wcOrder->add_order_note(
__('Payment successfully captured.', 'woocommerce-paypal-commerce-gateway')
);
$wcOrder->set_status('processing');
}
if ( $this->authorizedPayments->lastStatus() === AuthorizedPaymentsProcessor::ALREADY_CAPTURED ) {
if ( $wcOrder->get_status() === 'on-hold' ) {
$wcOrder->add_order_note(
__( 'Payment successfully captured.', 'woocommerce-paypal-commerce-gateway' )
);
$wcOrder->set_status( 'processing' );
}
$wcOrder->update_meta_data(self::CAPTURED_META_KEY, 'true');
$wcOrder->save();
return true;
}
return false;
}
$wcOrder->update_meta_data( self::CAPTURED_META_KEY, 'true' );
$wcOrder->save();
return true;
}
return false;
}
private function renderAuthorizationMessageForStatus(string $status)
{
private function renderAuthorizationMessageForStatus( string $status ) {
$messageMapping = [
AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
];
$displayMessage = (isset($messageMapping[$status])) ?
$messageMapping[$status]
: AuthorizeOrderActionNotice::FAILED;
$this->notice->displayMessage($displayMessage);
}
$messageMapping = array(
AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
);
$displayMessage = ( isset( $messageMapping[ $status ] ) ) ?
$messageMapping[ $status ]
: AuthorizeOrderActionNotice::FAILED;
$this->notice->displayMessage( $displayMessage );
}
public function generate_ppcp_html(): string
{
public function generate_ppcp_html(): string {
ob_start();
$this->settingsRenderer->render(false);
$content = ob_get_contents();
ob_end_clean();
return $content;
}
ob_start();
$this->settingsRenderer->render( false );
$content = ob_get_contents();
ob_end_clean();
return $content;
}
}

View file

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\WcGateway\Gateway;
interface WcGatewayInterface
{
interface WcGatewayInterface {
}

View file

@ -6,85 +6,82 @@ namespace Inpsyde\PayPalCommerce\WcGateway\Notice;
use Inpsyde\PayPalCommerce\AdminNotices\Entity\Message;
class AuthorizeOrderActionNotice
{
public const QUERY_PARAM = 'ppcp-authorized-message';
class AuthorizeOrderActionNotice {
public const NO_INFO = 81;
public const ALREADY_CAPTURED = 82;
public const FAILED = 83;
public const SUCCESS = 84;
public const NOT_FOUND = 85;
public const QUERY_PARAM = 'ppcp-authorized-message';
public function message(): ?Message
{
public const NO_INFO = 81;
public const ALREADY_CAPTURED = 82;
public const FAILED = 83;
public const SUCCESS = 84;
public const NOT_FOUND = 85;
$message = $this->currentMessage();
if (! $message) {
return null;
}
public function message(): ?Message {
return new Message($message['message'], $message['type']);
}
$message = $this->currentMessage();
if ( ! $message ) {
return null;
}
private function currentMessage(): array
{
$messages[self::NO_INFO] = [
'message' => __(
'Could not retrieve information. Try again later.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
];
$messages[self::ALREADY_CAPTURED] = [
'message' => __(
'Payment already captured.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
];
$messages[self::FAILED] = [
'message' => __(
'Failed to capture. Try again later.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
];
$messages[self::NOT_FOUND] = [
'message' => __(
'Could not find payment to process.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
];
$messages[self::SUCCESS] = [
'message' => __(
'Payment successfully captured.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'success',
];
return new Message( $message['message'], $message['type'] );
}
private function currentMessage(): array {
$messages[ self::NO_INFO ] = array(
'message' => __(
'Could not retrieve information. Try again later.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
);
$messages[ self::ALREADY_CAPTURED ] = array(
'message' => __(
'Payment already captured.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
);
$messages[ self::FAILED ] = array(
'message' => __(
'Failed to capture. Try again later.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
);
$messages[ self::NOT_FOUND ] = array(
'message' => __(
'Could not find payment to process.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'error',
);
$messages[ self::SUCCESS ] = array(
'message' => __(
'Payment successfully captured.',
'woocommerce-paypal-commerce-gateway'
),
'type' => 'success',
);
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if (! isset($_GET[self::QUERY_PARAM])) { // Input ok.
return [];
}
$messageId = absint($_GET[self::QUERY_PARAM]); // Input ok.
if ( ! isset( $_GET[ self::QUERY_PARAM ] ) ) { // Input ok.
return array();
}
$messageId = absint( $_GET[ self::QUERY_PARAM ] ); // Input ok.
//phpcs:enable WordPress.Security.NonceVerification.Recommended
return (isset($messages[$messageId])) ? $messages[$messageId] : [];
}
return ( isset( $messages[ $messageId ] ) ) ? $messages[ $messageId ] : array();
}
public function displayMessage(int $messageCode): void
{
add_filter(
'redirect_post_location',
static function ($location) use ($messageCode) {
return add_query_arg(
self::QUERY_PARAM,
$messageCode,
$location
);
}
);
}
public function displayMessage( int $messageCode ): void {
add_filter(
'redirect_post_location',
static function ( $location ) use ( $messageCode ) {
return add_query_arg(
self::QUERY_PARAM,
$messageCode,
$location
);
}
);
}
}

View file

@ -9,38 +9,35 @@ use Inpsyde\PayPalCommerce\Onboarding\State;
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
use Psr\Container\ContainerInterface;
class ConnectAdminNotice
{
private $state;
private $settings;
class ConnectAdminNotice {
public function __construct(State $state, ContainerInterface $settings)
{
$this->state = $state;
$this->settings = $settings;
}
private $state;
private $settings;
public function connectMessage(): ?Message
{
if (!$this->shouldDisplay()) {
return null;
}
public function __construct( State $state, ContainerInterface $settings ) {
$this->state = $state;
$this->settings = $settings;
}
$message = sprintf(
/* translators: %1$s the gateway name */
__(
'PayPal Payments is almost ready. To get started, <a href="%1$s">connect your account</a>.',
'woocommerce-paypal-commerce-gateway'
),
// TODO: find a better way to get the url
admin_url('admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway')
);
return new Message($message, 'warning');
}
public function connectMessage(): ?Message {
if ( ! $this->shouldDisplay() ) {
return null;
}
protected function shouldDisplay(): bool
{
// TODO: decide on what condition to display
return $this->state->currentState() < State::STATE_PROGRESSIVE;
}
$message = sprintf(
/* translators: %1$s the gateway name */
__(
'PayPal Payments is almost ready. To get started, <a href="%1$s">connect your account</a>.',
'woocommerce-paypal-commerce-gateway'
),
// TODO: find a better way to get the url
admin_url( 'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway' )
);
return new Message( $message, 'warning' );
}
protected function shouldDisplay(): bool {
// TODO: decide on what condition to display
return $this->state->currentState() < State::STATE_PROGRESSIVE;
}
}

View file

@ -12,106 +12,99 @@ use Inpsyde\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
class AuthorizedPaymentsProcessor
{
public const SUCCESSFUL = 'SUCCESSFUL';
public const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
public const FAILED = 'FAILED';
public const INACCESSIBLE = 'INACCESSIBLE';
public const NOT_FOUND = 'NOT_FOUND';
private $orderEndpoint;
private $paymentsEndpoint;
private $lastStatus = '';
class AuthorizedPaymentsProcessor {
public function __construct(
OrderEndpoint $orderEndpoint,
PaymentsEndpoint $paymentsEndpoint
) {
public const SUCCESSFUL = 'SUCCESSFUL';
public const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
public const FAILED = 'FAILED';
public const INACCESSIBLE = 'INACCESSIBLE';
public const NOT_FOUND = 'NOT_FOUND';
private $orderEndpoint;
private $paymentsEndpoint;
private $lastStatus = '';
$this->orderEndpoint = $orderEndpoint;
$this->paymentsEndpoint = $paymentsEndpoint;
}
public function __construct(
OrderEndpoint $orderEndpoint,
PaymentsEndpoint $paymentsEndpoint
) {
public function process(\WC_Order $wcOrder): bool
{
try {
$order = $this->payPalOrderFromWcOrder($wcOrder);
} catch (Exception $exception) {
if ($exception->getCode() === 404) {
$this->lastStatus = self::NOT_FOUND;
return false;
}
$this->lastStatus = self::INACCESSIBLE;
return false;
}
$this->orderEndpoint = $orderEndpoint;
$this->paymentsEndpoint = $paymentsEndpoint;
}
$authorizations = $this->allAuthorizations($order);
public function process( \WC_Order $wcOrder ): bool {
try {
$order = $this->payPalOrderFromWcOrder( $wcOrder );
} catch ( Exception $exception ) {
if ( $exception->getCode() === 404 ) {
$this->lastStatus = self::NOT_FOUND;
return false;
}
$this->lastStatus = self::INACCESSIBLE;
return false;
}
if (!$this->areAuthorizationToCapture(...$authorizations)) {
$this->lastStatus = self::ALREADY_CAPTURED;
return false;
}
$authorizations = $this->allAuthorizations( $order );
try {
$this->captureAuthorizations(...$authorizations);
} catch (Exception $exception) {
$this->lastStatus = self::FAILED;
return false;
}
if ( ! $this->areAuthorizationToCapture( ...$authorizations ) ) {
$this->lastStatus = self::ALREADY_CAPTURED;
return false;
}
$this->lastStatus = self::SUCCESSFUL;
return true;
}
try {
$this->captureAuthorizations( ...$authorizations );
} catch ( Exception $exception ) {
$this->lastStatus = self::FAILED;
return false;
}
public function lastStatus(): string
{
$this->lastStatus = self::SUCCESSFUL;
return true;
}
return $this->lastStatus;
}
public function lastStatus(): string {
private function payPalOrderFromWcOrder(\WC_Order $wcOrder): Order
{
$orderId = $wcOrder->get_meta(PayPalGateway::ORDER_ID_META_KEY);
return $this->orderEndpoint->order($orderId);
}
return $this->lastStatus;
}
private function allAuthorizations(Order $order): array
{
$authorizations = [];
foreach ($order->purchaseUnits() as $purchaseUnit) {
foreach ($purchaseUnit->payments()->authorizations() as $authorization) {
$authorizations[] = $authorization;
}
}
private function payPalOrderFromWcOrder( \WC_Order $wcOrder ): Order {
$orderId = $wcOrder->get_meta( PayPalGateway::ORDER_ID_META_KEY );
return $this->orderEndpoint->order( $orderId );
}
return $authorizations;
}
private function allAuthorizations( Order $order ): array {
$authorizations = array();
foreach ( $order->purchaseUnits() as $purchaseUnit ) {
foreach ( $purchaseUnit->payments()->authorizations() as $authorization ) {
$authorizations[] = $authorization;
}
}
private function areAuthorizationToCapture(Authorization ...$authorizations): bool
{
return (bool) count($this->authorizationsToCapture(...$authorizations));
}
return $authorizations;
}
private function captureAuthorizations(Authorization ...$authorizations)
{
$uncapturedAuthorizations = $this->authorizationsToCapture(...$authorizations);
foreach ($uncapturedAuthorizations as $authorization) {
$this->paymentsEndpoint->capture($authorization->id());
}
}
private function areAuthorizationToCapture( Authorization ...$authorizations ): bool {
return (bool) count( $this->authorizationsToCapture( ...$authorizations ) );
}
/**
* @param Authorization ...$authorizations
* @return Authorization[]
*/
private function authorizationsToCapture(Authorization ...$authorizations): array
{
return array_filter(
$authorizations,
static function (Authorization $authorization): bool {
return $authorization->status()->is(AuthorizationStatus::CREATED)
|| $authorization->status()->is(AuthorizationStatus::PENDING);
}
);
}
private function captureAuthorizations( Authorization ...$authorizations ) {
$uncapturedAuthorizations = $this->authorizationsToCapture( ...$authorizations );
foreach ( $uncapturedAuthorizations as $authorization ) {
$this->paymentsEndpoint->capture( $authorization->id() );
}
}
/**
* @param Authorization ...$authorizations
* @return Authorization[]
*/
private function authorizationsToCapture( Authorization ...$authorizations ): array {
return array_filter(
$authorizations,
static function ( Authorization $authorization ): bool {
return $authorization->status()->is( AuthorizationStatus::CREATED )
|| $authorization->status()->is( AuthorizationStatus::PENDING );
}
);
}
}

View file

@ -15,156 +15,151 @@ use Inpsyde\PayPalCommerce\Session\SessionHandler;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
class OrderProcessor
{
private $sessionHandler;
private $cartRepository;
private $orderEndpoint;
private $paymentsEndpoint;
private $orderFactory;
private $threedSecure;
private $authorizedPaymentsProcessor;
private $settings;
class OrderProcessor {
private $lastError = '';
private $sessionHandler;
private $cartRepository;
private $orderEndpoint;
private $paymentsEndpoint;
private $orderFactory;
private $threedSecure;
private $authorizedPaymentsProcessor;
private $settings;
public function __construct(
SessionHandler $sessionHandler,
CartRepository $cartRepository,
OrderEndpoint $orderEndpoint,
PaymentsEndpoint $paymentsEndpoint,
OrderFactory $orderFactory,
ThreeDSecure $threedSecure,
AuthorizedPaymentsProcessor $authorizedPaymentsProcessor,
Settings $settings
) {
private $lastError = '';
$this->sessionHandler = $sessionHandler;
$this->cartRepository = $cartRepository;
$this->orderEndpoint = $orderEndpoint;
$this->paymentsEndpoint = $paymentsEndpoint;
$this->orderFactory = $orderFactory;
$this->threedSecure = $threedSecure;
$this->authorizedPaymentsProcessor = $authorizedPaymentsProcessor;
$this->settings = $settings;
}
public function __construct(
SessionHandler $sessionHandler,
CartRepository $cartRepository,
OrderEndpoint $orderEndpoint,
PaymentsEndpoint $paymentsEndpoint,
OrderFactory $orderFactory,
ThreeDSecure $threedSecure,
AuthorizedPaymentsProcessor $authorizedPaymentsProcessor,
Settings $settings
) {
public function process(\WC_Order $wcOrder, \WooCommerce $woocommerce): bool
{
$order = $this->sessionHandler->order();
$wcOrder->update_meta_data(PayPalGateway::ORDER_ID_META_KEY, $order->id());
$wcOrder->update_meta_data(PayPalGateway::INTENT_META_KEY, $order->intent());
$this->sessionHandler = $sessionHandler;
$this->cartRepository = $cartRepository;
$this->orderEndpoint = $orderEndpoint;
$this->paymentsEndpoint = $paymentsEndpoint;
$this->orderFactory = $orderFactory;
$this->threedSecure = $threedSecure;
$this->authorizedPaymentsProcessor = $authorizedPaymentsProcessor;
$this->settings = $settings;
}
$errorMessage = null;
if (!$order || ! $this->orderIsApproved($order)) {
$errorMessage = __(
'The payment has not been approved yet.',
'woocommerce-paypal-commerce-gateway'
);
}
if ($errorMessage) {
$this->lastError = sprintf(
// translators: %s is the message of the error.
__('Payment error: %s', 'woocommerce-paypal-commerce-gateway'),
$errorMessage
);
return false;
}
public function process( \WC_Order $wcOrder, \WooCommerce $woocommerce ): bool {
$order = $this->sessionHandler->order();
$wcOrder->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wcOrder->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
$order = $this->patchOrder($wcOrder, $order);
if ($order->intent() === 'CAPTURE') {
$order = $this->orderEndpoint->capture($order);
}
$errorMessage = null;
if ( ! $order || ! $this->orderIsApproved( $order ) ) {
$errorMessage = __(
'The payment has not been approved yet.',
'woocommerce-paypal-commerce-gateway'
);
}
if ( $errorMessage ) {
$this->lastError = sprintf(
// translators: %s is the message of the error.
__( 'Payment error: %s', 'woocommerce-paypal-commerce-gateway' ),
$errorMessage
);
return false;
}
if ($order->intent() === 'AUTHORIZE') {
$order = $this->orderEndpoint->authorize($order);
$wcOrder->update_meta_data(PayPalGateway::CAPTURED_META_KEY, 'false');
}
$order = $this->patchOrder( $wcOrder, $order );
if ( $order->intent() === 'CAPTURE' ) {
$order = $this->orderEndpoint->capture( $order );
}
$wcOrder->update_status(
'on-hold',
__('Awaiting payment.', 'woocommerce-paypal-commerce-gateway')
);
if ($order->status()->is(OrderStatus::COMPLETED) && $order->intent() === 'CAPTURE') {
$wcOrder->update_status(
'processing',
__('Payment received.', 'woocommerce-paypal-commerce-gateway')
);
}
if ( $order->intent() === 'AUTHORIZE' ) {
$order = $this->orderEndpoint->authorize( $order );
$wcOrder->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
}
if ($this->captureAuthorizedDownloads($order) && $this->authorizedPaymentsProcessor->process($wcOrder)) {
$wcOrder->add_order_note(
__('Payment successfully captured.', 'woocommerce-paypal-commerce-gateway')
);
$wcOrder->update_meta_data(PayPalGateway::CAPTURED_META_KEY, 'true');
$wcOrder->update_status('processing');
}
$woocommerce->cart->empty_cart();
$this->sessionHandler->destroySessionData();
$this->lastError = '';
return true;
}
$wcOrder->update_status(
'on-hold',
__( 'Awaiting payment.', 'woocommerce-paypal-commerce-gateway' )
);
if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) {
$wcOrder->update_status(
'processing',
__( 'Payment received.', 'woocommerce-paypal-commerce-gateway' )
);
}
private function captureAuthorizedDownloads(Order $order): bool
{
if (
! $this->settings->has('capture_for_virtual_only')
|| ! $this->settings->get('capture_for_virtual_only')
) {
return false;
}
if ( $this->captureAuthorizedDownloads( $order ) && $this->authorizedPaymentsProcessor->process( $wcOrder ) ) {
$wcOrder->add_order_note(
__( 'Payment successfully captured.', 'woocommerce-paypal-commerce-gateway' )
);
$wcOrder->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'true' );
$wcOrder->update_status( 'processing' );
}
$woocommerce->cart->empty_cart();
$this->sessionHandler->destroySessionData();
$this->lastError = '';
return true;
}
if ($order->intent() === 'CAPTURE') {
return false;
}
private function captureAuthorizedDownloads( Order $order ): bool {
if (
! $this->settings->has( 'capture_for_virtual_only' )
|| ! $this->settings->get( 'capture_for_virtual_only' )
) {
return false;
}
/**
* We fetch the order again as the authorize endpoint (from which the Order derives)
* drops the item's category, making it impossible to check, if purchase units contain
* physical goods.
*/
$order = $this->orderEndpoint->order($order->id());
if ( $order->intent() === 'CAPTURE' ) {
return false;
}
foreach ($order->purchaseUnits() as $unit) {
if ($unit->containsPhysicalGoodsItems()) {
return false;
}
}
return true;
}
/**
* We fetch the order again as the authorize endpoint (from which the Order derives)
* drops the item's category, making it impossible to check, if purchase units contain
* physical goods.
*/
$order = $this->orderEndpoint->order( $order->id() );
public function lastError(): string
{
foreach ( $order->purchaseUnits() as $unit ) {
if ( $unit->containsPhysicalGoodsItems() ) {
return false;
}
}
return true;
}
return $this->lastError;
}
public function lastError(): string {
public function patchOrder(\WC_Order $wcOrder, Order $order): Order
{
$updatedOrder = $this->orderFactory->fromWcOrder($wcOrder, $order);
$order = $this->orderEndpoint->patchOrderWith($order, $updatedOrder);
return $order;
}
return $this->lastError;
}
private function orderIsApproved(Order $order): bool
{
public function patchOrder( \WC_Order $wcOrder, Order $order ): Order {
$updatedOrder = $this->orderFactory->fromWcOrder( $wcOrder, $order );
$order = $this->orderEndpoint->patchOrderWith( $order, $updatedOrder );
return $order;
}
if ($order->status()->is(OrderStatus::APPROVED)) {
return true;
}
private function orderIsApproved( Order $order ): bool {
if (! $order->paymentSource() || ! $order->paymentSource()->card()) {
return false;
}
if ( $order->status()->is( OrderStatus::APPROVED ) ) {
return true;
}
$isApproved = in_array(
$this->threedSecure->proceedWithOrder($order),
[
ThreeDSecure::NO_DECISION,
ThreeDSecure::PROCCEED,
],
true
);
return $isApproved;
}
if ( ! $order->paymentSource() || ! $order->paymentSource()->card() ) {
return false;
}
$isApproved = in_array(
$this->threedSecure->proceedWithOrder( $order ),
array(
ThreeDSecure::NO_DECISION,
ThreeDSecure::PROCCEED,
),
true
);
return $isApproved;
}
}

View file

@ -8,91 +8,84 @@ use Inpsyde\PayPalCommerce\WcGateway\Exception\NotFoundException;
use Inpsyde\PayPalCommerce\WcGateway\Gateway\WcGatewayInterface;
use Psr\Container\ContainerInterface;
class Settings implements ContainerInterface
{
public const KEY = 'woocommerce-ppcp-settings';
private $settings = [];
class Settings implements ContainerInterface {
public function __construct()
{
}
public const KEY = 'woocommerce-ppcp-settings';
private $settings = array();
public function __construct() {
}
// phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType
// phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
public function get($id)
{
if (!$this->has($id)) {
throw new NotFoundException();
}
return $this->settings[$id];
}
public function get( $id ) {
if ( ! $this->has( $id ) ) {
throw new NotFoundException();
}
return $this->settings[ $id ];
}
public function has($id)
{
$this->load();
return array_key_exists($id, $this->settings);
}
public function has( $id ) {
$this->load();
return array_key_exists( $id, $this->settings );
}
public function set($id, $value)
{
$this->load();
$this->settings[$id] = $value;
}
public function set( $id, $value ) {
$this->load();
$this->settings[ $id ] = $value;
}
public function persist()
{
public function persist() {
update_option(self::KEY, $this->settings);
}
update_option( self::KEY, $this->settings );
}
public function reset(): bool
{
$this->load();
$fieldsToReset = [
'enabled',
'dcc_gateway_enabled',
'intent',
'client_id',
'client_secret',
'merchant_email',
];
foreach ($fieldsToReset as $id) {
$this->settings[$id] = null;
}
public function reset(): bool {
$this->load();
$fieldsToReset = array(
'enabled',
'dcc_gateway_enabled',
'intent',
'client_id',
'client_secret',
'merchant_email',
);
foreach ( $fieldsToReset as $id ) {
$this->settings[ $id ] = null;
}
return true;
}
return true;
}
private function load(): bool
{
private function load(): bool {
if ($this->settings) {
return false;
}
$this->settings = get_option(self::KEY, []);
if ( $this->settings ) {
return false;
}
$this->settings = get_option( self::KEY, array() );
$defaults = [
'title' => __('PayPal', 'woocommerce-paypal-commerce-gateway'),
'description' => __(
'Pay via PayPal.',
'woocommerce-paypal-commerce-gateway'
),
'button_single_product_enabled' => true,
'button_mini-cart_enabled' => true,
'button_cart_enabled' => true,
'brand_name' => get_bloginfo('name'),
'dcc_gateway_title' => __('Credit Cards', 'woocommerce-paypal-commerce-gateway'),
'dcc_gateway_description' => __(
'Pay with your credit card.',
'woocommerce-paypal-commerce-gateway'
),
];
foreach ($defaults as $key => $value) {
if (isset($this->settings[$key])) {
continue;
}
$this->settings[$key] = $value;
}
return true;
}
$defaults = array(
'title' => __( 'PayPal', 'woocommerce-paypal-commerce-gateway' ),
'description' => __(
'Pay via PayPal.',
'woocommerce-paypal-commerce-gateway'
),
'button_single_product_enabled' => true,
'button_mini-cart_enabled' => true,
'button_cart_enabled' => true,
'brand_name' => get_bloginfo( 'name' ),
'dcc_gateway_title' => __( 'Credit Cards', 'woocommerce-paypal-commerce-gateway' ),
'dcc_gateway_description' => __(
'Pay with your credit card.',
'woocommerce-paypal-commerce-gateway'
),
);
foreach ( $defaults as $key => $value ) {
if ( isset( $this->settings[ $key ] ) ) {
continue;
}
$this->settings[ $key ] = $value;
}
return true;
}
}

View file

@ -11,195 +11,191 @@ use Inpsyde\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use Inpsyde\PayPalCommerce\Webhooks\WebhookRegistrar;
use Psr\SimpleCache\CacheInterface;
class SettingsListener
{
class SettingsListener {
public const NONCE = 'ppcp-settings';
private $settings;
private $settingFields;
private $webhookRegistrar;
private $cache;
private $state;
public function __construct(
Settings $settings,
array $settingFields,
WebhookRegistrar $webhookRegistrar,
CacheInterface $cache,
State $state
) {
$this->settings = $settings;
$this->settingFields = $settingFields;
$this->webhookRegistrar = $webhookRegistrar;
$this->cache = $cache;
$this->state = $state;
}
public const NONCE = 'ppcp-settings';
private $settings;
private $settingFields;
private $webhookRegistrar;
private $cache;
private $state;
public function __construct(
Settings $settings,
array $settingFields,
WebhookRegistrar $webhookRegistrar,
CacheInterface $cache,
State $state
) {
public function listen()
{
$this->settings = $settings;
$this->settingFields = $settingFields;
$this->webhookRegistrar = $webhookRegistrar;
$this->cache = $cache;
$this->state = $state;
}
if (! $this->isValidUpdateRequest()) {
return;
}
public function listen() {
/**
* Nonce verification is done in self::isValidUpdateRequest
*/
if ( ! $this->isValidUpdateRequest() ) {
return;
}
/**
* Nonce verification is done in self::isValidUpdateRequest
*/
//phpcs:disable WordPress.Security.NonceVerification.Missing
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if (isset($_POST['save']) && sanitize_text_field(wp_unslash($_POST['save'])) === 'reset') {
$this->settings->reset();
$this->settings->persist();
$this->webhookRegistrar->unregister();
if ($this->cache->has(PayPalBearer::CACHE_KEY)) {
$this->cache->delete(PayPalBearer::CACHE_KEY);
}
return;
}
if ( isset( $_POST['save'] ) && sanitize_text_field( wp_unslash( $_POST['save'] ) ) === 'reset' ) {
$this->settings->reset();
$this->settings->persist();
$this->webhookRegistrar->unregister();
if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) {
$this->cache->delete( PayPalBearer::CACHE_KEY );
}
return;
}
//phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
//phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
/**
* Sanitization is done at a later stage.
*/
$rawData = (isset($_POST['ppcp'])) ? (array) wp_unslash($_POST['ppcp']) : [];
$settings = $this->retrieveSettingsFromRawData($rawData);
if ($_GET['section'] === PayPalGateway::ID) {
$settings['enabled'] = isset($_POST['woocommerce_ppcp-gateway_enabled'])
&& absint($_POST['woocommerce_ppcp-gateway_enabled']) === 1;
}
if ($_GET['section'] === CreditCardGateway::ID) {
$dccEnabledPostKey = 'woocommerce_ppcp-credit-card-gateway_enabled';
$settings['dcc_gateway_enabled'] = isset($_POST[$dccEnabledPostKey])
&& absint($_POST[$dccEnabledPostKey]) === 1;
}
$this->maybeRegisterWebhooks($settings);
/**
* Sanitization is done at a later stage.
*/
$rawData = ( isset( $_POST['ppcp'] ) ) ? (array) wp_unslash( $_POST['ppcp'] ) : array();
$settings = $this->retrieveSettingsFromRawData( $rawData );
if ( $_GET['section'] === PayPalGateway::ID ) {
$settings['enabled'] = isset( $_POST['woocommerce_ppcp-gateway_enabled'] )
&& absint( $_POST['woocommerce_ppcp-gateway_enabled'] ) === 1;
}
if ( $_GET['section'] === CreditCardGateway::ID ) {
$dccEnabledPostKey = 'woocommerce_ppcp-credit-card-gateway_enabled';
$settings['dcc_gateway_enabled'] = isset( $_POST[ $dccEnabledPostKey ] )
&& absint( $_POST[ $dccEnabledPostKey ] ) === 1;
}
$this->maybeRegisterWebhooks( $settings );
foreach ($settings as $id => $value) {
$this->settings->set($id, $value);
}
$this->settings->persist();
if ($this->cache->has(PayPalBearer::CACHE_KEY)) {
$this->cache->delete(PayPalBearer::CACHE_KEY);
}
foreach ( $settings as $id => $value ) {
$this->settings->set( $id, $value );
}
$this->settings->persist();
if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) {
$this->cache->delete( PayPalBearer::CACHE_KEY );
}
//phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
//phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
//phpcs:enable WordPress.Security.NonceVerification.Missing
}
}
private function maybeRegisterWebhooks(array $settings)
{
private function maybeRegisterWebhooks( array $settings ) {
if (! $this->settings->has('client_id') && $settings['client_id']) {
$this->webhookRegistrar->register();
}
if ($this->settings->has('client_id') && $this->settings->get('client_id')) {
$currentSecret = $this->settings->has('client_secret') ?
$this->settings->get('client_secret') : '';
if (
$settings['client_id'] !== $this->settings->get('client_id')
|| $settings['client_secret'] !== $currentSecret
) {
$this->webhookRegistrar->unregister();
$this->webhookRegistrar->register();
}
}
}
if ( ! $this->settings->has( 'client_id' ) && $settings['client_id'] ) {
$this->webhookRegistrar->register();
}
if ( $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) ) {
$currentSecret = $this->settings->has( 'client_secret' ) ?
$this->settings->get( 'client_secret' ) : '';
if (
$settings['client_id'] !== $this->settings->get( 'client_id' )
|| $settings['client_secret'] !== $currentSecret
) {
$this->webhookRegistrar->unregister();
$this->webhookRegistrar->register();
}
}
}
//phpcs:disable Inpsyde.CodeQuality.NestingLevel.MaxExceeded
//phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
//phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
private function retrieveSettingsFromRawData(array $rawData): array
{
if (! isset($_GET['section'])) {
return [];
}
$settings = [];
foreach ($this->settingFields as $key => $config) {
if (! in_array($this->state->currentState(), $config['screens'], true)) {
continue;
}
if (
$config['gateway'] === 'dcc'
&& sanitize_text_field(wp_unslash($_GET['section'])) !== 'ppcp-credit-card-gateway'
) {
continue;
}
if (
$config['gateway'] === 'paypal'
&& sanitize_text_field(wp_unslash($_GET['section'])) !== 'ppcp-gateway'
) {
continue;
}
switch ($config['type']) {
case 'checkbox':
$settings[$key] = isset($rawData[$key]);
break;
case 'text':
case 'ppcp-text-input':
case 'ppcp-password':
$settings[$key] = isset($rawData[$key]) ? sanitize_text_field($rawData[$key]) : '';
break;
case 'password':
if (empty($rawData[$key])) {
break;
}
$settings[$key] = sanitize_text_field($rawData[$key]);
break;
case 'ppcp-multiselect':
$values = isset($rawData[$key]) ? (array) $rawData[$key] : [];
$valuesToSave = [];
foreach ($values as $index => $rawValue) {
$value = sanitize_text_field($rawValue);
if (! in_array($value, array_keys($config['options']), true)) {
continue;
}
$valuesToSave[] = $value;
}
$settings[$key] = $valuesToSave;
break;
case 'select':
$options = array_keys($config['options']);
$settings[$key] = isset($rawData[$key]) && in_array(
sanitize_text_field($rawData[$key]),
$options,
true
) ? sanitize_text_field($rawData[$key]) : null;
break;
}
}
return $settings;
}
private function retrieveSettingsFromRawData( array $rawData ): array {
if ( ! isset( $_GET['section'] ) ) {
return array();
}
$settings = array();
foreach ( $this->settingFields as $key => $config ) {
if ( ! in_array( $this->state->currentState(), $config['screens'], true ) ) {
continue;
}
if (
$config['gateway'] === 'dcc'
&& sanitize_text_field( wp_unslash( $_GET['section'] ) ) !== 'ppcp-credit-card-gateway'
) {
continue;
}
if (
$config['gateway'] === 'paypal'
&& sanitize_text_field( wp_unslash( $_GET['section'] ) ) !== 'ppcp-gateway'
) {
continue;
}
switch ( $config['type'] ) {
case 'checkbox':
$settings[ $key ] = isset( $rawData[ $key ] );
break;
case 'text':
case 'ppcp-text-input':
case 'ppcp-password':
$settings[ $key ] = isset( $rawData[ $key ] ) ? sanitize_text_field( $rawData[ $key ] ) : '';
break;
case 'password':
if ( empty( $rawData[ $key ] ) ) {
break;
}
$settings[ $key ] = sanitize_text_field( $rawData[ $key ] );
break;
case 'ppcp-multiselect':
$values = isset( $rawData[ $key ] ) ? (array) $rawData[ $key ] : array();
$valuesToSave = array();
foreach ( $values as $index => $rawValue ) {
$value = sanitize_text_field( $rawValue );
if ( ! in_array( $value, array_keys( $config['options'] ), true ) ) {
continue;
}
$valuesToSave[] = $value;
}
$settings[ $key ] = $valuesToSave;
break;
case 'select':
$options = array_keys( $config['options'] );
$settings[ $key ] = isset( $rawData[ $key ] ) && in_array(
sanitize_text_field( $rawData[ $key ] ),
$options,
true
) ? sanitize_text_field( $rawData[ $key ] ) : null;
break;
}
}
return $settings;
}
//phpcs:enable Inpsyde.CodeQuality.NestingLevel.MaxExceeded
//phpcs:enable Generic.Metrics.CyclomaticComplexity.TooHigh
private function isValidUpdateRequest(): bool
{
private function isValidUpdateRequest(): bool {
if (
! isset($_REQUEST['section'])
|| ! in_array(
sanitize_text_field(wp_unslash($_REQUEST['section'])),
['ppcp-gateway', 'ppcp-credit-card-gateway'],
true
)
) {
return false;
}
if (
! isset( $_REQUEST['section'] )
|| ! in_array(
sanitize_text_field( wp_unslash( $_REQUEST['section'] ) ),
array( 'ppcp-gateway', 'ppcp-credit-card-gateway' ),
true
)
) {
return false;
}
if (! current_user_can('manage_options')) {
return false;
}
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
if (
! isset($_POST['ppcp-nonce'])
|| !wp_verify_nonce(
sanitize_text_field(wp_unslash($_POST['ppcp-nonce'])),
self::NONCE
)
) {
return false;
}
return true;
}
if (
! isset( $_POST['ppcp-nonce'] )
|| ! wp_verify_nonce(
sanitize_text_field( wp_unslash( $_POST['ppcp-nonce'] ) ),
self::NONCE
)
) {
return false;
}
return true;
}
}

View file

@ -9,229 +9,229 @@ use Inpsyde\PayPalCommerce\Button\Helper\MessagesApply;
use Inpsyde\PayPalCommerce\Onboarding\State;
use Psr\Container\ContainerInterface;
class SettingsRenderer
{
class SettingsRenderer {
private $settings;
private $state;
private $fields;
private $dccApplies;
private $messagesApply;
public function __construct(
ContainerInterface $settings,
State $state,
array $fields,
DccApplies $dccApplies,
MessagesApply $messagesApply
) {
$this->settings = $settings;
$this->state = $state;
$this->fields = $fields;
$this->dccApplies = $dccApplies;
$this->messagesApply = $messagesApply;
}
private $settings;
private $state;
private $fields;
private $dccApplies;
private $messagesApply;
public function __construct(
ContainerInterface $settings,
State $state,
array $fields,
DccApplies $dccApplies,
MessagesApply $messagesApply
) {
$this->settings = $settings;
$this->state = $state;
$this->fields = $fields;
$this->dccApplies = $dccApplies;
$this->messagesApply = $messagesApply;
}
//phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
public function renderMultiSelect($field, $key, $config, $value): string
{
public function renderMultiSelect( $field, $key, $config, $value ): string {
if ($config['type'] !== 'ppcp-multiselect') {
return $field;
}
if ( $config['type'] !== 'ppcp-multiselect' ) {
return $field;
}
$options = [];
foreach ($config['options'] as $optionKey => $optionValue) {
$selected = (in_array($optionKey, $value, true)) ? 'selected="selected"' : '';
$options = array();
foreach ( $config['options'] as $optionKey => $optionValue ) {
$selected = ( in_array( $optionKey, $value, true ) ) ? 'selected="selected"' : '';
$options[] = '<option value="' . esc_attr($optionKey) . '" ' . $selected . '>' .
esc_html($optionValue) .
'</option>';
}
$options[] = '<option value="' . esc_attr( $optionKey ) . '" ' . $selected . '>' .
esc_html( $optionValue ) .
'</option>';
}
$html = sprintf(
'<select
$html = sprintf(
'<select
multiple
class="%s"
name="%s"
>%s</select>',
esc_attr(implode(' ', $config['class'])),
esc_attr($key) . '[]',
implode('', $options)
);
esc_attr( implode( ' ', $config['class'] ) ),
esc_attr( $key ) . '[]',
implode( '', $options )
);
return $html;
}
return $html;
}
public function renderPassword($field, $key, $config, $value): string
{
public function renderPassword( $field, $key, $config, $value ): string {
if ($config['type'] !== 'ppcp-password') {
return $field;
}
if ( $config['type'] !== 'ppcp-password' ) {
return $field;
}
$html = sprintf(
'<input
$html = sprintf(
'<input
type="password"
autocomplete="new-password"
class="%s"
name="%s"
value="%s"
>',
esc_attr(implode(' ', $config['class'])),
esc_attr($key),
esc_attr($value)
);
esc_attr( implode( ' ', $config['class'] ) ),
esc_attr( $key ),
esc_attr( $value )
);
return $html;
}
return $html;
}
public function renderTextInput($field, $key, $config, $value): string
{
public function renderTextInput( $field, $key, $config, $value ): string {
if ($config['type'] !== 'ppcp-text-input') {
return $field;
}
if ( $config['type'] !== 'ppcp-text-input' ) {
return $field;
}
$html = sprintf(
'<input
$html = sprintf(
'<input
type="text"
autocomplete="off"
class="%s"
name="%s"
value="%s"
>',
esc_attr(implode(' ', $config['class'])),
esc_attr($key),
esc_attr($value)
);
esc_attr( implode( ' ', $config['class'] ) ),
esc_attr( $key ),
esc_attr( $value )
);
return $html;
}
return $html;
}
public function renderHeading($field, $key, $config, $value): string
{
public function renderHeading( $field, $key, $config, $value ): string {
if ($config['type'] !== 'ppcp-heading') {
return $field;
}
if ( $config['type'] !== 'ppcp-heading' ) {
return $field;
}
$html = sprintf(
'<h3 class="%s">%s</h3>',
esc_attr(implode(' ', $config['class'])),
esc_html($config['heading'])
);
$html = sprintf(
'<h3 class="%s">%s</h3>',
esc_attr( implode( ' ', $config['class'] ) ),
esc_html( $config['heading'] )
);
return $html;
}
return $html;
}
//phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
//phpcs:disable Inpsyde.CodeQuality.NestingLevel.High
//phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
public function render(bool $isDcc)
{
public function render( bool $isDcc ) {
$nonce = wp_create_nonce(SettingsListener::NONCE);
?>
<input type="hidden" name="ppcp-nonce" value="<?php echo esc_attr($nonce); ?>">
<?php
foreach ($this->fields as $field => $config) :
if (! in_array($this->state->currentState(), $config['screens'], true)) {
continue;
}
if ($isDcc && ! in_array($config['gateway'], ['all', 'dcc'], true)) {
continue;
}
if (! $isDcc && ! in_array($config['gateway'], ['all', 'paypal'], true)) {
continue;
}
if (
in_array('dcc', $config['requirements'], true)
&& ! $this->dccApplies->forCountryCurrency()
) {
continue;
}
if (
in_array('messages', $config['requirements'], true)
&& ! $this->messagesApply->forCountry()
) {
continue;
}
$value = $this->settings->has($field) ? $this->settings->get($field) : null;
$key = 'ppcp[' . $field . ']';
$id = 'ppcp-' . $field;
$config['id'] = $id;
$thTd = $config['type'] !== 'ppcp-heading' ? 'td' : 'th';
$colspan = $config['type'] !== 'ppcp-heading' ? 1 : 2;
$nonce = wp_create_nonce( SettingsListener::NONCE );
?>
<input type="hidden" name="ppcp-nonce" value="<?php echo esc_attr( $nonce ); ?>">
<?php
foreach ( $this->fields as $field => $config ) :
if ( ! in_array( $this->state->currentState(), $config['screens'], true ) ) {
continue;
}
if ( $isDcc && ! in_array( $config['gateway'], array( 'all', 'dcc' ), true ) ) {
continue;
}
if ( ! $isDcc && ! in_array( $config['gateway'], array( 'all', 'paypal' ), true ) ) {
continue;
}
if (
in_array( 'dcc', $config['requirements'], true )
&& ! $this->dccApplies->forCountryCurrency()
) {
continue;
}
if (
in_array( 'messages', $config['requirements'], true )
&& ! $this->messagesApply->forCountry()
) {
continue;
}
$value = $this->settings->has( $field ) ? $this->settings->get( $field ) : null;
$key = 'ppcp[' . $field . ']';
$id = 'ppcp-' . $field;
$config['id'] = $id;
$thTd = $config['type'] !== 'ppcp-heading' ? 'td' : 'th';
$colspan = $config['type'] !== 'ppcp-heading' ? 1 : 2;
?>
<tr valign="top" id="<?php echo esc_attr('field-' . $field); ?>">
<?php if ($config['type'] !== 'ppcp-heading') : ?>
<th>
<label
for="<?php echo esc_attr($id); ?>"
><?php echo esc_html($config['title']); ?></label>
<?php if (isset($config['desc_tip']) && $config['desc_tip']) : ?>
<span
class="woocommerce-help-tip"
data-tip="<?php echo esc_attr($config['description']); ?>"
></span>
<?php unset($config['description']);
endif; ?>
</th>
<?php endif; ?>
<<?php echo esc_attr($thTd); ?> colspan="<?php echo (int) $colspan; ?>"><?php
$config['type'] === 'ppcp-text' ?
$this->renderText($config)
: woocommerce_form_field($key, $config, $value); ?>
</<?php echo esc_attr($thTd); ?>>
</tr>
<?php endforeach;
?>
<tr valign="top" id="<?php echo esc_attr( 'field-' . $field ); ?>">
<?php if ( $config['type'] !== 'ppcp-heading' ) : ?>
<th>
<label
for="<?php echo esc_attr( $id ); ?>"
><?php echo esc_html( $config['title'] ); ?></label>
<?php if ( isset( $config['desc_tip'] ) && $config['desc_tip'] ) : ?>
<span
class="woocommerce-help-tip"
data-tip="<?php echo esc_attr( $config['description'] ); ?>"
></span>
<?php
unset( $config['description'] );
endif;
?>
</th>
<?php endif; ?>
<<?php echo esc_attr( $thTd ); ?> colspan="<?php echo (int) $colspan; ?>">
<?php
$config['type'] === 'ppcp-text' ?
$this->renderText( $config )
: woocommerce_form_field( $key, $config, $value );
?>
</<?php echo esc_attr( $thTd ); ?>>
</tr>
<?php
endforeach;
if ($isDcc) :
?>
<tr>
<th><?php esc_html_e('3D Secure', 'woocommerce-paypal-commerce-gateway'); ?></th>
<td>
<p>
<?php
/**
* @todo: Provide link to documentation.
*/
echo wp_kses_post(
sprintf(
// translators: %1$s and %2$s is a link tag.
__(
'3D Secure benefits cardholders and merchants by providing
if ( $isDcc ) :
?>
<tr>
<th><?php esc_html_e( '3D Secure', 'woocommerce-paypal-commerce-gateway' ); ?></th>
<td>
<p>
<?php
/**
* @todo: Provide link to documentation.
*/
echo wp_kses_post(
sprintf(
// translators: %1$s and %2$s is a link tag.
__(
'3D Secure benefits cardholders and merchants by providing
an additional layer of verification using Verified by Visa,
MasterCard SecureCode and American Express SafeKey.
%1$sLearn more about 3D Secure.%2$s',
'woocommerce - paypal - commerce - gateway'
),
'<a href = "#">',
'</a>'
)
); ?>
</p>
</td>
</tr>
<?php
endif;
}
'woocommerce - paypal - commerce - gateway'
),
'<a href = "#">',
'</a>'
)
);
?>
</p>
</td>
</tr>
<?php
endif;
}
//phpcs:enable Inpsyde.CodeQuality.NestingLevel.High
private function renderText(array $config)
{
echo wp_kses_post($config['text']);
if (isset($config['hidden'])) {
$value = $this->settings->has($config['hidden']) ?
(string) $this->settings->get($config['hidden'])
: '';
echo ' <input
private function renderText( array $config ) {
echo wp_kses_post( $config['text'] );
if ( isset( $config['hidden'] ) ) {
$value = $this->settings->has( $config['hidden'] ) ?
(string) $this->settings->get( $config['hidden'] )
: '';
echo ' <input
type = "hidden"
name = "ppcp[' . esc_attr($config['hidden']) . ']"
value = "' . esc_attr($value) . '"
name = "ppcp[' . esc_attr( $config['hidden'] ) . ']"
value = "' . esc_attr( $value ) . '"
> ';
}
}
}
}
}
}

View file

@ -23,240 +23,233 @@ use Inpsyde\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
class WcGatewayModule implements ModuleInterface
{
public function setup(): ServiceProviderInterface
{
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
}
class WcGatewayModule implements ModuleInterface {
public function run(ContainerInterface $container)
{
$this->registerPaymentGateway($container);
$this->registerOrderFunctionality($container);
$this->registerColumns($container);
$this->registerCheckoutAddressPreset($container);
$this->ajaxGatewayEnabler($container);
public function setup(): ServiceProviderInterface {
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
}
add_filter(
Repository::NOTICES_FILTER,
static function ($notices) use ($container): array {
$notice = $container->get('wcgateway.notice.connect');
/**
* @var ConnectAdminNotice $notice
*/
$connectMessage = $notice->connectMessage();
if ($connectMessage) {
$notices[] = $connectMessage;
}
$authorizeOrderAction = $container->get('wcgateway.notice.authorize-order-action');
$authorizedMessage = $authorizeOrderAction->message();
if ($authorizedMessage) {
$notices[] = $authorizedMessage;
}
public function run( ContainerInterface $container ) {
$this->registerPaymentGateway( $container );
$this->registerOrderFunctionality( $container );
$this->registerColumns( $container );
$this->registerCheckoutAddressPreset( $container );
$this->ajaxGatewayEnabler( $container );
return $notices;
}
);
add_action(
'woocommerce-paypal-commerce-gateway.deactivate',
static function () use ($container) {
delete_option(Settings::KEY);
delete_option(PayPalRequestIdRepository::KEY);
delete_option('woocommerce_' . PayPalGateway::ID . '_settings');
delete_option('woocommerce_' . CreditCardGateway::ID . '_settings');
}
);
add_filter(
Repository::NOTICES_FILTER,
static function ( $notices ) use ( $container ): array {
$notice = $container->get( 'wcgateway.notice.connect' );
/**
* @var ConnectAdminNotice $notice
*/
$connectMessage = $notice->connectMessage();
if ( $connectMessage ) {
$notices[] = $connectMessage;
}
$authorizeOrderAction = $container->get( 'wcgateway.notice.authorize-order-action' );
$authorizedMessage = $authorizeOrderAction->message();
if ( $authorizedMessage ) {
$notices[] = $authorizedMessage;
}
add_action(
'wc_ajax_' . ReturnUrlEndpoint::ENDPOINT,
static function () use ($container) {
$endpoint = $container->get('wcgateway.endpoint.return-url');
/**
* @var ReturnUrlEndpoint $endpoint
*/
$endpoint->handleRequest();
}
);
}
return $notices;
}
);
add_action(
'woocommerce-paypal-commerce-gateway.deactivate',
static function () use ( $container ) {
delete_option( Settings::KEY );
delete_option( PayPalRequestIdRepository::KEY );
delete_option( 'woocommerce_' . PayPalGateway::ID . '_settings' );
delete_option( 'woocommerce_' . CreditCardGateway::ID . '_settings' );
}
);
private function ajaxGatewayEnabler(ContainerInterface $container)
{
add_action(
'wp_ajax_woocommerce_toggle_gateway_enabled',
static function () use ($container) {
if (
! current_user_can('manage_woocommerce')
|| ! check_ajax_referer(
'woocommerce-toggle-payment-gateway-enabled',
'security'
)
|| ! isset($_POST['gateway_id'])
) {
return;
}
add_action(
'wc_ajax_' . ReturnUrlEndpoint::ENDPOINT,
static function () use ( $container ) {
$endpoint = $container->get( 'wcgateway.endpoint.return-url' );
/**
* @var ReturnUrlEndpoint $endpoint
*/
$endpoint->handleRequest();
}
);
}
/**
* @var Settings $settings
*/
$settings = $container->get('wcgateway.settings');
$key = $_POST['gateway_id'] === PayPalGateway::ID ? 'enabled' : '';
if ($_POST['gateway_id'] === CreditCardGateway::ID) {
$key = 'dcc_gateway_enabled';
}
if (! $key) {
return;
}
$enabled = $settings->has($key) ? $settings->get($key) : false;
if (! $enabled) {
return;
}
$settings->set($key, false);
$settings->persist();
},
9
);
}
private function ajaxGatewayEnabler( ContainerInterface $container ) {
add_action(
'wp_ajax_woocommerce_toggle_gateway_enabled',
static function () use ( $container ) {
if (
! current_user_can( 'manage_woocommerce' )
|| ! check_ajax_referer(
'woocommerce-toggle-payment-gateway-enabled',
'security'
)
|| ! isset( $_POST['gateway_id'] )
) {
return;
}
private function registerPaymentGateWay(ContainerInterface $container)
{
/**
* @var Settings $settings
*/
$settings = $container->get( 'wcgateway.settings' );
$key = $_POST['gateway_id'] === PayPalGateway::ID ? 'enabled' : '';
if ( $_POST['gateway_id'] === CreditCardGateway::ID ) {
$key = 'dcc_gateway_enabled';
}
if ( ! $key ) {
return;
}
$enabled = $settings->has( $key ) ? $settings->get( $key ) : false;
if ( ! $enabled ) {
return;
}
$settings->set( $key, false );
$settings->persist();
},
9
);
}
add_filter(
'woocommerce_payment_gateways',
static function ($methods) use ($container): array {
$methods[] = $container->get('wcgateway.paypal-gateway');
$dccApplies = $container->get('api.helpers.dccapplies');
/**
* @var DccApplies $dccApplies
*/
if ($dccApplies->forCountryCurrency()) {
$methods[] = $container->get('wcgateway.credit-card-gateway');
}
return (array)$methods;
}
);
private function registerPaymentGateWay( ContainerInterface $container ) {
add_action(
'woocommerce_settings_save_checkout',
static function () use ($container) {
$listener = $container->get('wcgateway.settings.listener');
$listener->listen();
}
);
add_filter(
'woocommerce_payment_gateways',
static function ( $methods ) use ( $container ): array {
$methods[] = $container->get( 'wcgateway.paypal-gateway' );
$dccApplies = $container->get( 'api.helpers.dccapplies' );
/**
* @var DccApplies $dccApplies
*/
if ( $dccApplies->forCountryCurrency() ) {
$methods[] = $container->get( 'wcgateway.credit-card-gateway' );
}
return (array) $methods;
}
);
add_filter(
'woocommerce_form_field',
static function ($field, $key, $args, $value) use ($container) {
$renderer = $container->get('wcgateway.settings.render');
/**
* @var SettingsRenderer $renderer
*/
$field = $renderer->renderMultiSelect($field, $key, $args, $value);
$field = $renderer->renderPassword($field, $key, $args, $value);
$field = $renderer->renderTextInput($field, $key, $args, $value);
$field = $renderer->renderHeading($field, $key, $args, $value);
return $field;
},
10,
4
);
add_action(
'woocommerce_settings_save_checkout',
static function () use ( $container ) {
$listener = $container->get( 'wcgateway.settings.listener' );
$listener->listen();
}
);
add_filter(
'woocommerce_available_payment_gateways',
static function ($methods) use ($container): array {
$disabler = $container->get('wcgateway.disabler');
/**
* @var DisableGateways $disabler
*/
return $disabler->handler((array)$methods);
}
);
}
add_filter(
'woocommerce_form_field',
static function ( $field, $key, $args, $value ) use ( $container ) {
$renderer = $container->get( 'wcgateway.settings.render' );
/**
* @var SettingsRenderer $renderer
*/
$field = $renderer->renderMultiSelect( $field, $key, $args, $value );
$field = $renderer->renderPassword( $field, $key, $args, $value );
$field = $renderer->renderTextInput( $field, $key, $args, $value );
$field = $renderer->renderHeading( $field, $key, $args, $value );
return $field;
},
10,
4
);
private function registerOrderFunctionality(ContainerInterface $container)
{
add_filter(
'woocommerce_order_actions',
static function ($orderActions): array {
$orderActions['ppcp_authorize_order'] = __(
'Capture authorized PayPal payment',
'woocommerce-paypal-commerce-gateway'
);
return $orderActions;
}
);
add_filter(
'woocommerce_available_payment_gateways',
static function ( $methods ) use ( $container ): array {
$disabler = $container->get( 'wcgateway.disabler' );
/**
* @var DisableGateways $disabler
*/
return $disabler->handler( (array) $methods );
}
);
}
add_action(
'woocommerce_order_action_ppcp_authorize_order',
static function (\WC_Order $wcOrder) use ($container) {
/**
* @var PayPalGateway $gateway
*/
$gateway = $container->get('wcgateway.paypal-gateway');
$gateway->captureAuthorizedPayment($wcOrder);
}
);
}
private function registerOrderFunctionality( ContainerInterface $container ) {
add_filter(
'woocommerce_order_actions',
static function ( $orderActions ): array {
$orderActions['ppcp_authorize_order'] = __(
'Capture authorized PayPal payment',
'woocommerce-paypal-commerce-gateway'
);
return $orderActions;
}
);
private function registerColumns(ContainerInterface $container)
{
add_action(
'woocommerce_order_actions_start',
static function ($wcOrderId) use ($container) {
/**
* @var PaymentStatusOrderDetail $class
*/
$class = $container->get('wcgateway.admin.order-payment-status');
$class->render(intval($wcOrderId));
}
);
add_action(
'woocommerce_order_action_ppcp_authorize_order',
static function ( \WC_Order $wcOrder ) use ( $container ) {
/**
* @var PayPalGateway $gateway
*/
$gateway = $container->get( 'wcgateway.paypal-gateway' );
$gateway->captureAuthorizedPayment( $wcOrder );
}
);
}
add_filter(
'manage_edit-shop_order_columns',
static function ($columns) use ($container) {
/**
* @var OrderTablePaymentStatusColumn $paymentStatusColumn
*/
$paymentStatusColumn = $container->get('wcgateway.admin.orders-payment-status-column');
return $paymentStatusColumn->register($columns);
}
);
private function registerColumns( ContainerInterface $container ) {
add_action(
'woocommerce_order_actions_start',
static function ( $wcOrderId ) use ( $container ) {
/**
* @var PaymentStatusOrderDetail $class
*/
$class = $container->get( 'wcgateway.admin.order-payment-status' );
$class->render( intval( $wcOrderId ) );
}
);
add_action(
'manage_shop_order_posts_custom_column',
static function ($column, $wcOrderId) use ($container) {
/**
* @var OrderTablePaymentStatusColumn $paymentStatusColumn
*/
$paymentStatusColumn = $container->get('wcgateway.admin.orders-payment-status-column');
$paymentStatusColumn->render($column, intval($wcOrderId));
},
10,
2
);
}
add_filter(
'manage_edit-shop_order_columns',
static function ( $columns ) use ( $container ) {
/**
* @var OrderTablePaymentStatusColumn $paymentStatusColumn
*/
$paymentStatusColumn = $container->get( 'wcgateway.admin.orders-payment-status-column' );
return $paymentStatusColumn->register( $columns );
}
);
private function registerCheckoutAddressPreset(ContainerInterface $container): void
{
add_filter(
'woocommerce_checkout_get_value',
static function (...$args) use ($container) {
add_action(
'manage_shop_order_posts_custom_column',
static function ( $column, $wcOrderId ) use ( $container ) {
/**
* @var OrderTablePaymentStatusColumn $paymentStatusColumn
*/
$paymentStatusColumn = $container->get( 'wcgateway.admin.orders-payment-status-column' );
$paymentStatusColumn->render( $column, intval( $wcOrderId ) );
},
10,
2
);
}
// Its important to not instantiate the service too early as it
// depends on SessionHandler and WooCommerce Session
private function registerCheckoutAddressPreset( ContainerInterface $container ): void {
add_filter(
'woocommerce_checkout_get_value',
static function ( ...$args ) use ( $container ) {
/**
* @var CheckoutPayPalAddressPreset $service
*/
$service = $container->get('wcgateway.checkout.address-preset');
// Its important to not instantiate the service too early as it
// depends on SessionHandler and WooCommerce Session
return $service->filterCheckoutFiled(...$args);
},
10,
2
);
}
/**
* @var CheckoutPayPalAddressPreset $service
*/
$service = $container->get( 'wcgateway.checkout.address-preset' );
return $service->filterCheckoutFiled( ...$args );
},
10,
2
);
}
}