mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge branch 'trunk' into PCP-327-prevent-subscription-from-being-
This commit is contained in:
commit
a21aa9111a
14 changed files with 412 additions and 338 deletions
|
@ -138,6 +138,7 @@ class PaymentsEndpoint {
|
|||
*
|
||||
* @return Authorization
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function capture( string $authorization_id ): Authorization {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
@ -155,39 +156,18 @@ class PaymentsEndpoint {
|
|||
$json = json_decode( $response['body'] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error = new RuntimeException(
|
||||
__( 'Could not capture authorized payment.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
throw new RuntimeException( 'Could not capture authorized payment.' );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
$error = new PayPalApiException(
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$authorization = $this->authorizations_factory->from_paypal_response( $json );
|
||||
return $authorization;
|
||||
return $this->authorizations_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,10 +175,11 @@ class PaymentsEndpoint {
|
|||
*
|
||||
* @param Refund $refund The refund to be processed.
|
||||
*
|
||||
* @return bool
|
||||
* @return void
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function refund( Refund $refund ) : bool {
|
||||
public function refund( Refund $refund ) : void {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund';
|
||||
$args = array(
|
||||
|
@ -215,37 +196,50 @@ class PaymentsEndpoint {
|
|||
$json = json_decode( $response['body'] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error = new RuntimeException(
|
||||
__( 'Could not refund payment.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
throw new RuntimeException( 'Could not refund payment.' );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
$error = new PayPalApiException(
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Voids a transaction.
|
||||
*
|
||||
* @param Authorization $authorization The PayPal payment authorization to void.
|
||||
*
|
||||
* @return void
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function void( Authorization $authorization ) : void {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization->id() . '/void';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new RuntimeException( 'Could not void transaction.' );
|
||||
}
|
||||
|
||||
return true;
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
// Currently it can return body with 200 status, despite the docs saying that it should be 204 No content.
|
||||
// We don't care much about body, so just checking that it was successful.
|
||||
if ( $status_code < 200 || $status_code > 299 ) {
|
||||
throw new PayPalApiException( null, $status_code );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,13 @@ class PayPalApiException extends RuntimeException {
|
|||
$response = new \stdClass();
|
||||
}
|
||||
if ( ! isset( $response->message ) ) {
|
||||
$response->message = __(
|
||||
'Unknown error while connecting to PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
$response->message = sprintf(
|
||||
/* translators: %1$d - HTTP status code number (404, 500, ...) */
|
||||
__(
|
||||
'Unknown error while connecting to PayPal. Status code: %1$d.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
$this->status_code
|
||||
);
|
||||
}
|
||||
if ( ! isset( $response->name ) ) {
|
||||
|
|
|
@ -608,7 +608,10 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private function get_3ds_contingency(): string {
|
||||
if ( $this->settings->has( '3d_secure_contingency' ) ) {
|
||||
return $this->settings->get( '3d_secure_contingency' );
|
||||
$value = $this->settings->get( '3d_secure_contingency' );
|
||||
if ( $value ) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return 'SCA_WHEN_REQUIRED';
|
||||
|
|
|
@ -219,19 +219,22 @@ return array(
|
|||
'wcgateway.processor.refunds' => static function ( $container ): RefundProcessor {
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
$payments_endpoint = $container->get( 'api.endpoint.payments' );
|
||||
return new RefundProcessor( $order_endpoint, $payments_endpoint );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new RefundProcessor( $order_endpoint, $payments_endpoint, $logger );
|
||||
},
|
||||
'wcgateway.processor.authorized-payments' => static function ( $container ): AuthorizedPaymentsProcessor {
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
$payments_endpoint = $container->get( 'api.endpoint.payments' );
|
||||
return new AuthorizedPaymentsProcessor( $order_endpoint, $payments_endpoint );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new AuthorizedPaymentsProcessor( $order_endpoint, $payments_endpoint, $logger );
|
||||
},
|
||||
'wcgateway.admin.render-authorize-action' => static function ( $container ): RenderAuthorizeAction {
|
||||
|
||||
return new RenderAuthorizeAction();
|
||||
},
|
||||
'wcgateway.admin.order-payment-status' => static function ( $container ): PaymentStatusOrderDetail {
|
||||
return new PaymentStatusOrderDetail();
|
||||
$column = $container->get( 'wcgateway.admin.orders-payment-status-column' );
|
||||
return new PaymentStatusOrderDetail( $column );
|
||||
},
|
||||
'wcgateway.admin.orders-payment-status-column' => static function ( $container ): OrderTablePaymentStatusColumn {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
|
|
@ -81,7 +81,7 @@ class OrderTablePaymentStatusColumn {
|
|||
|
||||
$wc_order = wc_get_order( $wc_order_id );
|
||||
|
||||
if ( ! is_a( $wc_order, \WC_Order::class ) || ! $this->render_for_order( $wc_order ) ) {
|
||||
if ( ! is_a( $wc_order, \WC_Order::class ) || ! $this->should_render_for_order( $wc_order ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -100,8 +100,14 @@ class OrderTablePaymentStatusColumn {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function render_for_order( \WC_Order $order ): bool {
|
||||
return ! empty( $order->get_meta( PayPalGateway::CAPTURED_META_KEY ) );
|
||||
public function should_render_for_order( \WC_Order $order ): bool {
|
||||
$intent = $order->get_meta( PayPalGateway::INTENT_META_KEY );
|
||||
$captured = $order->get_meta( PayPalGateway::CAPTURED_META_KEY );
|
||||
$status = $order->get_status();
|
||||
$not_allowed_statuses = array( 'refunded' );
|
||||
return ! empty( $intent ) && strtoupper( self::INTENT ) === strtoupper( $intent ) &&
|
||||
! empty( $captured ) &&
|
||||
! in_array( $status, $not_allowed_statuses, true );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +117,7 @@ class OrderTablePaymentStatusColumn {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_captured( \WC_Order $wc_order ): bool {
|
||||
public function is_captured( \WC_Order $wc_order ): bool {
|
||||
$captured = $wc_order->get_meta( PayPalGateway::CAPTURED_META_KEY );
|
||||
return wc_string_to_bool( $captured );
|
||||
}
|
||||
|
|
|
@ -16,6 +16,22 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|||
*/
|
||||
class PaymentStatusOrderDetail {
|
||||
|
||||
/**
|
||||
* The capture info column.
|
||||
*
|
||||
* @var OrderTablePaymentStatusColumn
|
||||
*/
|
||||
private $column;
|
||||
|
||||
/**
|
||||
* PaymentStatusOrderDetail constructor.
|
||||
*
|
||||
* @param OrderTablePaymentStatusColumn $column The capture info column.
|
||||
*/
|
||||
public function __construct( OrderTablePaymentStatusColumn $column ) {
|
||||
$this->column = $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the not captured information.
|
||||
*
|
||||
|
@ -23,14 +39,8 @@ class PaymentStatusOrderDetail {
|
|||
*/
|
||||
public function render( int $wc_order_id ) {
|
||||
$wc_order = new \WC_Order( $wc_order_id );
|
||||
$intent = $wc_order->get_meta( PayPalGateway::INTENT_META_KEY );
|
||||
$captured = $wc_order->get_meta( PayPalGateway::CAPTURED_META_KEY );
|
||||
|
||||
if ( strcasecmp( $intent, 'AUTHORIZE' ) !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $captured ) && wc_string_to_bool( $captured ) ) {
|
||||
if ( ! $this->column->should_render_for_order( $wc_order ) || $this->column->is_captured( $wc_order ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -257,10 +257,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
* @return bool
|
||||
*/
|
||||
public function capture_authorized_payment( \WC_Order $wc_order ): bool {
|
||||
$is_processed = $this->authorized_payments->process( $wc_order );
|
||||
$this->render_authorization_message_for_status( $this->authorized_payments->last_status() );
|
||||
$result_status = $this->authorized_payments->process( $wc_order );
|
||||
$this->render_authorization_message_for_status( $result_status );
|
||||
|
||||
if ( $is_processed ) {
|
||||
if ( AuthorizedPaymentsProcessor::SUCCESSFUL === $result_status ) {
|
||||
$wc_order->add_order_note(
|
||||
__( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
|
@ -270,7 +270,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
return true;
|
||||
}
|
||||
|
||||
if ( $this->authorized_payments->last_status() === AuthorizedPaymentsProcessor::ALREADY_CAPTURED ) {
|
||||
if ( AuthorizedPaymentsProcessor::ALREADY_CAPTURED === $result_status ) {
|
||||
if ( $wc_order->get_status() === 'on-hold' ) {
|
||||
$wc_order->add_order_note(
|
||||
__( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
|
||||
|
@ -293,10 +293,11 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
private function render_authorization_message_for_status( string $status ) {
|
||||
|
||||
$message_mapping = array(
|
||||
AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
|
||||
AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
|
||||
AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
|
||||
AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
|
||||
AuthorizedPaymentsProcessor::SUCCESSFUL => AuthorizeOrderActionNotice::SUCCESS,
|
||||
AuthorizedPaymentsProcessor::ALREADY_CAPTURED => AuthorizeOrderActionNotice::ALREADY_CAPTURED,
|
||||
AuthorizedPaymentsProcessor::INACCESSIBLE => AuthorizeOrderActionNotice::NO_INFO,
|
||||
AuthorizedPaymentsProcessor::NOT_FOUND => AuthorizeOrderActionNotice::NOT_FOUND,
|
||||
AuthorizedPaymentsProcessor::BAD_AUTHORIZATION => AuthorizeOrderActionNotice::BAD_AUTHORIZATION,
|
||||
);
|
||||
$display_message = ( isset( $message_mapping[ $status ] ) ) ?
|
||||
$message_mapping[ $status ]
|
||||
|
|
|
@ -18,11 +18,12 @@ class AuthorizeOrderActionNotice {
|
|||
|
||||
const QUERY_PARAM = 'ppcp-authorized-message';
|
||||
|
||||
const NO_INFO = 81;
|
||||
const ALREADY_CAPTURED = 82;
|
||||
const FAILED = 83;
|
||||
const SUCCESS = 84;
|
||||
const NOT_FOUND = 85;
|
||||
const NO_INFO = 81;
|
||||
const ALREADY_CAPTURED = 82;
|
||||
const FAILED = 83;
|
||||
const SUCCESS = 84;
|
||||
const NOT_FOUND = 85;
|
||||
const BAD_AUTHORIZATION = 86;
|
||||
|
||||
/**
|
||||
* Returns the current message if there is one.
|
||||
|
@ -45,35 +46,42 @@ class AuthorizeOrderActionNotice {
|
|||
* @return array
|
||||
*/
|
||||
private function current_message(): array {
|
||||
$messages[ self::NO_INFO ] = array(
|
||||
$messages[ self::NO_INFO ] = array(
|
||||
'message' => __(
|
||||
'Could not retrieve information. Try again later.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'type' => 'error',
|
||||
);
|
||||
$messages[ self::ALREADY_CAPTURED ] = array(
|
||||
$messages[ self::ALREADY_CAPTURED ] = array(
|
||||
'message' => __(
|
||||
'Payment already captured.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'type' => 'error',
|
||||
);
|
||||
$messages[ self::FAILED ] = array(
|
||||
$messages[ self::FAILED ] = array(
|
||||
'message' => __(
|
||||
'Failed to capture. Try again later.',
|
||||
'Failed to capture. Try again later or checks the logs.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'type' => 'error',
|
||||
);
|
||||
$messages[ self::NOT_FOUND ] = array(
|
||||
$messages[ self::BAD_AUTHORIZATION ] = array(
|
||||
'message' => __(
|
||||
'Cannot capture, no valid payment authorization.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'type' => 'error',
|
||||
);
|
||||
$messages[ self::NOT_FOUND ] = array(
|
||||
'message' => __(
|
||||
'Could not find payment to process.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'type' => 'error',
|
||||
);
|
||||
$messages[ self::SUCCESS ] = array(
|
||||
$messages[ self::SUCCESS ] = array(
|
||||
'message' => __(
|
||||
'Payment successfully captured.',
|
||||
'woocommerce-paypal-payments'
|
||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
|
@ -22,11 +23,12 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|||
*/
|
||||
class AuthorizedPaymentsProcessor {
|
||||
|
||||
const SUCCESSFUL = 'SUCCESSFUL';
|
||||
const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
|
||||
const FAILED = 'FAILED';
|
||||
const INACCESSIBLE = 'INACCESSIBLE';
|
||||
const NOT_FOUND = 'NOT_FOUND';
|
||||
const SUCCESSFUL = 'SUCCESSFUL';
|
||||
const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
|
||||
const FAILED = 'FAILED';
|
||||
const INACCESSIBLE = 'INACCESSIBLE';
|
||||
const NOT_FOUND = 'NOT_FOUND';
|
||||
const BAD_AUTHORIZATION = 'BAD_AUTHORIZATION';
|
||||
|
||||
/**
|
||||
* The Order endpoint.
|
||||
|
@ -43,25 +45,28 @@ class AuthorizedPaymentsProcessor {
|
|||
private $payments_endpoint;
|
||||
|
||||
/**
|
||||
* The last status.
|
||||
* The logger.
|
||||
*
|
||||
* @var string
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $last_status = '';
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* AuthorizedPaymentsProcessor constructor.
|
||||
*
|
||||
* @param OrderEndpoint $order_endpoint The Order endpoint.
|
||||
* @param PaymentsEndpoint $payments_endpoint The Payments endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
OrderEndpoint $order_endpoint,
|
||||
PaymentsEndpoint $payments_endpoint
|
||||
PaymentsEndpoint $payments_endpoint,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,46 +74,36 @@ class AuthorizedPaymentsProcessor {
|
|||
*
|
||||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
*
|
||||
* @return bool
|
||||
* @return string One of the AuthorizedPaymentsProcessor status constants.
|
||||
*/
|
||||
public function process( \WC_Order $wc_order ): bool {
|
||||
public function process( \WC_Order $wc_order ): string {
|
||||
try {
|
||||
$order = $this->paypal_order_from_wc_order( $wc_order );
|
||||
} catch ( Exception $exception ) {
|
||||
if ( $exception->getCode() === 404 ) {
|
||||
$this->last_status = self::NOT_FOUND;
|
||||
return false;
|
||||
return self::NOT_FOUND;
|
||||
}
|
||||
$this->last_status = self::INACCESSIBLE;
|
||||
return false;
|
||||
return self::INACCESSIBLE;
|
||||
}
|
||||
|
||||
$authorizations = $this->all_authorizations( $order );
|
||||
|
||||
if ( ! $this->are_authorzations_to_capture( ...$authorizations ) ) {
|
||||
$this->last_status = self::ALREADY_CAPTURED;
|
||||
return false;
|
||||
if ( ! $this->authorizations_to_capture( ...$authorizations ) ) {
|
||||
if ( $this->captured_authorizations( ...$authorizations ) ) {
|
||||
return self::ALREADY_CAPTURED;
|
||||
}
|
||||
|
||||
return self::BAD_AUTHORIZATION;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->capture_authorizations( ...$authorizations );
|
||||
} catch ( Exception $exception ) {
|
||||
$this->last_status = self::FAILED;
|
||||
return false;
|
||||
$this->logger->error( 'Failed to capture authorization: ' . $exception->getMessage() );
|
||||
return self::FAILED;
|
||||
}
|
||||
|
||||
$this->last_status = self::SUCCESSFUL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last status.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function last_status(): string {
|
||||
|
||||
return $this->last_status;
|
||||
return self::SUCCESSFUL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,17 +136,6 @@ class AuthorizedPaymentsProcessor {
|
|||
return $authorizations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether Authorizations need to be captured.
|
||||
*
|
||||
* @param Authorization ...$authorizations All Authorizations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function are_authorzations_to_capture( Authorization ...$authorizations ): bool {
|
||||
return (bool) count( $this->authorizations_to_capture( ...$authorizations ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the authorizations.
|
||||
*
|
||||
|
@ -171,11 +155,38 @@ class AuthorizedPaymentsProcessor {
|
|||
* @return Authorization[]
|
||||
*/
|
||||
private function authorizations_to_capture( Authorization ...$authorizations ): array {
|
||||
return $this->filter_authorizations(
|
||||
$authorizations,
|
||||
array( AuthorizationStatus::CREATED, AuthorizationStatus::PENDING )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The authorizations which were captured.
|
||||
*
|
||||
* @param Authorization ...$authorizations All Authorizations.
|
||||
* @return Authorization[]
|
||||
*/
|
||||
private function captured_authorizations( Authorization ...$authorizations ): array {
|
||||
return $this->filter_authorizations(
|
||||
$authorizations,
|
||||
array( AuthorizationStatus::CAPTURED )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The authorizations which need to be filtered.
|
||||
*
|
||||
* @param Authorization[] $authorizations All Authorizations.
|
||||
* @param string[] $statuses Allowed statuses, the constants from AuthorizationStatus.
|
||||
* @return Authorization[]
|
||||
*/
|
||||
private function filter_authorizations( array $authorizations, array $statuses ): array {
|
||||
return array_filter(
|
||||
$authorizations,
|
||||
static function ( Authorization $authorization ): bool {
|
||||
return $authorization->status()->is( AuthorizationStatus::CREATED )
|
||||
|| $authorization->status()->is( AuthorizationStatus::PENDING );
|
||||
static function ( Authorization $authorization ) use ( $statuses ): bool {
|
||||
$status = $authorization->status();
|
||||
return in_array( $status->name(), $statuses, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ class OrderProcessor {
|
|||
$wc_order->payment_complete();
|
||||
}
|
||||
|
||||
if ( $this->capture_authorized_downloads( $order ) && $this->authorized_payments_processor->process( $wc_order ) ) {
|
||||
if ( $this->capture_authorized_downloads( $order ) && AuthorizedPaymentsProcessor::SUCCESSFUL === $this->authorized_payments_processor->process( $wc_order ) ) {
|
||||
$wc_order->add_order_note(
|
||||
__( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
|
|
|
@ -9,10 +9,15 @@ declare( strict_types=1 );
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
@ -22,6 +27,10 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|||
*/
|
||||
class RefundProcessor {
|
||||
|
||||
private const REFUND_MODE_REFUND = 'refund';
|
||||
private const REFUND_MODE_VOID = 'void';
|
||||
private const REFUND_MODE_UNKNOWN = 'unknown';
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
|
@ -36,16 +45,25 @@ class RefundProcessor {
|
|||
*/
|
||||
private $payments_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* RefundProcessor constructor.
|
||||
*
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( OrderEndpoint $order_endpoint, PaymentsEndpoint $payments_endpoint ) {
|
||||
public function __construct( OrderEndpoint $order_endpoint, PaymentsEndpoint $payments_endpoint, LoggerInterface $logger ) {
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,44 +74,115 @@ class RefundProcessor {
|
|||
* @param string $reason The reason for the refund.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.Missing
|
||||
*/
|
||||
public function process( \WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
if ( ! $order ) {
|
||||
return false;
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
throw new RuntimeException( 'PayPal order ID not found in meta.' );
|
||||
}
|
||||
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
|
||||
$purchase_units = $order->purchase_units();
|
||||
if ( ! $purchase_units ) {
|
||||
return false;
|
||||
throw new RuntimeException( 'No purchase units.' );
|
||||
}
|
||||
|
||||
$payments = $purchase_units[0]->payments();
|
||||
if ( ! $payments ) {
|
||||
return false;
|
||||
}
|
||||
$captures = $payments->captures();
|
||||
if ( ! $captures ) {
|
||||
return false;
|
||||
throw new RuntimeException( 'No payments.' );
|
||||
}
|
||||
|
||||
$capture = $captures[0];
|
||||
$refund = new Refund(
|
||||
$capture,
|
||||
$capture->invoice_id(),
|
||||
$reason,
|
||||
new Amount(
|
||||
new Money( $amount, $wc_order->get_currency() )
|
||||
$this->logger->debug(
|
||||
sprintf(
|
||||
'Trying to refund/void order %1$s, payments: %2$s.',
|
||||
$order->id(),
|
||||
wp_json_encode( $payments->to_array() )
|
||||
)
|
||||
);
|
||||
return $this->payments_endpoint->refund( $refund );
|
||||
} catch ( RuntimeException $error ) {
|
||||
|
||||
$mode = $this->determine_refund_mode( $payments );
|
||||
|
||||
switch ( $mode ) {
|
||||
case self::REFUND_MODE_REFUND:
|
||||
$captures = $payments->captures();
|
||||
if ( ! $captures ) {
|
||||
throw new RuntimeException( 'No capture.' );
|
||||
}
|
||||
|
||||
$capture = $captures[0];
|
||||
$refund = new Refund(
|
||||
$capture,
|
||||
$capture->invoice_id(),
|
||||
$reason,
|
||||
new Amount(
|
||||
new Money( $amount, $wc_order->get_currency() )
|
||||
)
|
||||
);
|
||||
$this->payments_endpoint->refund( $refund );
|
||||
break;
|
||||
case self::REFUND_MODE_VOID:
|
||||
$voidable_authorizations = array_filter(
|
||||
$payments->authorizations(),
|
||||
array( $this, 'is_voidable_authorization' )
|
||||
);
|
||||
if ( ! $voidable_authorizations ) {
|
||||
throw new RuntimeException( 'No voidable authorizations.' );
|
||||
}
|
||||
|
||||
foreach ( $voidable_authorizations as $authorization ) {
|
||||
$this->payments_endpoint->void( $authorization );
|
||||
}
|
||||
|
||||
$wc_order->set_status( 'refunded' );
|
||||
$wc_order->save();
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException( 'Nothing to refund/void.' );
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( Exception $error ) {
|
||||
$this->logger->error( 'Refund failed: ' . $error->getMessage() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the refunding mode.
|
||||
*
|
||||
* @param Payments $payments The order payments state.
|
||||
*
|
||||
* @return string One of the REFUND_MODE_ constants.
|
||||
*/
|
||||
private function determine_refund_mode( Payments $payments ): string {
|
||||
$authorizations = $payments->authorizations();
|
||||
if ( $authorizations ) {
|
||||
foreach ( $authorizations as $authorization ) {
|
||||
if ( $this->is_voidable_authorization( $authorization ) ) {
|
||||
return self::REFUND_MODE_VOID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $payments->captures() ) {
|
||||
return self::REFUND_MODE_REFUND;
|
||||
}
|
||||
|
||||
return self::REFUND_MODE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the authorization can be voided.
|
||||
*
|
||||
* @param Authorization $authorization The authorization to check.
|
||||
* @return bool
|
||||
*/
|
||||
private function is_voidable_authorization( Authorization $authorization ): bool {
|
||||
return $authorization->status()->is( AuthorizationStatus::CREATED );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\NullLogger;
|
||||
use Requests_Utility_CaseInsensitiveDictionary;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
|
@ -235,10 +236,6 @@ class PaymentsEndpointTest extends TestCase
|
|||
|
||||
$authorizationFactory = Mockery::mock(AuthorizationFactory::class);
|
||||
|
||||
$logger = Mockery::mock(LoggerInterface::class);
|
||||
$logger->expects('log');
|
||||
$logger->expects('debug');
|
||||
|
||||
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
|
||||
$headers->shouldReceive('getAll');
|
||||
$rawResponse = [
|
||||
|
@ -250,7 +247,7 @@ class PaymentsEndpointTest extends TestCase
|
|||
$host,
|
||||
$bearer,
|
||||
$authorizationFactory,
|
||||
$logger
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
expect('wp_remote_get')->andReturn($rawResponse);
|
||||
|
@ -281,15 +278,11 @@ class PaymentsEndpointTest extends TestCase
|
|||
'headers' => $headers,
|
||||
];
|
||||
|
||||
$logger = Mockery::mock(LoggerInterface::class);
|
||||
$logger->expects('log');
|
||||
$logger->expects('debug');
|
||||
|
||||
$testee = new PaymentsEndpoint(
|
||||
$host,
|
||||
$bearer,
|
||||
$authorizationFactory,
|
||||
$logger
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
expect('wp_remote_get')->andReturn($rawResponse);
|
||||
|
|
|
@ -227,10 +227,7 @@ class WcGatewayTest extends TestCase
|
|||
$authorizedPaymentsProcessor
|
||||
->expects('process')
|
||||
->with($wcOrder)
|
||||
->andReturnTrue();
|
||||
$authorizedPaymentsProcessor
|
||||
->expects('last_status')
|
||||
->andReturn(AuthorizedPaymentsProcessor::SUCCESSFUL);
|
||||
->andReturn(AuthorizedPaymentsProcessor::SUCCESSFUL);
|
||||
$authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class);
|
||||
$authorizedOrderActionNotice
|
||||
->expects('display_message')
|
||||
|
@ -286,10 +283,7 @@ class WcGatewayTest extends TestCase
|
|||
$authorizedPaymentsProcessor
|
||||
->expects('process')
|
||||
->with($wcOrder)
|
||||
->andReturnFalse();
|
||||
$authorizedPaymentsProcessor
|
||||
->shouldReceive('last_status')
|
||||
->andReturn(AuthorizedPaymentsProcessor::ALREADY_CAPTURED);
|
||||
->andReturn(AuthorizedPaymentsProcessor::ALREADY_CAPTURED);
|
||||
$authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class);
|
||||
$authorizedOrderActionNotice
|
||||
->expects('display_message')
|
||||
|
@ -338,10 +332,7 @@ class WcGatewayTest extends TestCase
|
|||
$authorizedPaymentsProcessor
|
||||
->expects('process')
|
||||
->with($wcOrder)
|
||||
->andReturnFalse();
|
||||
$authorizedPaymentsProcessor
|
||||
->shouldReceive('last_status')
|
||||
->andReturn($lastStatus);
|
||||
->andReturn($lastStatus);
|
||||
$authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class);
|
||||
$authorizedOrderActionNotice
|
||||
->expects('display_message')
|
||||
|
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
||||
|
||||
|
||||
use Psr\Log\NullLogger;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
|
@ -17,186 +19,145 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|||
use Mockery;
|
||||
class AuthorizedPaymentsProcessorTest extends TestCase
|
||||
{
|
||||
private $wcOrder;
|
||||
private $paypalOrderId = 'abc';
|
||||
|
||||
public function testDefault() {
|
||||
$orderId = 'abc';
|
||||
$authorizationId = 'def';
|
||||
$authorizationStatus = Mockery::mock(AuthorizationStatus::class);
|
||||
$authorizationStatus
|
||||
->shouldReceive('is')
|
||||
->with(AuthorizationStatus::CREATED)
|
||||
->andReturn(true);
|
||||
$authorization = Mockery::mock(Authorization::class);
|
||||
$authorization
|
||||
->shouldReceive('id')
|
||||
->andReturn($authorizationId);
|
||||
$authorization
|
||||
->shouldReceive('status')
|
||||
->andReturn($authorizationStatus);
|
||||
$payments = Mockery::mock(Payments::class);
|
||||
$payments
|
||||
->expects('authorizations')
|
||||
->andReturn([$authorization]);
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit
|
||||
->expects('payments')
|
||||
->andReturn($payments);
|
||||
$order = Mockery::mock(Order::class);
|
||||
$order
|
||||
->expects('purchase_units')
|
||||
->andReturn([$purchaseUnit]);
|
||||
$orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$orderEndpoint
|
||||
->expects('order')
|
||||
->with($orderId)
|
||||
->andReturn($order);
|
||||
$paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$paymentsEndpoint
|
||||
->expects('capture')
|
||||
->with($authorizationId)
|
||||
->andReturn($authorization);
|
||||
$testee = new AuthorizedPaymentsProcessor($orderEndpoint, $paymentsEndpoint);
|
||||
private $authorizationId = 'qwe';
|
||||
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder
|
||||
->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($orderId);
|
||||
$this->assertTrue($testee->process($wcOrder));
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $testee->last_status());
|
||||
private $paypalOrder;
|
||||
|
||||
private $orderEndpoint;
|
||||
|
||||
private $paymentsEndpoint;
|
||||
|
||||
private $testee;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->wcOrder = $this->createWcOrder($this->paypalOrderId);
|
||||
|
||||
$this->paypalOrder = $this->createPaypalOrder([$this->createAuthorization($this->authorizationId, AuthorizationStatus::CREATED)]);
|
||||
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$this->orderEndpoint
|
||||
->shouldReceive('order')
|
||||
->with($this->paypalOrderId)
|
||||
->andReturnUsing(function () {
|
||||
return $this->paypalOrder;
|
||||
})
|
||||
->byDefault();
|
||||
|
||||
$this->paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
|
||||
$this->testee = new AuthorizedPaymentsProcessor($this->orderEndpoint, $this->paymentsEndpoint, new NullLogger());
|
||||
}
|
||||
|
||||
public function testSuccess() {
|
||||
$this->paymentsEndpoint
|
||||
->expects('capture')
|
||||
->with($this->authorizationId)
|
||||
->andReturn($this->createAuthorization($this->authorizationId, AuthorizationStatus::CAPTURED));
|
||||
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
public function testCapturesAllCaptureable() {
|
||||
$authorizations = [
|
||||
$this->createAuthorization('id1', AuthorizationStatus::CREATED),
|
||||
$this->createAuthorization('id2', AuthorizationStatus::VOIDED),
|
||||
$this->createAuthorization('id3', AuthorizationStatus::PENDING),
|
||||
$this->createAuthorization('id4', AuthorizationStatus::CAPTURED),
|
||||
$this->createAuthorization('id5', AuthorizationStatus::DENIED),
|
||||
$this->createAuthorization('id6', AuthorizationStatus::EXPIRED),
|
||||
$this->createAuthorization('id7', AuthorizationStatus::COMPLETED),
|
||||
];
|
||||
$this->paypalOrder = $this->createPaypalOrder($authorizations);
|
||||
|
||||
foreach ([$authorizations[0], $authorizations[2]] as $authorization) {
|
||||
$this->paymentsEndpoint
|
||||
->expects('capture')
|
||||
->with($authorization->id())
|
||||
->andReturn($this->createAuthorization($authorization->id(), AuthorizationStatus::CAPTURED));
|
||||
}
|
||||
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
public function testInaccessible() {
|
||||
$orderId = 'abc';
|
||||
$orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$orderEndpoint
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with($orderId)
|
||||
->with($this->paypalOrderId)
|
||||
->andThrow(RuntimeException::class);
|
||||
$paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$testee = new AuthorizedPaymentsProcessor($orderEndpoint, $paymentsEndpoint);
|
||||
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder
|
||||
->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($orderId);
|
||||
$this->assertFalse($testee->process($wcOrder));
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::INACCESSIBLE, $testee->last_status());
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::INACCESSIBLE, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
public function testNotFound() {
|
||||
$orderId = 'abc';
|
||||
$orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$orderEndpoint
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with($orderId)
|
||||
->andThrow(new RuntimeException("text", 404));
|
||||
$paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$testee = new AuthorizedPaymentsProcessor($orderEndpoint, $paymentsEndpoint);
|
||||
->with($this->paypalOrderId)
|
||||
->andThrow(new RuntimeException('text', 404));
|
||||
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder
|
||||
->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($orderId);
|
||||
$this->assertFalse($testee->process($wcOrder));
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::NOT_FOUND, $testee->last_status());
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::NOT_FOUND, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
public function testCaptureFails() {
|
||||
$orderId = 'abc';
|
||||
$authorizationId = 'def';
|
||||
$authorizationStatus = Mockery::mock(AuthorizationStatus::class);
|
||||
$authorizationStatus
|
||||
->shouldReceive('is')
|
||||
->with(AuthorizationStatus::CREATED)
|
||||
->andReturn(true);
|
||||
$authorization = Mockery::mock(Authorization::class);
|
||||
$authorization
|
||||
->shouldReceive('id')
|
||||
->andReturn($authorizationId);
|
||||
$authorization
|
||||
->shouldReceive('status')
|
||||
->andReturn($authorizationStatus);
|
||||
$payments = Mockery::mock(Payments::class);
|
||||
$payments
|
||||
->expects('authorizations')
|
||||
->andReturn([$authorization]);
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit
|
||||
->expects('payments')
|
||||
->andReturn($payments);
|
||||
$order = Mockery::mock(Order::class);
|
||||
$order
|
||||
->expects('purchase_units')
|
||||
->andReturn([$purchaseUnit]);
|
||||
$orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$orderEndpoint
|
||||
->expects('order')
|
||||
->with($orderId)
|
||||
->andReturn($order);
|
||||
$paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$paymentsEndpoint
|
||||
$this->paymentsEndpoint
|
||||
->expects('capture')
|
||||
->with($authorizationId)
|
||||
->with($this->authorizationId)
|
||||
->andThrow(RuntimeException::class);
|
||||
$testee = new AuthorizedPaymentsProcessor($orderEndpoint, $paymentsEndpoint);
|
||||
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder
|
||||
->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($orderId);
|
||||
$this->assertFalse($testee->process($wcOrder));
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::FAILED, $testee->last_status());
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::FAILED, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
public function testAllAreCaptured() {
|
||||
$orderId = 'abc';
|
||||
$authorizationId = 'def';
|
||||
$authorizationStatus = Mockery::mock(AuthorizationStatus::class);
|
||||
$authorizationStatus
|
||||
->shouldReceive('is')
|
||||
->with(AuthorizationStatus::CREATED)
|
||||
->andReturn(false);
|
||||
$authorizationStatus
|
||||
->shouldReceive('is')
|
||||
->with(AuthorizationStatus::PENDING)
|
||||
->andReturn(false);
|
||||
$authorization = Mockery::mock(Authorization::class);
|
||||
$authorization
|
||||
->shouldReceive('id')
|
||||
->andReturn($authorizationId);
|
||||
$authorization
|
||||
->shouldReceive('status')
|
||||
->andReturn($authorizationStatus);
|
||||
$payments = Mockery::mock(Payments::class);
|
||||
$payments
|
||||
->expects('authorizations')
|
||||
->andReturn([$authorization]);
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit
|
||||
->expects('payments')
|
||||
->andReturn($payments);
|
||||
$order = Mockery::mock(Order::class);
|
||||
$order
|
||||
->expects('purchase_units')
|
||||
->andReturn([$purchaseUnit]);
|
||||
$orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
$orderEndpoint
|
||||
->expects('order')
|
||||
->with($orderId)
|
||||
->andReturn($order);
|
||||
$paymentsEndpoint = Mockery::mock(PaymentsEndpoint::class);
|
||||
$testee = new AuthorizedPaymentsProcessor($orderEndpoint, $paymentsEndpoint);
|
||||
public function testAlreadyCaptured() {
|
||||
$this->paypalOrder = $this->createPaypalOrder([$this->createAuthorization($this->authorizationId, AuthorizationStatus::CAPTURED)]);
|
||||
|
||||
$wcOrder = Mockery::mock(\WC_Order::class);
|
||||
$wcOrder
|
||||
->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($orderId);
|
||||
$this->assertFalse($testee->process($wcOrder));
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::ALREADY_CAPTURED, $testee->last_status());
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::ALREADY_CAPTURED, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
}
|
||||
|
||||
public function testBadAuthorization() {
|
||||
$this->paypalOrder = $this->createPaypalOrder([$this->createAuthorization($this->authorizationId, AuthorizationStatus::DENIED)]);
|
||||
|
||||
$this->assertEquals(AuthorizedPaymentsProcessor::BAD_AUTHORIZATION, $this->testee->process($this->wcOrder));
|
||||
}
|
||||
|
||||
private function createWcOrder(string $paypalOrderId): WC_Order {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder
|
||||
->shouldReceive('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn($paypalOrderId);
|
||||
return $wcOrder;
|
||||
}
|
||||
|
||||
private function createAuthorization(string $id, string $status): Authorization {
|
||||
$authorization = Mockery::mock(Authorization::class);
|
||||
$authorization
|
||||
->shouldReceive('id')
|
||||
->andReturn($id);
|
||||
$authorization
|
||||
->shouldReceive('status')
|
||||
->andReturn(new AuthorizationStatus($status));
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function createPaypalOrder(array $authorizations): Order {
|
||||
$payments = Mockery::mock(Payments::class);
|
||||
$payments
|
||||
->shouldReceive('authorizations')
|
||||
->andReturn($authorizations);
|
||||
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit
|
||||
->shouldReceive('payments')
|
||||
->andReturn($payments);
|
||||
|
||||
$order = Mockery::mock(Order::class);
|
||||
$order
|
||||
->shouldReceive('purchase_units')
|
||||
->andReturn([$purchaseUnit]);
|
||||
return $order;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue