mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 10:55:00 +08:00
Merge pull request #44 from woocommerce/issue-30-support-refunds
Enable refunds through gateway
This commit is contained in:
commit
5fa0c293c7
14 changed files with 790 additions and 37 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
197
modules/ppcp-api-client/src/Entity/class-capture.php
Normal file
197
modules/ppcp-api-client/src/Entity/class-capture.php
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
/**
|
||||
* The capture entity.
|
||||
*
|
||||
* @link https://developer.paypal.com/docs/api/orders/v2/#definition-capture
|
||||
*
|
||||
* @package Woocommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace Woocommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class Capture
|
||||
*/
|
||||
class Capture {
|
||||
|
||||
/**
|
||||
* The ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* The status.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* The status details.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $status_details;
|
||||
|
||||
/**
|
||||
* The amount.
|
||||
*
|
||||
* @var Amount
|
||||
*/
|
||||
private $amount;
|
||||
|
||||
/**
|
||||
* Whether this is the final capture or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $final_capture;
|
||||
|
||||
/**
|
||||
* The seller protection.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $seller_protection;
|
||||
|
||||
/**
|
||||
* The invoice id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $invoice_id;
|
||||
|
||||
/**
|
||||
* The custom id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $custom_id;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function __construct(
|
||||
string $id,
|
||||
string $status,
|
||||
string $status_details,
|
||||
Amount $amount,
|
||||
bool $final_capture,
|
||||
string $seller_protection,
|
||||
string $invoice_id,
|
||||
string $custom_id
|
||||
) {
|
||||
|
||||
$this->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(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
118
modules/ppcp-api-client/src/Entity/class-refund.php
Normal file
118
modules/ppcp-api-client/src/Entity/class-refund.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/**
|
||||
* The refund object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class Refund
|
||||
*/
|
||||
class Refund {
|
||||
|
||||
/**
|
||||
* The Capture.
|
||||
*
|
||||
* @var Capture
|
||||
*/
|
||||
private $capture;
|
||||
|
||||
/**
|
||||
* The invoice id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $invoice_id;
|
||||
|
||||
/**
|
||||
* The note to the payer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $note_to_payer;
|
||||
|
||||
/**
|
||||
* The Amount.
|
||||
*
|
||||
* @var Amount|null
|
||||
*/
|
||||
private $amount;
|
||||
|
||||
/**
|
||||
* Refund constructor.
|
||||
*
|
||||
* @param Capture $capture The capture where the refund is supposed to be applied at.
|
||||
* @param string $invoice_id The invoice id.
|
||||
* @param string $note_to_payer The note to the payer.
|
||||
* @param Amount|null $amount The Amount.
|
||||
*/
|
||||
public function __construct(
|
||||
Capture $capture,
|
||||
string $invoice_id,
|
||||
string $note_to_payer = '',
|
||||
Amount $amount = null
|
||||
) {
|
||||
$this->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;
|
||||
}
|
||||
}
|
57
modules/ppcp-api-client/src/Factory/class-capturefactory.php
Normal file
57
modules/ppcp-api-client/src/Factory/class-capturefactory.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* The capture factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
|
||||
/**
|
||||
* Class CaptureFactory
|
||||
*/
|
||||
class CaptureFactory {
|
||||
|
||||
/**
|
||||
* The Amount factory.
|
||||
*
|
||||
* @var AmountFactory
|
||||
*/
|
||||
private $amount_factory;
|
||||
|
||||
/**
|
||||
* CaptureFactory constructor.
|
||||
*
|
||||
* @param AmountFactory $amount_factory The amount factory.
|
||||
*/
|
||||
public function __construct( AmountFactory $amount_factory ) {
|
||||
|
||||
$this->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 {
|
||||
|
||||
$reason = isset( $data->status_details->reason ) ? (string) $data->status_details->reason : '';
|
||||
return new Capture(
|
||||
(string) $data->id,
|
||||
(string) $data->status,
|
||||
$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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue