Refactor payment source to handle arbitrary types

This commit is contained in:
Emili Castells Guasch 2023-10-20 12:54:00 +02:00
parent 99d05ca44d
commit 518a1f4e4e
13 changed files with 59 additions and 287 deletions

View file

@ -15,7 +15,6 @@ use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerPayableBreakdown;
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\RefundFactory;
@ -51,7 +50,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayeeFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentsFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentSourceFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenActionLinksFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlatformFeeFactory;
@ -377,21 +375,16 @@ return array(
'api.factory.address' => static function ( ContainerInterface $container ): AddressFactory {
return new AddressFactory();
},
'api.factory.payment-source' => static function ( ContainerInterface $container ): PaymentSourceFactory {
return new PaymentSourceFactory();
},
'api.factory.order' => static function ( ContainerInterface $container ): OrderFactory {
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
$payer_factory = $container->get( 'api.factory.payer' );
$application_context_repository = $container->get( 'api.repository.application-context' );
$application_context_factory = $container->get( 'api.factory.application-context' );
$payment_source_factory = $container->get( 'api.factory.payment-source' );
return new OrderFactory(
$purchase_unit_factory,
$payer_factory,
$application_context_repository,
$application_context_factory,
$payment_source_factory
$application_context_factory
);
},
'api.factory.payments' => static function ( ContainerInterface $container ): PaymentsFactory {

View file

@ -107,14 +107,6 @@ class Order {
$this->id = $id;
$this->application_context = $application_context;
$this->purchase_units = array_values(
array_filter(
$purchase_units,
static function ( $unit ): bool {
return is_a( $unit, PurchaseUnit::class );
}
)
);
$this->payer = $payer;
$this->order_status = $order_status;
$this->intent = ( 'CAPTURE' === $intent ) ? 'CAPTURE' : 'AUTHORIZE';
@ -237,7 +229,7 @@ class Order {
$order['application_context'] = $this->application_context()->to_array();
}
if ( $this->payment_source() ) {
$order['payment_source'] = $this->payment_source()->to_array();
$order['payment_source'] = $this->payment_source();
}
return $order;

View file

@ -9,74 +9,53 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
use stdClass;
/**
* Class PaymentSource
*/
class PaymentSource {
/**
* The card.
* Payment source name.
*
* @var PaymentSourceCard|null
* @var string
*/
private $card;
private $name;
/**
* The wallet.
* Payment source properties.
*
* @var PaymentSourceWallet|null
* @var stdClass
*/
private $wallet;
private $properties;
/**
* PaymentSource constructor.
*
* @param PaymentSourceCard|null $card The card.
* @param PaymentSourceWallet|null $wallet The wallet.
* @param string $name Payment source name.
* @param stdClass $properties Payment source properties.
*/
public function __construct(
PaymentSourceCard $card = null,
PaymentSourceWallet $wallet = null
) {
$this->card = $card;
$this->wallet = $wallet;
public function __construct( string $name, stdClass $properties ) {
$this->name = $name;
$this->properties = $properties;
}
/**
* Returns the card.
* Payment source name.
*
* @return PaymentSourceCard|null
* @return string
*/
public function card() {
return $this->card;
public function name(): string {
return $this->name;
}
/**
* Returns the wallet.
* Payment source properties.
*
* @return PaymentSourceWallet|null
* @return stdClass
*/
public function wallet() {
return $this->wallet;
}
/**
* Returns the array of the object.
*
* @return array
*/
public function to_array(): array {
$data = array();
if ( $this->card() ) {
$data['card'] = $this->card()->to_array();
}
if ( $this->wallet() ) {
$data['wallet'] = $this->wallet()->to_array();
}
return $data;
public function properties(): stdClass {
return $this->properties;
}
}

View file

@ -1,123 +0,0 @@
<?php
/**
* The PaymentSourceCard object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentSourceCard
*/
class PaymentSourceCard {
/**
* The last digits of the card.
*
* @var string
*/
private $last_digits;
/**
* The brand.
*
* @var string
*/
private $brand;
/**
* The type.
*
* @var string
*/
private $type;
/**
* The authentication result.
*
* @var CardAuthenticationResult|null
*/
private $authentication_result;
/**
* PaymentSourceCard constructor.
*
* @param string $last_digits The last digits of the card.
* @param string $brand The brand of the card.
* @param string $type The type of the card.
* @param CardAuthenticationResult|null $authentication_result The authentication result.
*/
public function __construct(
string $last_digits,
string $brand,
string $type,
CardAuthenticationResult $authentication_result = null
) {
$this->last_digits = $last_digits;
$this->brand = $brand;
$this->type = $type;
$this->authentication_result = $authentication_result;
}
/**
* Returns the last digits.
*
* @return string
*/
public function last_digits(): string {
return $this->last_digits;
}
/**
* Returns the brand.
*
* @return string
*/
public function brand(): string {
return $this->brand;
}
/**
* Returns the type.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the authentication result.
*
* @return CardAuthenticationResult|null
*/
public function authentication_result() {
return $this->authentication_result;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array(
'last_digits' => $this->last_digits(),
'brand' => $this->brand(),
'type' => $this->type(),
);
if ( $this->authentication_result() ) {
$data['authentication_result'] = $this->authentication_result()->to_array();
}
return $data;
}
}

View file

@ -1,25 +0,0 @@
<?php
/**
* The PaymentSourcewallet.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentSourceWallet
*/
class PaymentSourceWallet {
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array();
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
@ -48,13 +49,6 @@ class OrderFactory {
*/
private $application_context_factory;
/**
* The PaymentSource factory.
*
* @var PaymentSourceFactory
*/
private $payment_source_factory;
/**
* OrderFactory constructor.
*
@ -62,21 +56,18 @@ class OrderFactory {
* @param PayerFactory $payer_factory The Payer factory.
* @param ApplicationContextRepository $application_context_repository The Application Context repository.
* @param ApplicationContextFactory $application_context_factory The Application Context factory.
* @param PaymentSourceFactory $payment_source_factory The Payment Source factory.
*/
public function __construct(
PurchaseUnitFactory $purchase_unit_factory,
PayerFactory $payer_factory,
ApplicationContextRepository $application_context_repository,
ApplicationContextFactory $application_context_factory,
PaymentSourceFactory $payment_source_factory
ApplicationContextFactory $application_context_factory
) {
$this->purchase_unit_factory = $purchase_unit_factory;
$this->payer_factory = $payer_factory;
$this->application_context_repository = $application_context_repository;
$this->application_context_factory = $application_context_factory;
$this->payment_source_factory = $payment_source_factory;
}
/**
@ -152,9 +143,18 @@ class OrderFactory {
$application_context = ( isset( $order_data->application_context ) ) ?
$this->application_context_factory->from_paypal_response( $order_data->application_context )
: null;
$payment_source = ( isset( $order_data->payment_source ) ) ?
$this->payment_source_factory->from_paypal_response( $order_data->payment_source ) :
null;
$payment_source = null;
if ( isset( $order_data->payment_source ) ) {
$payment_source_as_array = json_decode( json_encode( $order_data->payment_source ), true );
if ( $payment_source_as_array ) {
$name = array_key_first( $payment_source_as_array );
$payment_source = new PaymentSource(
array_key_first( $payment_source_as_array ),
$order_data->payment_source->$name
);
}
}
return new Order(
$order_data->id,

View file

@ -1,53 +0,0 @@
<?php
/**
* The PaymentSource factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSourceCard;
/**
* Class PaymentSourceFactory
*/
class PaymentSourceFactory {
/**
* Returns a PaymentSource for a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return PaymentSource
*/
public function from_paypal_response( \stdClass $data ): PaymentSource {
$card = null;
$wallet = null;
if ( isset( $data->card ) ) {
$authentication_result = null;
if ( isset( $data->card->authentication_result ) ) {
$authentication_result = new CardAuthenticationResult(
isset( $data->card->authentication_result->liability_shift ) ?
(string) $data->card->authentication_result->liability_shift : '',
isset( $data->card->authentication_result->three_d_secure->enrollment_status ) ?
(string) $data->card->authentication_result->three_d_secure->enrollment_status : '',
isset( $data->card->authentication_result->three_d_secure->authentication_status ) ?
(string) $data->card->authentication_result->three_d_secure->authentication_status : ''
);
}
$card = new PaymentSourceCard(
isset( $data->card->last_digits ) ? (string) $data->card->last_digits : '',
isset( $data->card->brand ) ? (string) $data->card->brand : '',
isset( $data->card->type ) ? (string) $data->card->type : '',
$authentication_result
);
}
return new PaymentSource( $card, $wallet );
}
}

View file

@ -144,10 +144,10 @@ class ApproveOrderEndpoint implements EndpointInterface {
$order = $this->api_endpoint->order( $data['order_id'] );
if ( $order->payment_source() && $order->payment_source()->card() ) {
if ( $order->payment_source() && $order->payment_source()->name() === 'card' ) {
if ( $this->settings->has( 'disable_cards' ) ) {
$disabled_cards = (array) $this->settings->get( 'disable_cards' );
$card = strtolower( $order->payment_source()->card()->brand() );
$card = strtolower( $order->payment_source()->properties()->brand ?? '' );
if ( 'master_card' === $card ) {
$card = 'mastercard';
}

View file

@ -115,7 +115,7 @@ trait ContextTrait {
}
$source = $order->payment_source();
if ( $source && $source->card() ) {
if ( $source && $source->name() === 'card' ) {
return false; // Ignore for DCC.
}

View file

@ -52,14 +52,14 @@ class ThreeDSecure {
if ( ! $order->payment_source() ) {
return self::NO_DECISION;
}
if ( ! $order->payment_source()->card() ) {
if ( ! $order->payment_source()->properties()->brand ?? '' ) {
return self::NO_DECISION;
}
if ( ! $order->payment_source()->card()->authentication_result() ) {
if ( ! $order->payment_source()->properties()->authentication_result ?? '' ) {
return self::NO_DECISION;
}
$result = $order->payment_source()->card()->authentication_result();
$result = $order->payment_source()->properties()->authentication_result;
$this->logger->info( '3DS authentication result: ' . wc_print_r( $result->to_array(), true ) );
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) {

View file

@ -10,7 +10,9 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
use Psr\Log\LoggerInterface;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
@ -90,5 +92,14 @@ class SavePaymentMethodsModule implements ModuleInterface {
return $data;
}
);
add_action(
'woocommerce_paypal_payments_after_order_processor',
function( WC_Order $wc_order, Order $order ) {
// vault payment here ...
},
10,
2
);
}
}

View file

@ -59,12 +59,7 @@ trait OrderMetaTrait {
private function get_payment_source( Order $order ): ?string {
$source = $order->payment_source();
if ( $source ) {
if ( $source->card() ) {
return 'card';
}
if ( $source->wallet() ) {
return 'wallet';
}
return $source->name();
}
return null;

View file

@ -242,6 +242,9 @@ class OrderProcessor {
if ( $this->capture_authorized_downloads( $order ) ) {
$this->authorized_payments_processor->capture_authorized_payment( $wc_order );
}
do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order );
$this->last_error = '';
return true;
}
@ -321,7 +324,7 @@ class OrderProcessor {
return true;
}
if ( ! $order->payment_source() || ! $order->payment_source()->card() ) {
if ( ! $order->payment_source() || $order->payment_source()->name() !== 'card' ) {
return false;
}