Merge trunk (WIP)

This commit is contained in:
dinamiko 2021-10-07 17:36:29 +02:00
commit e36d77fad2
25 changed files with 1052 additions and 195 deletions

View file

@ -141,12 +141,14 @@ return array(
},
'api.endpoint.payments' => static function ( $container ): PaymentsEndpoint {
$authorizations_factory = $container->get( 'api.factory.authorization' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$capture_factory = $container->get( 'api.factory.capture' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new PaymentsEndpoint(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$authorizations_factory,
$capture_factory,
$logger
);
},

View file

@ -11,6 +11,8 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
@ -349,8 +351,8 @@ class OrderEndpoint {
$order = $this->order_factory->from_paypal_response( $json );
$purchase_units_payments_captures_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? '';
if ( $purchase_units_payments_captures_status && 'DECLINED' === $purchase_units_payments_captures_status ) {
$capture_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? null;
if ( $capture_status && $capture_status->is( CaptureStatus::DECLINED ) ) {
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
}
@ -423,6 +425,12 @@ class OrderEndpoint {
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
$authorization_status = $order->purchase_units()[0]->payments()->authorizations()[0]->status() ?? null;
if ( $authorization_status && $authorization_status->is( AuthorizationStatus::DENIED ) ) {
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
}
return $order;
}

View file

@ -11,11 +11,13 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
/**
* Class PaymentsEndpoint
@ -45,6 +47,13 @@ class PaymentsEndpoint {
*/
private $authorizations_factory;
/**
* The capture factory.
*
* @var CaptureFactory
*/
private $capture_factory;
/**
* The logger.
*
@ -58,18 +67,21 @@ class PaymentsEndpoint {
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param AuthorizationFactory $authorization_factory The authorization factory.
* @param CaptureFactory $capture_factory The capture factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
AuthorizationFactory $authorization_factory,
CaptureFactory $capture_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->authorizations_factory = $authorization_factory;
$this->capture_factory = $capture_factory;
$this->logger = $logger;
}
@ -136,11 +148,11 @@ class PaymentsEndpoint {
*
* @param string $authorization_id The id.
*
* @return Authorization
* @return Capture
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function capture( string $authorization_id ): Authorization {
public function capture( string $authorization_id ): Capture {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
$args = array(
@ -167,7 +179,7 @@ class PaymentsEndpoint {
);
}
return $this->authorizations_factory->from_paypal_response( $json );
return $this->capture_factory->from_paypal_response( $json );
}
/**

View file

@ -44,13 +44,21 @@ class AuthorizationStatus {
*/
private $status;
/**
* The details.
*
* @var AuthorizationStatusDetails|null
*/
private $details;
/**
* AuthorizationStatus constructor.
*
* @param string $status The status.
* @param string $status The status.
* @param AuthorizationStatusDetails|null $details The details.
* @throws RuntimeException When the status is not valid.
*/
public function __construct( string $status ) {
public function __construct( string $status, ?AuthorizationStatusDetails $details = null ) {
if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
throw new RuntimeException(
sprintf(
@ -60,7 +68,8 @@ class AuthorizationStatus {
)
);
}
$this->status = $status;
$this->status = $status;
$this->details = $details;
}
/**
@ -91,4 +100,13 @@ class AuthorizationStatus {
public function name(): string {
return $this->status;
}
/**
* Returns the details.
*
* @return AuthorizationStatusDetails|null
*/
public function details(): ?AuthorizationStatusDetails {
return $this->details;
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* The AuthorizationStatusDetails object.
*
* @see https://developer.paypal.com/docs/api/payments/v2/#definition-authorization_status_details
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class AuthorizationStatusDetails
*/
class AuthorizationStatusDetails {
const PENDING_REVIEW = 'PENDING_REVIEW';
/**
* The reason.
*
* @var string
*/
private $reason;
/**
* AuthorizationStatusDetails constructor.
*
* @param string $reason The reason explaining authorization status.
*/
public function __construct( string $reason ) {
$this->reason = $reason;
}
/**
* Compares the current reason with a given one.
*
* @param string $reason The reason to compare with.
*
* @return bool
*/
public function is( string $reason ): bool {
return $this->reason === $reason;
}
/**
* Returns the reason explaining authorization status.
*
* @return string
*/
public function reason(): string {
return $this->reason;
}
}

View file

@ -26,17 +26,10 @@ class Capture {
/**
* The status.
*
* @var string
* @var CaptureStatus
*/
private $status;
/**
* The status details.
*
* @var string
*/
private $status_details;
/**
* The amount.
*
@ -75,19 +68,17 @@ class Capture {
/**
* Capture constructor.
*
* @param string $id The ID.
* @param string $status The status.
* @param string $status_details The status details.
* @param Amount $amount The amount.
* @param bool $final_capture The final capture.
* @param string $seller_protection The seller protection.
* @param string $invoice_id The invoice id.
* @param string $custom_id The custom id.
* @param string $id The ID.
* @param CaptureStatus $status The status.
* @param Amount $amount The amount.
* @param bool $final_capture The final capture.
* @param string $seller_protection The seller protection.
* @param string $invoice_id The invoice id.
* @param string $custom_id The custom id.
*/
public function __construct(
string $id,
string $status,
string $status_details,
CaptureStatus $status,
Amount $amount,
bool $final_capture,
string $seller_protection,
@ -97,7 +88,6 @@ class Capture {
$this->id = $id;
$this->status = $status;
$this->status_details = $status_details;
$this->amount = $amount;
$this->final_capture = $final_capture;
$this->seller_protection = $seller_protection;
@ -117,21 +107,12 @@ class Capture {
/**
* Returns the status.
*
* @return string
* @return CaptureStatus
*/
public function status() : string {
public function status() : CaptureStatus {
return $this->status;
}
/**
* Returns the status details object.
*
* @return \stdClass
*/
public function status_details() : \stdClass {
return (object) array( 'reason' => $this->status_details );
}
/**
* Returns the amount.
*
@ -183,15 +164,18 @@ class Capture {
* @return array
*/
public function to_array() : array {
return array(
$data = array(
'id' => $this->id(),
'status' => $this->status(),
'status_details' => (array) $this->status_details(),
'status' => $this->status()->name(),
'amount' => $this->amount()->to_array(),
'final_capture' => $this->final_capture(),
'seller_protection' => (array) $this->seller_protection(),
'invoice_id' => $this->invoice_id(),
'custom_id' => $this->custom_id(),
);
if ( $this->status()->details() ) {
$data['status_details'] = array( 'reason' => $this->status()->details()->reason() );
}
return $data;
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* The CaptureStatus object.
*
* @see https://developer.paypal.com/docs/api/orders/v2/#definition-capture_status
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class CaptureStatus
*/
class CaptureStatus {
const COMPLETED = 'COMPLETED';
const DECLINED = 'DECLINED';
const PARTIALLY_REFUNDED = 'PARTIALLY_REFUNDED';
const REFUNDED = 'REFUNDED';
const FAILED = 'FAILED';
const PENDING = 'PENDING';
/**
* The status.
*
* @var string
*/
private $status;
/**
* The details.
*
* @var CaptureStatusDetails|null
*/
private $details;
/**
* CaptureStatus constructor.
*
* @param string $status The status.
* @param CaptureStatusDetails|null $details The details.
*/
public function __construct( string $status, ?CaptureStatusDetails $details = null ) {
$this->status = $status;
$this->details = $details;
}
/**
* Compares the current status with a given one.
*
* @param string $status The status to compare with.
*
* @return bool
*/
public function is( string $status ): bool {
return $this->status === $status;
}
/**
* Returns the status.
*
* @return string
*/
public function name(): string {
return $this->status;
}
/**
* Returns the details.
*
* @return CaptureStatusDetails|null
*/
public function details(): ?CaptureStatusDetails {
return $this->details;
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* The CaptureStatusDetails object.
*
* @see https://developer.paypal.com/docs/api/payments/v2/#definition-capture_status_details
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class CaptureStatusDetails
*/
class CaptureStatusDetails {
const BUYER_COMPLAINT = 'BUYER_COMPLAINT';
const CHARGEBACK = 'CHARGEBACK';
const ECHECK = 'ECHECK';
const INTERNATIONAL_WITHDRAWAL = 'INTERNATIONAL_WITHDRAWAL';
const OTHER = 'OTHER';
const PENDING_REVIEW = 'PENDING_REVIEW';
const RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION = 'RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION';
const REFUNDED = 'REFUNDED';
const TRANSACTION_APPROVED_AWAITING_FUNDING = 'TRANSACTION_APPROVED_AWAITING_FUNDING';
const UNILATERAL = 'REFUNDED';
const VERIFICATION_REQUIRED = 'VERIFICATION_REQUIRED';
/**
* The reason.
*
* @var string
*/
private $reason;
/**
* CaptureStatusDetails constructor.
*
* @param string $reason The reason explaining capture status.
*/
public function __construct( string $reason ) {
$this->reason = $reason;
}
/**
* Compares the current reason with a given one.
*
* @param string $reason The reason to compare with.
*
* @return bool
*/
public function is( string $reason ): bool {
return $this->reason === $reason;
}
/**
* Returns the reason explaining capture status.
*
* @return string
*/
public function reason(): string {
return $this->reason;
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatusDetails;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
@ -39,9 +40,14 @@ class AuthorizationFactory {
);
}
$reason = $data->status_details->reason ?? null;
return new Authorization(
$data->id,
new AuthorizationStatus( $data->status )
new AuthorizationStatus(
$data->status,
$reason ? new AuthorizationStatusDetails( $reason ) : null
)
);
}
}

View file

@ -10,6 +10,8 @@ declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatusDetails;
/**
* Class CaptureFactory
@ -42,11 +44,14 @@ class CaptureFactory {
*/
public function from_paypal_response( \stdClass $data ) : Capture {
$reason = isset( $data->status_details->reason ) ? (string) $data->status_details->reason : '';
$reason = $data->status_details->reason ?? null;
return new Capture(
(string) $data->id,
(string) $data->status,
$reason,
new CaptureStatus(
(string) $data->status,
$reason ? new CaptureStatusDetails( $reason ) : null
),
$this->amount_factory->from_paypal_response( $data->amount ),
(bool) $data->final_capture,
(string) $data->seller_protection->status,