mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 09:08:09 +08:00
Merge branch 'trunk' into PCP-1544-pay-order-currency
This commit is contained in:
commit
d2a5ecf3b8
125 changed files with 9483 additions and 789 deletions
|
@ -9,6 +9,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlanFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingOptionFactory;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
|
@ -209,6 +217,30 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.catalog-products' => static function ( ContainerInterface $container ): CatalogProducts {
|
||||
return new CatalogProducts(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.product' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.billing-plans' => static function( ContainerInterface $container ): BillingPlans {
|
||||
return new BillingPlans(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.billing-cycle' ),
|
||||
$container->get( 'api.factory.plan' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.billing-subscriptions' => static function( ContainerInterface $container ): BillingSubscriptions {
|
||||
return new BillingSubscriptions(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
@ -289,12 +321,19 @@ return array(
|
|||
);
|
||||
},
|
||||
'api.factory.shipping' => static function ( ContainerInterface $container ): ShippingFactory {
|
||||
$address_factory = $container->get( 'api.factory.address' );
|
||||
return new ShippingFactory( $address_factory );
|
||||
return new ShippingFactory(
|
||||
$container->get( 'api.factory.address' ),
|
||||
$container->get( 'api.factory.shipping-option' )
|
||||
);
|
||||
},
|
||||
'api.factory.shipping-preference' => static function ( ContainerInterface $container ): ShippingPreferenceFactory {
|
||||
return new ShippingPreferenceFactory();
|
||||
},
|
||||
'api.factory.shipping-option' => static function ( ContainerInterface $container ): ShippingOptionFactory {
|
||||
return new ShippingOptionFactory(
|
||||
$container->get( 'api.factory.money' )
|
||||
);
|
||||
},
|
||||
'api.factory.amount' => static function ( ContainerInterface $container ): AmountFactory {
|
||||
$item_factory = $container->get( 'api.factory.item' );
|
||||
return new AmountFactory(
|
||||
|
@ -357,6 +396,21 @@ return array(
|
|||
'api.factory.fraud-processor-response' => static function ( ContainerInterface $container ): FraudProcessorResponseFactory {
|
||||
return new FraudProcessorResponseFactory();
|
||||
},
|
||||
'api.factory.product' => static function( ContainerInterface $container ): ProductFactory {
|
||||
return new ProductFactory();
|
||||
},
|
||||
'api.factory.billing-cycle' => static function( ContainerInterface $container ): BillingCycleFactory {
|
||||
return new BillingCycleFactory( $container->get( 'api.shop.currency' ) );
|
||||
},
|
||||
'api.factory.payment-preferences' => static function( ContainerInterface $container ):PaymentPreferencesFactory {
|
||||
return new PaymentPreferencesFactory( $container->get( 'api.shop.currency' ) );
|
||||
},
|
||||
'api.factory.plan' => static function( ContainerInterface $container ): PlanFactory {
|
||||
return new PlanFactory(
|
||||
$container->get( 'api.factory.billing-cycle' ),
|
||||
$container->get( 'api.factory.payment-preferences' )
|
||||
);
|
||||
},
|
||||
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
||||
return new DccApplies(
|
||||
$container->get( 'api.dcc-supported-country-currency-matrix' ),
|
||||
|
@ -631,6 +685,27 @@ return array(
|
|||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'MX' => array(
|
||||
'MXN',
|
||||
),
|
||||
'JP' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -687,6 +762,17 @@ return array(
|
|||
'amex' => array( 'CAD' ),
|
||||
'jcb' => array( 'CAD' ),
|
||||
),
|
||||
'MX' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array(),
|
||||
),
|
||||
'JP' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'JPY' ),
|
||||
'jcb' => array( 'JPY' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
|
@ -120,6 +121,10 @@ class BillingAgreementsEndpoint {
|
|||
*/
|
||||
public function reference_transaction_enabled(): bool {
|
||||
try {
|
||||
if ( get_transient( 'ppcp_reference_transaction_enabled' ) === true ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->is_request_logging_enabled = false;
|
||||
|
||||
try {
|
||||
|
@ -130,10 +135,12 @@ class BillingAgreementsEndpoint {
|
|||
);
|
||||
} finally {
|
||||
$this->is_request_logging_enabled = true;
|
||||
set_transient( 'ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS );
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
} catch ( Exception $exception ) {
|
||||
delete_transient( 'ppcp_reference_transaction_enabled' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
229
modules/ppcp-api-client/src/Endpoint/BillingPlans.php
Normal file
229
modules/ppcp-api-client/src/Endpoint/BillingPlans.php
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
/**
|
||||
* The Billing Plans endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\BillingCycle;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Plan;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlanFactory;
|
||||
|
||||
/**
|
||||
* Class BillingPlans
|
||||
*/
|
||||
class BillingPlans {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* Billing cycle factory
|
||||
*
|
||||
* @var BillingCycleFactory
|
||||
*/
|
||||
private $billing_cycle_factory;
|
||||
|
||||
/**
|
||||
* Plan factory
|
||||
*
|
||||
* @var PlanFactory
|
||||
*/
|
||||
private $plan_factory;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingPlans constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
|
||||
* @param PlanFactory $plan_factory Plan factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
BillingCycleFactory $billing_cycle_factory,
|
||||
PlanFactory $plan_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->billing_cycle_factory = $billing_cycle_factory;
|
||||
$this->plan_factory = $plan_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subscription plan.
|
||||
*
|
||||
* @param string $name Product name.
|
||||
* @param string $product_id Product ID.
|
||||
* @param array $billing_cycles Billing cycles.
|
||||
* @param array $payment_preferences Payment preferences.
|
||||
*
|
||||
* @return Plan
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function create(
|
||||
string $name,
|
||||
string $product_id,
|
||||
array $billing_cycles,
|
||||
array $payment_preferences
|
||||
): Plan {
|
||||
|
||||
$data = array(
|
||||
'name' => $name,
|
||||
'product_id' => $product_id,
|
||||
'billing_cycles' => $billing_cycles,
|
||||
'payment_preferences' => $payment_preferences,
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/plans';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create plan.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $this->plan_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plan,
|
||||
*
|
||||
* @param string $id Plan ID.
|
||||
*
|
||||
* @return Plan
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function plan( string $id ): Plan {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/plans/' . $id;
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to get plan.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $this->plan_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates pricing.
|
||||
*
|
||||
* @param string $id Plan ID.
|
||||
* @param BillingCycle $billing_cycle Billing cycle.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function update_pricing( string $id, BillingCycle $billing_cycle ): void {
|
||||
$data = array(
|
||||
'pricing_schemes' => array(
|
||||
(object) array(
|
||||
'billing_cycle_sequence' => 1,
|
||||
'pricing_scheme' => $billing_cycle->pricing_scheme(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/plans/' . $id . '/update-pricing-schemes';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Could not update pricing.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
217
modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php
Normal file
217
modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php
Normal file
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
/**
|
||||
* The Billing Subscriptions endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class BillingSubscriptions
|
||||
*/
|
||||
class BillingSubscriptions {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingSubscriptions constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( string $host, Bearer $bearer, LoggerInterface $logger ) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends a subscription.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function suspend( string $id ):void {
|
||||
$data = array(
|
||||
'reason' => 'Suspended by customer',
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/suspend';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to suspend subscription.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates a subscription.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function activate( string $id ): void {
|
||||
$data = array(
|
||||
'reason' => 'Reactivated by customer',
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/activate';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to reactivate subscription.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a Subscription.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function cancel( string $id ): void {
|
||||
$data = array(
|
||||
'reason' => 'Cancelled by customer',
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/cancel';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to cancel subscription.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Subscription object from the given ID.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
*
|
||||
* @return stdClass
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function subscription( string $id ): stdClass {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id;
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to get subscription.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
}
|
199
modules/ppcp-api-client/src/Endpoint/CatalogProducts.php
Normal file
199
modules/ppcp-api-client/src/Endpoint/CatalogProducts.php
Normal file
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
/**
|
||||
* The Catalog Products endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
|
||||
|
||||
/**
|
||||
* Class CatalogProduct
|
||||
*/
|
||||
class CatalogProducts {
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Product factory.
|
||||
*
|
||||
* @var ProductFactory
|
||||
*/
|
||||
private $product_factory;
|
||||
|
||||
/**
|
||||
* CatalogProducts constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param ProductFactory $product_factory Product factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
ProductFactory $product_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->product_factory = $product_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a product.
|
||||
*
|
||||
* @param string $name Product name.
|
||||
* @param string $description Product description.
|
||||
*
|
||||
* @return Product
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function create( string $name, string $description ): Product {
|
||||
$data = array(
|
||||
'name' => $name,
|
||||
);
|
||||
|
||||
if ( $description ) {
|
||||
$data['description'] = $description;
|
||||
}
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/catalogs/products';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create product.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $this->product_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Product.
|
||||
*
|
||||
* @param string $id Product ID.
|
||||
* @param array $data Data to update.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function update( string $id, array $data ): void {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id;
|
||||
$args = array(
|
||||
'method' => 'PATCH',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to update product.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Product from the given ID.
|
||||
*
|
||||
* @param string $id Product ID.
|
||||
*
|
||||
* @return Product
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function product( string $id ): Product {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id;
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to get product.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $this->product_factory->from_paypal_response( $json );
|
||||
}
|
||||
}
|
|
@ -103,7 +103,8 @@ class IdentityToken {
|
|||
);
|
||||
if (
|
||||
( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) )
|
||||
&& defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION
|
||||
|| ( $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ) )
|
||||
|| ( $this->settings->has( 'subscriptions_mode' ) && $this->settings->get( 'subscriptions_mode' ) === 'vaulting_api' )
|
||||
) {
|
||||
$customer_id = $this->customer_repository->customer_id_for_user( ( $user_id ) );
|
||||
update_user_meta( $user_id, 'ppcp_customer_id', $customer_id );
|
||||
|
|
|
@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentMethod;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
|
@ -180,6 +181,8 @@ class OrderEndpoint {
|
|||
* @param Payer|null $payer The payer off the order.
|
||||
* @param PaymentToken|null $payment_token The payment token.
|
||||
* @param PaymentMethod|null $payment_method The payment method.
|
||||
* @param string $paypal_request_id The paypal request id.
|
||||
* @param string $user_action The user action.
|
||||
*
|
||||
* @return Order
|
||||
* @throws RuntimeException If the request fails.
|
||||
|
@ -189,19 +192,28 @@ class OrderEndpoint {
|
|||
string $shipping_preference,
|
||||
Payer $payer = null,
|
||||
PaymentToken $payment_token = null,
|
||||
PaymentMethod $payment_method = null
|
||||
PaymentMethod $payment_method = null,
|
||||
string $paypal_request_id = '',
|
||||
string $user_action = ApplicationContext::USER_ACTION_CONTINUE
|
||||
): Order {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$data = array(
|
||||
'intent' => ( $this->subscription_helper->cart_contains_subscription() || $this->subscription_helper->current_product_is_subscription() ) ? 'AUTHORIZE' : $this->intent,
|
||||
'purchase_units' => array_map(
|
||||
static function ( PurchaseUnit $item ): array {
|
||||
return $item->to_array();
|
||||
static function ( PurchaseUnit $item ) use ( $shipping_preference ): array {
|
||||
$data = $item->to_array();
|
||||
|
||||
if ( $shipping_preference !== ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE ) {
|
||||
// Shipping options are not allowed to be sent when not getting the address from PayPal.
|
||||
unset( $data['shipping']['options'] );
|
||||
}
|
||||
|
||||
return $data;
|
||||
},
|
||||
$items
|
||||
),
|
||||
'application_context' => $this->application_context_repository
|
||||
->current_context( $shipping_preference )->to_array(),
|
||||
->current_context( $shipping_preference, $user_action )->to_array(),
|
||||
);
|
||||
if ( $payer && ! empty( $payer->email_address() ) ) {
|
||||
$data['payer'] = $payer->to_array();
|
||||
|
@ -510,13 +522,22 @@ class OrderEndpoint {
|
|||
return $order_to_update;
|
||||
}
|
||||
|
||||
$this->patch( $order_to_update->id(), $patches );
|
||||
|
||||
$new_order = $this->order( $order_to_update->id() );
|
||||
return $new_order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches an order.
|
||||
*
|
||||
* @param string $order_id The PayPal order ID.
|
||||
* @param PatchCollection $patches The patches.
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
*/
|
||||
public function patch( string $order_id, PatchCollection $patches ): void {
|
||||
$patches_array = $patches->to_array();
|
||||
if ( ! isset( $patches_array[0]['value']['shipping'] ) ) {
|
||||
$shipping = isset( $order_to_update->purchase_units()[0] ) && null !== $order_to_update->purchase_units()[0]->shipping() ? $order_to_update->purchase_units()[0]->shipping() : null;
|
||||
if ( $shipping ) {
|
||||
$patches_array[0]['value']['shipping'] = $shipping->to_array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter can be used to modify the order patching request body data (the final prices, items).
|
||||
|
@ -524,7 +545,7 @@ class OrderEndpoint {
|
|||
$patches_array = apply_filters( 'ppcp_patch_order_request_body_data', $patches_array );
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_to_update->id();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_id;
|
||||
$args = array(
|
||||
'method' => 'PATCH',
|
||||
'headers' => array(
|
||||
|
@ -540,11 +561,8 @@ class OrderEndpoint {
|
|||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error = new RuntimeException(
|
||||
__( 'Could not retrieve order.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error = new RuntimeException( 'Could not patch order.' );
|
||||
$this->logger->warning(
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
|
@ -560,8 +578,7 @@ class OrderEndpoint {
|
|||
$json,
|
||||
$status_code
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$this->logger->warning(
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
|
@ -570,9 +587,6 @@ class OrderEndpoint {
|
|||
);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$new_order = $this->order( $order_to_update->id() );
|
||||
return $new_order;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
134
modules/ppcp-api-client/src/Entity/BillingCycle.php
Normal file
134
modules/ppcp-api-client/src/Entity/BillingCycle.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
/**
|
||||
* The Billing Cycle object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class BillingCycle
|
||||
*/
|
||||
class BillingCycle {
|
||||
|
||||
/**
|
||||
* Frequency.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $frequency;
|
||||
|
||||
/**
|
||||
* Sequence.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $sequence;
|
||||
|
||||
/**
|
||||
* Tenure Type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $tenure_type;
|
||||
|
||||
/**
|
||||
* Pricing scheme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $pricing_scheme;
|
||||
|
||||
/**
|
||||
* Total cycles.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $total_cycles;
|
||||
|
||||
/**
|
||||
* BillingCycle constructor.
|
||||
*
|
||||
* @param array $frequency Frequency.
|
||||
* @param int $sequence Sequence.
|
||||
* @param string $tenure_type Tenure type.
|
||||
* @param array $pricing_scheme Pricing scheme.
|
||||
* @param int $total_cycles Total cycles.
|
||||
*/
|
||||
public function __construct(
|
||||
array $frequency,
|
||||
int $sequence,
|
||||
string $tenure_type,
|
||||
array $pricing_scheme = array(),
|
||||
int $total_cycles = 1
|
||||
) {
|
||||
$this->frequency = $frequency;
|
||||
$this->sequence = $sequence;
|
||||
$this->tenure_type = $tenure_type;
|
||||
$this->pricing_scheme = $pricing_scheme;
|
||||
$this->total_cycles = $total_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns frequency.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frequency(): array {
|
||||
return $this->frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sequence.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function sequence(): int {
|
||||
return $this->sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tenure type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function tenure_type(): string {
|
||||
return $this->tenure_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pricing scheme.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pricing_scheme(): array {
|
||||
return $this->pricing_scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return total cycles.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function total_cycles(): int {
|
||||
return $this->total_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Billing Cycle as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'frequency' => $this->frequency(),
|
||||
'sequence' => $this->sequence(),
|
||||
'tenure_type' => $this->tenure_type(),
|
||||
'pricing_scheme' => $this->pricing_scheme(),
|
||||
'total_cycles' => $this->total_cycles(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -83,8 +83,8 @@ class Patch {
|
|||
public function to_array(): array {
|
||||
return array(
|
||||
'op' => $this->op(),
|
||||
'value' => $this->value(),
|
||||
'path' => $this->path(),
|
||||
'value' => $this->value(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
115
modules/ppcp-api-client/src/Entity/PaymentPreferences.php
Normal file
115
modules/ppcp-api-client/src/Entity/PaymentPreferences.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/**
|
||||
* The Payment Preferences object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class PaymentPreferences
|
||||
*/
|
||||
class PaymentPreferences {
|
||||
|
||||
/**
|
||||
* Setup fee.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $setup_fee;
|
||||
|
||||
/**
|
||||
* Auto bill outstanding.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $auto_bill_outstanding;
|
||||
|
||||
/**
|
||||
* Setup fee failure action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $setup_fee_failure_action;
|
||||
|
||||
/**
|
||||
* Payment failure threshold.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $payment_failure_threshold;
|
||||
|
||||
/**
|
||||
* PaymentPreferences constructor.
|
||||
*
|
||||
* @param array $setup_fee Setup fee.
|
||||
* @param bool $auto_bill_outstanding Auto bill outstanding.
|
||||
* @param string $setup_fee_failure_action Setup fee failure action.
|
||||
* @param int $payment_failure_threshold payment failure threshold.
|
||||
*/
|
||||
public function __construct(
|
||||
array $setup_fee,
|
||||
bool $auto_bill_outstanding = true,
|
||||
string $setup_fee_failure_action = 'CONTINUE',
|
||||
int $payment_failure_threshold = 3
|
||||
) {
|
||||
|
||||
$this->setup_fee = $setup_fee;
|
||||
$this->auto_bill_outstanding = $auto_bill_outstanding;
|
||||
$this->setup_fee_failure_action = $setup_fee_failure_action;
|
||||
$this->payment_failure_threshold = $payment_failure_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup fee.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function setup_fee(): array {
|
||||
return $this->setup_fee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto bill outstanding.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function auto_bill_outstanding(): bool {
|
||||
return $this->auto_bill_outstanding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup fee failure action.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function setup_fee_failure_action(): string {
|
||||
return $this->setup_fee_failure_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment failure threshold.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function payment_failure_threshold(): int {
|
||||
return $this->payment_failure_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Payment Preferences as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array():array {
|
||||
return array(
|
||||
'setup_fee' => $this->setup_fee(),
|
||||
'auto_bill_outstanding' => $this->auto_bill_outstanding(),
|
||||
'setup_fee_failure_action' => $this->setup_fee_failure_action(),
|
||||
'payment_failure_threshold' => $this->payment_failure_threshold(),
|
||||
);
|
||||
}
|
||||
}
|
154
modules/ppcp-api-client/src/Entity/Plan.php
Normal file
154
modules/ppcp-api-client/src/Entity/Plan.php
Normal file
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
/**
|
||||
* The Plan object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class Plan
|
||||
*/
|
||||
class Plan {
|
||||
|
||||
/**
|
||||
* Plan ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* Plan name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Product ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $product_id;
|
||||
|
||||
/**
|
||||
* Billing cycles.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $billing_cycles;
|
||||
|
||||
/**
|
||||
* Payment preferences.
|
||||
*
|
||||
* @var PaymentPreferences
|
||||
*/
|
||||
private $payment_preferences;
|
||||
|
||||
/**
|
||||
* Plan status.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* Plan constructor.
|
||||
*
|
||||
* @param string $id Plan ID.
|
||||
* @param string $name Plan name.
|
||||
* @param string $product_id Product ID.
|
||||
* @param array $billing_cycles Billing cycles.
|
||||
* @param PaymentPreferences $payment_preferences Payment preferences.
|
||||
* @param string $status Plan status.
|
||||
*/
|
||||
public function __construct(
|
||||
string $id,
|
||||
string $name,
|
||||
string $product_id,
|
||||
array $billing_cycles,
|
||||
PaymentPreferences $payment_preferences,
|
||||
string $status = ''
|
||||
) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->product_id = $product_id;
|
||||
$this->billing_cycles = $billing_cycles;
|
||||
$this->payment_preferences = $payment_preferences;
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Plan ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Plan name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Product ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function product_id(): string {
|
||||
return $this->product_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Billing cycles.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function billing_cycles(): array {
|
||||
return $this->billing_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Payment preferences.
|
||||
*
|
||||
* @return PaymentPreferences
|
||||
*/
|
||||
public function payment_preferences(): PaymentPreferences {
|
||||
return $this->payment_preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Plan status.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function status(): string {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Plan as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array():array {
|
||||
return array(
|
||||
'id' => $this->id(),
|
||||
'name' => $this->name(),
|
||||
'product_id' => $this->product_id(),
|
||||
'billing_cycles' => $this->billing_cycles(),
|
||||
'payment_preferences' => $this->payment_preferences(),
|
||||
'status' => $this->status(),
|
||||
);
|
||||
}
|
||||
}
|
90
modules/ppcp-api-client/src/Entity/Product.php
Normal file
90
modules/ppcp-api-client/src/Entity/Product.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* The Product object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class Product
|
||||
*/
|
||||
class Product {
|
||||
|
||||
/**
|
||||
* Product ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* Product name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Product description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* Product constructor.
|
||||
*
|
||||
* @param string $id Product ID.
|
||||
* @param string $name Product name.
|
||||
* @param string $description Product description.
|
||||
*/
|
||||
public function __construct( string $id, string $name, string $description = '' ) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array() {
|
||||
return array(
|
||||
'id' => $this->id(),
|
||||
'name' => $this->name(),
|
||||
'description' => $this->description(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -28,15 +28,24 @@ class Shipping {
|
|||
*/
|
||||
private $address;
|
||||
|
||||
/**
|
||||
* Shipping methods.
|
||||
*
|
||||
* @var ShippingOption[]
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Shipping constructor.
|
||||
*
|
||||
* @param string $name The name.
|
||||
* @param Address $address The address.
|
||||
* @param string $name The name.
|
||||
* @param Address $address The address.
|
||||
* @param ShippingOption[] $options Shipping methods.
|
||||
*/
|
||||
public function __construct( string $name, Address $address ) {
|
||||
public function __construct( string $name, Address $address, array $options = array() ) {
|
||||
$this->name = $name;
|
||||
$this->address = $address;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,17 +66,35 @@ class Shipping {
|
|||
return $this->address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shipping methods.
|
||||
*
|
||||
* @return ShippingOption[]
|
||||
*/
|
||||
public function options(): array {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
$result = array(
|
||||
'name' => array(
|
||||
'full_name' => $this->name(),
|
||||
),
|
||||
'address' => $this->address()->to_array(),
|
||||
);
|
||||
if ( $this->options ) {
|
||||
$result['options'] = array_map(
|
||||
function ( ShippingOption $opt ): array {
|
||||
return $opt->to_array();
|
||||
},
|
||||
$this->options
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
139
modules/ppcp-api-client/src/Entity/ShippingOption.php
Normal file
139
modules/ppcp-api-client/src/Entity/ShippingOption.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
/**
|
||||
* The ShippingOption object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class ShippingOption
|
||||
*/
|
||||
class ShippingOption {
|
||||
const TYPE_SHIPPING = 'SHIPPING';
|
||||
const TYPE_PICKUP = 'PICKUP';
|
||||
|
||||
/**
|
||||
* The name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* The label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $label;
|
||||
|
||||
/**
|
||||
* Whether the method is selected by default.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $selected;
|
||||
|
||||
/**
|
||||
* The price.
|
||||
*
|
||||
* @var Money
|
||||
*/
|
||||
private $amount;
|
||||
|
||||
/**
|
||||
* SHIPPING or PICKUP.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* ShippingOption constructor.
|
||||
*
|
||||
* @param string $id The name.
|
||||
* @param string $label The label.
|
||||
* @param bool $selected Whether the method is selected by default.
|
||||
* @param Money $amount The price.
|
||||
* @param string $type SHIPPING or PICKUP.
|
||||
*/
|
||||
public function __construct( string $id, string $label, bool $selected, Money $amount, string $type ) {
|
||||
$this->id = $id;
|
||||
$this->label = $label;
|
||||
$this->selected = $selected;
|
||||
$this->amount = $amount;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the method is selected by default.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function selected(): bool {
|
||||
return $this->selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the method is selected by default.
|
||||
*
|
||||
* @param bool $selected The value to be set.
|
||||
*/
|
||||
public function set_selected( bool $selected ): void {
|
||||
$this->selected = $selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* The price.
|
||||
*
|
||||
* @return Money
|
||||
*/
|
||||
public function amount(): Money {
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* SHIPPING or PICKUP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'label' => $this->label,
|
||||
'selected' => $this->selected,
|
||||
'amount' => $this->amount->to_array(),
|
||||
'type' => $this->type,
|
||||
);
|
||||
}
|
||||
}
|
84
modules/ppcp-api-client/src/Factory/BillingCycleFactory.php
Normal file
84
modules/ppcp-api-client/src/Factory/BillingCycleFactory.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/**
|
||||
* The Billing Cycle factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\BillingCycle;
|
||||
|
||||
/**
|
||||
* Class BillingCycleFactory
|
||||
*/
|
||||
class BillingCycleFactory {
|
||||
|
||||
/**
|
||||
* The currency.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* BillingCycleFactory constructor.
|
||||
*
|
||||
* @param string $currency The currency.
|
||||
*/
|
||||
public function __construct( string $currency ) {
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BillingCycle object from the given WC product.
|
||||
*
|
||||
* @param WC_Product $product WC product.
|
||||
* @return BillingCycle
|
||||
*/
|
||||
public function from_wc_product( WC_Product $product ): BillingCycle {
|
||||
return new BillingCycle(
|
||||
array(
|
||||
'interval_unit' => $product->get_meta( '_subscription_period' ),
|
||||
'interval_count' => $product->get_meta( '_subscription_period_interval' ),
|
||||
),
|
||||
1,
|
||||
'REGULAR',
|
||||
array(
|
||||
'fixed_price' => array(
|
||||
'value' => $product->get_meta( '_subscription_price' ),
|
||||
'currency_code' => $this->currency,
|
||||
),
|
||||
),
|
||||
(int) $product->get_meta( '_subscription_length' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BillingCycle object based off a PayPal response.
|
||||
*
|
||||
* @param stdClass $data the data.
|
||||
* @return BillingCycle
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): BillingCycle {
|
||||
return new BillingCycle(
|
||||
array(
|
||||
'interval_unit' => $data->frequency->interval_unit,
|
||||
'interval_count' => $data->frequency->interval_count,
|
||||
),
|
||||
$data->sequence,
|
||||
$data->tenure_type,
|
||||
array(
|
||||
'fixed_price' => array(
|
||||
'value' => $data->pricing_scheme->fixed_price->value,
|
||||
'currency_code' => $data->pricing_scheme->fixed_price->currency_code,
|
||||
),
|
||||
),
|
||||
$data->total_cycles
|
||||
);
|
||||
}
|
||||
}
|
|
@ -71,7 +71,15 @@ class PatchCollectionFactory {
|
|||
);
|
||||
$operation = $purchase_unit_from ? 'replace' : 'add';
|
||||
$value = $purchase_unit_to->to_array();
|
||||
$patches[] = new Patch(
|
||||
|
||||
if ( ! isset( $value['shipping'] ) ) {
|
||||
$shipping = $purchase_unit_from && null !== $purchase_unit_from->shipping() ? $purchase_unit_from->shipping() : null;
|
||||
if ( $shipping ) {
|
||||
$value['shipping'] = $shipping->to_array();
|
||||
}
|
||||
}
|
||||
|
||||
$patches[] = new Patch(
|
||||
$operation,
|
||||
$path . "/@reference_id=='" . $purchase_unit_to->reference_id() . "'",
|
||||
$value
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* The Payment Preferences factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentPreferences;
|
||||
|
||||
/**
|
||||
* Class PaymentPreferencesFactory
|
||||
*/
|
||||
class PaymentPreferencesFactory {
|
||||
|
||||
/**
|
||||
* The currency.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* PaymentPreferencesFactory constructor.
|
||||
*
|
||||
* @param string $currency The currency.
|
||||
*/
|
||||
public function __construct( string $currency ) {
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PaymentPreferences object from the given WC product.
|
||||
*
|
||||
* @param WC_Product $product WC product.
|
||||
* @return PaymentPreferences
|
||||
*/
|
||||
public function from_wc_product( WC_Product $product ):PaymentPreferences {
|
||||
return new PaymentPreferences(
|
||||
array(
|
||||
'value' => $product->get_meta( '_subscription_sign_up_fee' ) ?: '0',
|
||||
'currency_code' => $this->currency,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PaymentPreferences object based off a PayPal response.
|
||||
*
|
||||
* @param stdClass $data The data.
|
||||
* @return PaymentPreferences
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ) {
|
||||
return new PaymentPreferences(
|
||||
array(
|
||||
'value' => $data->setup_fee->value,
|
||||
'currency_code' => $data->setup_fee->currency_code,
|
||||
),
|
||||
$data->auto_bill_outstanding,
|
||||
$data->setup_fee_failure_action,
|
||||
$data->payment_failure_threshold
|
||||
);
|
||||
}
|
||||
}
|
96
modules/ppcp-api-client/src/Factory/PlanFactory.php
Normal file
96
modules/ppcp-api-client/src/Factory/PlanFactory.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* Plan Factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Plan;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PlanFactory
|
||||
*/
|
||||
class PlanFactory {
|
||||
|
||||
/**
|
||||
* Billing cycle factory.
|
||||
*
|
||||
* @var BillingCycleFactory
|
||||
*/
|
||||
private $billing_cycle_factory;
|
||||
|
||||
/**
|
||||
* Payment preferences factory.
|
||||
*
|
||||
* @var PaymentPreferencesFactory
|
||||
*/
|
||||
private $payment_preferences_factory;
|
||||
|
||||
/**
|
||||
* PlanFactory constructor.
|
||||
*
|
||||
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
|
||||
* @param PaymentPreferencesFactory $payment_preferences_factory Payment preferences factory.
|
||||
*/
|
||||
public function __construct(
|
||||
BillingCycleFactory $billing_cycle_factory,
|
||||
PaymentPreferencesFactory $payment_preferences_factory
|
||||
) {
|
||||
$this->billing_cycle_factory = $billing_cycle_factory;
|
||||
$this->payment_preferences_factory = $payment_preferences_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Plan from PayPal response.
|
||||
*
|
||||
* @param stdClass $data The data.
|
||||
*
|
||||
* @return Plan
|
||||
*
|
||||
* @throws RuntimeException If it could not create Plan.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): Plan {
|
||||
if ( ! isset( $data->id ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No id for given plan', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
if ( ! isset( $data->name ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No name for plan given', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
if ( ! isset( $data->product_id ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No product id for given plan', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
if ( ! isset( $data->billing_cycles ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No billing cycles for given plan', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
$billing_cycles = array();
|
||||
foreach ( $data->billing_cycles as $billing_cycle ) {
|
||||
$billing_cycles[] = $this->billing_cycle_factory->from_paypal_response( $billing_cycle );
|
||||
}
|
||||
|
||||
$payment_preferences = $this->payment_preferences_factory->from_paypal_response( $data->payment_preferences );
|
||||
|
||||
return new Plan(
|
||||
$data->id,
|
||||
$data->name,
|
||||
$data->product_id,
|
||||
$billing_cycles,
|
||||
$payment_preferences,
|
||||
$data->status ?? ''
|
||||
);
|
||||
}
|
||||
}
|
47
modules/ppcp-api-client/src/Factory/ProductFactory.php
Normal file
47
modules/ppcp-api-client/src/Factory/ProductFactory.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* The Product factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class ProductFactory
|
||||
*/
|
||||
class ProductFactory {
|
||||
|
||||
/**
|
||||
* Creates a Product based off a PayPal response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return Product
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): Product {
|
||||
if ( ! isset( $data->id ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No id for product given', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
if ( ! isset( $data->name ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No name for product given', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
return new Product(
|
||||
$data->id,
|
||||
$data->name,
|
||||
$data->description ?? ''
|
||||
);
|
||||
}
|
||||
}
|
|
@ -153,10 +153,11 @@ class PurchaseUnitFactory {
|
|||
* Creates a PurchaseUnit based off a WooCommerce cart.
|
||||
*
|
||||
* @param \WC_Cart|null $cart The cart.
|
||||
* @param bool $with_shipping_options Include WC shipping methods.
|
||||
*
|
||||
* @return PurchaseUnit
|
||||
*/
|
||||
public function from_wc_cart( ?\WC_Cart $cart = null ): PurchaseUnit {
|
||||
public function from_wc_cart( ?\WC_Cart $cart = null, bool $with_shipping_options = false ): PurchaseUnit {
|
||||
if ( ! $cart ) {
|
||||
$cart = WC()->cart ?? new \WC_Cart();
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ class PurchaseUnitFactory {
|
|||
$shipping = null;
|
||||
$customer = \WC()->customer;
|
||||
if ( $this->shipping_needed( ... array_values( $items ) ) && is_a( $customer, \WC_Customer::class ) ) {
|
||||
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer );
|
||||
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer, $with_shipping_options );
|
||||
if (
|
||||
2 !== strlen( $shipping->address()->country_code() ) ||
|
||||
( ! $shipping->address()->postal_code() && ! $this->country_without_postal_code( $shipping->address()->country_code() ) )
|
||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
|
@ -24,23 +25,33 @@ class ShippingFactory {
|
|||
*/
|
||||
private $address_factory;
|
||||
|
||||
/**
|
||||
* The shipping option factory.
|
||||
*
|
||||
* @var ShippingOptionFactory
|
||||
*/
|
||||
private $shipping_option_factory;
|
||||
|
||||
/**
|
||||
* ShippingFactory constructor.
|
||||
*
|
||||
* @param AddressFactory $address_factory The address factory.
|
||||
* @param AddressFactory $address_factory The address factory.
|
||||
* @param ShippingOptionFactory $shipping_option_factory The shipping option factory.
|
||||
*/
|
||||
public function __construct( AddressFactory $address_factory ) {
|
||||
$this->address_factory = $address_factory;
|
||||
public function __construct( AddressFactory $address_factory, ShippingOptionFactory $shipping_option_factory ) {
|
||||
$this->address_factory = $address_factory;
|
||||
$this->shipping_option_factory = $shipping_option_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shipping object based off a WooCommerce customer.
|
||||
*
|
||||
* @param \WC_Customer $customer The WooCommerce customer.
|
||||
* @param bool $with_shipping_options Include WC shipping methods.
|
||||
*
|
||||
* @return Shipping
|
||||
*/
|
||||
public function from_wc_customer( \WC_Customer $customer ): Shipping {
|
||||
public function from_wc_customer( \WC_Customer $customer, bool $with_shipping_options = false ): Shipping {
|
||||
// Replicates the Behavior of \WC_Order::get_formatted_shipping_full_name().
|
||||
$full_name = sprintf(
|
||||
// translators: %1$s is the first name and %2$s is the second name. wc translation.
|
||||
|
@ -51,7 +62,8 @@ class ShippingFactory {
|
|||
$address = $this->address_factory->from_wc_customer( $customer );
|
||||
return new Shipping(
|
||||
$full_name,
|
||||
$address
|
||||
$address,
|
||||
$with_shipping_options ? $this->shipping_option_factory->from_wc_cart() : array()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -91,9 +103,14 @@ class ShippingFactory {
|
|||
);
|
||||
}
|
||||
$address = $this->address_factory->from_paypal_response( $data->address );
|
||||
$options = array_map(
|
||||
array( $this->shipping_option_factory, 'from_paypal_response' ),
|
||||
$data->options ?? array()
|
||||
);
|
||||
return new Shipping(
|
||||
$data->name->full_name,
|
||||
$address
|
||||
$address,
|
||||
$options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
111
modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php
Normal file
111
modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/**
|
||||
* The shipping options factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WC_Cart;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class ShippingOptionFactory
|
||||
*/
|
||||
class ShippingOptionFactory {
|
||||
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @var MoneyFactory
|
||||
*/
|
||||
private $money_factory;
|
||||
|
||||
/**
|
||||
* ShippingOptionFactory constructor.
|
||||
*
|
||||
* @param MoneyFactory $money_factory The Money factory.
|
||||
*/
|
||||
public function __construct( MoneyFactory $money_factory ) {
|
||||
$this->money_factory = $money_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of ShippingOption objects for the shipping methods available in the cart.
|
||||
*
|
||||
* @param WC_Cart|null $cart The cart.
|
||||
* @return ShippingOption[]
|
||||
*/
|
||||
public function from_wc_cart( ?WC_Cart $cart = null ): array {
|
||||
if ( ! $cart ) {
|
||||
$cart = WC()->cart ?? new WC_Cart();
|
||||
}
|
||||
|
||||
$cart->calculate_shipping();
|
||||
|
||||
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', array() );
|
||||
if ( ! is_array( $chosen_shipping_methods ) ) {
|
||||
$chosen_shipping_methods = array();
|
||||
}
|
||||
|
||||
$packages = WC()->shipping()->get_packages();
|
||||
$options = array();
|
||||
foreach ( $packages as $package ) {
|
||||
$rates = $package['rates'] ?? array();
|
||||
foreach ( $rates as $rate ) {
|
||||
if ( ! $rate instanceof \WC_Shipping_Rate ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[] = new ShippingOption(
|
||||
$rate->get_id(),
|
||||
$rate->get_label(),
|
||||
in_array( $rate->get_id(), $chosen_shipping_methods, true ),
|
||||
new Money(
|
||||
(float) $rate->get_cost(),
|
||||
get_woocommerce_currency()
|
||||
),
|
||||
ShippingOption::TYPE_SHIPPING
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $chosen_shipping_methods && $options ) {
|
||||
$options[0]->set_selected( true );
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ShippingOption object from the PayPal JSON object.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return ShippingOption
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): ShippingOption {
|
||||
if ( ! isset( $data->id ) ) {
|
||||
throw new RuntimeException( 'No id was given for shipping option.' );
|
||||
}
|
||||
if ( ! isset( $data->amount ) ) {
|
||||
throw new RuntimeException( 'Shipping option amount not found' );
|
||||
}
|
||||
|
||||
$amount = $this->money_factory->from_paypal_response( $data->amount );
|
||||
return new ShippingOption(
|
||||
$data->id,
|
||||
$data->label ?? '',
|
||||
isset( $data->selected ) ? (bool) $data->selected : false,
|
||||
$amount,
|
||||
$data->type ?? ShippingOption::TYPE_SHIPPING
|
||||
);
|
||||
}
|
||||
}
|
|
@ -38,11 +38,13 @@ class ApplicationContextRepository {
|
|||
* Returns the current application context.
|
||||
*
|
||||
* @param string $shipping_preferences The shipping preferences.
|
||||
* @param string $user_action The user action.
|
||||
*
|
||||
* @return ApplicationContext
|
||||
*/
|
||||
public function current_context(
|
||||
string $shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING
|
||||
string $shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING,
|
||||
string $user_action = ApplicationContext::USER_ACTION_CONTINUE
|
||||
): ApplicationContext {
|
||||
|
||||
$brand_name = $this->settings->has( 'brand_name' ) ? $this->settings->get( 'brand_name' ) : '';
|
||||
|
@ -55,7 +57,8 @@ class ApplicationContextRepository {
|
|||
(string) $brand_name,
|
||||
$locale,
|
||||
(string) $landingpage,
|
||||
$shipping_preferences
|
||||
$shipping_preferences,
|
||||
$user_action
|
||||
);
|
||||
return $context;
|
||||
}
|
||||
|
|
14
modules/ppcp-blocks/.babelrc
Normal file
14
modules/ppcp-blocks/.babelrc
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react"
|
||||
]
|
||||
]
|
||||
}
|
3
modules/ppcp-blocks/.gitignore
vendored
Normal file
3
modules/ppcp-blocks/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
assets/js
|
||||
assets/css
|
17
modules/ppcp-blocks/composer.json
Normal file
17
modules/ppcp-blocks/composer.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "woocommerce/ppcp-blocks",
|
||||
"type": "dhii-mod",
|
||||
"description": "Blocks module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\Blocks\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
74
modules/ppcp-blocks/extensions.php
Normal file
74
modules/ppcp-blocks/extensions.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* The blocks module extensions.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'wcgateway.button.locations' => function ( ContainerInterface $container, array $locations ): array {
|
||||
return array_merge(
|
||||
$locations,
|
||||
array(
|
||||
'checkout-block-express' => _x( 'Block Express Checkout', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
'cart-block' => _x( 'Block Cart', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
)
|
||||
);
|
||||
},
|
||||
'wcgateway.settings.pay-later.messaging-locations' => function ( ContainerInterface $container, array $locations ): array {
|
||||
unset( $locations['checkout-block-express'] );
|
||||
unset( $locations['cart-block'] );
|
||||
return $locations;
|
||||
},
|
||||
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
$insert_after = function( array $array, string $key, array $new ): array {
|
||||
$keys = array_keys( $array );
|
||||
$index = array_search( $key, $keys, true );
|
||||
$pos = false === $index ? count( $array ) : $index + 1;
|
||||
|
||||
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
|
||||
};
|
||||
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'smart_button_locations',
|
||||
array(
|
||||
'blocks_final_review_enabled' => array(
|
||||
'title' => __( 'Require final confirmation on checkout', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __(
|
||||
'Require customers to confirm express payments from the Block Cart and Block Express Checkout on the checkout page.
|
||||
<p class="description">If this setting is not enabled, <a href="https://woocommerce.com/document/woocommerce-paypal-payments/#blocks-faq" target="_blank">payment confirmation on the checkout will be skipped</a>.
|
||||
Skipping the final confirmation on the checkout page may impact the buyer experience during the PayPal payment process.</p>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'default' => true,
|
||||
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
'button.pay-now-contexts' => function ( ContainerInterface $container, array $contexts ): array {
|
||||
if ( ! $container->get( 'blocks.settings.final_review_enabled' ) ) {
|
||||
$contexts[] = 'checkout-block';
|
||||
$contexts[] = 'cart-block';
|
||||
}
|
||||
|
||||
return $contexts;
|
||||
},
|
||||
|
||||
'button.handle-shipping-in-paypal' => function ( ContainerInterface $container ): bool {
|
||||
return ! $container->get( 'blocks.settings.final_review_enabled' );
|
||||
},
|
||||
);
|
16
modules/ppcp-blocks/module.php
Normal file
16
modules/ppcp-blocks/module.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* The blocks module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return new BlocksModule();
|
||||
};
|
33
modules/ppcp-blocks/package.json
Normal file
33
modules/ppcp-blocks/package.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "ppcp-blocks",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"browserslist": [
|
||||
"> 0.5%",
|
||||
"Safari >= 8",
|
||||
"Chrome >= 41",
|
||||
"Firefox >= 43",
|
||||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"core-js": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19",
|
||||
"@babel/preset-env": "^7.19",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@woocommerce/dependency-extraction-webpack-plugin": "^2.2.0",
|
||||
"babel-loader": "^8.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.76",
|
||||
"webpack-cli": "^4.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
126
modules/ppcp-blocks/resources/js/Helper/Address.js
Normal file
126
modules/ppcp-blocks/resources/js/Helper/Address.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* @param {String} fullName
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const splitFullName = (fullName) => {
|
||||
fullName = fullName.trim()
|
||||
if (!fullName.includes(' ')) {
|
||||
return [fullName, ''];
|
||||
}
|
||||
const parts = fullName.split(' ');
|
||||
const firstName = parts[0];
|
||||
parts.shift();
|
||||
const lastName = parts.join(' ');
|
||||
return [firstName, lastName];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} address
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const paypalAddressToWc = (address) => {
|
||||
let map = {
|
||||
country_code: 'country',
|
||||
address_line_1: 'address_1',
|
||||
address_line_2: 'address_2',
|
||||
admin_area_1: 'state',
|
||||
admin_area_2: 'city',
|
||||
postal_code: 'postcode',
|
||||
};
|
||||
if (address.city) { // address not from API, such as onShippingChange
|
||||
map = {
|
||||
country_code: 'country',
|
||||
state: 'state',
|
||||
city: 'city',
|
||||
postal_code: 'postcode',
|
||||
};
|
||||
}
|
||||
const result = {};
|
||||
Object.entries(map).forEach(([paypalKey, wcKey]) => {
|
||||
if (address[paypalKey]) {
|
||||
result[wcKey] = address[paypalKey];
|
||||
}
|
||||
});
|
||||
|
||||
const defaultAddress = {
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
company: '',
|
||||
address_1: '',
|
||||
address_2: '',
|
||||
city: '',
|
||||
state: '',
|
||||
postcode: '',
|
||||
country: '',
|
||||
phone: '',
|
||||
};
|
||||
|
||||
return {...defaultAddress, ...result};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} shipping
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const paypalShippingToWc = (shipping) => {
|
||||
const [firstName, lastName] = splitFullName(shipping.name.full_name);
|
||||
return {
|
||||
...paypalAddressToWc(shipping.address),
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} payer
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const paypalPayerToWc = (payer) => {
|
||||
const firstName = payer.name.given_name;
|
||||
const lastName = payer.name.surname;
|
||||
const address = payer.address ? paypalAddressToWc(payer.address) : {};
|
||||
return {
|
||||
...address,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email: payer.email_address,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} order
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const paypalOrderToWcShippingAddress = (order) => {
|
||||
const shipping = order.purchase_units[0].shipping;
|
||||
if (!shipping) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const res = paypalShippingToWc(shipping);
|
||||
|
||||
// use the name from billing if the same, to avoid possible mistakes when splitting full_name
|
||||
const billingAddress = paypalPayerToWc(order.payer);
|
||||
if (`${res.first_name} ${res.last_name}` === `${billingAddress.first_name} ${billingAddress.last_name}`) {
|
||||
res.first_name = billingAddress.first_name;
|
||||
res.last_name = billingAddress.last_name;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param order
|
||||
* @returns {{shippingAddress: Object, billingAddress: Object}}
|
||||
*/
|
||||
export const paypalOrderToWcAddresses = (order) => {
|
||||
const shippingAddress = paypalOrderToWcShippingAddress(order);
|
||||
let billingAddress = paypalPayerToWc(order.payer);
|
||||
// no billing address, such as if billing address retrieval is not allowed in the merchant account
|
||||
if (!billingAddress.address_line_1) {
|
||||
billingAddress = {...shippingAddress, ...paypalPayerToWc(order.payer)};
|
||||
}
|
||||
|
||||
return {billingAddress, shippingAddress};
|
||||
}
|
269
modules/ppcp-blocks/resources/js/checkout-block.js
Normal file
269
modules/ppcp-blocks/resources/js/checkout-block.js
Normal file
|
@ -0,0 +1,269 @@
|
|||
import {useEffect, useState} from '@wordpress/element';
|
||||
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {paypalAddressToWc, paypalOrderToWcAddresses} from "./Helper/Address";
|
||||
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
|
||||
|
||||
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
|
||||
|
||||
window.ppcpFundingSource = config.fundingSource;
|
||||
|
||||
const PayPalComponent = ({
|
||||
onClick,
|
||||
onClose,
|
||||
onSubmit,
|
||||
onError,
|
||||
eventRegistration,
|
||||
emitResponse,
|
||||
activePaymentMethod,
|
||||
shippingData,
|
||||
}) => {
|
||||
const {onPaymentSetup, onCheckoutAfterProcessingWithError} = eventRegistration;
|
||||
const {responseTypes} = emitResponse;
|
||||
|
||||
const [paypalOrder, setPaypalOrder] = useState(null);
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
useEffect(() => {
|
||||
if (!loaded) {
|
||||
loadPaypalScript(config.scriptData, () => {
|
||||
setLoaded(true);
|
||||
});
|
||||
}
|
||||
}, [loaded]);
|
||||
|
||||
const createOrder = async () => {
|
||||
try {
|
||||
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.scriptData.ajax.create_order.nonce,
|
||||
bn_code: '',
|
||||
context: config.scriptData.context,
|
||||
payment_method: 'ppcp-gateway',
|
||||
createaccount: false
|
||||
}),
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
if (json.data?.details?.length > 0) {
|
||||
throw new Error(json.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
|
||||
} else if (json.data?.message) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
|
||||
throw new Error(config.scriptData.labels.error.generic);
|
||||
}
|
||||
return json.data.id;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
onError(err.message);
|
||||
|
||||
onClose();
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const handleApprove = async (data, actions) => {
|
||||
try {
|
||||
const res = await fetch(config.scriptData.ajax.approve_order.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.scriptData.ajax.approve_order.nonce,
|
||||
order_id: data.orderID,
|
||||
funding_source: window.ppcpFundingSource ?? 'paypal',
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
if (typeof actions !== 'undefined' && typeof actions.restart !== 'undefined') {
|
||||
return actions.restart();
|
||||
}
|
||||
if (json.data?.message) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
|
||||
throw new Error(config.scriptData.labels.error.generic)
|
||||
}
|
||||
|
||||
const order = await actions.order.get();
|
||||
|
||||
setPaypalOrder(order);
|
||||
|
||||
if (config.finalReviewEnabled) {
|
||||
const addresses = paypalOrderToWcAddresses(order);
|
||||
|
||||
await wp.data.dispatch('wc/store/cart').updateCustomerData({
|
||||
billing_address: addresses.billingAddress,
|
||||
shipping_address: addresses.shippingAddress,
|
||||
});
|
||||
const checkoutUrl = new URL(config.scriptData.redirect);
|
||||
// sometimes some browsers may load some kind of cached version of the page,
|
||||
// so adding a parameter to avoid that
|
||||
checkoutUrl.searchParams.append('ppcp-continuation-redirect', (new Date()).getTime().toString());
|
||||
|
||||
location.href = checkoutUrl.toString();
|
||||
} else {
|
||||
onSubmit();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
onError(err.message);
|
||||
|
||||
onClose();
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = (data, actions) => {
|
||||
window.ppcpFundingSource = data.fundingSource;
|
||||
|
||||
onClick();
|
||||
};
|
||||
|
||||
let handleShippingChange = null;
|
||||
if (shippingData.needsShipping && !config.finalReviewEnabled) {
|
||||
handleShippingChange = async (data, actions) => {
|
||||
try {
|
||||
const shippingOptionId = data.selected_shipping_option?.id;
|
||||
if (shippingOptionId) {
|
||||
await shippingData.setSelectedRates(shippingOptionId);
|
||||
}
|
||||
|
||||
const address = paypalAddressToWc(data.shipping_address);
|
||||
|
||||
await wp.data.dispatch('wc/store/cart').updateCustomerData({
|
||||
shipping_address: address,
|
||||
});
|
||||
|
||||
await shippingData.setShippingAddress(address);
|
||||
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
actions.reject();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (activePaymentMethod !== config.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsubscribeProcessing = onPaymentSetup(() => {
|
||||
if (config.scriptData.continuation) {
|
||||
return {
|
||||
type: responseTypes.SUCCESS,
|
||||
meta: {
|
||||
paymentMethodData: {
|
||||
'paypal_order_id': config.scriptData.continuation.order_id,
|
||||
'funding_source': window.ppcpFundingSource ?? 'paypal',
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const addresses = paypalOrderToWcAddresses(paypalOrder);
|
||||
|
||||
return {
|
||||
type: responseTypes.SUCCESS,
|
||||
meta: {
|
||||
paymentMethodData: {
|
||||
'paypal_order_id': paypalOrder.id,
|
||||
'funding_source': window.ppcpFundingSource ?? 'paypal',
|
||||
},
|
||||
...addresses,
|
||||
},
|
||||
};
|
||||
});
|
||||
return () => {
|
||||
unsubscribeProcessing();
|
||||
};
|
||||
}, [onPaymentSetup, paypalOrder, activePaymentMethod]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = onCheckoutAfterProcessingWithError(({ processingResponse }) => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
if (processingResponse?.paymentDetails?.errorMessage) {
|
||||
return {
|
||||
type: emitResponse.responseTypes.ERROR,
|
||||
message: processingResponse.paymentDetails.errorMessage,
|
||||
messageContext: config.scriptData.continuation ? emitResponse.noticeContexts.PAYMENTS : emitResponse.noticeContexts.EXPRESS_PAYMENTS,
|
||||
};
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [onCheckoutAfterProcessingWithError, onClose]);
|
||||
|
||||
if (config.scriptData.continuation) {
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{__html: config.scriptData.continuation.cancel.html}}>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
|
||||
|
||||
return (
|
||||
<PayPalButton
|
||||
style={config.scriptData.button.style}
|
||||
onClick={handleClick}
|
||||
onCancel={onClose}
|
||||
onError={onClose}
|
||||
createOrder={createOrder}
|
||||
onApprove={handleApprove}
|
||||
onShippingChange={handleShippingChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const features = ['products'];
|
||||
let registerMethod = registerExpressPaymentMethod;
|
||||
if (config.scriptData.continuation) {
|
||||
features.push('ppcp_continuation');
|
||||
registerMethod = registerPaymentMethod;
|
||||
}
|
||||
|
||||
registerMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <PayPalComponent/>,
|
||||
edit: <b>TODO: editing</b>,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => config.enabled,
|
||||
supports: {
|
||||
features: features,
|
||||
},
|
||||
});
|
57
modules/ppcp-blocks/services.php
Normal file
57
modules/ppcp-blocks/services.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* The blocks module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'blocks.url' => static function ( ContainerInterface $container ): string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-blocks/',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'blocks.method' => static function ( ContainerInterface $container ): PayPalPaymentMethod {
|
||||
return new PayPalPaymentMethod(
|
||||
$container->get( 'blocks.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'button.smart-button' ),
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.settings.status' ),
|
||||
$container->get( 'wcgateway.paypal-gateway' ),
|
||||
$container->get( 'blocks.settings.final_review_enabled' ),
|
||||
$container->get( 'session.cancellation.view' ),
|
||||
$container->get( 'session.handler' )
|
||||
);
|
||||
},
|
||||
'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof ContainerInterface );
|
||||
|
||||
return $settings->has( 'blocks_final_review_enabled' ) ?
|
||||
(bool) $settings->get( 'blocks_final_review_enabled' ) :
|
||||
true;
|
||||
},
|
||||
|
||||
'blocks.endpoint.update-shipping' => static function ( ContainerInterface $container ): UpdateShippingEndpoint {
|
||||
return new UpdateShippingEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
100
modules/ppcp-blocks/src/BlocksModule.php
Normal file
100
modules/ppcp-blocks/src/BlocksModule.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* The blocks module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class BlocksModule
|
||||
*/
|
||||
class BlocksModule implements ModuleInterface {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
if (
|
||||
! class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' )
|
||||
|| ! function_exists( 'woocommerce_store_api_register_payment_requirements' )
|
||||
) {
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function () {
|
||||
printf(
|
||||
'<div class="notice notice-error"><p>%1$s</p></div>',
|
||||
wp_kses_post(
|
||||
__(
|
||||
'PayPal checkout block initialization failed, possibly old WooCommerce version or disabled WooCommerce Blocks plugin.',
|
||||
'woocommerce-paypal-payments'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
add_action(
|
||||
'woocommerce_blocks_payment_method_type_registration',
|
||||
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
||||
$payment_method_registry->register( $c->get( 'blocks.method' ) );
|
||||
}
|
||||
);
|
||||
|
||||
woocommerce_store_api_register_payment_requirements(
|
||||
array(
|
||||
'data_callback' => function() use ( $c ): array {
|
||||
$smart_button = $c->get( 'button.smart-button' );
|
||||
assert( $smart_button instanceof SmartButtonInterface );
|
||||
|
||||
if ( isset( $smart_button->script_data()['continuation'] ) ) {
|
||||
return array( 'ppcp_continuation' );
|
||||
}
|
||||
|
||||
return array();
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . UpdateShippingEndpoint::ENDPOINT,
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'blocks.endpoint.update-shipping' );
|
||||
assert( $endpoint instanceof UpdateShippingEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
}
|
||||
}
|
133
modules/ppcp-blocks/src/Endpoint/UpdateShippingEndpoint.php
Normal file
133
modules/ppcp-blocks/src/Endpoint/UpdateShippingEndpoint.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
/**
|
||||
* Updates PayPal order with the current shipping methods.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Button\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Patch;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
|
||||
/**
|
||||
* Class UpdateShippingEndpoint
|
||||
*/
|
||||
class UpdateShippingEndpoint implements EndpointInterface {
|
||||
const ENDPOINT = 'ppc-update-shipping';
|
||||
|
||||
/**
|
||||
* The Request Data Helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* UpdateShippingEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The Request Data Helper.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->request_data = $request_data;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$order_id = $data['order_id'];
|
||||
|
||||
$pu = $this->purchase_unit_factory->from_wc_cart( null, true );
|
||||
$pu_data = $pu->to_array();
|
||||
|
||||
if ( ! isset( $pu_data['shipping']['options'] ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => 'No shipping methods.',
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: maybe should patch only if methods changed.
|
||||
// But it seems a bit difficult to detect,
|
||||
// e.g. ->order($id) may not have Shipping because we drop it when address or name are missing.
|
||||
// Also may consider patching only amount and options instead of the whole PU, though not sure if it makes any difference.
|
||||
$patches = new PatchCollection(
|
||||
new Patch(
|
||||
'replace',
|
||||
"/purchase_units/@reference_id=='{$pu->reference_id()}'",
|
||||
$pu_data
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint->patch( $order_id, $patches );
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
} catch ( Exception $error ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => $error->getMessage(),
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
188
modules/ppcp-blocks/src/PayPalPaymentMethod.php
Normal file
188
modules/ppcp-blocks/src/PayPalPaymentMethod.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* The blocks module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;
|
||||
use WC_AJAX;
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Session\Cancellation\CancelController;
|
||||
use WooCommerce\PayPalCommerce\Session\Cancellation\CancelView;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class PayPalPaymentMethod
|
||||
*/
|
||||
class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
||||
/**
|
||||
* The URL of this module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The smart button script loading handler.
|
||||
*
|
||||
* @var SmartButtonInterface
|
||||
*/
|
||||
private $smart_button;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
private $plugin_settings;
|
||||
|
||||
/**
|
||||
* The Settings status helper.
|
||||
*
|
||||
* @var SettingsStatus
|
||||
*/
|
||||
protected $settings_status;
|
||||
|
||||
/**
|
||||
* The WC gateway.
|
||||
*
|
||||
* @var PayPalGateway
|
||||
*/
|
||||
private $gateway;
|
||||
|
||||
/**
|
||||
* Whether the final review is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $final_review_enabled;
|
||||
|
||||
/**
|
||||
* The cancellation view.
|
||||
*
|
||||
* @var CancelView
|
||||
*/
|
||||
private $cancellation_view;
|
||||
|
||||
/**
|
||||
* The Session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* Assets constructor.
|
||||
*
|
||||
* @param string $module_url The url of this module.
|
||||
* @param string $version The assets version.
|
||||
* @param SmartButtonInterface $smart_button The smart button script loading handler.
|
||||
* @param Settings $plugin_settings The settings.
|
||||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param PayPalGateway $gateway The WC gateway.
|
||||
* @param bool $final_review_enabled Whether the final review is enabled.
|
||||
* @param CancelView $cancellation_view The cancellation view.
|
||||
* @param SessionHandler $session_handler The Session handler.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
SmartButtonInterface $smart_button,
|
||||
Settings $plugin_settings,
|
||||
SettingsStatus $settings_status,
|
||||
PayPalGateway $gateway,
|
||||
bool $final_review_enabled,
|
||||
CancelView $cancellation_view,
|
||||
SessionHandler $session_handler
|
||||
) {
|
||||
$this->name = PayPalGateway::ID;
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->smart_button = $smart_button;
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->gateway = $gateway;
|
||||
$this->final_review_enabled = $final_review_enabled;
|
||||
$this->cancellation_view = $cancellation_view;
|
||||
$this->session_handler = $session_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function initialize() { }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function is_active() {
|
||||
// Do not load when definitely not needed,
|
||||
// but we still need to check the locations later and handle in JS
|
||||
// because has_block cannot be called here (too early).
|
||||
return $this->plugin_settings->has( 'enabled' ) && $this->plugin_settings->get( 'enabled' )
|
||||
&& ( $this->settings_status->is_smart_button_enabled_for_location( 'checkout-block-express' ) ||
|
||||
$this->settings_status->is_smart_button_enabled_for_location( 'cart-block' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_payment_method_script_handles() {
|
||||
wp_register_script(
|
||||
'ppcp-checkout-block',
|
||||
trailingslashit( $this->module_url ) . 'assets/js/checkout-block.js',
|
||||
array(),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
return array( 'ppcp-checkout-block' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_payment_method_data() {
|
||||
$script_data = $this->smart_button->script_data();
|
||||
|
||||
if ( isset( $script_data['continuation'] ) ) {
|
||||
$url = add_query_arg( array( CancelController::NONCE => wp_create_nonce( CancelController::NONCE ) ), wc_get_checkout_url() );
|
||||
|
||||
$script_data['continuation']['cancel'] = array(
|
||||
'html' => $this->cancellation_view->render_session_cancellation( $url, $this->session_handler->funding_source() ),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $this->gateway->id,
|
||||
'title' => $this->gateway->title,
|
||||
'description' => $this->gateway->description,
|
||||
'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ),
|
||||
'fundingSource' => $this->session_handler->funding_source(),
|
||||
'finalReviewEnabled' => $this->final_review_enabled,
|
||||
'ajax' => array(
|
||||
'update_shipping' => array(
|
||||
'endpoint' => WC_AJAX::get_endpoint( UpdateShippingEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( UpdateShippingEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
'scriptData' => $script_data,
|
||||
);
|
||||
}
|
||||
}
|
38
modules/ppcp-blocks/webpack.config.js
Normal file
38
modules/ppcp-blocks/webpack.config.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
const path = require('path');
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
|
||||
|
||||
module.exports = {
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||
entry: {
|
||||
'checkout-block': path.resolve('./resources/js/checkout-block.js')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'css/[name].css',
|
||||
}
|
||||
},
|
||||
{loader:'sass-loader'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
2201
modules/ppcp-blocks/yarn.lock
Normal file
2201
modules/ppcp-blocks/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,39 @@ class CartActionHandler {
|
|||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
return {
|
||||
createSubscription: (data, actions) => {
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
fetch(this.config.ajax.approve_subscription.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.approve_subscription.nonce,
|
||||
order_id: data.orderID,
|
||||
subscription_id: data.subscriptionID
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
}).then((data) => {
|
||||
if (!data.success) {
|
||||
console.log(data)
|
||||
throw Error(data.data.message);
|
||||
}
|
||||
|
||||
location.href = this.config.redirect;
|
||||
});
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration() {
|
||||
const createOrder = (data, actions) => {
|
||||
const payer = payerData();
|
||||
|
|
|
@ -11,6 +11,34 @@ class CheckoutActionHandler {
|
|||
this.spinner = spinner;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
return {
|
||||
createSubscription: (data, actions) => {
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
fetch(this.config.ajax.approve_subscription.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.approve_subscription.nonce,
|
||||
order_id: data.orderID,
|
||||
subscription_id: data.subscriptionID
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
}).then((data) => {
|
||||
document.querySelector('#place_order').click();
|
||||
});
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration() {
|
||||
const spinner = this.spinner;
|
||||
const createOrder = (data, actions) => {
|
||||
|
@ -81,7 +109,7 @@ class CheckoutActionHandler {
|
|||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'hidden');
|
||||
input.setAttribute('name', 'ppcp-resume-order');
|
||||
input.setAttribute('value', data.data.purchase_units[0].custom_id);
|
||||
input.setAttribute('value', data.data.custom_id);
|
||||
document.querySelector(formSelector).appendChild(input);
|
||||
return data.data.id;
|
||||
});
|
||||
|
|
|
@ -18,6 +18,56 @@ class SingleProductActionHandler {
|
|||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
return {
|
||||
createSubscription: (data, actions) => {
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
fetch(this.config.ajax.approve_subscription.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.approve_subscription.nonce,
|
||||
order_id: data.orderID,
|
||||
subscription_id: data.subscriptionID
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
}).then(() => {
|
||||
const id = document.querySelector('[name="add-to-cart"]').value;
|
||||
const products = [new Product(id, 1, null)];
|
||||
|
||||
fetch(this.config.ajax.change_cart.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.change_cart.nonce,
|
||||
products,
|
||||
})
|
||||
}).then((result) => {
|
||||
return result.json();
|
||||
}).then((result) => {
|
||||
if (!result.success) {
|
||||
console.log(result)
|
||||
throw Error(result.data.message);
|
||||
}
|
||||
|
||||
location.href = this.config.redirect;
|
||||
})
|
||||
});
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration()
|
||||
{
|
||||
return {
|
||||
|
|
|
@ -50,6 +50,14 @@ class CartBootstrap {
|
|||
this.errorHandler,
|
||||
);
|
||||
|
||||
if(
|
||||
PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
&& PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration());
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.render(
|
||||
actionHandler.configuration()
|
||||
);
|
||||
|
|
|
@ -64,9 +64,15 @@ class CheckoutBootstap {
|
|||
this.spinner
|
||||
);
|
||||
|
||||
this.renderer.render(
|
||||
actionHandler.configuration()
|
||||
);
|
||||
if(
|
||||
PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
&& PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration(), {}, actionHandler.configuration());
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.render(actionHandler.configuration(), {}, actionHandler.configuration());
|
||||
}
|
||||
|
||||
updateUi() {
|
||||
|
|
|
@ -110,6 +110,14 @@ class SingleProductBootstap {
|
|||
this.errorHandler,
|
||||
);
|
||||
|
||||
if(
|
||||
PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
&& PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration());
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.render(
|
||||
actionHandler.configuration()
|
||||
);
|
||||
|
|
|
@ -10,7 +10,7 @@ class Renderer {
|
|||
this.renderedSources = new Set();
|
||||
}
|
||||
|
||||
render(contextConfig, settingsOverride = {}) {
|
||||
render(contextConfig, settingsOverride = {}, contextConfigOverride = () => {}) {
|
||||
const settings = merge(this.defaultSettings, settingsOverride);
|
||||
|
||||
const enabledSeparateGateways = Object.fromEntries(Object.entries(
|
||||
|
@ -50,7 +50,7 @@ class Renderer {
|
|||
}
|
||||
|
||||
if (this.creditCardRenderer) {
|
||||
this.creditCardRenderer.render(settings.hosted_fields.wrapper, contextConfig);
|
||||
this.creditCardRenderer.render(settings.hosted_fields.wrapper, contextConfigOverride);
|
||||
}
|
||||
|
||||
for (const [fundingSource, data] of Object.entries(enabledSeparateGateways)) {
|
||||
|
|
|
@ -9,11 +9,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Button;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
|
||||
|
@ -65,25 +67,37 @@ return array(
|
|||
|
||||
return $dummy_ids[ $shop_country ] ?? $container->get( 'button.client_id' );
|
||||
},
|
||||
'button.smart-button' => static function ( ContainerInterface $container ): SmartButtonInterface {
|
||||
'button.is_paypal_continuation' => static function( ContainerInterface $container ): bool {
|
||||
$session_handler = $container->get( 'session.handler' );
|
||||
|
||||
$order = $session_handler->order();
|
||||
if ( ! $order ) {
|
||||
return false;
|
||||
}
|
||||
$source = $order->payment_source();
|
||||
if ( $source && $source->card() ) {
|
||||
return false; // Ignore for DCC.
|
||||
}
|
||||
if ( 'card' === $session_handler->funding_source() ) {
|
||||
return false; // Ignore for card buttons.
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'button.smart-button' => static function ( ContainerInterface $container ): SmartButtonInterface {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
/**
|
||||
* The state.
|
||||
*
|
||||
* @var State $state
|
||||
*/
|
||||
if ( $state->current_state() !== State::STATE_ONBOARDED ) {
|
||||
return new DisabledSmartButton();
|
||||
}
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$paypal_disabled = ! $settings->has( 'enabled' ) || ! $settings->get( 'enabled' );
|
||||
if ( $paypal_disabled ) {
|
||||
return new DisabledSmartButton();
|
||||
}
|
||||
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
$request_data = $container->get( 'button.request-data' );
|
||||
|
||||
$client_id = $container->get( 'button.client_id' );
|
||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
|
@ -110,6 +124,7 @@ return array(
|
|||
$container->get( 'wcgateway.all-funding-sources' ),
|
||||
$container->get( 'button.basic-checkout-validation-enabled' ),
|
||||
$container->get( 'button.early-wc-checkout-validation-enabled' ),
|
||||
$container->get( 'button.pay-now-contexts' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
@ -119,6 +134,9 @@ return array(
|
|||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'button.pay-now-contexts' => static function ( ContainerInterface $container ): array {
|
||||
return array( 'checkout', 'pay-now' );
|
||||
},
|
||||
'button.request-data' => static function ( ContainerInterface $container ): RequestData {
|
||||
return new RequestData();
|
||||
},
|
||||
|
@ -156,6 +174,8 @@ return array(
|
|||
$registration_needed,
|
||||
$container->get( 'wcgateway.settings.card_billing_data_mode' ),
|
||||
$container->get( 'button.early-wc-checkout-validation-enabled' ),
|
||||
$container->get( 'button.pay-now-contexts' ),
|
||||
$container->get( 'button.handle-shipping-in-paypal' ),
|
||||
$logger
|
||||
);
|
||||
},
|
||||
|
@ -187,6 +207,13 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'button.endpoint.approve-subscription' => static function( ContainerInterface $container ): ApproveSubscriptionEndpoint {
|
||||
return new ApproveSubscriptionEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'session.handler' )
|
||||
);
|
||||
},
|
||||
'button.checkout-form-saver' => static function ( ContainerInterface $container ): CheckoutFormSaver {
|
||||
return new CheckoutFormSaver();
|
||||
},
|
||||
|
@ -266,4 +293,12 @@ return array(
|
|||
'button.validation.wc-checkout-validator' => static function ( ContainerInterface $container ): CheckoutFormValidator {
|
||||
return new CheckoutFormValidator();
|
||||
},
|
||||
|
||||
/**
|
||||
* If true, the shipping methods are sent to PayPal allowing the customer to select it inside the popup.
|
||||
* May result in slower popup performance, additional loading.
|
||||
*/
|
||||
'button.handle-shipping-in-paypal' => static function ( ContainerInterface $container ): bool {
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
|
|
@ -26,7 +26,7 @@ class DisabledSmartButton implements SmartButtonInterface {
|
|||
/**
|
||||
* Whether the scripts should be loaded.
|
||||
*/
|
||||
public function should_load(): bool {
|
||||
public function should_load_ppcp_script(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
||||
|
@ -68,13 +69,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The Session Handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
|
@ -166,13 +160,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
protected $early_validation_enabled;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Cached payment tokens.
|
||||
*
|
||||
|
@ -180,12 +167,33 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $payment_tokens = null;
|
||||
|
||||
/**
|
||||
* The contexts that should have the Pay Now button.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $pay_now_contexts;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* SmartButton constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param string $version The assets version.
|
||||
* @param SessionHandler $session_handler The Session handler.
|
||||
* @param Settings $settings The Settings.
|
||||
* @param PayerFactory $payer_factory The Payer factory.
|
||||
* @param string $client_id The client ID.
|
||||
|
@ -200,6 +208,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param array $all_funding_sources All existing funding sources.
|
||||
* @param bool $basic_checkout_validation_enabled Whether the basic JS validation of the form iss enabled.
|
||||
* @param bool $early_validation_enabled Whether to execute WC validation of the checkout form.
|
||||
* @param array $pay_now_contexts The contexts that should have the Pay Now button.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -220,6 +229,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
array $all_funding_sources,
|
||||
bool $basic_checkout_validation_enabled,
|
||||
bool $early_validation_enabled,
|
||||
array $pay_now_contexts,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
|
@ -240,6 +250,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->all_funding_sources = $all_funding_sources;
|
||||
$this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled;
|
||||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
@ -254,10 +265,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->render_message_wrapper_registrar();
|
||||
}
|
||||
|
||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
$this->settings->has( 'dcc_enabled' )
|
||||
&& $this->settings->get( 'dcc_enabled' )
|
||||
|
@ -284,7 +291,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
add_filter(
|
||||
'woocommerce_credit_card_form_fields',
|
||||
function ( array $default_fields, $id ) use ( $subscription_helper ) : array {
|
||||
if ( is_user_logged_in() && $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) && CreditCardGateway::ID === $id ) {
|
||||
if ( is_user_logged_in() && $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ) && CreditCardGateway::ID === $id ) {
|
||||
|
||||
$default_fields['card-vault'] = sprintf(
|
||||
'<p class="form-row form-row-wide"><label for="ppcp-credit-card-vault"><input class="ppcp-credit-card-vault" type="checkbox" id="ppcp-credit-card-vault" name="vault">%s</label></p>',
|
||||
|
@ -455,10 +462,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
add_action(
|
||||
$this->mini_cart_button_renderer_hook(),
|
||||
function () {
|
||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_cart_price_total_zero() || $this->is_free_trial_cart() ) {
|
||||
return;
|
||||
}
|
||||
|
@ -509,31 +512,70 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Whether the scripts should be loaded.
|
||||
* Whether any of our scripts (for DCC or product, mini-cart, non-block cart/checkout) should be loaded.
|
||||
*/
|
||||
public function should_load(): bool {
|
||||
public function should_load_ppcp_script(): bool {
|
||||
$buttons_enabled = $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' );
|
||||
if ( ! is_checkout() && ! $buttons_enabled ) {
|
||||
if ( ! $buttons_enabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if ( in_array( $this->context(), array( 'checkout-block', 'cart-block' ), true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->should_load_buttons() || $this->can_render_dcc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the scripts.
|
||||
* Determines whether the button component should be loaded.
|
||||
*/
|
||||
public function enqueue(): void {
|
||||
if ( ! $this->should_load() ) {
|
||||
return;
|
||||
public function should_load_buttons() : bool {
|
||||
$buttons_enabled = $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' );
|
||||
if ( ! $buttons_enabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$load_script = false;
|
||||
if ( is_checkout() && $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) ) {
|
||||
$load_script = true;
|
||||
$smart_button_enabled_for_current_location = $this->settings_status->is_smart_button_enabled_for_location( $this->context() );
|
||||
$smart_button_enabled_for_mini_cart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' );
|
||||
$messaging_enabled_for_current_location = $this->settings_status->is_pay_later_messaging_enabled_for_location( $this->context() );
|
||||
|
||||
switch ( $this->context() ) {
|
||||
case 'checkout':
|
||||
case 'cart':
|
||||
case 'pay-now':
|
||||
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location;
|
||||
case 'checkout-block':
|
||||
case 'cart-block':
|
||||
return $smart_button_enabled_for_current_location;
|
||||
case 'product':
|
||||
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location || $smart_button_enabled_for_mini_cart;
|
||||
default:
|
||||
return $smart_button_enabled_for_mini_cart;
|
||||
}
|
||||
if ( $this->load_button_component() ) {
|
||||
$load_script = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether DCC fields can be rendered.
|
||||
*/
|
||||
public function can_render_dcc() : bool {
|
||||
return $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' )
|
||||
&& $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' )
|
||||
&& $this->dcc_applies->for_country_currency()
|
||||
&& in_array( $this->context(), array( 'checkout', 'pay-now' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues our scripts/styles (for DCC and product, mini-cart and non-block cart/checkout)
|
||||
*/
|
||||
public function enqueue(): void {
|
||||
if ( $this->can_render_dcc() ) {
|
||||
wp_enqueue_style(
|
||||
'ppcp-hosted-fields',
|
||||
untrailingslashit( $this->module_url ) . '/assets/css/hosted-fields.css',
|
||||
array(),
|
||||
$this->version
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( $this->context(), array( 'pay-now', 'checkout' ), true ) ) {
|
||||
|
@ -543,31 +585,21 @@ class SmartButton implements SmartButtonInterface {
|
|||
array(),
|
||||
$this->version
|
||||
);
|
||||
|
||||
if ( $this->can_render_dcc() ) {
|
||||
wp_enqueue_style(
|
||||
'ppcp-hosted-fields',
|
||||
untrailingslashit( $this->module_url ) . '/assets/css/hosted-fields.css',
|
||||
array(),
|
||||
$this->version
|
||||
);
|
||||
}
|
||||
}
|
||||
if ( $load_script ) {
|
||||
wp_enqueue_script(
|
||||
'ppcp-smart-button',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/button.js',
|
||||
array( 'jquery' ),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-smart-button',
|
||||
'PayPalCommerceGateway',
|
||||
$this->script_data()
|
||||
);
|
||||
}
|
||||
wp_enqueue_script(
|
||||
'ppcp-smart-button',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/button.js',
|
||||
array( 'jquery' ),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-smart-button',
|
||||
'PayPalCommerceGateway',
|
||||
$this->script_data()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,10 +609,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
public function button_renderer( string $gateway_id ) {
|
||||
|
||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
|
||||
if ( ! isset( $available_gateways[ $gateway_id ] ) ) {
|
||||
|
@ -596,9 +624,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
* Renders the HTML for the credit messaging.
|
||||
*/
|
||||
public function message_renderer() {
|
||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$product = wc_get_product();
|
||||
|
||||
|
@ -666,18 +691,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether DCC fields can be rendered.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException When a setting was not found.
|
||||
*/
|
||||
private function can_render_dcc() : bool {
|
||||
|
||||
$can_render = $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) && $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) && $this->dcc_applies->for_country_currency();
|
||||
return $can_render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for the DCC fields.
|
||||
*/
|
||||
|
@ -710,7 +723,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
* Whether we can store vault tokens or not.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException If a setting hasn't been found.
|
||||
*/
|
||||
public function can_save_vault_token(): bool {
|
||||
|
||||
|
@ -744,6 +756,25 @@ class SmartButton implements SmartButtonInterface {
|
|||
return $this->subscription_helper->cart_contains_subscription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether PayPal subscriptions is enabled or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function paypal_subscriptions_enabled(): bool {
|
||||
if ( defined( 'PPCP_FLAG_SUBSCRIPTIONS_API' ) && ! PPCP_FLAG_SUBSCRIPTIONS_API ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$subscriptions_mode = $this->settings->get( 'subscriptions_mode' );
|
||||
} catch ( NotFoundException $exception ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $subscriptions_mode === 'subscriptions_api';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the 3D Secure contingency settings.
|
||||
*
|
||||
|
@ -764,7 +795,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
* The configuration for the smart buttons.
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException If a setting hasn't been found.
|
||||
*/
|
||||
public function script_data(): array {
|
||||
$is_free_trial_cart = $this->is_free_trial_cart();
|
||||
|
@ -777,43 +807,49 @@ class SmartButton implements SmartButtonInterface {
|
|||
'url_params' => $url_params,
|
||||
'script_attributes' => $this->attributes(),
|
||||
'data_client_id' => array(
|
||||
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ),
|
||||
'user' => get_current_user_id(),
|
||||
'has_subscriptions' => $this->has_subscriptions(),
|
||||
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ),
|
||||
'user' => get_current_user_id(),
|
||||
'has_subscriptions' => $this->has_subscriptions(),
|
||||
'paypal_subscriptions_enabled' => $this->paypal_subscriptions_enabled(),
|
||||
),
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
'context' => $this->context(),
|
||||
'ajax' => array(
|
||||
'change_cart' => array(
|
||||
'change_cart' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ),
|
||||
),
|
||||
'create_order' => array(
|
||||
'create_order' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ),
|
||||
),
|
||||
'approve_order' => array(
|
||||
'approve_order' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ),
|
||||
),
|
||||
'vault_paypal' => array(
|
||||
'approve_subscription' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ApproveSubscriptionEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ApproveSubscriptionEndpoint::nonce() ),
|
||||
),
|
||||
'vault_paypal' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ),
|
||||
),
|
||||
'save_checkout_form' => array(
|
||||
'save_checkout_form' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ),
|
||||
),
|
||||
'validate_checkout' => array(
|
||||
'validate_checkout' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ),
|
||||
),
|
||||
'cart_script_params' => array(
|
||||
'cart_script_params' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
'subscription_plan_id' => $this->paypal_subscription_id(),
|
||||
'enforce_vault' => $this->has_subscriptions(),
|
||||
'can_save_vault_token' => $this->can_save_vault_token(),
|
||||
'is_free_trial_cart' => $is_free_trial_cart,
|
||||
|
@ -911,6 +947,15 @@ class SmartButton implements SmartButtonInterface {
|
|||
$localize['button']['style']['tagline'] = false;
|
||||
}
|
||||
|
||||
if ( $this->is_paypal_continuation() ) {
|
||||
$order = $this->session_handler->order();
|
||||
assert( $order !== null );
|
||||
|
||||
$localize['continuation'] = array(
|
||||
'order_id' => $order->id(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->request_data->dequeue_nonce_fix();
|
||||
return $localize;
|
||||
}
|
||||
|
@ -933,21 +978,24 @@ class SmartButton implements SmartButtonInterface {
|
|||
* The JavaScript SDK url parameters.
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException If a setting was not found.
|
||||
*/
|
||||
private function url_params(): array {
|
||||
$intent = ( $this->settings->has( 'intent' ) ) ? $this->settings->get( 'intent' ) : 'capture';
|
||||
$product_intent = $this->subscription_helper->current_product_is_subscription() ? 'authorize' : $intent;
|
||||
$other_context_intent = $this->subscription_helper->cart_contains_subscription() ? 'authorize' : $intent;
|
||||
$context = $this->context();
|
||||
try {
|
||||
$intent = $this->intent();
|
||||
} catch ( NotFoundException $exception ) {
|
||||
$intent = 'capture';
|
||||
}
|
||||
|
||||
$params = array(
|
||||
$subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : '';
|
||||
$params = array(
|
||||
'client-id' => $this->client_id,
|
||||
'currency' => $this->currency,
|
||||
'integration-date' => PAYPAL_INTEGRATION_DATE,
|
||||
'components' => implode( ',', $this->components() ),
|
||||
'vault' => $this->can_save_vault_token() ? 'true' : 'false',
|
||||
'commit' => is_checkout() ? 'true' : 'false',
|
||||
'intent' => $this->context() === 'product' ? $product_intent : $other_context_intent,
|
||||
'vault' => ( $this->can_save_vault_token() || $this->subscription_helper->need_subscription_intent( $subscription_mode ) ) ? 'true' : 'false',
|
||||
'commit' => in_array( $context, $this->pay_now_contexts, true ) ? 'true' : 'false',
|
||||
'intent' => $intent,
|
||||
);
|
||||
if (
|
||||
$this->environment->current_environment_is( Environment::SANDBOX )
|
||||
|
@ -991,6 +1039,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
}
|
||||
|
||||
if ( in_array( $context, array( 'checkout-block', 'cart-block' ), true ) ) {
|
||||
$disable_funding = array_diff(
|
||||
array_keys( $this->all_funding_sources ),
|
||||
array( 'venmo', 'paylater' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->is_free_trial_cart() ) {
|
||||
$all_sources = array_keys( $this->all_funding_sources );
|
||||
if ( $is_dcc_enabled || $is_separate_card_enabled ) {
|
||||
|
@ -1001,8 +1056,8 @@ class SmartButton implements SmartButtonInterface {
|
|||
|
||||
$enable_funding = array( 'venmo' );
|
||||
|
||||
if ( $this->settings_status->is_pay_later_button_enabled_for_location( $this->context() ) ||
|
||||
$this->settings_status->is_pay_later_messaging_enabled_for_location( $this->context() )
|
||||
if ( $this->settings_status->is_pay_later_button_enabled_for_location( $context ) ||
|
||||
$this->settings_status->is_pay_later_messaging_enabled_for_location( $context )
|
||||
) {
|
||||
$enable_funding[] = 'paylater';
|
||||
} else {
|
||||
|
@ -1071,7 +1126,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
private function components(): array {
|
||||
$components = array();
|
||||
|
||||
if ( $this->load_button_component() ) {
|
||||
if ( $this->should_load_buttons() ) {
|
||||
$components[] = 'buttons';
|
||||
$components[] = 'funding-eligibility';
|
||||
}
|
||||
|
@ -1087,34 +1142,10 @@ class SmartButton implements SmartButtonInterface {
|
|||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the button component should be loaded.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException If a setting has not been found.
|
||||
*/
|
||||
private function load_button_component() : bool {
|
||||
$smart_button_enabled_for_current_location = $this->settings_status->is_smart_button_enabled_for_location( $this->context() );
|
||||
$smart_button_enabled_for_mini_cart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' );
|
||||
$messaging_enabled_for_current_location = $this->settings_status->is_pay_later_messaging_enabled_for_location( $this->context() );
|
||||
|
||||
switch ( $this->context() ) {
|
||||
case 'checkout':
|
||||
case 'cart':
|
||||
case 'pay-now':
|
||||
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location;
|
||||
case 'product':
|
||||
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location || $smart_button_enabled_for_mini_cart;
|
||||
default:
|
||||
return $smart_button_enabled_for_mini_cart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether DCC is enabled or not.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException If a setting has not been found.
|
||||
*/
|
||||
private function dcc_is_enabled(): bool {
|
||||
if ( ! is_checkout() ) {
|
||||
|
@ -1143,9 +1174,11 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param string $context The context.
|
||||
*
|
||||
* @return string
|
||||
* @throws NotFoundException When a setting hasn't been found.
|
||||
*/
|
||||
private function style_for_context( string $style, string $context ): string {
|
||||
// Use the cart/checkout styles for blocks.
|
||||
$context = str_replace( '-block', '', $context );
|
||||
|
||||
$defaults = array(
|
||||
'layout' => 'vertical',
|
||||
'size' => 'responsive',
|
||||
|
@ -1362,6 +1395,57 @@ class SmartButton implements SmartButtonInterface {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PayPal subscription plan id from WC subscription product.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function paypal_subscription_id(): string {
|
||||
if ( $this->subscription_helper->current_product_is_subscription() ) {
|
||||
$product = wc_get_product();
|
||||
assert( $product instanceof WC_Product );
|
||||
|
||||
if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
return $product->get_meta( 'ppcp_subscription_plan' )['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$cart = WC()->cart ?? null;
|
||||
if ( ! $cart || $cart->is_empty() ) {
|
||||
return '';
|
||||
}
|
||||
$items = $cart->get_cart_contents();
|
||||
foreach ( $items as $item ) {
|
||||
$product = wc_get_product( $item['product_id'] );
|
||||
assert( $product instanceof WC_Product );
|
||||
|
||||
if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
return $product->get_meta( 'ppcp_subscription_plan' )['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intent.
|
||||
*
|
||||
* @return string
|
||||
* @throws NotFoundException If intent is not found.
|
||||
*/
|
||||
private function intent(): string {
|
||||
$intent = ( $this->settings->has( 'intent' ) ) ? $this->settings->get( 'intent' ) : 'capture';
|
||||
$product_intent = $this->subscription_helper->current_product_is_subscription() ? 'authorize' : $intent;
|
||||
$other_context_intent = $this->subscription_helper->cart_contains_subscription() ? 'authorize' : $intent;
|
||||
|
||||
$subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( $this->subscription_helper->need_subscription_intent( $subscription_mode ) ) {
|
||||
return 'subscription';
|
||||
}
|
||||
|
||||
return $this->context() === 'product' ? $product_intent : $other_context_intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of WC order on the order-pay page, or 0.
|
||||
*
|
||||
|
|
|
@ -22,22 +22,15 @@ interface SmartButtonInterface {
|
|||
public function render_wrapper(): bool;
|
||||
|
||||
/**
|
||||
* Whether the scripts should be loaded.
|
||||
* Whether any of our scripts (for DCC or product, mini-cart, non-block cart/checkout) should be loaded.
|
||||
*/
|
||||
public function should_load(): bool;
|
||||
public function should_load_ppcp_script(): bool;
|
||||
|
||||
/**
|
||||
* Enqueues the necessary scripts.
|
||||
* Enqueues our scripts/styles (for DCC and product, mini-cart and non-block cart/checkout)
|
||||
*/
|
||||
public function enqueue(): void;
|
||||
|
||||
/**
|
||||
* Whether the running installation could save vault tokens or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_save_vault_token(): bool;
|
||||
|
||||
/**
|
||||
* The configuration for the smart buttons.
|
||||
*
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Button;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
|
||||
|
@ -63,14 +64,12 @@ class ButtonModule implements ModuleInterface {
|
|||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
static function () use ( $c ) {
|
||||
|
||||
$smart_button = $c->get( 'button.smart-button' );
|
||||
/**
|
||||
* The Smart Button.
|
||||
*
|
||||
* @var SmartButtonInterface $smart_button
|
||||
*/
|
||||
$smart_button->enqueue();
|
||||
assert( $smart_button instanceof SmartButtonInterface );
|
||||
|
||||
if ( $smart_button->should_load_ppcp_script() ) {
|
||||
$smart_button->enqueue();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -147,6 +146,16 @@ class ButtonModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . ApproveSubscriptionEndpoint::ENDPOINT,
|
||||
static function () use ( $container ) {
|
||||
$endpoint = $container->get( 'button.endpoint.approve-subscription' );
|
||||
assert( $endpoint instanceof ApproveSubscriptionEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . CreateOrderEndpoint::ENDPOINT,
|
||||
static function () use ( $container ) {
|
||||
|
|
|
@ -180,7 +180,7 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
);
|
||||
}
|
||||
$this->session_handler->replace_order( $order );
|
||||
wp_send_json_success( $order );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
if ( $this->order_helper->contains_physical_goods( $order ) && ! $order->status()->is( OrderStatus::APPROVED ) && ! $order->status()->is( OrderStatus::CREATED ) ) {
|
||||
|
@ -198,7 +198,7 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
$this->session_handler->replace_funding_source( $funding_source );
|
||||
|
||||
$this->session_handler->replace_order( $order );
|
||||
wp_send_json_success( $order );
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
} catch ( Exception $error ) {
|
||||
$this->logger->error( 'Order approve failed: ' . $error->getMessage() );
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/**
|
||||
* Endpoint to handle PayPal Subscription created.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Button\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
|
||||
/**
|
||||
* Class ApproveSubscriptionEndpoint
|
||||
*/
|
||||
class ApproveSubscriptionEndpoint implements EndpointInterface {
|
||||
|
||||
const ENDPOINT = 'ppc-approve-subscription';
|
||||
|
||||
/**
|
||||
* The request data helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* The session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* ApproveSubscriptionEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param SessionHandler $session_handler The session handler.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
OrderEndpoint $order_endpoint,
|
||||
SessionHandler $session_handler
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->session_handler = $session_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws RuntimeException When order not found or handling failed.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
if ( ! isset( $data['order_id'] ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No order id given', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
$order = $this->order_endpoint->order( $data['order_id'] );
|
||||
$this->session_handler->replace_order( $order );
|
||||
|
||||
if ( isset( $data['subscription_id'] ) ) {
|
||||
WC()->session->set( 'ppcp_subscription_id', $data['subscription_id'] );
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -138,6 +138,20 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
protected $early_validation_enabled;
|
||||
|
||||
/**
|
||||
* The contexts that should have the Pay Now button.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $pay_now_contexts;
|
||||
|
||||
/**
|
||||
* If true, the shipping methods are sent to PayPal allowing the customer to select it inside the popup.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $handle_shipping_in_paypal;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -159,6 +173,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* @param bool $registration_needed Whether a new user must be registered during checkout.
|
||||
* @param string $card_billing_data_mode The value of card_billing_data_mode from the settings.
|
||||
* @param bool $early_validation_enabled Whether to execute WC validation of the checkout form.
|
||||
* @param string[] $pay_now_contexts The contexts that should have the Pay Now button.
|
||||
* @param bool $handle_shipping_in_paypal If true, the shipping methods are sent to PayPal allowing the customer to select it inside the popup.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -173,6 +189,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
bool $registration_needed,
|
||||
string $card_billing_data_mode,
|
||||
bool $early_validation_enabled,
|
||||
array $pay_now_contexts,
|
||||
bool $handle_shipping_in_paypal,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
|
@ -187,6 +205,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$this->registration_needed = $registration_needed;
|
||||
$this->card_billing_data_mode = $card_billing_data_mode;
|
||||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->handle_shipping_in_paypal = $handle_shipping_in_paypal;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
@ -226,7 +246,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
}
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
} else {
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart();
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart( null, $this->handle_shipping_in_paypal );
|
||||
|
||||
// The cart does not have any info about payment method, so we must handle free trial here.
|
||||
if ( (
|
||||
|
@ -272,7 +292,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
! $this->early_order_handler->should_create_early_order()
|
||||
|| $this->registration_needed
|
||||
|| isset( $data['createaccount'] ) && '1' === $data['createaccount'] ) {
|
||||
wp_send_json_success( $order->to_array() );
|
||||
wp_send_json_success( $this->make_response( $order ) );
|
||||
}
|
||||
|
||||
$this->early_order_handler->register_for_order( $order );
|
||||
|
@ -284,7 +304,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$wc_order->save_meta_data();
|
||||
}
|
||||
|
||||
wp_send_json_success( $order->to_array() );
|
||||
wp_send_json_success( $this->make_response( $order ) );
|
||||
return true;
|
||||
|
||||
} catch ( ValidationException $error ) {
|
||||
|
@ -342,7 +362,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* during the "onApprove"-JS callback or the webhook listener.
|
||||
*/
|
||||
if ( ! $this->early_order_handler->should_create_early_order() ) {
|
||||
wp_send_json_success( $order->to_array() );
|
||||
wp_send_json_success( $this->make_response( $order ) );
|
||||
}
|
||||
$this->early_order_handler->register_for_order( $order );
|
||||
return $data;
|
||||
|
@ -385,6 +405,9 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$funding_source
|
||||
);
|
||||
|
||||
$action = in_array( $this->parsed_request_data['context'], $this->pay_now_contexts, true ) ?
|
||||
ApplicationContext::USER_ACTION_PAY_NOW : ApplicationContext::USER_ACTION_CONTINUE;
|
||||
|
||||
if ( 'card' === $funding_source ) {
|
||||
if ( CardBillingMode::MINIMAL_INPUT === $this->card_billing_data_mode ) {
|
||||
if ( ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS === $shipping_preference ) {
|
||||
|
@ -410,7 +433,9 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$shipping_preference,
|
||||
$payer,
|
||||
null,
|
||||
$this->payment_method()
|
||||
$this->payment_method(),
|
||||
'',
|
||||
$action
|
||||
);
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
// Looks like currently there is no proper way to validate the shipping address for PayPal,
|
||||
|
@ -544,4 +569,17 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response data for success response.
|
||||
*
|
||||
* @param Order $order The PayPal order.
|
||||
* @return array
|
||||
*/
|
||||
private function make_response( Order $order ): array {
|
||||
return array(
|
||||
'id' => $order->id(),
|
||||
'custom_id' => $order->purchase_units()[0]->custom_id(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,24 +17,32 @@ trait ContextTrait {
|
|||
* @return string
|
||||
*/
|
||||
protected function context(): string {
|
||||
$context = 'mini-cart';
|
||||
if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) {
|
||||
$context = 'product';
|
||||
return 'product';
|
||||
}
|
||||
|
||||
// has_block may not work if called too early, such as during the block registration.
|
||||
if ( has_block( 'woocommerce/cart' ) ) {
|
||||
return 'cart-block';
|
||||
}
|
||||
|
||||
if ( is_cart() ) {
|
||||
$context = 'cart';
|
||||
}
|
||||
|
||||
if ( is_checkout() && ! $this->is_paypal_continuation() ) {
|
||||
$context = 'checkout';
|
||||
return 'cart';
|
||||
}
|
||||
|
||||
if ( is_checkout_pay_page() ) {
|
||||
$context = 'pay-now';
|
||||
return 'pay-now';
|
||||
}
|
||||
|
||||
return $context;
|
||||
if ( has_block( 'woocommerce/checkout' ) ) {
|
||||
return 'checkout-block';
|
||||
}
|
||||
|
||||
if ( ( is_checkout() ) && ! $this->is_paypal_continuation() ) {
|
||||
return 'checkout';
|
||||
}
|
||||
|
||||
return 'mini-cart';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,6 +49,9 @@ return array(
|
|||
'ppcp-gateway-settings',
|
||||
'ppcp-webhooks-status-page',
|
||||
'ppcp-tracking',
|
||||
'ppcp-fraudnet',
|
||||
'ppcp-gzd-compat',
|
||||
'ppcp-clear-db',
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -67,8 +67,12 @@ class PPECHelper {
|
|||
* @return bool
|
||||
*/
|
||||
public static function site_has_ppec_subscriptions() {
|
||||
global $wpdb;
|
||||
$has_ppec_subscriptions = get_transient( 'ppcp_has_ppec_subscriptions' );
|
||||
if ( $has_ppec_subscriptions !== false ) {
|
||||
return $has_ppec_subscriptions === 'true';
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT 1 FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
|
||||
|
@ -80,6 +84,12 @@ class PPECHelper {
|
|||
)
|
||||
);
|
||||
|
||||
set_transient(
|
||||
'ppcp_has_ppec_subscriptions',
|
||||
! empty( $result ) ? 'true' : 'false',
|
||||
3 * MONTH_IN_SECONDS
|
||||
);
|
||||
|
||||
return ! empty( $result );
|
||||
}
|
||||
|
||||
|
@ -92,7 +102,9 @@ class PPECHelper {
|
|||
/**
|
||||
* The filter returning whether the compatibility layer for PPEC Subscriptions should be initialized.
|
||||
*/
|
||||
return ( ! self::is_gateway_available() ) && self::site_has_ppec_subscriptions() && apply_filters( 'woocommerce_paypal_payments_process_legacy_subscriptions', true );
|
||||
return ( ! self::is_gateway_available() )
|
||||
&& self::site_has_ppec_subscriptions()
|
||||
&& apply_filters( 'woocommerce_paypal_payments_process_legacy_subscriptions', true );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ class SubscriptionsHandler {
|
|||
/**
|
||||
* Adds a mock gateway to disguise as PPEC when needed. Hooked onto `woocommerce_payment_gateways`.
|
||||
* The mock gateway fixes display issues where subscriptions paid via PPEC appear as "via Manual Renewal" and also
|
||||
* prevents Subscriptions from automatically changing the payment method to "manual" when a subscription is edited.
|
||||
* prevents subscriptions from automatically changing the payment method to "manual" when a subscription is edited.
|
||||
*
|
||||
* @param array $gateways List of gateways.
|
||||
* @return array
|
||||
|
|
|
@ -123,6 +123,11 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
public function handle_request(): bool {
|
||||
|
||||
try {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
$is_sandbox = isset( $data['env'] ) && 'sandbox' === $data['env'];
|
||||
$this->settings->set( 'sandbox_on', $is_sandbox );
|
||||
|
|
|
@ -107,6 +107,11 @@ class PayUponInvoiceEndpoint implements EndpointInterface {
|
|||
* @throws NotFoundException When order not found or handling failed.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
$signup_links = array();
|
||||
|
||||
try {
|
||||
|
|
|
@ -85,6 +85,11 @@ class OrderTrackingEndpoint {
|
|||
* Handles the request.
|
||||
*/
|
||||
public function handle_request(): void {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
$action = $data['action'];
|
||||
|
|
|
@ -15,17 +15,7 @@ use WooCommerce\PayPalCommerce\Session\Cancellation\CancelView;
|
|||
|
||||
return array(
|
||||
'session.handler' => function ( ContainerInterface $container ) : SessionHandler {
|
||||
|
||||
if ( is_null( WC()->session ) ) {
|
||||
return new SessionHandler();
|
||||
}
|
||||
$result = WC()->session->get( SessionHandler::ID );
|
||||
if ( is_a( $result, SessionHandler::class ) ) {
|
||||
return $result;
|
||||
}
|
||||
$session_handler = new SessionHandler();
|
||||
WC()->session->set( SessionHandler::ID, $session_handler );
|
||||
return $session_handler;
|
||||
return new SessionHandler();
|
||||
},
|
||||
'session.cancellation.view' => function ( ContainerInterface $container ) : CancelView {
|
||||
return new CancelView(
|
||||
|
|
|
@ -16,6 +16,8 @@ use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
|||
*/
|
||||
class CancelController {
|
||||
|
||||
public const NONCE = 'ppcp-cancel';
|
||||
|
||||
/**
|
||||
* The Session handler.
|
||||
*
|
||||
|
@ -49,12 +51,11 @@ class CancelController {
|
|||
* Runs the controller.
|
||||
*/
|
||||
public function run() {
|
||||
$param_name = 'ppcp-cancel';
|
||||
$nonce = 'ppcp-cancel-' . get_current_user_id();
|
||||
$param_name = self::NONCE;
|
||||
if ( isset( $_GET[ $param_name ] ) && // Input var ok.
|
||||
wp_verify_nonce(
|
||||
sanitize_text_field( wp_unslash( $_GET[ $param_name ] ) ), // Input var ok.
|
||||
$nonce
|
||||
self::NONCE
|
||||
)
|
||||
) { // Input var ok.
|
||||
$this->session_handler->destroy_session_data();
|
||||
|
@ -74,11 +75,12 @@ class CancelController {
|
|||
return; // Ignore for card buttons.
|
||||
}
|
||||
|
||||
$url = add_query_arg( array( $param_name => wp_create_nonce( $nonce ) ), wc_get_checkout_url() );
|
||||
$url = add_query_arg( array( $param_name => wp_create_nonce( self::NONCE ) ), wc_get_checkout_url() );
|
||||
add_action(
|
||||
'woocommerce_review_order_after_submit',
|
||||
function () use ( $url ) {
|
||||
$this->view->render_session_cancellation( $url, $this->session_handler->funding_source() );
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput
|
||||
echo $this->view->render_session_cancellation( $url, $this->session_handler->funding_source() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ class CancelView {
|
|||
* @param string $url The URL.
|
||||
* @param string|null $funding_source The ID of the funding source, such as 'venmo'.
|
||||
*/
|
||||
public function render_session_cancellation( string $url, ?string $funding_source ) {
|
||||
public function render_session_cancellation( string $url, ?string $funding_source ): string {
|
||||
ob_start();
|
||||
?>
|
||||
<p id="ppcp-cancel"
|
||||
class="has-text-align-center ppcp-cancel"
|
||||
|
@ -73,5 +74,6 @@ class CancelView {
|
|||
?>
|
||||
</p>
|
||||
<?php
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
|||
*/
|
||||
class SessionHandler {
|
||||
|
||||
const ID = 'ppcp';
|
||||
private const SESSION_KEY = 'ppcp';
|
||||
|
||||
/**
|
||||
* The Order.
|
||||
|
@ -33,7 +33,7 @@ class SessionHandler {
|
|||
private $bn_code = '';
|
||||
|
||||
/**
|
||||
* If PayPal respondes with INSTRUMENT_DECLINED, we only
|
||||
* If PayPal responds with INSTRUMENT_DECLINED, we only
|
||||
* want to go max. three times through the process of trying again.
|
||||
*
|
||||
* @var int
|
||||
|
@ -53,6 +53,8 @@ class SessionHandler {
|
|||
* @return Order|null
|
||||
*/
|
||||
public function order() {
|
||||
$this->load_session();
|
||||
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
|
@ -60,13 +62,13 @@ class SessionHandler {
|
|||
* Replaces the current order.
|
||||
*
|
||||
* @param Order $order The new order.
|
||||
*
|
||||
* @return SessionHandler
|
||||
*/
|
||||
public function replace_order( Order $order ) : SessionHandler {
|
||||
public function replace_order( Order $order ): void {
|
||||
$this->load_session();
|
||||
|
||||
$this->order = $order;
|
||||
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,6 +77,8 @@ class SessionHandler {
|
|||
* @return string
|
||||
*/
|
||||
public function bn_code() : string {
|
||||
$this->load_session();
|
||||
|
||||
return $this->bn_code;
|
||||
}
|
||||
|
||||
|
@ -82,13 +86,13 @@ class SessionHandler {
|
|||
* Replaces the BN Code.
|
||||
*
|
||||
* @param string $bn_code The new BN Code.
|
||||
*
|
||||
* @return SessionHandler
|
||||
*/
|
||||
public function replace_bn_code( string $bn_code ) : SessionHandler {
|
||||
public function replace_bn_code( string $bn_code ) : void {
|
||||
$this->load_session();
|
||||
|
||||
$this->bn_code = $bn_code;
|
||||
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,6 +101,8 @@ class SessionHandler {
|
|||
* @return string|null
|
||||
*/
|
||||
public function funding_source(): ?string {
|
||||
$this->load_session();
|
||||
|
||||
return $this->funding_source;
|
||||
}
|
||||
|
||||
|
@ -104,13 +110,13 @@ class SessionHandler {
|
|||
* Replaces the funding source of the current checkout.
|
||||
*
|
||||
* @param string|null $funding_source The funding source.
|
||||
*
|
||||
* @return SessionHandler
|
||||
*/
|
||||
public function replace_funding_source( ?string $funding_source ): SessionHandler {
|
||||
public function replace_funding_source( ?string $funding_source ): void {
|
||||
$this->load_session();
|
||||
|
||||
$this->funding_source = $funding_source;
|
||||
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,18 +125,20 @@ class SessionHandler {
|
|||
* @return int
|
||||
*/
|
||||
public function insufficient_funding_tries() : int {
|
||||
$this->load_session();
|
||||
|
||||
return $this->insufficient_funding_tries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the number of tries, the customer has done in this session.
|
||||
*
|
||||
* @return SessionHandler
|
||||
*/
|
||||
public function increment_insufficient_funding_tries() : SessionHandler {
|
||||
public function increment_insufficient_funding_tries(): void {
|
||||
$this->load_session();
|
||||
|
||||
$this->insufficient_funding_tries++;
|
||||
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,9 +156,52 @@ class SessionHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stores the session.
|
||||
* Stores the data into the WC session.
|
||||
*/
|
||||
private function store_session() {
|
||||
WC()->session->set( self::ID, $this );
|
||||
private function store_session(): void {
|
||||
WC()->session->set( self::SESSION_KEY, self::make_array( $this ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data from the session.
|
||||
*/
|
||||
private function load_session(): void {
|
||||
if ( isset( WC()->session ) ) {
|
||||
$data = WC()->session->get( self::SESSION_KEY );
|
||||
} else {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
if ( $data instanceof SessionHandler ) {
|
||||
$data = self::make_array( $data );
|
||||
} elseif ( ! is_array( $data ) ) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
$this->order = $data['order'] ?? null;
|
||||
if ( ! $this->order instanceof Order ) {
|
||||
$this->order = null;
|
||||
}
|
||||
$this->bn_code = (string) ( $data['bn_code'] ?? '' );
|
||||
$this->insufficient_funding_tries = (int) ( $data['insufficient_funding_tries'] ?? '' );
|
||||
$this->funding_source = $data['funding_source'] ?? null;
|
||||
if ( ! is_string( $this->funding_source ) ) {
|
||||
$this->funding_source = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given SessionHandler object into an array.
|
||||
*
|
||||
* @param SessionHandler $obj The object to convert.
|
||||
* @return array
|
||||
*/
|
||||
private static function make_array( SessionHandler $obj ): array {
|
||||
return array(
|
||||
'order' => $obj->order,
|
||||
'bn_code' => $obj->bn_code,
|
||||
'insufficient_funding_tries' => $obj->insufficient_funding_tries,
|
||||
'funding_source' => $obj->funding_source,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,4 +43,15 @@ return array(
|
|||
$endpoint = $container->get( 'api.endpoint.payment-token' );
|
||||
return new PaymentTokenRepository( $factory, $endpoint );
|
||||
},
|
||||
'subscription.api-handler' => static function( ContainerInterface $container ): SubscriptionsApiHandler {
|
||||
return new SubscriptionsApiHandler(
|
||||
$container->get( 'api.endpoint.catalog-products' ),
|
||||
$container->get( 'api.factory.product' ),
|
||||
$container->get( 'api.endpoint.billing-plans' ),
|
||||
$container->get( 'api.factory.billing-cycle' ),
|
||||
$container->get( 'api.factory.payment-preferences' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -127,4 +127,27 @@ class SubscriptionHelper {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether subscription needs subscription intent.
|
||||
*
|
||||
* @param string $subscription_mode The subscriptiopn mode.
|
||||
* @return bool
|
||||
*/
|
||||
public function need_subscription_intent( string $subscription_mode ): bool {
|
||||
if ( defined( 'PPCP_FLAG_SUBSCRIPTIONS_API' ) && ! PPCP_FLAG_SUBSCRIPTIONS_API ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $subscription_mode === 'subscriptions_api' ) {
|
||||
if (
|
||||
$this->current_product_is_subscription()
|
||||
|| ( ( is_cart() || is_checkout() ) && $this->cart_contains_subscription() )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,13 +162,6 @@ class RenewalHandler {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Renewal for order %d is completed.',
|
||||
$wc_order->get_id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,6 +179,7 @@ class RenewalHandler {
|
|||
if ( ! $token ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
$payer = $this->payer_factory->from_customer( $customer );
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
|
@ -217,6 +211,13 @@ class RenewalHandler {
|
|||
if ( $this->capture_authorized_downloads( $order ) ) {
|
||||
$this->authorized_payments_processor->capture_authorized_payment( $wc_order );
|
||||
}
|
||||
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
'Renewal for order %d is completed.',
|
||||
$wc_order->get_id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use Exception;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -102,8 +107,19 @@ class SubscriptionModule implements ModuleInterface {
|
|||
function( array $data ) use ( $c ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$wc_order_action = wc_clean( wp_unslash( $_POST['wc_order_action'] ?? '' ) );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$subscription_id = wc_clean( wp_unslash( $_POST['post_ID'] ?? '' ) );
|
||||
if ( ! $subscription_id ) {
|
||||
return $data;
|
||||
}
|
||||
$subscription = wc_get_order( $subscription_id );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (
|
||||
$wc_order_action === 'wcs_process_renewal'
|
||||
$wc_order_action === 'wcs_process_renewal' && $subscription->get_payment_method() === CreditCardGateway::ID
|
||||
&& isset( $data['payment_source']['token'] ) && $data['payment_source']['token']['type'] === 'PAYMENT_METHOD_TOKEN'
|
||||
&& isset( $data['payment_source']['token']['source']->card )
|
||||
) {
|
||||
|
@ -135,6 +151,10 @@ class SubscriptionModule implements ModuleInterface {
|
|||
return $data;
|
||||
}
|
||||
);
|
||||
|
||||
if ( defined( 'PPCP_FLAG_SUBSCRIPTIONS_API' ) && PPCP_FLAG_SUBSCRIPTIONS_API ) {
|
||||
$this->subscriptions_api_integration( $c );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,8 +285,8 @@ class SubscriptionModule implements ModuleInterface {
|
|||
SubscriptionHelper $subscription_helper
|
||||
) {
|
||||
|
||||
if ( $settings->has( 'vault_enabled' )
|
||||
&& $settings->get( 'vault_enabled' )
|
||||
if ( $settings->has( 'vault_enabled_dcc' )
|
||||
&& $settings->get( 'vault_enabled_dcc' )
|
||||
&& $subscription_helper->is_subscription_change_payment()
|
||||
&& CreditCardGateway::ID === $id
|
||||
) {
|
||||
|
@ -303,4 +323,406 @@ class SubscriptionModule implements ModuleInterface {
|
|||
|
||||
return $default_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds PayPal subscriptions API integration.
|
||||
*
|
||||
* @param ContainerInterface $c The container.
|
||||
* @return void
|
||||
* @throws Exception When something went wrong.
|
||||
*/
|
||||
protected function subscriptions_api_integration( ContainerInterface $c ): void {
|
||||
add_action(
|
||||
'save_post',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $product_id ) use ( $c ) {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
try {
|
||||
$subscriptions_mode = $settings->get( 'subscriptions_mode' );
|
||||
} catch ( NotFoundException $exception ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nonce = wc_clean( wp_unslash( $_POST['_wcsnonce'] ?? '' ) );
|
||||
if (
|
||||
$subscriptions_mode !== 'subscriptions_api'
|
||||
|| ! is_string( $nonce )
|
||||
|| ! wp_verify_nonce( $nonce, 'wcs_subscription_meta' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
if ( ! is_a( $product, WC_Product::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) );
|
||||
$product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product );
|
||||
$product->save();
|
||||
|
||||
if ( $product->get_type() === 'subscription' && $enable_subscription_product === 'yes' ) {
|
||||
$subscriptions_api_handler = $c->get( 'subscription.api-handler' );
|
||||
assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler );
|
||||
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$subscriptions_api_handler->update_product( $product );
|
||||
$subscriptions_api_handler->update_plan( $product );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) {
|
||||
$subscriptions_api_handler->create_product( $product );
|
||||
}
|
||||
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) );
|
||||
if ( ! is_string( $subscription_plan_name ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name );
|
||||
$product->save();
|
||||
|
||||
$subscriptions_api_handler->create_plan( $subscription_plan_name, $product );
|
||||
}
|
||||
}
|
||||
},
|
||||
12
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_process_shop_subscription_meta',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $id, $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id ) {
|
||||
$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 );
|
||||
}
|
||||
|
||||
$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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
20,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_order_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $actions, $subscription ): array {
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $actions;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id && isset( $actions['wcs_process_renewal'] ) ) {
|
||||
unset( $actions['wcs_process_renewal'] );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
},
|
||||
20,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'wcs_view_subscription_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $actions, $subscription ): array {
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $actions;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id && $subscription->get_status() === 'active' ) {
|
||||
$url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'change_subscription_to' => 'cancelled',
|
||||
'ppcp_cancel_subscription' => $subscription->get_id(),
|
||||
)
|
||||
),
|
||||
'ppcp_cancel_subscription_nonce'
|
||||
);
|
||||
|
||||
array_unshift(
|
||||
$actions,
|
||||
array(
|
||||
'url' => esc_url( $url ),
|
||||
'name' => esc_html__( 'Cancel', 'woocommerce-paypal-payments' ),
|
||||
)
|
||||
);
|
||||
|
||||
$actions['cancel']['name'] = esc_html__( 'Suspend', 'woocommerce-paypal-payments' );
|
||||
unset( $actions['subscription_renewal_early'] );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
},
|
||||
11,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_loaded',
|
||||
function() use ( $c ) {
|
||||
if ( ! function_exists( 'wcs_get_subscription' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cancel_subscription_id = wc_clean( wp_unslash( $_GET['ppcp_cancel_subscription'] ?? '' ) );
|
||||
$subscription = wcs_get_subscription( absint( $cancel_subscription_id ) );
|
||||
if ( ! wcs_is_subscription( $subscription ) || $subscription === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
$nonce = wc_clean( wp_unslash( $_GET['_wpnonce'] ?? '' ) );
|
||||
if ( ! is_string( $nonce ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$subscription_id
|
||||
&& $cancel_subscription_id
|
||||
&& $nonce
|
||||
) {
|
||||
if (
|
||||
! wp_verify_nonce( $nonce, 'ppcp_cancel_subscription_nonce' )
|
||||
|| ! user_can( get_current_user_id(), 'edit_shop_subscription_status', $subscription->get_id() )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' );
|
||||
try {
|
||||
$subscriptions_endpoint->cancel( $subscription_id );
|
||||
|
||||
$subscription->update_status( 'cancelled' );
|
||||
$subscription->add_order_note( __( 'Subscription cancelled by the subscriber from their account page.', 'woocommerce-paypal-payments' ) );
|
||||
wc_add_notice( __( 'Your subscription has been cancelled.', 'woocommerce-paypal-payments' ) );
|
||||
|
||||
wp_safe_redirect( $subscription->get_view_order_url() );
|
||||
exit;
|
||||
} 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 cancel subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_subscription_before_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id ) {
|
||||
$environment = $c->get( 'onboarding.environment' );
|
||||
$host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||
?>
|
||||
<tr>
|
||||
<td><?php esc_html_e( 'PayPal Subscription', 'woocommerce-paypal-payments' ); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo esc_url( $host . "/myaccount/autopay/connect/{$subscription_id}" ); ?>" id="ppcp-subscription-id" target="_blank"><?php echo esc_html( $subscription_id ); ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_product_options_general_product_data',
|
||||
function() use ( $c ) {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
try {
|
||||
$subscriptions_mode = $settings->get( 'subscriptions_mode' );
|
||||
if ( $subscriptions_mode === 'subscriptions_api' ) {
|
||||
/**
|
||||
* Needed for getting global post object.
|
||||
*
|
||||
* @psalm-suppress InvalidGlobal
|
||||
*/
|
||||
global $post;
|
||||
$product = wc_get_product( $post->ID );
|
||||
if ( ! is_a( $product, WC_Product::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' );
|
||||
$subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' );
|
||||
|
||||
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
|
||||
echo '<p class="form-field"><label for="_ppcp_enable_subscription_product">Connect to PayPal</label><input type="checkbox" id="ppcp_enable_subscription_product" name="_ppcp_enable_subscription_product" value="yes" ' . checked( $enable_subscription_product, 'yes', false ) . '/><span class="description">Connect Product to PayPal Subscriptions Plan</span></p>';
|
||||
|
||||
$subscription_product = $product->get_meta( 'ppcp_subscription_product' );
|
||||
$subscription_plan = $product->get_meta( 'ppcp_subscription_plan' );
|
||||
if ( $subscription_product && $subscription_plan ) {
|
||||
$environment = $c->get( 'onboarding.environment' );
|
||||
$host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||
echo '<p class="form-field"><label>Product</label><a href="' . esc_url( $host . '/billing/plans/products/' . $subscription_product['id'] ) . '" target="_blank">' . esc_attr( $subscription_product['id'] ) . '</a></p>';
|
||||
echo '<p class="form-field"><label>Plan</label><a href="' . esc_url( $host . '/billing/plans/' . $subscription_plan['id'] ) . '" target="_blank">' . esc_attr( $subscription_plan['id'] ) . '</a></p>';
|
||||
} else {
|
||||
echo '<p class="form-field"><label for="_ppcp_subscription_plan_name">Plan Name</label><input type="text" class="short" id="ppcp_subscription_plan_name" name="_ppcp_subscription_plan_name" value="' . esc_attr( $subscription_plan_name ) . '"></p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
} catch ( NotFoundException $exception ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_order_data_store_cpt_get_orders_query',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $query, $query_vars ): array {
|
||||
if ( ! empty( $query_vars['ppcp_subscription'] ) ) {
|
||||
$query['meta_query'][] = array(
|
||||
'key' => 'ppcp_subscription',
|
||||
'value' => esc_attr( $query_vars['ppcp_subscription'] ),
|
||||
);
|
||||
}
|
||||
|
||||
return $query;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_customer_changed_subscription_to_cancelled',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id ) {
|
||||
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
|
||||
assert( $subscriptions_endpoint instanceof BillingSubscriptions );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_customer_changed_subscription_to_active',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id ) {
|
||||
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
|
||||
assert( $subscriptions_endpoint instanceof BillingSubscriptions );
|
||||
|
||||
try {
|
||||
$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 active subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
277
modules/ppcp-subscription/src/SubscriptionsApiHandler.php
Normal file
277
modules/ppcp-subscription/src/SubscriptionsApiHandler.php
Normal file
|
@ -0,0 +1,277 @@
|
|||
<?php
|
||||
/**
|
||||
* The subscription module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\BillingCycle;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
|
||||
|
||||
/**
|
||||
* Class SubscriptionsApiHandler
|
||||
*/
|
||||
class SubscriptionsApiHandler {
|
||||
|
||||
/**
|
||||
* Catalog products.
|
||||
*
|
||||
* @var CatalogProducts
|
||||
*/
|
||||
private $products_endpoint;
|
||||
|
||||
/**
|
||||
* Product factory.
|
||||
*
|
||||
* @var ProductFactory
|
||||
*/
|
||||
private $product_factory;
|
||||
|
||||
/**
|
||||
* Billing plans.
|
||||
*
|
||||
* @var BillingPlans
|
||||
*/
|
||||
private $billing_plans_endpoint;
|
||||
|
||||
/**
|
||||
* Billing cycle factory.
|
||||
*
|
||||
* @var BillingCycleFactory
|
||||
*/
|
||||
private $billing_cycle_factory;
|
||||
|
||||
/**
|
||||
* Payment preferences factory.
|
||||
*
|
||||
* @var PaymentPreferencesFactory
|
||||
*/
|
||||
private $payment_preferences_factory;
|
||||
|
||||
/**
|
||||
* The currency.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* SubscriptionsApiHandler constructor.
|
||||
*
|
||||
* @param CatalogProducts $products_endpoint Products endpoint.
|
||||
* @param ProductFactory $product_factory Product factory.
|
||||
* @param BillingPlans $billing_plans_endpoint Billing plans endpoint.
|
||||
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
|
||||
* @param PaymentPreferencesFactory $payment_preferences_factory Payment preferences factory.
|
||||
* @param string $currency The currency.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
CatalogProducts $products_endpoint,
|
||||
ProductFactory $product_factory,
|
||||
BillingPlans $billing_plans_endpoint,
|
||||
BillingCycleFactory $billing_cycle_factory,
|
||||
PaymentPreferencesFactory $payment_preferences_factory,
|
||||
string $currency,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->products_endpoint = $products_endpoint;
|
||||
$this->product_factory = $product_factory;
|
||||
$this->billing_plans_endpoint = $billing_plans_endpoint;
|
||||
$this->billing_cycle_factory = $billing_cycle_factory;
|
||||
$this->payment_preferences_factory = $payment_preferences_factory;
|
||||
$this->currency = $currency;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Catalog Product and adds it as WC product meta.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function create_product( WC_Product $product ) {
|
||||
try {
|
||||
$subscription_product = $this->products_endpoint->create( $product->get_title(), $product->get_description() );
|
||||
$product->update_meta_data( 'ppcp_subscription_product', $subscription_product->to_array() );
|
||||
$product->save();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$this->logger->error( 'Could not create catalog product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subscription plan.
|
||||
*
|
||||
* @param string $plan_name The plan name.
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function create_plan( string $plan_name, WC_Product $product ): void {
|
||||
try {
|
||||
$subscription_plan = $this->billing_plans_endpoint->create(
|
||||
$plan_name,
|
||||
$product->get_meta( 'ppcp_subscription_product' )['id'] ?? '',
|
||||
$this->billing_cycles( $product ),
|
||||
$this->payment_preferences_factory->from_wc_product( $product )->to_array()
|
||||
);
|
||||
|
||||
$product->update_meta_data( 'ppcp_subscription_plan', $subscription_plan->to_array() );
|
||||
$product->save();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$this->logger->error( 'Could not create subscription plan on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a product.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function update_product( WC_Product $product ): void {
|
||||
try {
|
||||
$catalog_product_id = $product->get_meta( 'ppcp_subscription_product' )['id'] ?? '';
|
||||
if ( $catalog_product_id ) {
|
||||
$catalog_product = $this->products_endpoint->product( $catalog_product_id );
|
||||
$catalog_product_name = $catalog_product->name() ?: '';
|
||||
$catalog_product_description = $catalog_product->description() ?: '';
|
||||
if ( $catalog_product_name !== $product->get_title() || $catalog_product_description !== $product->get_description() ) {
|
||||
$data = array();
|
||||
if ( $catalog_product_name !== $product->get_title() ) {
|
||||
$data[] = (object) array(
|
||||
'op' => 'replace',
|
||||
'path' => '/name',
|
||||
'value' => $product->get_title(),
|
||||
);
|
||||
}
|
||||
if ( $catalog_product_description !== $product->get_description() ) {
|
||||
$data[] = (object) array(
|
||||
'op' => 'replace',
|
||||
'path' => '/description',
|
||||
'value' => $product->get_description(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->products_endpoint->update( $catalog_product_id, $data );
|
||||
}
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$this->logger->error( 'Could not update catalog product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a plan.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function update_plan( WC_Product $product ): void {
|
||||
try {
|
||||
$subscription_plan_id = $product->get_meta( 'ppcp_subscription_plan' )['id'] ?? '';
|
||||
if ( $subscription_plan_id ) {
|
||||
$subscription_plan = $this->billing_plans_endpoint->plan( $subscription_plan_id );
|
||||
|
||||
$price = $subscription_plan->billing_cycles()[0]->pricing_scheme()['fixed_price']['value'] ?? '';
|
||||
if ( $price && round( $price, 2 ) !== round( (float) $product->get_price(), 2 ) ) {
|
||||
$this->billing_plans_endpoint->update_pricing(
|
||||
$subscription_plan_id,
|
||||
$this->billing_cycle_factory->from_wc_product( $product )
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$this->logger->error( 'Could not update subscription plan on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns billing cycles based on WC Subscription product.
|
||||
*
|
||||
* @param WC_Product $product The WC Subscription product.
|
||||
* @return array
|
||||
*/
|
||||
private function billing_cycles( WC_Product $product ): array {
|
||||
$billing_cycles = array();
|
||||
$sequence = 1;
|
||||
|
||||
$trial_length = $product->get_meta( '_subscription_trial_length' ) ?? '';
|
||||
if ( $trial_length ) {
|
||||
$billing_cycles[] = ( new BillingCycle(
|
||||
array(
|
||||
'interval_unit' => $product->get_meta( '_subscription_trial_period' ),
|
||||
'interval_count' => $product->get_meta( '_subscription_trial_length' ),
|
||||
),
|
||||
$sequence,
|
||||
'TRIAL',
|
||||
array(
|
||||
'fixed_price' => array(
|
||||
'value' => '0',
|
||||
'currency_code' => $this->currency,
|
||||
),
|
||||
),
|
||||
1
|
||||
) )->to_array();
|
||||
|
||||
$sequence++;
|
||||
}
|
||||
|
||||
$billing_cycles[] = ( new BillingCycle(
|
||||
array(
|
||||
'interval_unit' => $product->get_meta( '_subscription_period' ),
|
||||
'interval_count' => $product->get_meta( '_subscription_period_interval' ),
|
||||
),
|
||||
$sequence,
|
||||
'REGULAR',
|
||||
array(
|
||||
'fixed_price' => array(
|
||||
'value' => $product->get_meta( '_subscription_price' ),
|
||||
'currency_code' => $this->currency,
|
||||
),
|
||||
),
|
||||
(int) $product->get_meta( '_subscription_length' )
|
||||
) )->to_array();
|
||||
|
||||
return $billing_cycles;
|
||||
}
|
||||
}
|
|
@ -81,8 +81,14 @@ class UninstallModule implements ModuleInterface {
|
|||
"wc_ajax_{$nonce}",
|
||||
static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names ) {
|
||||
try {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate nonce.
|
||||
$request_data->read_request( $nonce );
|
||||
|
||||
$clear_db->delete_options( $option_names );
|
||||
$clear_db->clear_scheduled_actions( $scheduled_action_names );
|
||||
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Payment token for PayPal ACDC (Advanced Credit and Debit Card).
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vaulting
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vaulting;
|
||||
|
||||
use WC_Payment_Token;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenACDC
|
||||
*/
|
||||
class PaymentTokenACDC extends WC_Payment_Token {
|
||||
/**
|
||||
* Token Type String.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'ACDC';
|
||||
|
||||
/**
|
||||
* Stores Credit Card payment token data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extra_data = array(
|
||||
'last4' => '',
|
||||
'card_type' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the last four digits.
|
||||
*
|
||||
* @param string $context The context.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get_last4( $context = 'view' ) {
|
||||
return $this->get_prop( 'last4', $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last four digits.
|
||||
*
|
||||
* @param string $last4 Last four digits.
|
||||
*/
|
||||
public function set_last4( $last4 ) {
|
||||
$this->set_prop( 'last4', $last4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the card type (mastercard, visa, ...).
|
||||
*
|
||||
* @param string $context The context.
|
||||
* @return string Card type
|
||||
*/
|
||||
public function get_card_type( $context = 'view' ) {
|
||||
return $this->get_prop( 'card_type', $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the card type (mastercard, visa, ...).
|
||||
*
|
||||
* @param string $type Credit card type (mastercard, visa, ...).
|
||||
*/
|
||||
public function set_card_type( $type ) {
|
||||
$this->set_prop( 'card_type', $type );
|
||||
}
|
||||
}
|
|
@ -19,14 +19,12 @@ class PaymentTokenFactory {
|
|||
*
|
||||
* @param string $type The type of WC payment token.
|
||||
*
|
||||
* @return void|PaymentTokenACDC|PaymentTokenPayPal
|
||||
* @return void|PaymentTokenPayPal
|
||||
*/
|
||||
public function create( string $type ) {
|
||||
switch ( $type ) {
|
||||
case 'paypal':
|
||||
return new PaymentTokenPayPal();
|
||||
case 'acdc':
|
||||
return new PaymentTokenACDC();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Vaulting;
|
|||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Payment_Token_CC;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
@ -64,23 +65,26 @@ class PaymentTokensMigration {
|
|||
* @param int $id WooCommerce customer id.
|
||||
*/
|
||||
public function migrate_payment_tokens_for_user( int $id ):void {
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $id );
|
||||
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id );
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $id );
|
||||
$total_tokens = count( $tokens );
|
||||
$this->logger->info( 'Migrating ' . (string) $total_tokens . ' tokens for user ' . (string) $id );
|
||||
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( $this->token_exist( $wc_tokens, $token ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $token->source()->card ) ) {
|
||||
$payment_token_acdc = $this->payment_token_factory->create( 'acdc' );
|
||||
assert( $payment_token_acdc instanceof PaymentTokenACDC );
|
||||
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, CreditCardGateway::ID );
|
||||
if ( $this->token_exist( $wc_tokens, $token ) ) {
|
||||
$this->logger->info( 'Token already exist for user ' . (string) $id );
|
||||
continue;
|
||||
}
|
||||
|
||||
$payment_token_acdc = new WC_Payment_Token_CC();
|
||||
$payment_token_acdc->set_token( $token->id() );
|
||||
$payment_token_acdc->set_user_id( $id );
|
||||
$payment_token_acdc->set_gateway_id( CreditCardGateway::ID );
|
||||
$payment_token_acdc->set_last4( $token->source()->card->last_digits );
|
||||
$payment_token_acdc->set_card_type( $token->source()->card->brand );
|
||||
$payment_token_acdc->set_expiry_year( '0000' );
|
||||
$payment_token_acdc->set_expiry_month( '00' );
|
||||
|
||||
try {
|
||||
$payment_token_acdc->save();
|
||||
|
@ -92,6 +96,12 @@ class PaymentTokensMigration {
|
|||
continue;
|
||||
}
|
||||
} elseif ( $token->source()->paypal ) {
|
||||
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, PayPalGateway::ID );
|
||||
if ( $this->token_exist( $wc_tokens, $token ) ) {
|
||||
$this->logger->info( 'Token already exist for user ' . (string) $id );
|
||||
continue;
|
||||
}
|
||||
|
||||
$payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
|
||||
assert( $payment_token_paypal instanceof PaymentTokenPayPal );
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WP_User_Query;
|
||||
|
||||
/**
|
||||
|
@ -47,11 +48,6 @@ class VaultingModule implements ModuleInterface {
|
|||
* @throws NotFoundException When service could not be found.
|
||||
*/
|
||||
public function run( ContainerInterface $container ): void {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
if ( ! $settings->has( 'vault_enabled' ) || ! $settings->get( 'vault_enabled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$listener = $container->get( 'vaulting.customer-approval-listener' );
|
||||
assert( $listener instanceof CustomerApprovalListener );
|
||||
|
||||
|
@ -92,10 +88,6 @@ class VaultingModule implements ModuleInterface {
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $type ) {
|
||||
if ( $type === 'WC_Payment_Token_ACDC' ) {
|
||||
return PaymentTokenACDC::class;
|
||||
}
|
||||
|
||||
if ( $type === 'WC_Payment_Token_PayPal' ) {
|
||||
return PaymentTokenPayPal::class;
|
||||
}
|
||||
|
@ -116,13 +108,6 @@ class VaultingModule implements ModuleInterface {
|
|||
return $item;
|
||||
}
|
||||
|
||||
if ( strtolower( $payment_token->get_type() ) === 'acdc' ) {
|
||||
assert( $payment_token instanceof PaymentTokenACDC );
|
||||
$item['method']['brand'] = $payment_token->get_card_type() . ' ...' . $payment_token->get_last4();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
if ( strtolower( $payment_token->get_type() ) === 'paypal' ) {
|
||||
assert( $payment_token instanceof PaymentTokenPayPal );
|
||||
$item['method']['brand'] = $payment_token->get_email();
|
||||
|
@ -179,23 +164,37 @@ class VaultingModule implements ModuleInterface {
|
|||
add_action(
|
||||
'woocommerce_paypal_payments_gateway_migrate_on_update',
|
||||
function () use ( $container ) {
|
||||
// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
$customers = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'limit' => -1,
|
||||
'meta_key' => 'ppcp-vault-token',
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
$migrate = $container->get( 'vaulting.payment-tokens-migration' );
|
||||
assert( $migrate instanceof PaymentTokensMigration );
|
||||
|
||||
foreach ( $customers->get_results() as $id ) {
|
||||
$migrate->migrate_payment_tokens_for_user( (int) $id );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
if ( $settings->has( 'vault_enabled' ) && $settings->get( 'vault_enabled' ) && $settings->has( 'vault_enabled_dcc' ) ) {
|
||||
$settings->set( 'vault_enabled_dcc', true );
|
||||
$settings->persist();
|
||||
}
|
||||
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
$this->migrate_payment_tokens( $logger );
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'pcp_migrate_payment_tokens',
|
||||
function() use ( $container ) {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
$this->migrate_payment_tokens( $logger );
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_payment_tokens_migration',
|
||||
function( int $customer_id ) use ( $container ) {
|
||||
$migration = $container->get( 'vaulting.payment-tokens-migration' );
|
||||
assert( $migration instanceof PaymentTokensMigration );
|
||||
|
||||
$migration->migrate_payment_tokens_for_user( $customer_id );
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -212,6 +211,51 @@ class VaultingModule implements ModuleInterface {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the payment tokens migration for users with saved payments.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @return void
|
||||
*/
|
||||
public function migrate_payment_tokens( LoggerInterface $logger ): void {
|
||||
// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
$customers = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'limit' => -1,
|
||||
'meta_key' => 'ppcp-vault-token',
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
$customers = $customers->get_results();
|
||||
if ( count( $customers ) === 0 ) {
|
||||
$logger->info( 'No customers for payment tokens migration.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$logger->info( 'Starting payment tokens migration for ' . (string) count( $customers ) . ' users' );
|
||||
|
||||
$interval_in_seconds = 5;
|
||||
$timestamp = time();
|
||||
|
||||
foreach ( $customers as $id ) {
|
||||
/**
|
||||
* Function already exist in WooCommerce
|
||||
*
|
||||
* @psalm-suppress UndefinedFunction
|
||||
*/
|
||||
as_schedule_single_action(
|
||||
$timestamp,
|
||||
'woocommerce_paypal_payments_payment_tokens_migration',
|
||||
array( 'customer_id' => $id )
|
||||
);
|
||||
|
||||
$timestamp += $interval_in_seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the emails when vaulting is failed for subscription orders.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
|
@ -32,13 +33,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderAuthorizeAction;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Assets\FraudNetAssets;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Cli\SettingsCommand;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
|
||||
|
@ -95,7 +96,8 @@ return array(
|
|||
$environment,
|
||||
$payment_token_repository,
|
||||
$logger,
|
||||
$api_shop_country
|
||||
$api_shop_country,
|
||||
$container->get( 'api.endpoint.order' )
|
||||
);
|
||||
},
|
||||
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
|
||||
|
@ -386,8 +388,6 @@ return array(
|
|||
$state = $container->get( 'onboarding.state' );
|
||||
assert( $state instanceof State );
|
||||
|
||||
$messages_disclaimers = $container->get( 'button.helper.messages-disclaimers' );
|
||||
|
||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
assert( $dcc_applies instanceof DccApplies );
|
||||
|
||||
|
@ -610,40 +610,6 @@ return array(
|
|||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
),
|
||||
'vault_enabled' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => $container->get( 'button.helper.vaulting-label' ),
|
||||
'description' => __( 'Allow registered buyers to save PayPal and Credit Card accounts. Allow Subscription renewals.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => array( 'paypal', 'dcc' ),
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'subscription_behavior_when_vault_fails' => array(
|
||||
'title' => __( 'Subscription capture behavior if Vault fails', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'classes' => $subscription_helper->plugin_is_active() ? array() : array( 'hide' ),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => 'void_auth',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'By default, subscription payments are captured only when saving the payment method was successful. Without a saved payment method, automatic renewal payments are not possible.', 'woocommerce-paypal-payments' ),
|
||||
'description_with_tip' => __( 'Determines whether authorized payments for subscription orders are captured or voided if there is no saved payment method. This only applies when the intent Capture is used for the subscription order.', 'woocommerce-paypal-payments' ),
|
||||
'options' => array(
|
||||
'void_auth' => __( 'Void authorization & fail the order/subscription', 'woocommerce-paypal-payments' ),
|
||||
'capture_auth' => __( 'Capture authorized payment & set subscription to Manual Renewal', 'woocommerce-paypal-payments' ),
|
||||
'capture_auth_ignore' => __( 'Capture authorized payment & disregard missing payment method', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => array( 'paypal', 'dcc' ),
|
||||
),
|
||||
'card_billing_data_mode' => array(
|
||||
'title' => __( 'Card billing data handling', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
|
@ -737,6 +703,25 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'vault_enabled_dcc' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'3d_secure_heading' => array(
|
||||
'heading' => __( '3D Secure', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
|
@ -793,9 +778,85 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'paypal_saved_payments' => array(
|
||||
'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'PayPal can save your customers’ payment methods.', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'screens' => array(
|
||||
State::STATE_START,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
),
|
||||
'subscriptions_mode' => array(
|
||||
'title' => __( 'Subscriptions Mode', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'class' => array(),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Utilize PayPal Vaulting for flexible subscription processing with saved payment methods, create “PayPal Subscriptions” to bill customers at regular intervals, or disable PayPal for subscription-type products.', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'vaulting_api',
|
||||
'options' => array(
|
||||
'vaulting_api' => __( 'PayPal Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'subscriptions_api' => __( 'PayPal Subscriptions', 'woocommerce-paypal-payments' ),
|
||||
'disable_paypal_subscriptions' => __( 'Disable PayPal for subscriptions', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
),
|
||||
'vault_enabled' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ PayPal accounts for a seamless checkout experience. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
) . $container->get( 'button.helper.vaulting-label' ),
|
||||
'description' => __( 'Allow registered buyers to save PayPal payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'subscription_behavior_when_vault_fails' => array(
|
||||
'title' => __( 'Subscription capture behavior if Vault fails', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'classes' => $subscription_helper->plugin_is_active() ? array() : array( 'hide' ),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => 'void_auth',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'By default, subscription payments are captured only when saving the payment method was successful. Without a saved payment method, automatic renewal payments are not possible.', 'woocommerce-paypal-payments' ),
|
||||
'description_with_tip' => __( 'Determines whether authorized payments for subscription orders are captured or voided if there is no saved payment method. This only applies when the intent Capture is used for the subscription order.', 'woocommerce-paypal-payments' ),
|
||||
'options' => array(
|
||||
'void_auth' => __( 'Void authorization & fail the order/subscription', 'woocommerce-paypal-payments' ),
|
||||
'capture_auth' => __( 'Capture authorized payment & set subscription to Manual Renewal', 'woocommerce-paypal-payments' ),
|
||||
'capture_auth_ignore' => __( 'Capture authorized payment & disregard missing payment method', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => array( 'paypal' ),
|
||||
),
|
||||
);
|
||||
if ( ! defined( 'PPCP_FLAG_SUBSCRIPTION' ) || ! PPCP_FLAG_SUBSCRIPTION ) {
|
||||
|
||||
if ( defined( 'PPCP_FLAG_SUBSCRIPTIONS_API' ) && ! PPCP_FLAG_SUBSCRIPTIONS_API || ! $subscription_helper->plugin_is_active() ) {
|
||||
unset( $fields['subscriptions_mode'] );
|
||||
}
|
||||
|
||||
$billing_agreements_endpoint = $container->get( 'api.endpoint.billing-agreements' );
|
||||
if ( ! $billing_agreements_endpoint->reference_transaction_enabled() ) {
|
||||
unset( $fields['vault_enabled'] );
|
||||
unset( $fields['vault_enabled_dcc'] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1014,15 +1075,6 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'wcgateway.endpoint.oxxo' => static function ( ContainerInterface $container ): OXXOEndpoint {
|
||||
return new OXXOEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'wcgateway.logging.is-enabled' => function ( ContainerInterface $container ) : bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
||||
|
@ -1045,13 +1097,7 @@ return array(
|
|||
},
|
||||
|
||||
'button.helper.vaulting-label' => static function ( ContainerInterface $container ): string {
|
||||
$vaulting_label = sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Enable saved cards, PayPal accounts, and subscription features on your store. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
|
||||
$vaulting_label = '';
|
||||
if ( ! $container->get( 'wcgateway.helper.vaulting-scope' ) ) {
|
||||
$vaulting_label .= sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
|
@ -1219,6 +1265,11 @@ return array(
|
|||
return 'https://www.paypal.com/bizsignup/entry?country.x=DE&product=payment_methods&capabilities=PAY_UPON_INVOICE';
|
||||
},
|
||||
'wcgateway.settings.connection.dcc-status-text' => static function ( ContainerInterface $container ): string {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( $state->current_state() < State::STATE_ONBOARDED ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' );
|
||||
assert( $dcc_product_status instanceof DCCProductStatus );
|
||||
|
||||
|
@ -1252,6 +1303,11 @@ return array(
|
|||
);
|
||||
},
|
||||
'wcgateway.settings.connection.pui-status-text' => static function ( ContainerInterface $container ): string {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( $state->current_state() < State::STATE_ONBOARDED ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$pui_product_status = $container->get( 'wcgateway.pay-upon-invoice-product-status' );
|
||||
assert( $pui_product_status instanceof PayUponInvoiceProductStatus );
|
||||
|
||||
|
@ -1348,4 +1404,9 @@ return array(
|
|||
$container->get( 'wcgateway.is-fraudnet-enabled' )
|
||||
);
|
||||
},
|
||||
'wcgateway.cli.settings.command' => function( ContainerInterface $container ) : SettingsCommand {
|
||||
return new SettingsCommand(
|
||||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -137,7 +137,7 @@ class CheckoutPayPalAddressPreset {
|
|||
}
|
||||
|
||||
$shipping = null;
|
||||
foreach ( $this->session_handler->order()->purchase_units() as $unit ) {
|
||||
foreach ( $order->purchase_units() as $unit ) {
|
||||
$shipping = $unit->shipping();
|
||||
if ( $shipping ) {
|
||||
break;
|
||||
|
|
70
modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php
Normal file
70
modules/ppcp-wc-gateway/src/Cli/SettingsCommand.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/**
|
||||
* WP-CLI commands for managing plugin settings.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Cli
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Cli;
|
||||
|
||||
use WP_CLI;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class SettingsCommand.
|
||||
*/
|
||||
class SettingsCommand {
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* SettingsCommand constructor.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
*/
|
||||
public function __construct( Settings $settings ) {
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified settings.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <id>
|
||||
* : The setting key.
|
||||
*
|
||||
* <value>
|
||||
* : The setting value.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp pcp settings update description "Pay via PayPal."
|
||||
* wp pcp settings update vault_enabled true
|
||||
* wp pcp settings update vault_enabled false
|
||||
*
|
||||
* @param array $args Positional args.
|
||||
* @param array $assoc_args Option args.
|
||||
*/
|
||||
public function update( array $args, array $assoc_args ): void {
|
||||
$key = (string) $args[0];
|
||||
$value = $args[1];
|
||||
|
||||
if ( 'true' === strtolower( $value ) ) {
|
||||
$value = true;
|
||||
} elseif ( 'false' === strtolower( $value ) ) {
|
||||
$value = false;
|
||||
}
|
||||
|
||||
$this->settings->set( $key, $value );
|
||||
$this->settings->persist();
|
||||
|
||||
WP_CLI::success( "Updated '{$key}' to '{$value}'." );
|
||||
}
|
||||
}
|
|
@ -174,12 +174,7 @@ class CardButtonGateway extends \WC_Payment_Gateway {
|
|||
if ( $this->onboarded ) {
|
||||
$this->supports = array( 'refunds' );
|
||||
}
|
||||
if (
|
||||
defined( 'PPCP_FLAG_SUBSCRIPTION' )
|
||||
&& PPCP_FLAG_SUBSCRIPTION
|
||||
&& $this->gateways_enabled()
|
||||
&& $this->vault_setting_enabled()
|
||||
) {
|
||||
if ( $this->gateways_enabled() ) {
|
||||
$this->supports = array(
|
||||
'refunds',
|
||||
'products',
|
||||
|
|
|
@ -174,26 +174,30 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
if ( $state->current_state() === State::STATE_ONBOARDED ) {
|
||||
$this->supports = array( 'refunds' );
|
||||
}
|
||||
if (
|
||||
defined( 'PPCP_FLAG_SUBSCRIPTION' )
|
||||
&& PPCP_FLAG_SUBSCRIPTION
|
||||
&& $this->gateways_enabled()
|
||||
&& $this->vault_setting_enabled()
|
||||
) {
|
||||
if ( $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' ) ) {
|
||||
$this->supports = array(
|
||||
'refunds',
|
||||
'products',
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
);
|
||||
|
||||
if (
|
||||
( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) )
|
||||
|| ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' )
|
||||
) {
|
||||
array_push(
|
||||
$this->supports,
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->method_title = __(
|
||||
|
|
|
@ -228,24 +228,5 @@ class OXXO {
|
|||
true
|
||||
);
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-oxxo',
|
||||
'OXXOConfig',
|
||||
array(
|
||||
'oxxo_endpoint' => \WC_AJAX::get_endpoint( 'ppc-oxxo' ),
|
||||
'oxxo_nonce' => wp_create_nonce( 'ppc-oxxo' ),
|
||||
'error' => array(
|
||||
'generic' => __(
|
||||
'Something went wrong. Please try again or choose another payment source.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'js_validation' => __(
|
||||
'Required form fields are not filled or invalid.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles OXXO payer action.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Onboarding\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
|
||||
/**
|
||||
* OXXOEndpoint constructor.
|
||||
*/
|
||||
class OXXOEndpoint implements EndpointInterface {
|
||||
|
||||
|
||||
/**
|
||||
* The request data
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
protected $request_data;
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
protected $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The shipping preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
protected $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
protected $order_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* OXXOEndpoint constructor
|
||||
*
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping preference factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonce
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return 'ppc-oxxo';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_cart();
|
||||
$payer_action = '';
|
||||
|
||||
try {
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
$purchase_unit,
|
||||
'checkout'
|
||||
);
|
||||
|
||||
$order = $this->order_endpoint->create( array( $purchase_unit ), $shipping_preference );
|
||||
|
||||
$payment_source = array(
|
||||
'oxxo' => array(
|
||||
'name' => 'John Doe',
|
||||
'email' => 'foo@bar.com',
|
||||
'country_code' => 'MX',
|
||||
),
|
||||
);
|
||||
|
||||
$payment_method = $this->order_endpoint->confirm_payment_source( $order->id(), $payment_source );
|
||||
|
||||
foreach ( $payment_method->links as $link ) {
|
||||
if ( $link->rel === 'payer-action' ) {
|
||||
$payer_action = $link->href;
|
||||
}
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
|
||||
if ( is_a( $exception, PayPalApiException::class ) && is_array( $exception->details() ) ) {
|
||||
$details = '';
|
||||
foreach ( $exception->details() as $detail ) {
|
||||
$issue = $detail->issue ?? '';
|
||||
$field = $detail->field ?? '';
|
||||
$description = $detail->description ?? '';
|
||||
$details .= $issue . ' ' . $field . ' ' . $description . '<br>';
|
||||
}
|
||||
|
||||
$error = $details;
|
||||
}
|
||||
|
||||
$this->logger->error( $error );
|
||||
wc_add_notice( $error, 'error' );
|
||||
|
||||
wp_send_json_error( 'Could not get OXXO payer action.' );
|
||||
return false;
|
||||
}
|
||||
|
||||
WC()->session->set( 'ppcp_payer_action', $payer_action );
|
||||
|
||||
wp_send_json_success(
|
||||
array( 'payer_action' => $payer_action )
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
|
|||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
@ -24,8 +25,11 @@ use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
@ -35,7 +39,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
|||
*/
|
||||
class PayPalGateway extends \WC_Payment_Gateway {
|
||||
|
||||
use ProcessPaymentTrait, FreeTrialHandlerTrait, GatewaySettingsRendererTrait;
|
||||
use ProcessPaymentTrait, FreeTrialHandlerTrait, GatewaySettingsRendererTrait, OrderMetaTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait;
|
||||
|
||||
const ID = 'ppcp-gateway';
|
||||
const INTENT_META_KEY = '_ppcp_paypal_intent';
|
||||
|
@ -150,6 +154,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
protected $api_shop_country;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* PayPalGateway constructor.
|
||||
*
|
||||
|
@ -165,8 +176,9 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param Environment $environment The environment.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $api_shop_country The api shop country.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsRenderer $settings_renderer,
|
||||
|
@ -182,7 +194,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
Environment $environment,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
LoggerInterface $logger,
|
||||
string $api_shop_country
|
||||
string $api_shop_country,
|
||||
OrderEndpoint $order_endpoint
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
$this->settings_renderer = $settings_renderer;
|
||||
|
@ -204,27 +217,31 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
if ( $this->onboarded ) {
|
||||
$this->supports = array( 'refunds', 'tokenization' );
|
||||
}
|
||||
if (
|
||||
defined( 'PPCP_FLAG_SUBSCRIPTION' )
|
||||
&& PPCP_FLAG_SUBSCRIPTION
|
||||
&& $this->gateways_enabled()
|
||||
&& $this->vault_setting_enabled()
|
||||
) {
|
||||
if ( $this->config->has( 'enabled' ) && $this->config->get( 'enabled' ) ) {
|
||||
$this->supports = array(
|
||||
'refunds',
|
||||
'products',
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
'tokenization',
|
||||
);
|
||||
|
||||
if (
|
||||
( $this->config->has( 'vault_enabled' ) && $this->config->get( 'vault_enabled' ) )
|
||||
|| ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' )
|
||||
) {
|
||||
array_push(
|
||||
$this->supports,
|
||||
'tokenization',
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->method_title = $this->define_method_title();
|
||||
|
@ -250,6 +267,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,7 +444,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$funding_source = wc_clean( wp_unslash( $_POST['ppcp-funding-source'] ?? '' ) );
|
||||
$funding_source = wc_clean( wp_unslash( $_POST['ppcp-funding-source'] ?? ( $_POST['funding_source'] ?? '' ) ) );
|
||||
|
||||
if ( $funding_source ) {
|
||||
$wc_order->set_payment_method_title( $this->funding_source_renderer->render_name( $funding_source ) );
|
||||
$wc_order->save();
|
||||
}
|
||||
|
||||
if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) ) {
|
||||
$user_id = (int) $wc_order->get_customer_id();
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $user_id );
|
||||
|
@ -467,6 +492,28 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
try {
|
||||
$paypal_subscription_id = WC()->session->get( 'ppcp_subscription_id' ) ?? '';
|
||||
if ( $paypal_subscription_id ) {
|
||||
$order = $this->session_handler->order();
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$subscription->update_meta_data( 'ppcp_subscription', $paypal_subscription_id );
|
||||
$subscription->save();
|
||||
|
||||
$subscription->add_order_note( "PayPal subscription {$paypal_subscription_id} added." );
|
||||
}
|
||||
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
if ( $transaction_id ) {
|
||||
$this->update_transaction_id( $transaction_id, $wc_order );
|
||||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
|
||||
if ( ! $this->order_processor->process( $wc_order ) ) {
|
||||
return $this->handle_payment_failure(
|
||||
$wc_order,
|
||||
|
|
|
@ -440,6 +440,7 @@ class PayUponInvoice {
|
|||
}
|
||||
|
||||
if (
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true'
|
||||
&& ! $this->pui_helper->is_pay_for_order_ready_for_pui()
|
||||
) {
|
||||
|
|
|
@ -33,19 +33,6 @@ trait ProcessPaymentTrait {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if vault setting is enabled.
|
||||
*
|
||||
* @return bool Whether vault settings are enabled or not.
|
||||
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting hasn't been found.
|
||||
*/
|
||||
protected function vault_setting_enabled(): bool {
|
||||
if ( $this->config->has( 'vault_enabled' ) && $this->config->get( 'vault_enabled' ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduled the vaulted payment check.
|
||||
*
|
||||
|
@ -94,8 +81,9 @@ trait ProcessPaymentTrait {
|
|||
wc_add_notice( $error->getMessage(), 'error' );
|
||||
|
||||
return array(
|
||||
'result' => 'failure',
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
'result' => 'failure',
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
'errorMessage' => $error->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,9 @@ class SettingsStatus {
|
|||
if ( 'pay-now' === $location ) {
|
||||
$location = 'checkout';
|
||||
}
|
||||
if ( 'checkout-block' === $location ) {
|
||||
$location = 'checkout-block-express';
|
||||
}
|
||||
return $location;
|
||||
}
|
||||
|
||||
|
|
|
@ -162,8 +162,13 @@ class OrderProcessor {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function process( WC_Order $wc_order ): bool {
|
||||
$order = $this->session_handler->order();
|
||||
public function process( \WC_Order $wc_order ): bool {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY ) ?: wc_clean( wp_unslash( $_POST['paypal_order_id'] ?? '' ) );
|
||||
$order = $this->session_handler->order();
|
||||
if ( ! $order && is_string( $order_id ) ) {
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
}
|
||||
if ( ! $order ) {
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
|
|
|
@ -408,7 +408,6 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'gateway' => Settings::CONNECTION_TAB_ID,
|
||||
'input_class' => $container->get( 'wcgateway.settings.should-disable-fraudnet-checkbox' ) ? array( 'ppcp-disabled-checkbox' ) : array(),
|
||||
),
|
||||
|
||||
'credentials_integration_configuration_heading' => array(
|
||||
'heading' => __( 'General integration configuration', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
|
|
|
@ -225,11 +225,13 @@ class SettingsListener {
|
|||
$token = $this->bearer->bearer();
|
||||
if ( ! $token->vaulting_available() ) {
|
||||
$this->settings->set( 'vault_enabled', false );
|
||||
$this->settings->set( 'vault_enabled_dcc', false );
|
||||
$this->settings->persist();
|
||||
return;
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->settings->set( 'vault_enabled', false );
|
||||
$this->settings->set( 'vault_enabled_dcc', false );
|
||||
$this->settings->persist();
|
||||
|
||||
throw $exception;
|
||||
|
@ -336,6 +338,11 @@ class SettingsListener {
|
|||
$this->dcc_status_cache->delete( DCCProductStatus::DCC_STATUS_CACHE_KEY );
|
||||
}
|
||||
|
||||
$ppcp_reference_transaction_enabled = get_transient( 'ppcp_reference_transaction_enabled' ) ?? '';
|
||||
if ( $ppcp_reference_transaction_enabled ) {
|
||||
delete_transient( 'ppcp_reference_transaction_enabled' );
|
||||
}
|
||||
|
||||
$redirect_url = false;
|
||||
if ( $credentials_change_status && self::CREDENTIALS_UNCHANGED !== $credentials_change_status ) {
|
||||
$redirect_url = $this->get_onboarding_redirect_url();
|
||||
|
|
|
@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface;
|
|||
use Throwable;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WC_Order;
|
||||
|
@ -322,14 +323,6 @@ class WCGatewayModule implements ModuleInterface {
|
|||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_ppc-oxxo',
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'wcgateway.endpoint.oxxo' );
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_order_status_changed',
|
||||
static function ( int $order_id, string $from, string $to ) use ( $c ) {
|
||||
|
@ -380,6 +373,13 @@ class WCGatewayModule implements ModuleInterface {
|
|||
10,
|
||||
3
|
||||
);
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
\WP_CLI::add_command(
|
||||
'pcp settings',
|
||||
$c->get( 'wcgateway.cli.settings.command' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,19 +14,24 @@ use Psr\Log\LoggerInterface;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\BillingPlanPricingChangeActivated;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\BillingPlanUpdated;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\BillingSubscriptionCancelled;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CatalogProductUpdated;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderApproved;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutPaymentApprovalReversed;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureDenied;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCapturePending;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureRefunded;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureReversed;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultCreditCardCreated;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentSaleCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentSaleRefunded;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultPaymentTokenCreated;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultPaymentTokenDeleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Status\Assets\WebhooksStatusPageAssets;
|
||||
|
@ -86,6 +91,12 @@ return array(
|
|||
new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory ),
|
||||
new VaultPaymentTokenDeleted( $logger ),
|
||||
new PaymentCapturePending( $logger ),
|
||||
new PaymentSaleCompleted( $logger ),
|
||||
new PaymentSaleRefunded( $logger ),
|
||||
new BillingSubscriptionCancelled( $logger ),
|
||||
new BillingPlanPricingChangeActivated( $logger ),
|
||||
new CatalogProductUpdated( $logger ),
|
||||
new BillingPlanUpdated( $logger ),
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -116,7 +127,12 @@ return array(
|
|||
$endpoint = $container->get( 'api.endpoint.webhook' );
|
||||
assert( $endpoint instanceof WebhookEndpoint );
|
||||
|
||||
return $endpoint->list();
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( $state->current_state() >= State::STATE_ONBOARDED ) {
|
||||
return $endpoint->list();
|
||||
}
|
||||
|
||||
return array();
|
||||
},
|
||||
|
||||
'webhook.status.registered-webhooks-data' => function( ContainerInterface $container ) : array {
|
||||
|
|
|
@ -58,6 +58,11 @@ class ResubscribeEndpoint {
|
|||
* Handles the incoming request.
|
||||
*/
|
||||
public function handle_request() {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate nonce.
|
||||
$this->request_data->read_request( $this->nonce() );
|
||||
|
|
|
@ -61,6 +61,11 @@ class SimulateEndpoint {
|
|||
* Handles the incoming request.
|
||||
*/
|
||||
public function handle_request() {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate nonce.
|
||||
$this->request_data->read_request( $this->nonce() );
|
||||
|
|
|
@ -51,6 +51,11 @@ class SimulationStateEndpoint {
|
|||
* Handles the incoming request.
|
||||
*/
|
||||
public function handle_request() {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Not admin.', 403 );
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$state = $this->simulation->get_state();
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook BILLING.PLAN.PRICING-CHANGE.ACTIVATED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class BillingPlanPricingChangeActivated
|
||||
*/
|
||||
class BillingPlanPricingChangeActivated implements RequestHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingPlanPricingChangeActivated constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'BILLING.PLAN.PRICING-CHANGE.ACTIVATED',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$plan_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
if ( $plan_id && ! empty( $request['resource']['billing_cycles'] ) ) {
|
||||
$this->logger->info( 'Starting stuff...' );
|
||||
$args = array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_key' => 'ppcp_subscription_plan',
|
||||
);
|
||||
|
||||
$products = wc_get_products( $args );
|
||||
if ( is_array( $products ) ) {
|
||||
foreach ( $products as $product ) {
|
||||
if ( $product->get_meta( 'ppcp_subscription_plan' )['id'] === $plan_id ) {
|
||||
foreach ( $request['resource']['billing_cycles'] as $cycle ) {
|
||||
if ( $cycle['tenure_type'] === 'REGULAR' ) {
|
||||
$product->update_meta_data( '_subscription_price', $cycle['pricing_scheme']['fixed_price']['value'] );
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
115
modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php
Normal file
115
modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook BILLING.PLAN.UPDATED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class BillingPlanUpdated
|
||||
*/
|
||||
class BillingPlanUpdated implements RequestHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingPlanUpdated constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'BILLING.PLAN.UPDATED',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$plan_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
if ( $plan_id ) {
|
||||
$products = wc_get_products(
|
||||
array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_key' => 'ppcp_subscription_product',
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_array( $products ) ) {
|
||||
foreach ( $products as $product ) {
|
||||
if ( $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$plan_name = wc_clean( wp_unslash( $request['resource']['name'] ?? '' ) );
|
||||
if ( $plan_name !== $product->get_meta( '_ppcp_subscription_plan_name' ) ) {
|
||||
$product->update_meta_data( '_ppcp_subscription_plan_name', $plan_name );
|
||||
$product->save();
|
||||
}
|
||||
|
||||
$billing_cycles = wc_clean( wp_unslash( $request['resource']['billing_cycles'] ?? array() ) );
|
||||
if ( $billing_cycles ) {
|
||||
$price = $billing_cycles[0]['pricing_scheme']['fixed_price']['value'] ?? '';
|
||||
if ( $price && round( $price, 2 ) !== round( $product->get_meta( '_subscription_price' ), 2 ) ) {
|
||||
$product->update_meta_data( '_subscription_price', $price );
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
|
||||
$payment_preferences = wc_clean( wp_unslash( $request['resource']['payment_preferences'] ?? array() ) );
|
||||
if ( $payment_preferences ) {
|
||||
$setup_fee = $payment_preferences['setup_fee']['value'] ?? '';
|
||||
if ( $setup_fee && round( $setup_fee, 2 ) !== round( $product->get_meta( '_subscription_sign_up_fee' ), 2 ) ) {
|
||||
$product->update_meta_data( '_subscription_sign_up_fee', $setup_fee );
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook BILLING.SUBSCRIPTION.CANCELLED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class BillingSubscriptionCancelled
|
||||
*/
|
||||
class BillingSubscriptionCancelled implements RequestHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingSubscriptionCancelled constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'BILLING.SUBSCRIPTION.CANCELLED',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$subscription_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
if ( $subscription_id ) {
|
||||
$args = array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'ppcp_subscription',
|
||||
'value' => $subscription_id,
|
||||
'compare' => '=',
|
||||
),
|
||||
),
|
||||
);
|
||||
$subscriptions = wcs_get_subscriptions( $args );
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$subscription->update_status( 'cancelled' );
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
110
modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php
Normal file
110
modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook CATALOG.PRODUCT.UPDATED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class CatalogProductUpdated
|
||||
*/
|
||||
class CatalogProductUpdated implements RequestHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* CatalogProductUpdated constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'CATALOG.PRODUCT.UPDATED',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$product_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
$name = wc_clean( wp_unslash( $request['resource']['name'] ?? '' ) );
|
||||
if ( $product_id && $name ) {
|
||||
$args = array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_key' => 'ppcp_subscription_product',
|
||||
);
|
||||
$products = wc_get_products( $args );
|
||||
|
||||
if ( is_array( $products ) ) {
|
||||
foreach ( $products as $product ) {
|
||||
if (
|
||||
$product->meta_exists( 'ppcp_subscription_product' )
|
||||
&& isset( $product->get_meta( 'ppcp_subscription_product' )['id'] )
|
||||
&& $product->get_meta( 'ppcp_subscription_product' )['id'] === $product_id
|
||||
&& $product->get_title() !== $name
|
||||
) {
|
||||
/**
|
||||
* Suppress ArgumentTypeCoercion
|
||||
*
|
||||
* @psalm-suppress ArgumentTypeCoercion
|
||||
*/
|
||||
wp_update_post(
|
||||
array(
|
||||
'ID' => $product->get_id(),
|
||||
'post_title' => $name,
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
105
modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php
Normal file
105
modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook PAYMENT.SALE.COMPLETED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class PaymentSaleCompleted
|
||||
*/
|
||||
class PaymentSaleCompleted implements RequestHandler {
|
||||
|
||||
use TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* PaymentSaleCompleted constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array( 'PAYMENT.SALE.COMPLETED' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$billing_agreement_id = wc_clean( wp_unslash( $request['resource']['billing_agreement_id'] ?? '' ) );
|
||||
if ( ! $billing_agreement_id ) {
|
||||
$message = 'Could not retrieve billing agreement id for subscription.';
|
||||
$this->logger->warning( $message, array( 'request' => $request ) );
|
||||
$response['message'] = $message;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$args = array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'ppcp_subscription',
|
||||
'value' => $billing_agreement_id,
|
||||
'compare' => '=',
|
||||
),
|
||||
),
|
||||
);
|
||||
$subscriptions = wcs_get_subscriptions( $args );
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$parent_order = wc_get_order( $subscription->get_parent() );
|
||||
$transaction_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
if ( $transaction_id && is_string( $transaction_id ) && is_a( $parent_order, WC_Order::class ) ) {
|
||||
$this->update_transaction_id( $transaction_id, $parent_order, $this->logger );
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
136
modules/ppcp-webhooks/src/Handler/PaymentSaleRefunded.php
Normal file
136
modules/ppcp-webhooks/src/Handler/PaymentSaleRefunded.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook PAYMENT.SALE.REFUNDED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundMetaTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WP_Error;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class PaymentSaleRefunded
|
||||
*/
|
||||
class PaymentSaleRefunded implements RequestHandler {
|
||||
|
||||
use TransactionIdHandlingTrait, RefundMetaTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* PaymentSaleRefunded constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types a handler handles.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function event_types(): array {
|
||||
return array( 'PAYMENT.SALE.REFUNDED' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a handler is responsible for a given request or not.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function responsible_for_request( WP_REST_Request $request ): bool {
|
||||
return in_array( $request['event_type'], $this->event_types(), true );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the request.
|
||||
*
|
||||
* @param WP_REST_Request $request The request.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
$response = array( 'success' => false );
|
||||
if ( is_null( $request['resource'] ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$refund_id = (string) ( $request['resource']['id'] ?? '' );
|
||||
$transaction_id = $request['resource']['sale_id'] ?? '';
|
||||
$total_refunded_amount = $request['resource']['total_refunded_amount']['value'] ?? '';
|
||||
if ( ! $refund_id || ! $transaction_id || ! $total_refunded_amount ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$args = array(
|
||||
// phpcs:disable WordPress.DB.SlowDBQuery
|
||||
'meta_key' => '_transaction_id',
|
||||
'meta_value' => $transaction_id,
|
||||
'meta_compare' => '=',
|
||||
// phpcs:enable
|
||||
);
|
||||
$wc_orders = wc_get_orders( $args );
|
||||
|
||||
if ( ! is_array( $wc_orders ) ) {
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
foreach ( $wc_orders as $wc_order ) {
|
||||
$refund = wc_create_refund(
|
||||
array(
|
||||
'order_id' => $wc_order->get_id(),
|
||||
'amount' => $total_refunded_amount,
|
||||
)
|
||||
);
|
||||
|
||||
if ( $refund instanceof WP_Error ) {
|
||||
$this->logger->warning(
|
||||
sprintf(
|
||||
// translators: %s is the order id.
|
||||
__( 'Order %s could not be refunded', 'woocommerce-paypal-payments' ),
|
||||
(string) $wc_order->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
$response['message'] = $refund->get_error_message();
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
$order_refunded_message = sprintf(
|
||||
// translators: %1$s is the order id %2$s is the amount which has been refunded.
|
||||
__(
|
||||
'Order %1$s has been refunded with %2$s through PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(string) $wc_order->get_id(),
|
||||
(string) $total_refunded_amount
|
||||
);
|
||||
$this->logger->info( $order_refunded_message );
|
||||
$wc_order->add_order_note( $order_refunded_message );
|
||||
|
||||
$this->update_transaction_id( $refund_id, $wc_order, $this->logger );
|
||||
$this->add_refund_to_meta( $wc_order, $refund_id );
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
|
@ -83,6 +83,14 @@ class IncomingWebhookEndpoint {
|
|||
*/
|
||||
private $last_webhook_event_storage;
|
||||
|
||||
/**
|
||||
* Cached webhook verification results
|
||||
* to avoid repeating requests when permission_callback is called multiple times.
|
||||
*
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $verification_results = array();
|
||||
|
||||
/**
|
||||
* IncomingWebhookEndpoint constructor.
|
||||
*
|
||||
|
@ -160,7 +168,17 @@ class IncomingWebhookEndpoint {
|
|||
|
||||
try {
|
||||
$event = $this->event_from_request( $request );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error( 'Webhook parsing failed: ' . $exception->getMessage() );
|
||||
return false;
|
||||
}
|
||||
|
||||
$cache_key = $event->id();
|
||||
if ( isset( $this->verification_results[ $cache_key ] ) ) {
|
||||
return $this->verification_results[ $cache_key ];
|
||||
}
|
||||
|
||||
try {
|
||||
if ( $this->simulation->is_simulation_event( $event ) ) {
|
||||
return true;
|
||||
}
|
||||
|
@ -169,9 +187,11 @@ class IncomingWebhookEndpoint {
|
|||
if ( ! $result ) {
|
||||
$this->logger->error( 'Webhook verification failed.' );
|
||||
}
|
||||
$this->verification_results[ $cache_key ] = $result;
|
||||
return $result;
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->error( 'Webhook verification failed: ' . $exception->getMessage() );
|
||||
$this->verification_results[ $cache_key ] = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use Exception;
|
||||
|
@ -126,8 +127,8 @@ class WebhookModule implements ModuleInterface {
|
|||
|
||||
try {
|
||||
$webhooks = $container->get( 'webhook.status.registered-webhooks' );
|
||||
|
||||
if ( empty( $webhooks ) ) {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( empty( $webhooks ) && $state->current_state() >= State::STATE_ONBOARDED ) {
|
||||
$registrar = $container->get( 'webhook.registrar' );
|
||||
assert( $registrar instanceof WebhookRegistrar );
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue