mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 16:24:33 +08:00
Merge branch 'trunk' into PCP-417-new-feature---pay-upon-invoice
This commit is contained in:
commit
6ed616802d
18 changed files with 326 additions and 63 deletions
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if ! wp core download; then
|
if ! wp core download --version="${WP_VERSION:-latest}"; then
|
||||||
echo 'WordPress is already installed.'
|
echo 'WordPress is already installed.'
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -27,6 +27,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\ApplicationContextFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ExchangeRateFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\ExchangeRateFactory;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\FraudProcessorResponseFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ItemFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\ItemFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\MoneyFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\MoneyFactory;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||||
|
@ -257,7 +258,8 @@ return array(
|
||||||
$amount_factory = $container->get( 'api.factory.amount' );
|
$amount_factory = $container->get( 'api.factory.amount' );
|
||||||
return new CaptureFactory(
|
return new CaptureFactory(
|
||||||
$amount_factory,
|
$amount_factory,
|
||||||
$container->get( 'api.factory.seller-receivable-breakdown' )
|
$container->get( 'api.factory.seller-receivable-breakdown' ),
|
||||||
|
$container->get( 'api.factory.fraud-processor-response' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'api.factory.purchase-unit' => static function ( ContainerInterface $container ): PurchaseUnitFactory {
|
'api.factory.purchase-unit' => static function ( ContainerInterface $container ): PurchaseUnitFactory {
|
||||||
|
@ -354,6 +356,9 @@ return array(
|
||||||
$container->get( 'api.factory.platform-fee' )
|
$container->get( 'api.factory.platform-fee' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
'api.factory.fraud-processor-response' => static function ( ContainerInterface $container ): FraudProcessorResponseFactory {
|
||||||
|
return new FraudProcessorResponseFactory();
|
||||||
|
},
|
||||||
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
||||||
return new DccApplies(
|
return new DccApplies(
|
||||||
$container->get( 'api.dcc-supported-country-currency-matrix' ),
|
$container->get( 'api.dcc-supported-country-currency-matrix' ),
|
||||||
|
|
|
@ -198,11 +198,11 @@ class PaymentsEndpoint {
|
||||||
*
|
*
|
||||||
* @param Refund $refund The refund to be processed.
|
* @param Refund $refund The refund to be processed.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return string Refund ID.
|
||||||
* @throws RuntimeException If the request fails.
|
* @throws RuntimeException If the request fails.
|
||||||
* @throws PayPalApiException If the request fails.
|
* @throws PayPalApiException If the request fails.
|
||||||
*/
|
*/
|
||||||
public function refund( Refund $refund ) : void {
|
public function refund( Refund $refund ) : string {
|
||||||
$bearer = $this->bearer->bearer();
|
$bearer = $this->bearer->bearer();
|
||||||
$url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund';
|
$url = trailingslashit( $this->host ) . 'v2/payments/captures/' . $refund->for_capture()->id() . '/refund';
|
||||||
$args = array(
|
$args = array(
|
||||||
|
@ -216,19 +216,21 @@ class PaymentsEndpoint {
|
||||||
);
|
);
|
||||||
|
|
||||||
$response = $this->request( $url, $args );
|
$response = $this->request( $url, $args );
|
||||||
$json = json_decode( $response['body'] );
|
|
||||||
|
|
||||||
if ( is_wp_error( $response ) ) {
|
if ( is_wp_error( $response ) ) {
|
||||||
throw new RuntimeException( 'Could not refund payment.' );
|
throw new RuntimeException( 'Could not refund payment.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||||
if ( 201 !== $status_code ) {
|
$json = json_decode( $response['body'] );
|
||||||
|
if ( 201 !== $status_code || ! is_object( $json ) ) {
|
||||||
throw new PayPalApiException(
|
throw new PayPalApiException(
|
||||||
$json,
|
$json,
|
||||||
$status_code
|
$status_code
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $json->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,6 +58,13 @@ class Capture {
|
||||||
*/
|
*/
|
||||||
private $seller_receivable_breakdown;
|
private $seller_receivable_breakdown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fraud processor response (AVS, CVV ...).
|
||||||
|
*
|
||||||
|
* @var FraudProcessorResponse|null
|
||||||
|
*/
|
||||||
|
protected $fraud_processor_response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The invoice id.
|
* The invoice id.
|
||||||
*
|
*
|
||||||
|
@ -83,6 +90,7 @@ class Capture {
|
||||||
* @param string $invoice_id The invoice id.
|
* @param string $invoice_id The invoice id.
|
||||||
* @param string $custom_id The custom id.
|
* @param string $custom_id The custom id.
|
||||||
* @param SellerReceivableBreakdown|null $seller_receivable_breakdown The detailed breakdown of the capture activity (fees, ...).
|
* @param SellerReceivableBreakdown|null $seller_receivable_breakdown The detailed breakdown of the capture activity (fees, ...).
|
||||||
|
* @param FraudProcessorResponse|null $fraud_processor_response The fraud processor response (AVS, CVV ...).
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $id,
|
string $id,
|
||||||
|
@ -92,7 +100,8 @@ class Capture {
|
||||||
string $seller_protection,
|
string $seller_protection,
|
||||||
string $invoice_id,
|
string $invoice_id,
|
||||||
string $custom_id,
|
string $custom_id,
|
||||||
?SellerReceivableBreakdown $seller_receivable_breakdown
|
?SellerReceivableBreakdown $seller_receivable_breakdown,
|
||||||
|
?FraudProcessorResponse $fraud_processor_response
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
|
@ -103,6 +112,7 @@ class Capture {
|
||||||
$this->invoice_id = $invoice_id;
|
$this->invoice_id = $invoice_id;
|
||||||
$this->custom_id = $custom_id;
|
$this->custom_id = $custom_id;
|
||||||
$this->seller_receivable_breakdown = $seller_receivable_breakdown;
|
$this->seller_receivable_breakdown = $seller_receivable_breakdown;
|
||||||
|
$this->fraud_processor_response = $fraud_processor_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,6 +187,15 @@ class Capture {
|
||||||
return $this->seller_receivable_breakdown;
|
return $this->seller_receivable_breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fraud processor response (AVS, CVV ...).
|
||||||
|
*
|
||||||
|
* @return FraudProcessorResponse|null
|
||||||
|
*/
|
||||||
|
public function fraud_processor_response() : ?FraudProcessorResponse {
|
||||||
|
return $this->fraud_processor_response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the entity as array.
|
* Returns the entity as array.
|
||||||
*
|
*
|
||||||
|
@ -199,6 +218,9 @@ class Capture {
|
||||||
if ( $this->seller_receivable_breakdown ) {
|
if ( $this->seller_receivable_breakdown ) {
|
||||||
$data['seller_receivable_breakdown'] = $this->seller_receivable_breakdown->to_array();
|
$data['seller_receivable_breakdown'] = $this->seller_receivable_breakdown->to_array();
|
||||||
}
|
}
|
||||||
|
if ( $this->fraud_processor_response ) {
|
||||||
|
$data['fraud_processor_response'] = $this->fraud_processor_response->to_array();
|
||||||
|
}
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The FraudProcessorResponse object.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FraudProcessorResponse
|
||||||
|
*/
|
||||||
|
class FraudProcessorResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AVS response code.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $avs_code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CVV response code.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $cvv_code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FraudProcessorResponse constructor.
|
||||||
|
*
|
||||||
|
* @param string|null $avs_code The AVS response code.
|
||||||
|
* @param string|null $cvv_code The CVV response code.
|
||||||
|
*/
|
||||||
|
public function __construct( ?string $avs_code, ?string $cvv_code ) {
|
||||||
|
$this->avs_code = $avs_code;
|
||||||
|
$this->cvv_code = $cvv_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the AVS response code.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function avs_code(): ?string {
|
||||||
|
return $this->avs_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the CVV response code.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function cvv_code(): ?string {
|
||||||
|
return $this->cvv_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object as array.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function to_array(): array {
|
||||||
|
return array(
|
||||||
|
'avs_code' => $this->avs_code() ?: '',
|
||||||
|
'address_match' => $this->avs_code() === 'M' ? 'Y' : 'N',
|
||||||
|
'postal_match' => $this->avs_code() === 'M' ? 'Y' : 'N',
|
||||||
|
'cvv_match' => $this->cvv_code() === 'M' ? 'Y' : 'N',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -127,7 +127,7 @@ class AmountFactory {
|
||||||
$total_value = (float) $order->get_total();
|
$total_value = (float) $order->get_total();
|
||||||
if ( (
|
if ( (
|
||||||
CreditCardGateway::ID === $order->get_payment_method()
|
CreditCardGateway::ID === $order->get_payment_method()
|
||||||
|| ( PayPalGateway::ID === $order->get_payment_method() && 'card' === $order->get_meta( PayPalGateway::ORDER_PAYMENT_SOURCE ) )
|
|| ( PayPalGateway::ID === $order->get_payment_method() && 'card' === $order->get_meta( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY ) )
|
||||||
)
|
)
|
||||||
&& $this->is_free_trial_order( $order )
|
&& $this->is_free_trial_order( $order )
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -32,19 +32,29 @@ class CaptureFactory {
|
||||||
*/
|
*/
|
||||||
private $seller_receivable_breakdown_factory;
|
private $seller_receivable_breakdown_factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FraudProcessorResponseFactory factory.
|
||||||
|
*
|
||||||
|
* @var FraudProcessorResponseFactory
|
||||||
|
*/
|
||||||
|
protected $fraud_processor_response_factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CaptureFactory constructor.
|
* CaptureFactory constructor.
|
||||||
*
|
*
|
||||||
* @param AmountFactory $amount_factory The amount factory.
|
* @param AmountFactory $amount_factory The amount factory.
|
||||||
* @param SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory The SellerReceivableBreakdown factory.
|
* @param SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory The SellerReceivableBreakdown factory.
|
||||||
|
* @param FraudProcessorResponseFactory $fraud_processor_response_factory The FraudProcessorResponseFactory factory.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AmountFactory $amount_factory,
|
AmountFactory $amount_factory,
|
||||||
SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory
|
SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory,
|
||||||
|
FraudProcessorResponseFactory $fraud_processor_response_factory
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$this->amount_factory = $amount_factory;
|
$this->amount_factory = $amount_factory;
|
||||||
$this->seller_receivable_breakdown_factory = $seller_receivable_breakdown_factory;
|
$this->seller_receivable_breakdown_factory = $seller_receivable_breakdown_factory;
|
||||||
|
$this->fraud_processor_response_factory = $fraud_processor_response_factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,12 +65,15 @@ class CaptureFactory {
|
||||||
* @return Capture
|
* @return Capture
|
||||||
*/
|
*/
|
||||||
public function from_paypal_response( \stdClass $data ) : Capture {
|
public function from_paypal_response( \stdClass $data ) : Capture {
|
||||||
|
|
||||||
$reason = $data->status_details->reason ?? null;
|
$reason = $data->status_details->reason ?? null;
|
||||||
$seller_receivable_breakdown = isset( $data->seller_receivable_breakdown ) ?
|
$seller_receivable_breakdown = isset( $data->seller_receivable_breakdown ) ?
|
||||||
$this->seller_receivable_breakdown_factory->from_paypal_response( $data->seller_receivable_breakdown )
|
$this->seller_receivable_breakdown_factory->from_paypal_response( $data->seller_receivable_breakdown )
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
$fraud_processor_response = isset( $data->processor_response ) ?
|
||||||
|
$this->fraud_processor_response_factory->from_paypal_response( $data->processor_response )
|
||||||
|
: null;
|
||||||
|
|
||||||
return new Capture(
|
return new Capture(
|
||||||
(string) $data->id,
|
(string) $data->id,
|
||||||
new CaptureStatus(
|
new CaptureStatus(
|
||||||
|
@ -72,7 +85,8 @@ class CaptureFactory {
|
||||||
(string) $data->seller_protection->status,
|
(string) $data->seller_protection->status,
|
||||||
(string) $data->invoice_id,
|
(string) $data->invoice_id,
|
||||||
(string) $data->custom_id,
|
(string) $data->custom_id,
|
||||||
$seller_receivable_breakdown
|
$seller_receivable_breakdown,
|
||||||
|
$fraud_processor_response
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The FraudProcessorResponseFactory Factory.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\FraudProcessorResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FraudProcessorResponseFactory
|
||||||
|
*/
|
||||||
|
class FraudProcessorResponseFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a FraudProcessorResponse object based off a PayPal Response.
|
||||||
|
*
|
||||||
|
* @param stdClass $data The JSON object.
|
||||||
|
*
|
||||||
|
* @return FraudProcessorResponse
|
||||||
|
*/
|
||||||
|
public function from_paypal_response( stdClass $data ): FraudProcessorResponse {
|
||||||
|
$avs_code = $data->avs_code ?: null;
|
||||||
|
$cvv_code = $data->cvv_code ?: null;
|
||||||
|
|
||||||
|
return new FraudProcessorResponse( $avs_code, $cvv_code );
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ class CreditCardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(wrapper, contextConfig) {
|
render(wrapper, contextConfig) {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(
|
(
|
||||||
this.defaultConfig.context !== 'checkout'
|
this.defaultConfig.context !== 'checkout'
|
||||||
|
@ -42,6 +41,9 @@ class CreditCardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
|
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
|
||||||
|
if(! gateWayBox) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const oldDisplayStyle = gateWayBox.style.display;
|
const oldDisplayStyle = gateWayBox.style.display;
|
||||||
gateWayBox.style.display = 'block';
|
gateWayBox.style.display = 'block';
|
||||||
|
|
||||||
|
|
|
@ -221,16 +221,15 @@ class SmartButton implements SmartButtonInterface {
|
||||||
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting was not found.
|
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting was not found.
|
||||||
*/
|
*/
|
||||||
public function render_wrapper(): bool {
|
public function render_wrapper(): bool {
|
||||||
|
|
||||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' ) ) {
|
if ( $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' ) ) {
|
||||||
$this->render_button_wrapper_registrar();
|
$this->render_button_wrapper_registrar();
|
||||||
$this->render_message_wrapper_registrar();
|
$this->render_message_wrapper_registrar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$this->settings->has( 'dcc_enabled' )
|
$this->settings->has( 'dcc_enabled' )
|
||||||
&& $this->settings->get( 'dcc_enabled' )
|
&& $this->settings->get( 'dcc_enabled' )
|
||||||
|
@ -433,6 +432,10 @@ class SmartButton implements SmartButtonInterface {
|
||||||
add_action(
|
add_action(
|
||||||
$this->mini_cart_button_renderer_hook(),
|
$this->mini_cart_button_renderer_hook(),
|
||||||
function () {
|
function () {
|
||||||
|
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( $this->is_cart_price_total_zero() || $this->is_free_trial_cart() ) {
|
if ( $this->is_cart_price_total_zero() || $this->is_free_trial_cart() ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -446,28 +449,21 @@ class SmartButton implements SmartButtonInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->is_cart_price_total_zero() && ! $this->is_free_trial_cart() ) {
|
add_action( $this->checkout_button_renderer_hook(), array( $this, 'button_renderer' ), 10 );
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$not_enabled_on_cart = $this->settings->has( 'button_cart_enabled' ) &&
|
$not_enabled_on_cart = $this->settings->has( 'button_cart_enabled' ) &&
|
||||||
! $this->settings->get( 'button_cart_enabled' );
|
! $this->settings->get( 'button_cart_enabled' );
|
||||||
if (
|
add_action(
|
||||||
is_cart()
|
$this->proceed_to_checkout_button_renderer_hook(),
|
||||||
&& ! $not_enabled_on_cart
|
function() use ( $not_enabled_on_cart ) {
|
||||||
&& ! $this->is_free_trial_cart()
|
if ( ! is_cart() || $not_enabled_on_cart || $this->is_free_trial_cart() || $this->is_cart_price_total_zero() ) {
|
||||||
) {
|
return;
|
||||||
add_action(
|
}
|
||||||
$this->proceed_to_checkout_button_renderer_hook(),
|
|
||||||
array(
|
|
||||||
$this,
|
|
||||||
'button_renderer',
|
|
||||||
),
|
|
||||||
20
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_action( $this->checkout_button_renderer_hook(), array( $this, 'button_renderer' ), 10 );
|
$this->button_renderer();
|
||||||
|
},
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -522,6 +518,11 @@ class SmartButton implements SmartButtonInterface {
|
||||||
* Renders the HTML for the buttons.
|
* Renders the HTML for the buttons.
|
||||||
*/
|
*/
|
||||||
public function button_renderer() {
|
public function button_renderer() {
|
||||||
|
|
||||||
|
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$product = wc_get_product();
|
$product = wc_get_product();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -547,6 +548,10 @@ class SmartButton implements SmartButtonInterface {
|
||||||
* Renders the HTML for the credit messaging.
|
* Renders the HTML for the credit messaging.
|
||||||
*/
|
*/
|
||||||
public function message_renderer() {
|
public function message_renderer() {
|
||||||
|
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$product = wc_get_product();
|
$product = wc_get_product();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1231,10 +1236,11 @@ class SmartButton implements SmartButtonInterface {
|
||||||
* Check if cart product price total is 0.
|
* Check if cart product price total is 0.
|
||||||
*
|
*
|
||||||
* @return bool true if is 0, otherwise false.
|
* @return bool true if is 0, otherwise false.
|
||||||
|
* @psalm-suppress RedundantConditionGivenDocblockType
|
||||||
*/
|
*/
|
||||||
protected function is_cart_price_total_zero(): bool {
|
protected function is_cart_price_total_zero(): bool {
|
||||||
// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||||
return WC()->cart->get_cart_contents_total() == 0;
|
return WC()->cart && WC()->cart->get_total( 'numeric' ) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -119,7 +119,7 @@ class PaymentTokenChecker {
|
||||||
try {
|
try {
|
||||||
if ( $this->is_free_trial_order( $wc_order ) ) {
|
if ( $this->is_free_trial_order( $wc_order ) ) {
|
||||||
if ( CreditCardGateway::ID === $wc_order->get_payment_method()
|
if ( CreditCardGateway::ID === $wc_order->get_payment_method()
|
||||||
|| ( PayPalGateway::ID === $wc_order->get_payment_method() && 'card' === $wc_order->get_meta( PayPalGateway::ORDER_PAYMENT_SOURCE ) )
|
|| ( PayPalGateway::ID === $wc_order->get_payment_method() && 'card' === $wc_order->get_meta( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY ) )
|
||||||
) {
|
) {
|
||||||
$order = $this->order_repository->for_wc_order( $wc_order );
|
$order = $this->order_repository->for_wc_order( $wc_order );
|
||||||
$this->authorized_payments_processor->void_authorizations( $order );
|
$this->authorized_payments_processor->void_authorizations( $order );
|
||||||
|
|
|
@ -69,7 +69,7 @@ class DisableGateways {
|
||||||
unset( $methods[ CreditCardGateway::ID ] );
|
unset( $methods[ CreditCardGateway::ID ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->settings->has( 'button_enabled' ) && ! $this->settings->get( 'button_enabled' ) && ! $this->session_handler->order() ) {
|
if ( $this->settings->has( 'button_enabled' ) && ! $this->settings->get( 'button_enabled' ) && ! $this->session_handler->order() && is_checkout() ) {
|
||||||
unset( $methods[ PayPalGateway::ID ] );
|
unset( $methods[ PayPalGateway::ID ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
||||||
|
|
||||||
use ProcessPaymentTrait;
|
use ProcessPaymentTrait;
|
||||||
|
|
||||||
const ID = 'ppcp-gateway';
|
const ID = 'ppcp-gateway';
|
||||||
const INTENT_META_KEY = '_ppcp_paypal_intent';
|
const INTENT_META_KEY = '_ppcp_paypal_intent';
|
||||||
const ORDER_ID_META_KEY = '_ppcp_paypal_order_id';
|
const ORDER_ID_META_KEY = '_ppcp_paypal_order_id';
|
||||||
const ORDER_PAYMENT_MODE_META_KEY = '_ppcp_paypal_payment_mode';
|
const ORDER_PAYMENT_MODE_META_KEY = '_ppcp_paypal_payment_mode';
|
||||||
const ORDER_PAYMENT_SOURCE = '_ppcp_paypal_payment_source';
|
const ORDER_PAYMENT_SOURCE_META_KEY = '_ppcp_paypal_payment_source';
|
||||||
const FEES_META_KEY = '_ppcp_paypal_fees';
|
const FEES_META_KEY = '_ppcp_paypal_fees';
|
||||||
|
const REFUNDS_META_KEY = '_ppcp_refunds';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Settings Renderer.
|
* The Settings Renderer.
|
||||||
|
|
|
@ -39,7 +39,7 @@ trait OrderMetaTrait {
|
||||||
);
|
);
|
||||||
$payment_source = $this->get_payment_source( $order );
|
$payment_source = $this->get_payment_source( $order );
|
||||||
if ( $payment_source ) {
|
if ( $payment_source ) {
|
||||||
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYMENT_SOURCE, $payment_source );
|
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY, $payment_source );
|
||||||
}
|
}
|
||||||
|
|
||||||
$wc_order->save();
|
$wc_order->save();
|
||||||
|
|
47
modules/ppcp-wc-gateway/src/Processor/RefundMetaTrait.php
Normal file
47
modules/ppcp-wc-gateway/src/Processor/RefundMetaTrait.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Operations with refund metadata.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\WcGateway\Processor
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
||||||
|
|
||||||
|
use WC_Order;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait RefundMetaTrait.
|
||||||
|
*/
|
||||||
|
trait RefundMetaTrait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a refund ID to the order metadata.
|
||||||
|
*
|
||||||
|
* @param WC_Order $wc_order The WC order to which metadata will be added.
|
||||||
|
* @param string $refund_id The refund ID to be added.
|
||||||
|
*/
|
||||||
|
protected function add_refund_to_meta( WC_Order $wc_order, string $refund_id ): void {
|
||||||
|
$refunds = $this->get_refunds_meta( $wc_order );
|
||||||
|
$refunds[] = $refund_id;
|
||||||
|
$wc_order->update_meta_data( PayPalGateway::REFUNDS_META_KEY, $refunds );
|
||||||
|
$wc_order->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns refund IDs from the order metadata.
|
||||||
|
*
|
||||||
|
* @param WC_Order $wc_order The WC order.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function get_refunds_meta( WC_Order $wc_order ): array {
|
||||||
|
$refunds = $wc_order->get_meta( PayPalGateway::REFUNDS_META_KEY );
|
||||||
|
if ( ! is_array( $refunds ) ) {
|
||||||
|
$refunds = array();
|
||||||
|
}
|
||||||
|
return $refunds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||||
* Class RefundProcessor
|
* Class RefundProcessor
|
||||||
*/
|
*/
|
||||||
class RefundProcessor {
|
class RefundProcessor {
|
||||||
|
use RefundMetaTrait;
|
||||||
|
|
||||||
private const REFUND_MODE_REFUND = 'refund';
|
private const REFUND_MODE_REFUND = 'refund';
|
||||||
private const REFUND_MODE_VOID = 'void';
|
private const REFUND_MODE_VOID = 'void';
|
||||||
|
@ -113,8 +114,8 @@ class RefundProcessor {
|
||||||
throw new RuntimeException( 'No capture.' );
|
throw new RuntimeException( 'No capture.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$capture = $captures[0];
|
$capture = $captures[0];
|
||||||
$refund = new Refund(
|
$refund = new Refund(
|
||||||
$capture,
|
$capture,
|
||||||
$capture->invoice_id(),
|
$capture->invoice_id(),
|
||||||
$reason,
|
$reason,
|
||||||
|
@ -122,7 +123,10 @@ class RefundProcessor {
|
||||||
new Money( $amount, $wc_order->get_currency() )
|
new Money( $amount, $wc_order->get_currency() )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->payments_endpoint->refund( $refund );
|
$refund_id = $this->payments_endpoint->refund( $refund );
|
||||||
|
|
||||||
|
$this->add_refund_to_meta( $wc_order, $refund_id );
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case self::REFUND_MODE_VOID:
|
case self::REFUND_MODE_VOID:
|
||||||
$voidable_authorizations = array_filter(
|
$voidable_authorizations = array_filter(
|
||||||
|
|
|
@ -81,6 +81,48 @@ class WCGatewayModule implements ModuleInterface {
|
||||||
if ( $breakdown ) {
|
if ( $breakdown ) {
|
||||||
$wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() );
|
$wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() );
|
||||||
$wc_order->save_meta_data();
|
$wc_order->save_meta_data();
|
||||||
|
$paypal_fee = $breakdown->paypal_fee();
|
||||||
|
if ( $paypal_fee ) {
|
||||||
|
update_post_meta( $wc_order->get_id(), 'PayPal Transaction Key', $paypal_fee->value() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fraud = $capture->fraud_processor_response();
|
||||||
|
if ( $fraud ) {
|
||||||
|
$fraud_responses = $fraud->to_array();
|
||||||
|
$avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' );
|
||||||
|
/* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */
|
||||||
|
$avs_response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' );
|
||||||
|
$avs_response_order_note_result_format = '<ul class="ppcp_avs_result">
|
||||||
|
<li>%1$s</li>
|
||||||
|
<ul class="ppcp_avs_result_inner">
|
||||||
|
<li>%2$s</li>
|
||||||
|
<li>%3$s</li>
|
||||||
|
</ul>
|
||||||
|
</ul>';
|
||||||
|
$avs_response_order_note_result = sprintf(
|
||||||
|
$avs_response_order_note_result_format,
|
||||||
|
/* translators: %s is fraud AVS code */
|
||||||
|
sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['avs_code'] ) ),
|
||||||
|
/* translators: %s is fraud AVS address match */
|
||||||
|
sprintf( __( 'Address Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['address_match'] ) ),
|
||||||
|
/* translators: %s is fraud AVS postal match */
|
||||||
|
sprintf( __( 'Postal Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['postal_match'] ) )
|
||||||
|
);
|
||||||
|
$avs_response_order_note = sprintf(
|
||||||
|
$avs_response_order_note_format,
|
||||||
|
esc_html( $avs_response_order_note_title ),
|
||||||
|
wp_kses_post( $avs_response_order_note_result )
|
||||||
|
);
|
||||||
|
$wc_order->add_order_note( $avs_response_order_note );
|
||||||
|
|
||||||
|
$cvv_response_order_note_format = '<ul class="ppcp_cvv_result"><li>%1$s</li></ul>';
|
||||||
|
$cvv_response_order_note = sprintf(
|
||||||
|
$cvv_response_order_note_format,
|
||||||
|
/* translators: %s is fraud CVV match */
|
||||||
|
sprintf( __( 'CVV2 Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['cvv_match'] ) )
|
||||||
|
);
|
||||||
|
$wc_order->add_order_note( $cvv_response_order_note );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
10,
|
10,
|
||||||
|
|
|
@ -10,14 +10,17 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundMetaTrait;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||||
|
use WP_REST_Request;
|
||||||
|
use WP_REST_Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class PaymentCaptureRefunded
|
* Class PaymentCaptureRefunded
|
||||||
*/
|
*/
|
||||||
class PaymentCaptureRefunded implements RequestHandler {
|
class PaymentCaptureRefunded implements RequestHandler {
|
||||||
|
|
||||||
use PrefixTrait, TransactionIdHandlingTrait;
|
use PrefixTrait, TransactionIdHandlingTrait, RefundMetaTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logger.
|
* The logger.
|
||||||
|
@ -49,25 +52,26 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
/**
|
/**
|
||||||
* Whether a handler is responsible for a given request or not.
|
* Whether a handler is responsible for a given request or not.
|
||||||
*
|
*
|
||||||
* @param \WP_REST_Request $request The request.
|
* @param WP_REST_Request $request The request.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function responsible_for_request( \WP_REST_Request $request ): bool {
|
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||||
return in_array( $request['event_type'], $this->event_types(), true );
|
return in_array( $request['event_type'], $this->event_types(), true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for handling the request.
|
* Responsible for handling the request.
|
||||||
*
|
*
|
||||||
* @param \WP_REST_Request $request The request.
|
* @param WP_REST_Request $request The request.
|
||||||
*
|
*
|
||||||
* @return \WP_REST_Response
|
* @return WP_REST_Response
|
||||||
*/
|
*/
|
||||||
public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
|
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||||
$response = array( 'success' => false );
|
$response = array( 'success' => false );
|
||||||
$order_id = isset( $request['resource']['custom_id'] ) ?
|
$order_id = isset( $request['resource']['custom_id'] ) ?
|
||||||
$this->sanitize_custom_id( $request['resource']['custom_id'] ) : 0;
|
$this->sanitize_custom_id( $request['resource']['custom_id'] ) : 0;
|
||||||
|
$refund_id = (string) ( $request['resource']['id'] ?? '' );
|
||||||
if ( ! $order_id ) {
|
if ( ! $order_id ) {
|
||||||
$message = sprintf(
|
$message = sprintf(
|
||||||
// translators: %s is the PayPal webhook Id.
|
// translators: %s is the PayPal webhook Id.
|
||||||
|
@ -85,7 +89,7 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$response['message'] = $message;
|
$response['message'] = $message;
|
||||||
return rest_ensure_response( $response );
|
return new WP_REST_Response( $response );
|
||||||
}
|
}
|
||||||
|
|
||||||
$wc_order = wc_get_order( $order_id );
|
$wc_order = wc_get_order( $order_id );
|
||||||
|
@ -93,7 +97,7 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
$message = sprintf(
|
$message = sprintf(
|
||||||
// translators: %s is the PayPal refund Id.
|
// translators: %s is the PayPal refund Id.
|
||||||
__( 'Order for PayPal refund %s not found.', 'woocommerce-paypal-payments' ),
|
__( 'Order for PayPal refund %s not found.', 'woocommerce-paypal-payments' ),
|
||||||
isset( $request['resource']['id'] ) ? $request['resource']['id'] : ''
|
$refund_id
|
||||||
);
|
);
|
||||||
$this->logger->log(
|
$this->logger->log(
|
||||||
'warning',
|
'warning',
|
||||||
|
@ -103,7 +107,13 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$response['message'] = $message;
|
$response['message'] = $message;
|
||||||
return rest_ensure_response( $response );
|
return new WP_REST_Response( $response );
|
||||||
|
}
|
||||||
|
|
||||||
|
$already_added_refunds = $this->get_refunds_meta( $wc_order );
|
||||||
|
if ( in_array( $refund_id, $already_added_refunds, true ) ) {
|
||||||
|
$this->logger->info( "Refund {$refund_id} is already handled." );
|
||||||
|
return new WP_REST_Response( $response );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +142,7 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
);
|
);
|
||||||
|
|
||||||
$response['message'] = $refund->get_error_message();
|
$response['message'] = $refund->get_error_message();
|
||||||
return rest_ensure_response( $response );
|
return new WP_REST_Response( $response );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->log(
|
$this->logger->log(
|
||||||
|
@ -152,11 +162,12 @@ class PaymentCaptureRefunded implements RequestHandler {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( is_array( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
|
if ( $refund_id ) {
|
||||||
$this->update_transaction_id( $request['resource']['id'], $wc_order, $this->logger );
|
$this->update_transaction_id( $refund_id, $wc_order, $this->logger );
|
||||||
|
$this->add_refund_to_meta( $wc_order, $refund_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
$response['success'] = true;
|
$response['success'] = true;
|
||||||
return rest_ensure_response( $response );
|
return new WP_REST_Response( $response );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue