diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 199b14e78..1b3174ac8 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\AddressFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\AmountFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ApplicationContextFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory; +use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ItemFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory; @@ -214,6 +215,11 @@ return array( 'api.factory.webhook' => static function ( $container ): WebhookFactory { return new WebhookFactory(); }, + 'api.factory.capture' => static function ( $container ): CaptureFactory { + + $amount_factory = $container->get( 'api.factory.amount' ); + return new CaptureFactory( $amount_factory ); + }, 'api.factory.purchase-unit' => static function ( $container ): PurchaseUnitFactory { $amount_factory = $container->get( 'api.factory.amount' ); @@ -277,7 +283,8 @@ return array( }, 'api.factory.payments' => static function ( $container ): PaymentsFactory { $authorizations_factory = $container->get( 'api.factory.authorization' ); - return new PaymentsFactory( $authorizations_factory ); + $capture_factory = $container->get( 'api.factory.capture' ); + return new PaymentsFactory( $authorizations_factory, $capture_factory ); }, 'api.factory.authorization' => static function ( $container ): AuthorizationFactory { return new AuthorizationFactory(); diff --git a/modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php b/modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php index 7ee7daaa3..4011668c1 100644 --- a/modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/class-paymentsendpoint.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; +use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory; @@ -188,4 +189,63 @@ class PaymentsEndpoint { $authorization = $this->authorizations_factory->from_paypal_response( $json ); return $authorization; } + + /** + * Refunds a payment. + * + * @param Refund $refund The refund to be processed. + * + * @return bool + * @throws RuntimeException If the request fails. + */ + public function refund( Refund $refund ) : bool { + $bearer = $this->bearer->bearer(); + $url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund'; + $args = array( + 'method' => 'POST', + 'headers' => array( + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', + 'Prefer' => 'return=representation', + ), + 'body' => wp_json_encode( $refund->to_array() ), + ); + + $response = $this->request( $url, $args ); + $json = json_decode( $response['body'] ); + + if ( is_wp_error( $response ) ) { + $error = new RuntimeException( + __( 'Could not refund payment.', 'paypal-payments-for-woocommerce' ) + ); + $this->logger->log( + 'warning', + $error->getMessage(), + array( + 'args' => $args, + 'response' => $response, + ) + ); + throw $error; + } + + $status_code = (int) wp_remote_retrieve_response_code( $response ); + if ( 201 !== $status_code ) { + $error = new PayPalApiException( + $json, + $status_code + ); + $this->logger->log( + 'warning', + $error->getMessage(), + array( + 'args' => $args, + 'response' => $response, + ) + ); + throw $error; + } + + return true; + } } diff --git a/modules/ppcp-api-client/src/Entity/class-capture.php b/modules/ppcp-api-client/src/Entity/class-capture.php new file mode 100644 index 000000000..9aad9e029 --- /dev/null +++ b/modules/ppcp-api-client/src/Entity/class-capture.php @@ -0,0 +1,197 @@ +id = $id; + $this->status = $status; + $this->status_details = $status_details; + $this->amount = $amount; + $this->final_capture = $final_capture; + $this->seller_protection = $seller_protection; + $this->invoice_id = $invoice_id; + $this->custom_id = $custom_id; + } + + /** + * Returns the ID. + * + * @return string + */ + public function id() : string { + return $this->id; + } + + /** + * Returns the status. + * + * @return string + */ + public function status() : string { + 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. + * + * @return Amount + */ + public function amount() : Amount { + return $this->amount; + } + + /** + * Returns whether this is the final capture or not. + * + * @return bool + */ + public function final_capture() : bool { + return $this->final_capture; + } + + /** + * Returns the seller protection object. + * + * @return \stdClass + */ + public function seller_protection() : \stdClass { + return (object) array( 'status' => $this->seller_protection ); + } + + /** + * Returns the invoice id. + * + * @return string + */ + public function invoice_id() : string { + return $this->invoice_id; + } + + /** + * Returns the custom id. + * + * @return string + */ + public function custom_id() : string { + return $this->custom_id; + } + + /** + * Returns the entity as array. + * + * @return array + */ + public function to_array() : array { + return array( + 'id' => $this->id(), + 'status' => $this->status(), + 'status_details' => (array) $this->status_details(), + '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(), + ); + } +} diff --git a/modules/ppcp-api-client/src/Entity/class-payments.php b/modules/ppcp-api-client/src/Entity/class-payments.php index 75febd6b6..d5e051f88 100644 --- a/modules/ppcp-api-client/src/Entity/class-payments.php +++ b/modules/ppcp-api-client/src/Entity/class-payments.php @@ -21,13 +21,34 @@ class Payments { */ private $authorizations; + /** + * The Captures. + * + * @var Capture[] + */ + private $captures; + /** * Payments constructor. * - * @param Authorization ...$authorizations The Authorizations. + * @param array $authorizations The Authorizations. + * @param array $captures The Captures. */ - public function __construct( Authorization ...$authorizations ) { + public function __construct( array $authorizations, array $captures ) { + foreach ( $authorizations as $key => $authorization ) { + if ( is_a( $authorization, Authorization::class ) ) { + continue; + } + unset( $authorizations[ $key ] ); + } + foreach ( $captures as $key => $capture ) { + if ( is_a( $capture, Capture::class ) ) { + continue; + } + unset( $captures[ $key ] ); + } $this->authorizations = $authorizations; + $this->captures = $captures; } /** @@ -43,6 +64,12 @@ class Payments { }, $this->authorizations() ), + 'captures' => array_map( + static function ( Capture $capture ): array { + return $capture->to_array(); + }, + $this->captures() + ), ); } @@ -54,4 +81,13 @@ class Payments { public function authorizations(): array { return $this->authorizations; } + + /** + * Returns the Captures. + * + * @return Capture[] + **/ + public function captures(): array { + return $this->captures; + } } diff --git a/modules/ppcp-api-client/src/Entity/class-refund.php b/modules/ppcp-api-client/src/Entity/class-refund.php new file mode 100644 index 000000000..71c2a2f56 --- /dev/null +++ b/modules/ppcp-api-client/src/Entity/class-refund.php @@ -0,0 +1,118 @@ +capture = $capture; + $this->invoice_id = $invoice_id; + $this->note_to_payer = $note_to_payer; + $this->amount = $amount; + } + + /** + * Returns the capture for the refund. + * + * @return Capture + */ + public function for_capture() : Capture { + return $this->capture; + } + + /** + * Return the invoice id. + * + * @return string + */ + public function invoice_id() : string { + return $this->invoice_id; + } + + /** + * Returns the note to the payer. + * + * @return string + */ + public function note_to_payer() : string { + return $this->note_to_payer; + } + + /** + * Returns the Amount. + * + * @return Amount|null + */ + public function amount() { + return $this->amount; + } + + /** + * Returns the object as array. + * + * @return array + */ + public function to_array() : array { + $data = array( + 'invoice_id' => $this->invoice_id(), + ); + if ( $this->note_to_payer() ) { + $data['note_to_payer'] = $this->note_to_payer(); + } + if ( $this->amount() ) { + $data['amount'] = $this->amount()->to_array(); + } + return $data; + } +} diff --git a/modules/ppcp-api-client/src/Factory/class-capturefactory.php b/modules/ppcp-api-client/src/Factory/class-capturefactory.php new file mode 100644 index 000000000..01e0ff29d --- /dev/null +++ b/modules/ppcp-api-client/src/Factory/class-capturefactory.php @@ -0,0 +1,56 @@ +amount_factory = $amount_factory; + } + + /** + * Returns the capture object based off the PayPal response. + * + * @param \stdClass $data The PayPal response. + * + * @return Capture + */ + public function from_paypal_response( \stdClass $data ) : Capture { + + return new Capture( + (string) $data->id, + (string) $data->status, + (string) $data->status_details->reason, + $this->amount_factory->from_paypal_response( $data->amount ), + (bool) $data->final_capture, + (string) $data->seller_protection->status, + (string) $data->invoice_id, + (string) $data->custom_id + ); + } +} diff --git a/modules/ppcp-api-client/src/Factory/class-paymentsfactory.php b/modules/ppcp-api-client/src/Factory/class-paymentsfactory.php index 241fc57eb..353c71f44 100644 --- a/modules/ppcp-api-client/src/Factory/class-paymentsfactory.php +++ b/modules/ppcp-api-client/src/Factory/class-paymentsfactory.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Factory; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; +use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments; /** @@ -24,16 +25,26 @@ class PaymentsFactory { */ private $authorization_factory; + /** + * The Capture factory. + * + * @var CaptureFactory + */ + private $capture_factory; + /** * PaymentsFactory constructor. * - * @param AuthorizationFactory $authorization_factory The AuthorizationFactory. + * @param AuthorizationFactory $authorization_factory The Authorization factory. + * @param CaptureFactory $capture_factory The Capture factory. */ public function __construct( - AuthorizationFactory $authorization_factory + AuthorizationFactory $authorization_factory, + CaptureFactory $capture_factory ) { $this->authorization_factory = $authorization_factory; + $this->capture_factory = $capture_factory; } /** @@ -50,7 +61,13 @@ class PaymentsFactory { }, isset( $data->authorizations ) ? $data->authorizations : array() ); - $payments = new Payments( ...$authorizations ); + $captures = array_map( + function ( \stdClass $authorization ): Capture { + return $this->capture_factory->from_paypal_response( $authorization ); + }, + isset( $data->captures ) ? $data->captures : array() + ); + $payments = new Payments( $authorizations, $captures ); return $payments; } } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 6928768ca..f0b3dd753 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -25,6 +25,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice; use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener; @@ -36,9 +37,11 @@ return array( $order_processor = $container->get( 'wcgateway.order-processor' ); $settings_renderer = $container->get( 'wcgateway.settings.render' ); $authorized_payments = $container->get( 'wcgateway.processor.authorized-payments' ); - $notice = $container->get( 'wcgateway.notice.authorize-order-action' ); - $settings = $container->get( 'wcgateway.settings' ); + $notice = $container->get( 'wcgateway.notice.authorize-order-action' ); + $settings = $container->get( 'wcgateway.settings' ); $session_handler = $container->get( 'session.handler' ); + $refund_processor = $container->get( 'wcgateway.processor.refunds' ); + $state = $container->get( 'onboarding.state' ); return new PayPalGateway( $settings_renderer, @@ -46,7 +49,9 @@ return array( $authorized_payments, $notice, $settings, - $session_handler + $session_handler, + $refund_processor, + $state ); }, 'wcgateway.credit-card-gateway' => static function ( $container ): CreditCardGateway { @@ -57,6 +62,8 @@ return array( $settings = $container->get( 'wcgateway.settings' ); $module_url = $container->get( 'wcgateway.url' ); $session_handler = $container->get( 'session.handler' ); + $refund_processor = $container->get( 'wcgateway.processor.refunds' ); + $state = $container->get( 'onboarding.state' ); return new CreditCardGateway( $settings_renderer, $order_processor, @@ -64,7 +71,9 @@ return array( $notice, $settings, $module_url, - $session_handler + $session_handler, + $refund_processor, + $state ); }, 'wcgateway.disabler' => static function ( $container ): DisableGateways { @@ -132,6 +141,11 @@ return array( $settings ); }, + '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 ); + }, 'wcgateway.processor.authorized-payments' => static function ( $container ): AuthorizedPaymentsProcessor { $order_endpoint = $container->get( 'api.endpoint.order' ); $payments_endpoint = $container->get( 'api.endpoint.payments' ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php b/modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php index e2888a2e1..08ca72ce6 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/class-creditcardgateway.php @@ -9,10 +9,12 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; +use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; use Psr\Container\ContainerInterface; @@ -32,6 +34,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { */ private $module_url; + /** + * The refund processor. + * + * @var RefundProcessor + */ + private $refund_processor; + /** * CreditCardGateway constructor. * @@ -42,6 +51,8 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { * @param ContainerInterface $config The settings. * @param string $module_url The URL to the module. * @param SessionHandler $session_handler The Session Handler. + * @param RefundProcessor $refund_processor The refund processor. + * @param State $state The state. */ public function __construct( SettingsRenderer $settings_renderer, @@ -50,7 +61,9 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { AuthorizeOrderActionNotice $notice, ContainerInterface $config, string $module_url, - SessionHandler $session_handler + SessionHandler $session_handler, + RefundProcessor $refund_processor, + State $state ) { $this->id = self::ID; @@ -60,6 +73,11 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { $this->settings_renderer = $settings_renderer; $this->config = $config; $this->session_handler = $session_handler; + $this->refund_processor = $refund_processor; + + if ( $state->current_state() === State::STATE_ONBOARDED ) { + $this->supports = array( 'refunds' ); + } if ( defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION @@ -67,6 +85,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { && $this->config->get( 'vault_enabled' ) ) { $this->supports = array( + 'refunds', 'products', 'subscriptions', 'subscription_cancellation', @@ -209,4 +228,24 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { public function is_available() : bool { return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' ); } + + + /** + * Process refund. + * + * If the gateway declares 'refunds' support, this will allow it to refund. + * a passed in amount. + * + * @param int $order_id Order ID. + * @param float $amount Refund amount. + * @param string $reason Refund reason. + * @return boolean True or false based on success, or a WP_Error object. + */ + public function process_refund( $order_id, $amount = null, $reason = '' ) { + $order = wc_get_order( $order_id ); + if ( ! is_a( $order, \WC_Order::class ) ) { + return false; + } + return $this->refund_processor->process( $order, (float) $amount, (string) $reason ); + } } diff --git a/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php b/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php index 6bb72fa96..6d2bb96ee 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php @@ -10,10 +10,12 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; +use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; use Psr\Container\ContainerInterface; @@ -72,6 +74,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ protected $session_handler; + /** + * The Refund Processor. + * + * @var RefundProcessor + */ + private $refund_processor; + /** * PayPalGateway constructor. * @@ -81,6 +90,8 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param AuthorizeOrderActionNotice $notice The Order Action Notice object. * @param ContainerInterface $config The settings. * @param SessionHandler $session_handler The Session Handler. + * @param RefundProcessor $refund_processor The Refund Processor. + * @param State $state The state. */ public function __construct( SettingsRenderer $settings_renderer, @@ -88,7 +99,9 @@ class PayPalGateway extends \WC_Payment_Gateway { AuthorizedPaymentsProcessor $authorized_payments_processor, AuthorizeOrderActionNotice $notice, ContainerInterface $config, - SessionHandler $session_handler + SessionHandler $session_handler, + RefundProcessor $refund_processor, + State $state ) { $this->id = self::ID; @@ -98,6 +111,11 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->settings_renderer = $settings_renderer; $this->config = $config; $this->session_handler = $session_handler; + $this->refund_processor = $refund_processor; + + if ( $state->current_state() === State::STATE_ONBOARDED ) { + $this->supports = array( 'refunds' ); + } if ( defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION @@ -105,6 +123,7 @@ class PayPalGateway extends \WC_Payment_Gateway { && $this->config->get( 'vault_enabled' ) ) { $this->supports = array( + 'refunds', 'products', 'subscriptions', 'subscription_cancellation', @@ -300,4 +319,23 @@ class PayPalGateway extends \WC_Payment_Gateway { && self::ID === sanitize_text_field( wp_unslash( $_GET['section'] ) ); } // phpcs:enable WordPress.Security.NonceVerification.Recommended + + /** + * Process refund. + * + * If the gateway declares 'refunds' support, this will allow it to refund. + * a passed in amount. + * + * @param int $order_id Order ID. + * @param float $amount Refund amount. + * @param string $reason Refund reason. + * @return boolean True or false based on success, or a WP_Error object. + */ + public function process_refund( $order_id, $amount = null, $reason = '' ) { + $order = wc_get_order( $order_id ); + if ( ! is_a( $order, \WC_Order::class ) ) { + return false; + } + return $this->refund_processor->process( $order, (float) $amount, (string) $reason ); + } } diff --git a/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php b/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php new file mode 100644 index 000000000..e52c8eb76 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Processor/class-refundprocessor.php @@ -0,0 +1,85 @@ +order_endpoint = $order_endpoint; + $this->payments_endpoint = $payments_endpoint; + } + + /** + * Processes a refund. + * + * @param \WC_Order $wc_order The WooCommerce order. + * @param float|null $amount The refund amount. + * @param string $reason The reason for the refund. + * + * @return bool + */ + 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; + } + + $capture = $order->purchase_units()[0]->payments()->captures()[0]; + $refund = new Refund( + $capture, + $capture->invoice_id(), + $reason, + new Amount( + new Money( $amount, get_woocommerce_currency() ) + ) + ); + return $this->payments_endpoint->refund( $refund ); + } catch ( RuntimeException $error ) { + return false; + } + } +} diff --git a/tests/PHPUnit/ApiClient/Entity/PaymentsTest.php b/tests/PHPUnit/ApiClient/Entity/PaymentsTest.php index a1062794d..adfa7798d 100644 --- a/tests/PHPUnit/ApiClient/Entity/PaymentsTest.php +++ b/tests/PHPUnit/ApiClient/Entity/PaymentsTest.php @@ -8,15 +8,24 @@ use WooCommerce\PayPalCommerce\ApiClient\TestCase; class PaymentsTest extends TestCase { - public function testAuthorizations() - { - $authorization = \Mockery::mock(Authorization::class); - $authorizations = [$authorization]; + public function testAuthorizations() + { + $authorization = \Mockery::mock(Authorization::class); + $authorizations = [$authorization]; - $testee = new Payments(...$authorizations); + $testee = new Payments($authorizations, []); - $this->assertEquals($authorizations, $testee->authorizations()); - } + $this->assertEquals($authorizations, $testee->authorizations()); + } + public function testCaptures() + { + $capture = \Mockery::mock(Capture::class); + $captures = [$capture]; + + $testee = new Payments([], $captures); + + $this->assertEquals($captures, $testee->captures()); + } public function testToArray() { @@ -27,9 +36,17 @@ class PaymentsTest extends TestCase 'status' => 'CREATED', ] ); + $capture = \Mockery::mock(Capture::class); + $capture->shouldReceive('to_array')->andReturn( + [ + 'id' => 'capture', + 'status' => 'CREATED', + ] + ); + $captures = [$capture]; $authorizations = [$authorization]; - $testee = new Payments(...$authorizations); + $testee = new Payments($authorizations, $captures); $this->assertEquals( [ @@ -39,6 +56,12 @@ class PaymentsTest extends TestCase 'status' => 'CREATED', ], ], + 'captures' => [ + [ + 'id' => 'capture', + 'status' => 'CREATED', + ], + ], ], $testee->to_array() ); diff --git a/tests/PHPUnit/ApiClient/Factory/PaymentsFactoryTest.php b/tests/PHPUnit/ApiClient/Factory/PaymentsFactoryTest.php index fe77baffc..639d940a4 100644 --- a/tests/PHPUnit/ApiClient/Factory/PaymentsFactoryTest.php +++ b/tests/PHPUnit/ApiClient/Factory/PaymentsFactoryTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Factory; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; +use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments; use WooCommerce\PayPalCommerce\ApiClient\TestCase; use Mockery; @@ -13,27 +14,36 @@ class PaymentsFactoryTest extends TestCase { public function testFromPayPalResponse() { - $authorization = Mockery::mock(Authorization::class); - $authorization->shouldReceive('to_array')->andReturn(['id' => 'foo', 'status' => 'CREATED']); + $authorization = Mockery::mock(Authorization::class); + $authorization->shouldReceive('to_array')->andReturn(['id' => 'foo', 'status' => 'CREATED']); + $capture = Mockery::mock(Capture::class); + $capture->shouldReceive('to_array')->andReturn(['id' => 'capture', 'status' => 'CREATED']); $authorizationsFactory = Mockery::mock(AuthorizationFactory::class); - $authorizationsFactory->shouldReceive('from_paypal_response')->andReturn($authorization); - + $authorizationsFactory->shouldReceive('from_paypal_response')->andReturn($authorization); + $captureFactory = Mockery::mock(CaptureFactory::class); + $captureFactory->shouldReceive('from_paypal_response')->andReturn($capture); $response = (object)[ - 'authorizations' => [ - (object)['id' => 'foo', 'status' => 'CREATED'], - ], + 'authorizations' => [ + (object)['id' => 'foo', 'status' => 'CREATED'], + ], + 'captures' => [ + (object)['id' => 'capture', 'status' => 'CREATED'], + ], ]; - $testee = new PaymentsFactory($authorizationsFactory); + $testee = new PaymentsFactory($authorizationsFactory, $captureFactory); $result = $testee->from_paypal_response($response); $this->assertInstanceOf(Payments::class, $result); $expectedToArray = [ - 'authorizations' => [ - ['id' => 'foo', 'status' => 'CREATED'], - ], + 'authorizations' => [ + ['id' => 'foo', 'status' => 'CREATED'], + ], + 'captures' => [ + ['id' => 'capture', 'status' => 'CREATED'], + ], ]; $this->assertEquals($expectedToArray, $result->to_array()); } diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index 0780e6f15..102069ef7 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -5,11 +5,13 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer; +use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; +use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsFields; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; @@ -42,13 +44,19 @@ class WcGatewayTest extends TestCase ->shouldReceive('destroy_session_data'); $settings ->shouldReceive('has')->andReturnFalse(); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); expect('wc_get_order') @@ -76,13 +84,19 @@ class WcGatewayTest extends TestCase $settings ->shouldReceive('has')->andReturnFalse(); $sessionHandler = Mockery::mock(SessionHandler::class); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); expect('wc_get_order') @@ -116,13 +130,19 @@ class WcGatewayTest extends TestCase $settings ->shouldReceive('has')->andReturnFalse(); $sessionHandler = Mockery::mock(SessionHandler::class); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); expect('wc_get_order') @@ -171,13 +191,19 @@ class WcGatewayTest extends TestCase $settings ->shouldReceive('has')->andReturnFalse(); $sessionHandler = Mockery::mock(SessionHandler::class); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); $this->assertTrue($testee->capture_authorized_payment($wcOrder)); @@ -218,13 +244,19 @@ class WcGatewayTest extends TestCase $settings ->shouldReceive('has')->andReturnFalse(); $sessionHandler = Mockery::mock(SessionHandler::class); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); $this->assertTrue($testee->capture_authorized_payment($wcOrder)); @@ -258,13 +290,19 @@ class WcGatewayTest extends TestCase $settings ->shouldReceive('has')->andReturnFalse(); $sessionHandler = Mockery::mock(SessionHandler::class); + $refundProcessor = Mockery::mock(RefundProcessor::class); + $state = Mockery::mock(State::class); + $state + ->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED); $testee = new PayPalGateway( $settingsRenderer, $orderProcessor, $authorizedPaymentsProcessor, $authorizedOrderActionNotice, $settings, - $sessionHandler + $sessionHandler, + $refundProcessor, + $state ); $this->assertFalse($testee->capture_authorized_payment($wcOrder));