mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-01 07:02:48 +08:00
Merge branch 'trunk' into PCP-3742-disable-gpay-sub
This commit is contained in:
commit
ad07c05cb7
37 changed files with 11765 additions and 301 deletions
|
@ -90,5 +90,12 @@ return function ( string $root_dir ): iterable {
|
|||
$modules[] = ( require "$modules_dir/ppcp-axo-block/module.php" )();
|
||||
}
|
||||
|
||||
if ( apply_filters(
|
||||
'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled',
|
||||
getenv( 'PCP_SETTINGS_ENABLED' ) === '1'
|
||||
) ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-settings/module.php" )();
|
||||
}
|
||||
|
||||
return $modules;
|
||||
};
|
||||
|
|
|
@ -67,7 +67,7 @@ class BillingSubscriptions {
|
|||
*/
|
||||
public function suspend( string $id ):void {
|
||||
$data = array(
|
||||
'reason' => 'Suspended by customer',
|
||||
'reason' => sprintf( 'Suspended by %s.', is_admin() ? 'merchant' : 'customer' ),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
@ -107,7 +107,7 @@ class BillingSubscriptions {
|
|||
*/
|
||||
public function activate( string $id ): void {
|
||||
$data = array(
|
||||
'reason' => 'Reactivated by customer',
|
||||
'reason' => sprintf( 'Reactivated by %s.', is_admin() ? 'merchant' : 'customer' ),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
@ -148,7 +148,7 @@ class BillingSubscriptions {
|
|||
*/
|
||||
public function cancel( string $id ): void {
|
||||
$data = array(
|
||||
'reason' => 'Cancelled by customer',
|
||||
'reason' => sprintf( 'Cancelled by %s.', is_admin() ? 'merchant' : 'customer' ),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
|
|
@ -19,11 +19,9 @@ export function CardFields( {
|
|||
config,
|
||||
eventRegistration,
|
||||
emitResponse,
|
||||
components,
|
||||
} ) {
|
||||
const { onPaymentSetup } = eventRegistration;
|
||||
const { responseTypes } = emitResponse;
|
||||
const { PaymentMethodIcons } = components;
|
||||
|
||||
const [ cardFieldsForm, setCardFieldsForm ] = useState();
|
||||
const getCardFieldsForm = ( cardFieldsForm ) => {
|
||||
|
@ -95,10 +93,6 @@ export function CardFields( {
|
|||
} }
|
||||
>
|
||||
<PayPalCardFieldsForm />
|
||||
<PaymentMethodIcons
|
||||
icons={ config.card_icons }
|
||||
align="left"
|
||||
/>
|
||||
<CheckoutHandler
|
||||
getCardFieldsForm={ getCardFieldsForm }
|
||||
getSavePayment={ getSavePayment }
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
import { registerPaymentMethod } from '@woocommerce/blocks-registry';
|
||||
import { CardFields } from './Components/card-fields';
|
||||
import {registerPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {CardFields} from './Components/card-fields';
|
||||
|
||||
const config = wc.wcSettings.getSetting( 'ppcp-credit-card-gateway_data' );
|
||||
const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data');
|
||||
|
||||
registerPaymentMethod( {
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={ { __html: config.title } } />,
|
||||
content: <CardFields config={ config } />,
|
||||
edit: <CardFields config={ config } />,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => {
|
||||
return true;
|
||||
},
|
||||
supports: {
|
||||
showSavedCards: true,
|
||||
features: config.supports,
|
||||
},
|
||||
} );
|
||||
const Label = ({components, config}) => {
|
||||
const {PaymentMethodIcons} = components;
|
||||
return <>
|
||||
<span dangerouslySetInnerHTML={{__html: config.title}}/>
|
||||
<PaymentMethodIcons
|
||||
icons={ config.card_icons }
|
||||
align="right"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <Label config={config}/>,
|
||||
content: <CardFields config={config}/>,
|
||||
edit: <CardFields config={config}/>,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => {
|
||||
return true;
|
||||
},
|
||||
supports: {
|
||||
showSavedCards: true,
|
||||
features: config.supports,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -233,6 +233,15 @@ class SingleProductBootstap {
|
|||
this.form(),
|
||||
this.errorHandler
|
||||
);
|
||||
if (
|
||||
! this.gateway.vaultingEnabled &&
|
||||
[ 'subscription', 'variable-subscription' ].includes(
|
||||
this.gateway.productType
|
||||
) &&
|
||||
this.gateway.manualRenewalEnabled !== '1'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
PayPalCommerceGateway.data_client_id.has_subscriptions &&
|
||||
|
|
|
@ -1321,8 +1321,26 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
'should_handle_shipping_in_paypal' => $this->should_handle_shipping_in_paypal && ! $this->is_checkout(),
|
||||
'needShipping' => $this->need_shipping(),
|
||||
'vaultingEnabled' => $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ),
|
||||
'productType' => null,
|
||||
'manualRenewalEnabled' => false,
|
||||
);
|
||||
|
||||
if ( is_product() ) {
|
||||
$product = wc_get_product( get_the_ID() );
|
||||
if ( is_a( $product, \WC_Product::class ) ) {
|
||||
$localize['productType'] = $product->get_type();
|
||||
}
|
||||
}
|
||||
|
||||
if ( class_exists( '\WCS_Manual_Renewal_Manager' ) ) {
|
||||
/**
|
||||
* We verify the existence of the class prior to invoking a static method.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
$localize['manualRenewalEnabled'] = \WCS_Manual_Renewal_Manager::is_manual_renewal_enabled();
|
||||
}
|
||||
|
||||
if ( 'pay-now' === $this->context() ) {
|
||||
$localize['pay_now'] = $this->pay_now_script_data();
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ class MetaBoxRenderer {
|
|||
<label for="ppcp-tracking-items"><?php echo esc_html__( 'Select items for this shipment', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select multiple class="wc-enhanced-select ppcp-tracking-items" id="ppcp-tracking-items" name="ppcp-tracking[items]">
|
||||
<?php foreach ( $order_items as $item ) : ?>
|
||||
<option value="<?php echo intval( $item->get_id() ); ?>"><?php echo esc_html( $item->get_name() ); ?></option>
|
||||
<option value="<?php echo intval( $item->get_id() ); ?>"><?php echo esc_attr( wp_strip_all_tags( $item->get_name() ) ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -40,4 +40,13 @@ return array(
|
|||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'paypal-subscriptions.renewal-handler' => static function ( ContainerInterface $container ): RenewalHandler {
|
||||
return new RenewalHandler( $container->get( 'woocommerce.logger.woocommerce' ) );
|
||||
},
|
||||
'paypal-subscriptions.status' => static function ( ContainerInterface $container ): SubscriptionStatus {
|
||||
return new SubscriptionStatus(
|
||||
$container->get( 'api.endpoint.billing-subscriptions' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -187,6 +187,9 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
30
|
||||
);
|
||||
|
||||
/**
|
||||
* Executed when updating WC Subscription.
|
||||
*/
|
||||
add_action(
|
||||
'woocommerce_process_shop_subscription_meta',
|
||||
/**
|
||||
|
@ -194,65 +197,41 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $id, $post ) use ( $c ) {
|
||||
function( $id ) use ( $c ) {
|
||||
$subscription = wcs_get_subscription( $id );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
if ( $subscription === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_id ) {
|
||||
return;
|
||||
}
|
||||
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
|
||||
assert( $subscriptions_endpoint instanceof BillingSubscriptions );
|
||||
|
||||
if ( $subscription->get_status() === 'cancelled' ) {
|
||||
try {
|
||||
$subscriptions_endpoint->cancel( $subscription_id );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
$subscription_status = $c->get( 'paypal-subscriptions.status' );
|
||||
assert( $subscription_status instanceof SubscriptionStatus );
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
$logger->error( 'Could not cancel subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $subscription->get_status() === 'pending-cancel' ) {
|
||||
try {
|
||||
$subscriptions_endpoint->suspend( $subscription_id );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
$logger->error( 'Could not suspend subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $subscription->get_status() === 'active' ) {
|
||||
try {
|
||||
$current_subscription = $subscriptions_endpoint->subscription( $subscription_id );
|
||||
if ( $current_subscription->status === 'SUSPENDED' ) {
|
||||
$subscriptions_endpoint->activate( $subscription_id );
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
$logger->error( 'Could not reactivate subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
$subscription_status->update_status( $subscription->get_status(), $subscription_id );
|
||||
},
|
||||
20,
|
||||
2
|
||||
20
|
||||
);
|
||||
|
||||
/**
|
||||
* Update subscription status from WC Subscriptions list page action link.
|
||||
*/
|
||||
add_action(
|
||||
'woocommerce_subscription_status_updated',
|
||||
function( WC_Subscription $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_status = $c->get( 'paypal-subscriptions.status' );
|
||||
assert( $subscription_status instanceof SubscriptionStatus );
|
||||
|
||||
$subscription_status->update_status( $subscription->get_status(), $subscription_id );
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
|
|
107
modules/ppcp-paypal-subscriptions/src/RenewalHandler.php
Normal file
107
modules/ppcp-paypal-subscriptions/src/RenewalHandler.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
/**
|
||||
* Subscriptions renewal handler.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Data_Exception;
|
||||
use WC_Order;
|
||||
use WC_Subscription;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* Class RenewalHandler
|
||||
*/
|
||||
class RenewalHandler {
|
||||
|
||||
use TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* RenewalHandler constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process subscription renewal.
|
||||
*
|
||||
* @param WC_Subscription[] $subscriptions WC Subscriptions.
|
||||
* @param string $transaction_id PayPal transaction ID.
|
||||
* @return void
|
||||
* @throws WC_Data_Exception If something goes wrong while setting payment method.
|
||||
*/
|
||||
public function process( array $subscriptions, string $transaction_id ): void {
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
if ( $this->is_for_renewal_order( $subscription ) ) {
|
||||
$renewal_order = wcs_create_renewal_order( $subscription );
|
||||
if ( is_a( $renewal_order, WC_Order::class ) ) {
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Processing renewal order #%s for subscription #%s',
|
||||
$renewal_order->get_id(),
|
||||
$subscription->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
$renewal_order->set_payment_method( $subscription->get_payment_method() );
|
||||
$renewal_order->payment_complete();
|
||||
$this->update_transaction_id( $transaction_id, $renewal_order, $this->logger );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$parent_order = wc_get_order( $subscription->get_parent() );
|
||||
if ( is_a( $parent_order, WC_Order::class ) ) {
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Processing parent order #%s for subscription #%s',
|
||||
$parent_order->get_id(),
|
||||
$subscription->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
$subscription->update_meta_data( '_ppcp_is_subscription_renewal', 'true' );
|
||||
$subscription->save_meta_data();
|
||||
$this->update_transaction_id( $transaction_id, $parent_order, $this->logger );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether subscription order is for renewal or not.
|
||||
*
|
||||
* @param WC_Subscription $subscription WC Subscription.
|
||||
* @return bool
|
||||
*/
|
||||
private function is_for_renewal_order( WC_Subscription $subscription ): bool {
|
||||
$subscription_renewal_meta = $subscription->get_meta( '_ppcp_is_subscription_renewal' ) ?? '';
|
||||
if ( $subscription_renewal_meta === 'true' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
time() >= $subscription->get_time( 'start' )
|
||||
&& ( time() - $subscription->get_time( 'start' ) ) <= ( 8 * HOUR_IN_SECONDS )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
144
modules/ppcp-paypal-subscriptions/src/SubscriptionStatus.php
Normal file
144
modules/ppcp-paypal-subscriptions/src/SubscriptionStatus.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles PayPal subscription status.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class SubscriptionStatus
|
||||
*/
|
||||
class SubscriptionStatus {
|
||||
|
||||
/**
|
||||
* Billing subscriptions endpoint.
|
||||
*
|
||||
* @var BillingSubscriptions
|
||||
*/
|
||||
private $subscriptions_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* SubscriptionStatus constructor.
|
||||
*
|
||||
* @param BillingSubscriptions $subscriptions_endpoint Billing subscriptions endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
BillingSubscriptions $subscriptions_endpoint,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->subscriptions_endpoint = $subscriptions_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates PayPal subscription status from the given WC Subscription status.
|
||||
*
|
||||
* @param string $subscription_status The WC Subscription status.
|
||||
* @param string $subscription_id The PayPal Subscription ID.
|
||||
* @return void
|
||||
*/
|
||||
public function update_status( string $subscription_status, string $subscription_id ): void {
|
||||
if ( $subscription_status === 'pending-cancel' || $subscription_status === 'cancelled' ) {
|
||||
try {
|
||||
$current_subscription = $this->subscriptions_endpoint->subscription( $subscription_id );
|
||||
if ( $current_subscription->status === 'CANCELLED' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Canceling PayPal subscription #%s.',
|
||||
$subscription_id
|
||||
)
|
||||
);
|
||||
|
||||
$this->subscriptions_endpoint->cancel( $subscription_id );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error(
|
||||
sprintf(
|
||||
'Could not cancel PayPal subscription #%s. %s',
|
||||
$subscription_id,
|
||||
$this->get_error( $exception )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $subscription_status === 'on-hold' ) {
|
||||
try {
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Suspending PayPal subscription #%s.',
|
||||
$subscription_id
|
||||
)
|
||||
);
|
||||
|
||||
$this->subscriptions_endpoint->suspend( $subscription_id );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error(
|
||||
sprintf(
|
||||
'Could not suspend PayPal subscription #%s. %s',
|
||||
$subscription_id,
|
||||
$this->get_error( $exception )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $subscription_status === 'active' ) {
|
||||
try {
|
||||
$current_subscription = $this->subscriptions_endpoint->subscription( $subscription_id );
|
||||
if ( $current_subscription->status === 'SUSPENDED' ) {
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Activating suspended PayPal subscription #%s.',
|
||||
$subscription_id
|
||||
)
|
||||
);
|
||||
|
||||
$this->subscriptions_endpoint->activate( $subscription_id );
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error(
|
||||
sprintf(
|
||||
'Could not reactivate PayPal subscription #%s. %s',
|
||||
$subscription_id,
|
||||
$this->get_error( $exception )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error from exception.
|
||||
*
|
||||
* @param RuntimeException $exception The exception.
|
||||
* @return string
|
||||
*/
|
||||
private function get_error( RuntimeException $exception ): string {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
}
|
16
modules/ppcp-settings/composer.json
Normal file
16
modules/ppcp-settings/composer.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "woocommerce/ppcp-settings",
|
||||
"type": "inpsyde-module",
|
||||
"description": "Settings module",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.4 | ^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\Settings\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
14
modules/ppcp-settings/module.php
Normal file
14
modules/ppcp-settings/module.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* The Settings module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Settings;
|
||||
|
||||
return static function () : SettingsModule {
|
||||
return new SettingsModule();
|
||||
};
|
12
modules/ppcp-settings/package.json
Normal file
12
modules/ppcp-settings/package.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "ppcp-settings",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"watch": "wp-scripts start --webpack-src-dir=resources/js --output-path=assets",
|
||||
"build": "wp-scripts build --webpack-src-dir=resources/js --output-path=assets"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wordpress/scripts": "^30.3.0"
|
||||
}
|
||||
}
|
1
modules/ppcp-settings/resources/css/style.scss
Normal file
1
modules/ppcp-settings/resources/css/style.scss
Normal file
|
@ -0,0 +1 @@
|
|||
.red {color:red;}
|
3
modules/ppcp-settings/resources/js/App.js
Normal file
3
modules/ppcp-settings/resources/js/App.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function App() {
|
||||
return <div className="red">App</div>;
|
||||
}
|
7
modules/ppcp-settings/resources/js/index.js
Normal file
7
modules/ppcp-settings/resources/js/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { App } from './App';
|
||||
|
||||
createRoot( document.getElementById( 'ppcp-settings-container' ) ).render(
|
||||
<App />
|
||||
);
|
26
modules/ppcp-settings/services.php
Normal file
26
modules/ppcp-settings/services.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* The Settings module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'settings.url' => static function ( ContainerInterface $container ) : string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-settings/',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
);
|
95
modules/ppcp-settings/src/SettingsModule.php
Normal file
95
modules/ppcp-settings/src/SettingsModule.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* The Settings module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\AxoBlock
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class SettingsModule
|
||||
*/
|
||||
class SettingsModule implements ServiceModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $container ): bool {
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function( $hook_suffix ) use ( $container ) {
|
||||
if ( 'woocommerce_page_wc-settings' !== $hook_suffix ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require resolves.
|
||||
*
|
||||
* @psalm-suppress UnresolvableInclude
|
||||
*/
|
||||
$script_asset_file = require dirname( realpath( __FILE__ ) ?: '', 2 ) . '/assets/index.asset.php';
|
||||
|
||||
$module_url = $container->get( 'settings.url' );
|
||||
|
||||
wp_register_script(
|
||||
'ppcp-admin-settings',
|
||||
$module_url . '/assets/index.js',
|
||||
$script_asset_file['dependencies'],
|
||||
$script_asset_file['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'ppcp-admin-settings' );
|
||||
|
||||
/**
|
||||
* Require resolves.
|
||||
*
|
||||
* @psalm-suppress UnresolvableInclude
|
||||
*/
|
||||
$style_asset_file = require dirname( realpath( __FILE__ ) ?: '', 2 ) . '/assets/style.asset.php';
|
||||
|
||||
wp_register_style(
|
||||
'ppcp-admin-settings',
|
||||
$module_url . '/assets/style-style.css',
|
||||
$style_asset_file['dependencies'],
|
||||
$style_asset_file['version']
|
||||
);
|
||||
|
||||
wp_enqueue_style( 'ppcp-admin-settings' );
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_gateway_admin_options_wrapper',
|
||||
static function(): void {
|
||||
global $hide_save_button;
|
||||
$hide_save_button = true;
|
||||
|
||||
echo '<div id="ppcp-settings-container"></div>';
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
12
modules/ppcp-settings/webpack.config.js
Normal file
12
modules/ppcp-settings/webpack.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
|
||||
const path = require( 'path' );
|
||||
|
||||
module.exports = {
|
||||
...defaultConfig,
|
||||
...{
|
||||
entry: {
|
||||
index: path.resolve( process.cwd(), 'resources/js', 'index.js' ),
|
||||
style: path.resolve( process.cwd(), 'resources/css', 'style.scss' ),
|
||||
},
|
||||
},
|
||||
};
|
10453
modules/ppcp-settings/yarn.lock
Normal file
10453
modules/ppcp-settings/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
53
modules/ppcp-wc-gateway/resources/js/void-button.js
Normal file
53
modules/ppcp-wc-gateway/resources/js/void-button.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {
|
||||
hide,
|
||||
show,
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
|
||||
document.addEventListener( 'DOMContentLoaded', function () {
|
||||
const refundButton = document.querySelector( 'button.refund-items' );
|
||||
if ( ! refundButton ) {
|
||||
return;
|
||||
}
|
||||
|
||||
refundButton.insertAdjacentHTML(
|
||||
'afterend',
|
||||
`<button class="button" type="button" id="pcpVoid">${ PcpVoidButton.button_text }</button>`
|
||||
);
|
||||
|
||||
hide( refundButton );
|
||||
|
||||
const voidButton = document.querySelector( '#pcpVoid' );
|
||||
|
||||
voidButton.addEventListener( 'click', async () => {
|
||||
if ( ! window.confirm( PcpVoidButton.popup_text ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
voidButton.setAttribute( 'disabled', 'disabled' );
|
||||
|
||||
const res = await fetch( PcpVoidButton.ajax.void.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify( {
|
||||
nonce: PcpVoidButton.ajax.void.nonce,
|
||||
wc_order_id: PcpVoidButton.wc_order_id,
|
||||
} ),
|
||||
} );
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if ( ! data.success ) {
|
||||
hide( voidButton );
|
||||
show( refundButton );
|
||||
|
||||
alert( PcpVoidButton.error_text );
|
||||
|
||||
throw Error( data.data.message );
|
||||
}
|
||||
|
||||
location.reload();
|
||||
} );
|
||||
} );
|
|
@ -25,8 +25,10 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
|||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderReauthorizeAction;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Assets\VoidButtonAssets;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\CaptureCardPayment;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\FeesUpdater;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirectTaskFactory;
|
||||
|
@ -46,7 +48,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Cli\SettingsCommand;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
|
@ -118,7 +119,8 @@ return array(
|
|||
$container->get( 'wcgateway.place-order-button-text' ),
|
||||
$container->get( 'api.endpoint.payment-tokens' ),
|
||||
$container->get( 'vaulting.vault-v3-enabled' ),
|
||||
$container->get( 'vaulting.wc-payment-tokens' )
|
||||
$container->get( 'vaulting.wc-payment-tokens' ),
|
||||
$container->get( 'wcgateway.settings.admin-settings-enabled' )
|
||||
);
|
||||
},
|
||||
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
|
||||
|
@ -514,12 +516,20 @@ return array(
|
|||
);
|
||||
},
|
||||
'wcgateway.processor.refunds' => static function ( ContainerInterface $container ): RefundProcessor {
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
$payments_endpoint = $container->get( 'api.endpoint.payments' );
|
||||
$refund_fees_updater = $container->get( 'wcgateway.helper.refund-fees-updater' );
|
||||
$prefix = $container->get( 'api.prefix' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new RefundProcessor( $order_endpoint, $payments_endpoint, $refund_fees_updater, $prefix, $logger );
|
||||
return new RefundProcessor(
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.endpoint.payments' ),
|
||||
$container->get( 'wcgateway.helper.refund-fees-updater' ),
|
||||
$container->get( 'wcgateway.allowed_refund_payment_methods' ),
|
||||
$container->get( 'api.prefix' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'wcgateway.allowed_refund_payment_methods' => static function ( ContainerInterface $container ): array {
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_allowed_refund_payment_methods',
|
||||
array( PayPalGateway::ID, CreditCardGateway::ID, CardButtonGateway::ID, PayUponInvoiceGateway::ID )
|
||||
);
|
||||
},
|
||||
'wcgateway.processor.authorized-payments' => static function ( ContainerInterface $container ): AuthorizedPaymentsProcessor {
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
|
@ -1420,7 +1430,10 @@ return array(
|
|||
return new OXXO(
|
||||
$container->get( 'wcgateway.checkout-helper' ),
|
||||
$container->get( 'wcgateway.url' ),
|
||||
$container->get( 'ppcp.asset-version' )
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.factory.capture' )
|
||||
);
|
||||
},
|
||||
'wcgateway.oxxo-gateway' => static function( ContainerInterface $container ): OXXOGateway {
|
||||
|
@ -1914,4 +1927,26 @@ return array(
|
|||
|
||||
return $simple_redirect_tasks;
|
||||
},
|
||||
|
||||
'wcgateway.void-button.assets' => function( ContainerInterface $container ) : VoidButtonAssets {
|
||||
return new VoidButtonAssets(
|
||||
$container->get( 'wcgateway.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'wcgateway.processor.refunds' ),
|
||||
$container->get( 'wcgateway.allowed_refund_payment_methods' )
|
||||
);
|
||||
},
|
||||
'wcgateway.void-button.endpoint' => function( ContainerInterface $container ) : VoidOrderEndpoint {
|
||||
return new VoidOrderEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'wcgateway.processor.refunds' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'wcgateway.settings.admin-settings-enabled' => static function( ContainerInterface $container ): bool {
|
||||
return $container->has( 'settings.url' );
|
||||
},
|
||||
);
|
||||
|
|
171
modules/ppcp-wc-gateway/src/Assets/VoidButtonAssets.php
Normal file
171
modules/ppcp-wc-gateway/src/Assets/VoidButtonAssets.php
Normal file
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
/**
|
||||
* Register and configure assets for the void button.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Assets
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
|
||||
|
||||
use Exception;
|
||||
use WC_AJAX;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
use WP_Screen;
|
||||
|
||||
/**
|
||||
* Class VoidButtonAssets
|
||||
*/
|
||||
class VoidButtonAssets {
|
||||
|
||||
/**
|
||||
* The URL of this module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* The Refund Processor.
|
||||
*
|
||||
* @var RefundProcessor
|
||||
*/
|
||||
private $refund_processor;
|
||||
|
||||
/**
|
||||
* The methods that can be refunded.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_refund_payment_methods;
|
||||
|
||||
/**
|
||||
* VoidButtonAssets constructor.
|
||||
*
|
||||
* @param string $module_url The url of this module.
|
||||
* @param string $version The assets version.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param array $allowed_refund_payment_methods The methods that can be refunded.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
OrderEndpoint $order_endpoint,
|
||||
RefundProcessor $refund_processor,
|
||||
array $allowed_refund_payment_methods
|
||||
) {
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->refund_processor = $refund_processor;
|
||||
$this->allowed_refund_payment_methods = $allowed_refund_payment_methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if should register assets on the current page.
|
||||
*/
|
||||
public function should_register(): bool {
|
||||
if ( ! is_admin() || wp_doing_ajax() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $theorder;
|
||||
|
||||
if ( ! $theorder instanceof WC_Order ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current_screen = get_current_screen();
|
||||
if ( ! $current_screen instanceof WP_Screen ) {
|
||||
return false;
|
||||
}
|
||||
if ( $current_screen->post_type !== 'shop_order' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! in_array( $theorder->get_payment_method(), $this->allowed_refund_payment_methods, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if there are refunds already, it is probably not voidable anymore + void cannot be partial.
|
||||
if ( $theorder->get_remaining_refund_amount() !== $theorder->get_total() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$order_id = $theorder->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
|
||||
if ( $this->refund_processor->determine_refund_mode( $order ) !== RefundProcessor::REFUND_MODE_VOID ) {
|
||||
return false;
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the assets.
|
||||
*/
|
||||
public function register(): void {
|
||||
global $theorder;
|
||||
assert( $theorder instanceof WC_Order );
|
||||
|
||||
wp_enqueue_script(
|
||||
'ppcp-void-button',
|
||||
trailingslashit( $this->module_url ) . 'assets/js/void-button.js',
|
||||
array(),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-void-button',
|
||||
'PcpVoidButton',
|
||||
array(
|
||||
'button_text' => __( 'Void authorization', 'woocommerce-paypal-payments' ),
|
||||
'popup_text' => __(
|
||||
'After voiding an authorized transaction, you cannot capture any funds associated with that transaction, and the funds are returned to the customer. Voiding an authorization cancels the entire open amount.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'error_text' => __(
|
||||
'The operation failed. Use the Refund button if the funds were already captured.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'wc_order_id' => $theorder->get_id(),
|
||||
'ajax' => array(
|
||||
'void' => array(
|
||||
'endpoint' => WC_AJAX::get_endpoint( VoidOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( VoidOrderEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -112,6 +112,7 @@ class ReturnUrlEndpoint {
|
|||
}
|
||||
|
||||
if ( $wc_order->get_payment_method() === OXXOGateway::ID ) {
|
||||
$this->session_handler->destroy_session_data();
|
||||
wp_safe_redirect( wc_get_checkout_url() );
|
||||
exit();
|
||||
}
|
||||
|
|
198
modules/ppcp-wc-gateway/src/Endpoint/VoidOrderEndpoint.php
Normal file
198
modules/ppcp-wc-gateway/src/Endpoint/VoidOrderEndpoint.php
Normal file
|
@ -0,0 +1,198 @@
|
|||
<?php
|
||||
/**
|
||||
* The Void Order endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WC_Order_Item_Fee;
|
||||
use WC_Order_Item_Product;
|
||||
use WC_Order_Item_Shipping;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
/**
|
||||
* Class VoidOrderEndpoint
|
||||
*/
|
||||
class VoidOrderEndpoint {
|
||||
|
||||
const ENDPOINT = 'ppc-void-order';
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* The Refund Processor.
|
||||
*
|
||||
* @var RefundProcessor
|
||||
*/
|
||||
private $refund_processor;
|
||||
|
||||
/**
|
||||
* The Request Data Helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* VoidOrderEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The Request Data Helper.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
OrderEndpoint $order_endpoint,
|
||||
RefundProcessor $refund_processor,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->refund_processor = $refund_processor;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the incoming request.
|
||||
*/
|
||||
public function handle_request(): void {
|
||||
$request = $this->request_data->read_request( self::nonce() );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => 'Invalid request.',
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$wc_order_id = (int) $request['wc_order_id'];
|
||||
|
||||
$wc_order = wc_get_order( $wc_order_id );
|
||||
if ( ! $wc_order instanceof WC_Order ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => 'WC order not found.',
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => 'PayPal order ID not found in meta.',
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
|
||||
$this->refund_processor->void( $order );
|
||||
|
||||
$this->make_refunded( $wc_order );
|
||||
} catch ( Exception $exception ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => 'Void failed. ' . $exception->getMessage(),
|
||||
)
|
||||
);
|
||||
$this->logger->error( 'Void failed. ' . $exception->getMessage() );
|
||||
return;
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of items for the wc_create_refund data,
|
||||
* making all items refunded (max qty, total, taxes).
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
*/
|
||||
protected function refund_items( WC_Order $wc_order ): array {
|
||||
$refunded_items = array();
|
||||
foreach ( $wc_order->get_items( array( 'line_item', 'fee', 'shipping' ) ) as $item ) {
|
||||
// Some methods like get_taxes() are not defined in WC_Order_Item.
|
||||
if (
|
||||
! $item instanceof WC_Order_Item_Product
|
||||
&& ! $item instanceof WC_Order_Item_Fee
|
||||
&& ! $item instanceof WC_Order_Item_Shipping
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$taxes = array();
|
||||
$item_taxes = $item->get_taxes();
|
||||
/**
|
||||
* The type is not really guaranteed in the code.
|
||||
*
|
||||
* @psalm-suppress RedundantConditionGivenDocblockType
|
||||
*/
|
||||
if ( is_array( $item_taxes ) && isset( $item_taxes['total'] ) ) {
|
||||
$taxes = $item_taxes['total'];
|
||||
}
|
||||
|
||||
$refunded_items[ $item->get_id() ] = array(
|
||||
'qty' => $item->get_type() === 'line_item' ? $item->get_quantity() : 0,
|
||||
'refund_total' => $item->get_total(),
|
||||
'refund_tax' => $taxes,
|
||||
);
|
||||
}
|
||||
return $refunded_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a full refund.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
*/
|
||||
private function make_refunded( WC_Order $wc_order ): void {
|
||||
wc_create_refund(
|
||||
array(
|
||||
'amount' => $wc_order->get_total(),
|
||||
'reason' => __( 'Voided authorization', 'woocommerce-paypal-payments' ),
|
||||
'order_id' => $wc_order->get_id(),
|
||||
'line_items' => $this->refund_items( $wc_order ),
|
||||
'refund_payment' => false,
|
||||
'restock_items' => (bool) apply_filters( 'woocommerce_paypal_payments_void_restock_items', true ),
|
||||
)
|
||||
);
|
||||
|
||||
$wc_order->set_status( 'refunded' );
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,19 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
|
||||
|
||||
/**
|
||||
* Class OXXO.
|
||||
*/
|
||||
class OXXO {
|
||||
public const ID = 'ppcp-oxxo-gateway';
|
||||
|
||||
/**
|
||||
* The checkout helper.
|
||||
|
@ -40,21 +46,51 @@ class OXXO {
|
|||
protected $asset_version;
|
||||
|
||||
/**
|
||||
* OXXO constructor.
|
||||
* The order endpoint.
|
||||
*
|
||||
* @param CheckoutHelper $checkout_helper The checkout helper.
|
||||
* @param string $module_url The module URL.
|
||||
* @param string $asset_version The asset version.
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
protected $order_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The capture factory.
|
||||
*
|
||||
* @var CaptureFactory
|
||||
*/
|
||||
protected $capture_factory;
|
||||
|
||||
/**
|
||||
* OXXO constructor
|
||||
*
|
||||
* @param CheckoutHelper $checkout_helper The checkout helper.
|
||||
* @param string $module_url The module URL.
|
||||
* @param string $asset_version The asset version.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param CaptureFactory $capture_factory The capture factory.
|
||||
*/
|
||||
public function __construct(
|
||||
CheckoutHelper $checkout_helper,
|
||||
string $module_url,
|
||||
string $asset_version
|
||||
string $asset_version,
|
||||
OrderEndpoint $order_endpoint,
|
||||
LoggerInterface $logger,
|
||||
CaptureFactory $capture_factory
|
||||
) {
|
||||
|
||||
$this->checkout_helper = $checkout_helper;
|
||||
$this->module_url = $module_url;
|
||||
$this->asset_version = $asset_version;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->logger = $logger;
|
||||
$this->capture_factory = $capture_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,6 +235,46 @@ class OXXO {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Process PayPal fees
|
||||
*/
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_payment_capture_completed_webhook_handler',
|
||||
function ( WC_Order $wc_order, string $order_id ) {
|
||||
try {
|
||||
if ( $wc_order->get_payment_method() !== OXXO::ID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
$payments = $order->purchase_units()[0]->payments();
|
||||
|
||||
if ( ! $payments ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$capture = $payments->captures()[0] ?? null;
|
||||
|
||||
if ( $capture ) {
|
||||
$breakdown = $capture->seller_receivable_breakdown();
|
||||
if ( $breakdown ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() );
|
||||
$paypal_fee = $breakdown->paypal_fee();
|
||||
if ( $paypal_fee ) {
|
||||
$wc_order->update_meta_data( 'PayPal Transaction Fee', (string) $paypal_fee->value() );
|
||||
}
|
||||
|
||||
$wc_order->save_meta_data();
|
||||
}
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error( $exception->getMessage() );
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -202,6 +202,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
private $wc_payment_tokens;
|
||||
|
||||
/**
|
||||
* Whether settings module is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $admin_settings_enabled;
|
||||
|
||||
/**
|
||||
* PayPalGateway constructor.
|
||||
*
|
||||
|
@ -225,6 +232,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
* @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint.
|
||||
* @param bool $vault_v3_enabled Whether Vault v3 module is enabled.
|
||||
* @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens.
|
||||
* @param bool $admin_settings_enabled Whether settings module is enabled.
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsRenderer $settings_renderer,
|
||||
|
@ -246,7 +254,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
string $place_order_button_text,
|
||||
PaymentTokensEndpoint $payment_tokens_endpoint,
|
||||
bool $vault_v3_enabled,
|
||||
WooCommercePaymentTokens $wc_payment_tokens
|
||||
WooCommercePaymentTokens $wc_payment_tokens,
|
||||
bool $admin_settings_enabled
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
$this->settings_renderer = $settings_renderer;
|
||||
|
@ -270,6 +279,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->wc_payment_tokens = $wc_payment_tokens;
|
||||
$this->admin_settings_enabled = $admin_settings_enabled;
|
||||
|
||||
$default_support = array(
|
||||
'products',
|
||||
|
@ -745,6 +755,17 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the parent admin_options method.
|
||||
*/
|
||||
public function admin_options() {
|
||||
if ( ! $this->admin_settings_enabled ) {
|
||||
parent::admin_options();
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_gateway_admin_options_wrapper', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the settings renderer.
|
||||
*
|
||||
|
|
|
@ -33,9 +33,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\RefundFeesUpdater;
|
|||
class RefundProcessor {
|
||||
use RefundMetaTrait;
|
||||
|
||||
private const REFUND_MODE_REFUND = 'refund';
|
||||
private const REFUND_MODE_VOID = 'void';
|
||||
private const REFUND_MODE_UNKNOWN = 'unknown';
|
||||
public const REFUND_MODE_REFUND = 'refund';
|
||||
public const REFUND_MODE_VOID = 'void';
|
||||
public const REFUND_MODE_UNKNOWN = 'unknown';
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
|
@ -72,12 +72,20 @@ class RefundProcessor {
|
|||
*/
|
||||
private $refund_fees_updater;
|
||||
|
||||
/**
|
||||
* The methods that can be refunded.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_refund_payment_methods;
|
||||
|
||||
/**
|
||||
* RefundProcessor constructor.
|
||||
*
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
|
||||
* @param RefundFeesUpdater $refund_fees_updater The refund fees updater.
|
||||
* @param array $allowed_refund_payment_methods The methods that can be refunded.
|
||||
* @param string $prefix The prefix.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
|
@ -85,15 +93,17 @@ class RefundProcessor {
|
|||
OrderEndpoint $order_endpoint,
|
||||
PaymentsEndpoint $payments_endpoint,
|
||||
RefundFeesUpdater $refund_fees_updater,
|
||||
array $allowed_refund_payment_methods,
|
||||
string $prefix,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->refund_fees_updater = $refund_fees_updater;
|
||||
$this->prefix = $prefix;
|
||||
$this->logger = $logger;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->refund_fees_updater = $refund_fees_updater;
|
||||
$this->allowed_refund_payment_methods = $allowed_refund_payment_methods;
|
||||
$this->prefix = $prefix;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,11 +119,7 @@ class RefundProcessor {
|
|||
*/
|
||||
public function process( WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
|
||||
try {
|
||||
$allowed_refund_payment_methods = apply_filters(
|
||||
'woocommerce_paypal_payments_allowed_refund_payment_methods',
|
||||
array( PayPalGateway::ID, CreditCardGateway::ID, CardButtonGateway::ID, PayUponInvoiceGateway::ID )
|
||||
);
|
||||
if ( ! in_array( $wc_order->get_payment_method(), $allowed_refund_payment_methods, true ) ) {
|
||||
if ( ! in_array( $wc_order->get_payment_method(), $this->allowed_refund_payment_methods, true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -134,7 +140,7 @@ class RefundProcessor {
|
|||
)
|
||||
);
|
||||
|
||||
$mode = $this->determine_refund_mode( $payments );
|
||||
$mode = $this->determine_refund_mode( $order );
|
||||
|
||||
switch ( $mode ) {
|
||||
case self::REFUND_MODE_REFUND:
|
||||
|
@ -226,11 +232,13 @@ class RefundProcessor {
|
|||
/**
|
||||
* Determines the refunding mode.
|
||||
*
|
||||
* @param Payments $payments The order payments state.
|
||||
* @param Order $order The order.
|
||||
*
|
||||
* @return string One of the REFUND_MODE_ constants.
|
||||
*/
|
||||
private function determine_refund_mode( Payments $payments ): string {
|
||||
public function determine_refund_mode( Order $order ): string {
|
||||
$payments = $this->get_payments( $order );
|
||||
|
||||
$authorizations = $payments->authorizations();
|
||||
if ( $authorizations ) {
|
||||
foreach ( $authorizations as $authorization ) {
|
||||
|
|
|
@ -21,7 +21,9 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule
|
|||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Assets\VoidButtonAssets;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\CreditCardOrderInfoHandlingTrait;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
|
@ -90,6 +92,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
|
|||
$this->register_columns( $c );
|
||||
$this->register_checkout_paypal_address_preset( $c );
|
||||
$this->register_wc_tasks( $c );
|
||||
$this->register_void_button( $c );
|
||||
|
||||
add_action(
|
||||
'woocommerce_sections_checkout',
|
||||
|
@ -848,21 +851,19 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
|
|||
* @return void
|
||||
*/
|
||||
protected function register_wc_tasks( ContainerInterface $container ): void {
|
||||
$simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' );
|
||||
if ( empty( $simple_redirect_tasks ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' );
|
||||
assert( $task_registrar instanceof TaskRegistrarInterface );
|
||||
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
static function () use ( $simple_redirect_tasks, $task_registrar, $logger ): void {
|
||||
static function () use ( $container ): void {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
try {
|
||||
$simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' );
|
||||
if ( empty( $simple_redirect_tasks ) ) {
|
||||
return;
|
||||
}
|
||||
$task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' );
|
||||
assert( $task_registrar instanceof TaskRegistrarInterface );
|
||||
|
||||
$task_registrar->register( $simple_redirect_tasks );
|
||||
} catch ( Exception $exception ) {
|
||||
$logger->error( "Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage() );
|
||||
|
@ -870,4 +871,33 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the assets and ajax endpoint for the void button.
|
||||
*
|
||||
* @param ContainerInterface $container The container.
|
||||
*/
|
||||
protected function register_void_button( ContainerInterface $container ): void {
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
static function () use ( $container ) {
|
||||
$assets = $container->get( 'wcgateway.void-button.assets' );
|
||||
assert( $assets instanceof VoidButtonAssets );
|
||||
|
||||
if ( $assets->should_register() ) {
|
||||
$assets->register();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . VoidOrderEndpoint::ENDPOINT,
|
||||
static function () use ( $container ) {
|
||||
$endpoint = $container->get( 'wcgateway.void-button.endpoint' );
|
||||
assert( $endpoint instanceof VoidOrderEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ module.exports = {
|
|||
'gateway-settings': path.resolve('./resources/js/gateway-settings.js'),
|
||||
'fraudnet': path.resolve('./resources/js/fraudnet.js'),
|
||||
'oxxo': path.resolve('./resources/js/oxxo.js'),
|
||||
'void-button': path.resolve('./resources/js/void-button.js'),
|
||||
'gateway-settings-style': path.resolve('./resources/css/gateway-settings.scss'),
|
||||
'common-style': path.resolve('./resources/css/common.scss'),
|
||||
},
|
||||
|
|
|
@ -99,7 +99,7 @@ return array(
|
|||
new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory, $payment_token_helper ),
|
||||
new VaultPaymentTokenDeleted( $logger ),
|
||||
new PaymentCapturePending( $logger ),
|
||||
new PaymentSaleCompleted( $logger ),
|
||||
new PaymentSaleCompleted( $logger, $container->get( 'paypal-subscriptions.renewal-handler' ) ),
|
||||
new PaymentSaleRefunded( $logger, $refund_fees_updater ),
|
||||
new BillingSubscriptionCancelled( $logger ),
|
||||
new BillingPlanPricingChangeActivated( $logger ),
|
||||
|
|
|
@ -10,8 +10,8 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WC_Data_Exception;
|
||||
use WooCommerce\PayPalCommerce\PayPalSubscriptions\RenewalHandler;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
|
@ -20,7 +20,14 @@ use WP_REST_Response;
|
|||
*/
|
||||
class PaymentSaleCompleted implements RequestHandler {
|
||||
|
||||
use TransactionIdHandlingTrait, RequestHandlerTrait;
|
||||
use RequestHandlerTrait;
|
||||
|
||||
/**
|
||||
* Renewal handler.
|
||||
*
|
||||
* @var RenewalHandler
|
||||
*/
|
||||
private $renewal_handler;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
|
@ -33,9 +40,11 @@ class PaymentSaleCompleted implements RequestHandler {
|
|||
* PaymentSaleCompleted constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param RenewalHandler $renewal_handler Renewal handler.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
public function __construct( LoggerInterface $logger, RenewalHandler $renewal_handler ) {
|
||||
$this->logger = $logger;
|
||||
$this->renewal_handler = $renewal_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +77,7 @@ class PaymentSaleCompleted implements RequestHandler {
|
|||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return $this->failure_response();
|
||||
return $this->failure_response( 'Could not retrieve resource.' );
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wcs_get_subscriptions' ) ) {
|
||||
|
@ -85,7 +94,7 @@ class PaymentSaleCompleted implements RequestHandler {
|
|||
return $this->failure_response( 'Could not retrieve transaction id for subscription.' );
|
||||
}
|
||||
|
||||
$args = array(
|
||||
$args = array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_query' => array(
|
||||
array(
|
||||
|
@ -95,24 +104,13 @@ class PaymentSaleCompleted implements RequestHandler {
|
|||
),
|
||||
),
|
||||
);
|
||||
$subscriptions = wcs_get_subscriptions( $args );
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$is_renewal = $subscription->get_meta( '_ppcp_is_subscription_renewal' ) ?? '';
|
||||
if ( $is_renewal ) {
|
||||
$renewal_order = wcs_create_renewal_order( $subscription );
|
||||
if ( is_a( $renewal_order, WC_Order::class ) ) {
|
||||
$renewal_order->set_payment_method( $subscription->get_payment_method() );
|
||||
$renewal_order->payment_complete();
|
||||
$this->update_transaction_id( $transaction_id, $renewal_order, $this->logger );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$parent_order = wc_get_order( $subscription->get_parent() );
|
||||
if ( is_a( $parent_order, WC_Order::class ) ) {
|
||||
$subscription->update_meta_data( '_ppcp_is_subscription_renewal', 'true' );
|
||||
$subscription->save_meta_data();
|
||||
$this->update_transaction_id( $transaction_id, $parent_order, $this->logger );
|
||||
$subscriptions = wcs_get_subscriptions( $args );
|
||||
if ( $subscriptions ) {
|
||||
try {
|
||||
$this->renewal_handler->process( $subscriptions, $transaction_id );
|
||||
} catch ( WC_Data_Exception $exception ) {
|
||||
return $this->failure_response( 'Could not update payment method.' );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install",
|
||||
"install:modules:ppcp-card-fields": "cd modules/ppcp-card-fields && yarn install",
|
||||
"install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install",
|
||||
"install:modules:ppcp-settings": "cd modules/ppcp-settings && yarn install",
|
||||
"install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install",
|
||||
"build:modules:ppcp-admin-notices": "cd modules/ppcp-admin-notices && yarn run build",
|
||||
"build:modules:ppcp-applepay": "cd modules/ppcp-applepay && yarn run build",
|
||||
|
@ -47,6 +48,7 @@
|
|||
"build:modules:ppcp-card-fields": "cd modules/ppcp-card-fields && yarn run build",
|
||||
"build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build",
|
||||
"build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build",
|
||||
"build:modules:ppcp-settings": "cd modules/ppcp-settings && yarn run build",
|
||||
"build:modules": "run-p build:modules:*",
|
||||
"watch:modules:ppcp-admin-notices": "cd modules/ppcp-admin-notices && yarn run watch",
|
||||
"watch:modules:ppcp-applepay": "cd modules/ppcp-applepay && yarn run watch",
|
||||
|
@ -68,6 +70,7 @@
|
|||
"watch:modules:ppcp-card-fields": "cd modules/ppcp-card-fields && yarn run watch",
|
||||
"watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch",
|
||||
"watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch",
|
||||
"watch:modules:ppcp-settings": "cd modules/ppcp-settings && yarn run watch",
|
||||
"watch:modules": "run-p watch:modules:*",
|
||||
"ddev:setup": "ddev start && ddev orchestrate",
|
||||
"ddev:start": "ddev start",
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Mockery;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Requests_Utility_CaseInsensitiveDictionary;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Token;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource;
|
||||
use function Brain\Monkey\Functions\expect;
|
||||
use function Brain\Monkey\Functions\when;
|
||||
|
||||
class PayUponInvoiceOrderEndpointTest extends TestCase
|
||||
{
|
||||
private $bearer;
|
||||
private $orderFactory;
|
||||
private $fraudnet;
|
||||
private $logger;
|
||||
private $testee;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->bearer = Mockery::mock(Bearer::class);
|
||||
$token = Mockery::mock(Token::class);
|
||||
$token->shouldReceive('token')->andReturn('');
|
||||
$this->bearer->shouldReceive('bearer')->andReturn($token);
|
||||
|
||||
$this->orderFactory = Mockery::mock(OrderFactory::class);
|
||||
$this->fraudnet = Mockery::mock(FraudNet::class);
|
||||
$this->logger = Mockery::mock(LoggerInterface::class);
|
||||
|
||||
$this->testee = new PayUponInvoiceOrderEndpoint(
|
||||
'',
|
||||
$this->bearer,
|
||||
$this->orderFactory,
|
||||
$this->fraudnet,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateOrder()
|
||||
{
|
||||
$this->markTestSkipped('must be revisited.');
|
||||
list($items, $paymentSource, $headers) = $this->setStubs();
|
||||
|
||||
$response = [
|
||||
'body' => '{"is_correct":true}',
|
||||
'headers' => $headers,
|
||||
];
|
||||
expect('wp_remote_get')->andReturn($response);
|
||||
expect('wp_remote_retrieve_response_code')->with($response)->andReturn(200);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
|
||||
$wc_order = Mockery::mock(WC_Order::class);
|
||||
|
||||
|
||||
$result = $this->testee->create($items, $paymentSource, $wc_order );
|
||||
$this->assertInstanceOf(Order::class, $result);
|
||||
}
|
||||
|
||||
public function testCreateOrderWpError()
|
||||
{
|
||||
$this->markTestSkipped('must be revisited.');
|
||||
list($items, $paymentSource) = $this->setStubsForError();
|
||||
|
||||
$wpError = Mockery::mock(\WP_Error::class);
|
||||
$wpError->shouldReceive('get_error_messages')->andReturn(['foo']);
|
||||
$wpError->shouldReceive('get_error_message')->andReturn('foo');
|
||||
expect('wp_remote_get')->andReturn($wpError);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
$wc_order = Mockery::mock(WC_Order::class);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->testee->create($items, $paymentSource, $wc_order);
|
||||
}
|
||||
|
||||
public function testCreateOrderApiError()
|
||||
{
|
||||
$this->markTestSkipped('must be revisited.');
|
||||
list($items, $paymentSource) = $this->setStubsForError();
|
||||
|
||||
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
|
||||
$headers->shouldReceive('getAll');
|
||||
$response = [
|
||||
'body' => '{"is_correct":true}',
|
||||
'headers' => $headers,
|
||||
];
|
||||
|
||||
when('get_bloginfo')->justReturn('de-DE');
|
||||
expect('wp_remote_get')->andReturn($response);
|
||||
expect('wp_remote_retrieve_response_code')->with($response)->andReturn(500);
|
||||
|
||||
$this->logger->shouldReceive('debug');
|
||||
|
||||
$wc_order = Mockery::mock(WC_Order::class);
|
||||
$this->expectException(PayPalApiException::class);
|
||||
$this->testee->create($items, $paymentSource, $wc_order);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function setStubs(): array
|
||||
{
|
||||
$order = Mockery::mock(Order::class);
|
||||
$this->orderFactory
|
||||
->expects('from_paypal_response')
|
||||
->andReturnUsing(function (\stdClass $object) use ($order): ?Order {
|
||||
return ($object->is_correct) ? $order : null;
|
||||
});
|
||||
|
||||
$this->fraudnet->shouldReceive('session_id')->andReturn('');
|
||||
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit->shouldReceive('to_array')->andReturn([
|
||||
'items' => [],
|
||||
]);
|
||||
$items = [$purchaseUnit];
|
||||
|
||||
$paymentSource = Mockery::mock(PaymentSource::class);
|
||||
$paymentSource->shouldReceive('to_array')->andReturn([]);
|
||||
|
||||
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
|
||||
$headers->shouldReceive('getAll');
|
||||
return array($items, $paymentSource, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function setStubsForError(): array
|
||||
{
|
||||
$this->fraudnet->shouldReceive('session_id')->andReturn('');
|
||||
$purchaseUnit = Mockery::mock(PurchaseUnit::class);
|
||||
$purchaseUnit->shouldReceive('to_array')->andReturn([
|
||||
'items' => [],
|
||||
]);
|
||||
$items = [$purchaseUnit];
|
||||
$paymentSource = Mockery::mock(PaymentSource::class);
|
||||
$paymentSource->shouldReceive('to_array')->andReturn([]);
|
||||
return array($items, $paymentSource);
|
||||
}
|
||||
}
|
|
@ -123,7 +123,8 @@ class WcGatewayTest extends TestCase
|
|||
'Pay via PayPal',
|
||||
$this->paymentTokensEndpoint,
|
||||
$this->vaultV3Enabled,
|
||||
$this->wcPaymentTokens
|
||||
$this->wcPaymentTokens,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
|
107
tests/e2e/PHPUnit/PayPalSubscriptionsRenewalTest.php
Normal file
107
tests/e2e/PHPUnit/PayPalSubscriptionsRenewalTest.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Tests\E2e;
|
||||
|
||||
use WooCommerce\PayPalCommerce\PayPalSubscriptions\RenewalHandler;
|
||||
|
||||
class PayPalSubscriptionsRenewalTest extends TestCase
|
||||
{
|
||||
public function test_parent_order()
|
||||
{
|
||||
$c = $this->getContainer();
|
||||
$handler = new RenewalHandler($c->get('woocommerce.logger.woocommerce'));
|
||||
|
||||
// Simulates receiving webhook 1 minute after subscription start.
|
||||
$subscription = $this->createSubscription('-1 minute');
|
||||
|
||||
$handler->process([$subscription], 'TRANSACTION-ID');
|
||||
$renewal = $subscription->get_related_orders( 'ids', array( 'renewal' ) );
|
||||
$this->assertEquals(count($renewal), 0);
|
||||
}
|
||||
|
||||
public function test_renewal_order()
|
||||
{
|
||||
$c = $this->getContainer();
|
||||
$handler = new RenewalHandler($c->get('woocommerce.logger.woocommerce'));
|
||||
|
||||
// Simulates receiving webhook 9 hours after subscription start.
|
||||
$subscription = $this->createSubscription('-9 hour');
|
||||
|
||||
$handler->process([$subscription], 'TRANSACTION-ID');
|
||||
$renewal = $subscription->get_related_orders( 'ids', array( 'renewal' ) );
|
||||
$this->assertEquals(count($renewal), 1);
|
||||
}
|
||||
|
||||
private function createSubscription(string $startDate)
|
||||
{
|
||||
$args = [
|
||||
'method' => 'POST',
|
||||
'headers' => [
|
||||
'Authorization' => 'Basic ' . base64_encode( 'admin:admin' ),
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => wp_json_encode([
|
||||
'customer_id' => 1,
|
||||
'set_paid' => true,
|
||||
'payment_method' => 'ppcp-gateway',
|
||||
'billing' => [
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'address_1' => '969 Market',
|
||||
'address_2' => '',
|
||||
'city' => 'San Francisco',
|
||||
'state' => 'CA',
|
||||
'postcode' => '94103',
|
||||
'country' => 'US',
|
||||
'email' => 'john.doe@example.com',
|
||||
'phone' => '(555) 555-5555'
|
||||
],
|
||||
'line_items' => [
|
||||
[
|
||||
'product_id' => 156,
|
||||
'quantity' => 1
|
||||
]
|
||||
],
|
||||
]),
|
||||
];
|
||||
|
||||
$response = wp_remote_request(
|
||||
'https://woocommerce-paypal-payments.ddev.site/wp-json/wc/v3/orders',
|
||||
$args
|
||||
);
|
||||
|
||||
$body = json_decode( $response['body'] );
|
||||
|
||||
$args = [
|
||||
'method' => 'POST',
|
||||
'headers' => [
|
||||
'Authorization' => 'Basic ' . base64_encode( 'admin:admin' ),
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => wp_json_encode([
|
||||
'start_date' => gmdate( 'Y-m-d H:i:s', strtotime($startDate) ),
|
||||
'parent_id' => $body->id,
|
||||
'customer_id' => 1,
|
||||
'status' => 'active',
|
||||
'billing_period' => 'day',
|
||||
'billing_interval' => 1,
|
||||
'payment_method' => 'ppcp-gateway',
|
||||
'line_items' => [
|
||||
[
|
||||
'product_id' => $_ENV['PAYPAL_SUBSCRIPTIONS_PRODUCT_ID'],
|
||||
'quantity' => 1
|
||||
]
|
||||
],
|
||||
]),
|
||||
];
|
||||
|
||||
$response = wp_remote_request(
|
||||
'https://woocommerce-paypal-payments.ddev.site/wp-json/wc/v3/subscriptions?per_page=1',
|
||||
$args
|
||||
);
|
||||
|
||||
$body = json_decode( $response['body'] );
|
||||
|
||||
return wcs_get_subscription($body->id);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue