Merge branch 'trunk' into PCP-412-when-there-is-a-payment-saved-th
|
@ -25,6 +25,9 @@ class Repository implements RepositoryInterface {
|
|||
*/
|
||||
public function current_message(): array {
|
||||
return array_filter(
|
||||
/**
|
||||
* Returns the list of admin messages.
|
||||
*/
|
||||
(array) apply_filters(
|
||||
self::NOTICES_FILTER,
|
||||
array()
|
||||
|
|
|
@ -26,7 +26,9 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\AmountFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ApplicationContextFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ExchangeRateFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ItemFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\MoneyFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayeeFactory;
|
||||
|
@ -34,7 +36,9 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentsFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentSourceFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlatformFeeFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerReceivableBreakdownFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerStatusFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
|
||||
|
@ -43,6 +47,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
|
||||
|
@ -108,7 +113,7 @@ return array(
|
|||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.payment-token' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.prefix' )
|
||||
$container->get( 'api.repository.customer' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.webhook' => static function ( ContainerInterface $container ) : WebhookEndpoint {
|
||||
|
@ -126,20 +131,19 @@ return array(
|
|||
return new PartnerReferrals(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.repository.partner-referrals-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.identity-token' => static function ( ContainerInterface $container ) : IdentityToken {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
$prefix = $container->get( 'api.prefix' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$customer_repository = $container->get( 'api.repository.customer' );
|
||||
return new IdentityToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$logger,
|
||||
$prefix,
|
||||
$settings
|
||||
$settings,
|
||||
$customer_repository
|
||||
);
|
||||
},
|
||||
'api.endpoint.payments' => static function ( ContainerInterface $container ): PaymentsEndpoint {
|
||||
|
@ -208,9 +212,8 @@ return array(
|
|||
},
|
||||
'api.repository.partner-referrals-data' => static function ( ContainerInterface $container ) : PartnerReferralsData {
|
||||
|
||||
$merchant_email = $container->get( 'api.merchant_email' );
|
||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
return new PartnerReferralsData( $merchant_email, $dcc_applies );
|
||||
return new PartnerReferralsData( $dcc_applies );
|
||||
},
|
||||
'api.repository.cart' => static function ( ContainerInterface $container ): CartRepository {
|
||||
$factory = $container->get( 'api.factory.purchase-unit' );
|
||||
|
@ -221,6 +224,10 @@ return array(
|
|||
$merchant_id = $container->get( 'api.merchant_id' );
|
||||
return new PayeeRepository( $merchant_email, $merchant_id );
|
||||
},
|
||||
'api.repository.customer' => static function( ContainerInterface $container ): CustomerRepository {
|
||||
$prefix = $container->get( 'api.prefix' );
|
||||
return new CustomerRepository( $prefix );
|
||||
},
|
||||
'api.factory.application-context' => static function ( ContainerInterface $container ) : ApplicationContextFactory {
|
||||
return new ApplicationContextFactory();
|
||||
},
|
||||
|
@ -236,7 +243,10 @@ return array(
|
|||
'api.factory.capture' => static function ( ContainerInterface $container ): CaptureFactory {
|
||||
|
||||
$amount_factory = $container->get( 'api.factory.amount' );
|
||||
return new CaptureFactory( $amount_factory );
|
||||
return new CaptureFactory(
|
||||
$amount_factory,
|
||||
$container->get( 'api.factory.seller-receivable-breakdown' )
|
||||
);
|
||||
},
|
||||
'api.factory.purchase-unit' => static function ( ContainerInterface $container ): PurchaseUnitFactory {
|
||||
|
||||
|
@ -277,9 +287,13 @@ return array(
|
|||
$item_factory = $container->get( 'api.factory.item' );
|
||||
return new AmountFactory(
|
||||
$item_factory,
|
||||
$container->get( 'api.factory.money' ),
|
||||
$container->get( 'api.shop.currency' )
|
||||
);
|
||||
},
|
||||
'api.factory.money' => static function ( ContainerInterface $container ): MoneyFactory {
|
||||
return new MoneyFactory();
|
||||
},
|
||||
'api.factory.payer' => static function ( ContainerInterface $container ): PayerFactory {
|
||||
$address_factory = $container->get( 'api.factory.address' );
|
||||
return new PayerFactory( $address_factory );
|
||||
|
@ -312,6 +326,22 @@ return array(
|
|||
'api.factory.authorization' => static function ( ContainerInterface $container ): AuthorizationFactory {
|
||||
return new AuthorizationFactory();
|
||||
},
|
||||
'api.factory.exchange-rate' => static function ( ContainerInterface $container ): ExchangeRateFactory {
|
||||
return new ExchangeRateFactory();
|
||||
},
|
||||
'api.factory.platform-fee' => static function ( ContainerInterface $container ): PlatformFeeFactory {
|
||||
return new PlatformFeeFactory(
|
||||
$container->get( 'api.factory.money' ),
|
||||
$container->get( 'api.factory.payee' )
|
||||
);
|
||||
},
|
||||
'api.factory.seller-receivable-breakdown' => static function ( ContainerInterface $container ): SellerReceivableBreakdownFactory {
|
||||
return new SellerReceivableBreakdownFactory(
|
||||
$container->get( 'api.factory.money' ),
|
||||
$container->get( 'api.factory.exchange-rate' ),
|
||||
$container->get( 'api.factory.platform-fee' )
|
||||
);
|
||||
},
|
||||
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
||||
return new DccApplies(
|
||||
$container->get( 'api.dcc-supported-country-currency-matrix' ),
|
||||
|
@ -322,7 +352,17 @@ return array(
|
|||
},
|
||||
|
||||
'api.shop.currency' => static function ( ContainerInterface $container ) : string {
|
||||
return get_woocommerce_currency();
|
||||
$currency = get_woocommerce_currency();
|
||||
if ( $currency ) {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
$currency = get_option( 'woocommerce_currency' );
|
||||
if ( ! $currency ) {
|
||||
return 'NO_CURRENCY'; // Unlikely to happen.
|
||||
}
|
||||
|
||||
return $currency;
|
||||
},
|
||||
'api.shop.country' => static function ( ContainerInterface $container ) : string {
|
||||
$location = wc_get_base_location();
|
||||
|
@ -382,123 +422,147 @@ return array(
|
|||
* The matrix which countries and currency combinations can be used for DCC.
|
||||
*/
|
||||
'api.dcc-supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for DCC.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_supported_country_currency_matrix',
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'JPY',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -506,43 +570,55 @@ return array(
|
|||
* Which countries support which credit cards. Empty credit card arrays mean no restriction on currency.
|
||||
*/
|
||||
'api.dcc-supported-country-card-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'AU' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
),
|
||||
'ES' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'FR' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'GB' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'GBP', 'USD' ),
|
||||
),
|
||||
'IT' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'US' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'USD' ),
|
||||
'discover' => array( 'USD' ),
|
||||
),
|
||||
'CA' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'CAD' ),
|
||||
'jcb' => array( 'CAD' ),
|
||||
),
|
||||
/**
|
||||
* Returns which countries support which credit cards. Empty credit card arrays mean no restriction on currency.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_supported_country_card_matrix',
|
||||
array(
|
||||
'AU' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'AUD' ),
|
||||
),
|
||||
'DE' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'ES' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'FR' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'GB' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'GBP', 'USD' ),
|
||||
),
|
||||
'IT' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'EUR' ),
|
||||
),
|
||||
'US' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'USD' ),
|
||||
'discover' => array( 'USD' ),
|
||||
),
|
||||
'CA' => array(
|
||||
'mastercard' => array(),
|
||||
'visa' => array(),
|
||||
'amex' => array( 'CAD' ),
|
||||
'jcb' => array( 'CAD' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -120,11 +120,17 @@ class BillingAgreementsEndpoint {
|
|||
*/
|
||||
public function reference_transaction_enabled(): bool {
|
||||
try {
|
||||
$this->create_token(
|
||||
'Checking if reference transactions are enabled',
|
||||
'https://example.com/return',
|
||||
'https://example.com/cancel'
|
||||
);
|
||||
$this->is_request_logging_enabled = false;
|
||||
|
||||
try {
|
||||
$this->create_token(
|
||||
'Checking if reference transactions are enabled',
|
||||
'https://example.com/return',
|
||||
'https://example.com/cancel'
|
||||
);
|
||||
} finally {
|
||||
$this->is_request_logging_enabled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
|
|
|
@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Token;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
|
@ -44,13 +45,6 @@ class IdentityToken {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* The settings
|
||||
*
|
||||
|
@ -58,32 +52,45 @@ class IdentityToken {
|
|||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* The customer repository.
|
||||
*
|
||||
* @var CustomerRepository
|
||||
*/
|
||||
protected $customer_repository;
|
||||
|
||||
/**
|
||||
* IdentityToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $prefix The prefix.
|
||||
* @param Settings $settings The settings.
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param Settings $settings The settings.
|
||||
* @param CustomerRepository $customer_repository The customer repository.
|
||||
*/
|
||||
public function __construct( string $host, Bearer $bearer, LoggerInterface $logger, string $prefix, Settings $settings ) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->prefix = $prefix;
|
||||
$this->settings = $settings;
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger,
|
||||
Settings $settings,
|
||||
CustomerRepository $customer_repository
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->settings = $settings;
|
||||
$this->customer_repository = $customer_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a token for a specific customer.
|
||||
* Generates a token for a specific user.
|
||||
*
|
||||
* @param int $customer_id The id of the customer.
|
||||
* @param int $user_id The id of the user.
|
||||
*
|
||||
* @return Token
|
||||
* @throws RuntimeException If the request fails.
|
||||
*/
|
||||
public function generate_for_customer( int $customer_id ): Token {
|
||||
public function generate_for_user( int $user_id ): Token {
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/identity/generate-token';
|
||||
|
@ -95,11 +102,16 @@ class IdentityToken {
|
|||
),
|
||||
);
|
||||
if (
|
||||
$customer_id
|
||||
&& ( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) )
|
||||
( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) )
|
||||
&& defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION
|
||||
) {
|
||||
$args['body'] = wp_json_encode( array( 'customer_id' => $this->prefix . $customer_id ) );
|
||||
$customer_id = $this->customer_repository->customer_id_for_user( ( $user_id ) );
|
||||
|
||||
$args['body'] = wp_json_encode(
|
||||
array(
|
||||
'customer_id' => $customer_id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
|
|
@ -198,14 +198,20 @@ class OrderEndpoint {
|
|||
return $is_purchase_unit;
|
||||
}
|
||||
);
|
||||
$shipping_preferences = $contains_physical_goods
|
||||
? $shipping_address_is_fixed ?
|
||||
ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS
|
||||
: ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE
|
||||
: ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
|
||||
if ( $this->has_items_without_shipping( $items ) ) {
|
||||
$shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
if ( $contains_physical_goods ) {
|
||||
if ( $shipping_address_is_fixed ) {
|
||||
// Checkout + no address given? Probably something weird happened, like no form validation?
|
||||
// Also note that $items currently always seems to be an array with one item.
|
||||
if ( $this->has_items_without_shipping( $items ) ) {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
|
||||
} else {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
|
||||
}
|
||||
} else {
|
||||
$shipping_preference = ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
@ -218,7 +224,7 @@ class OrderEndpoint {
|
|||
$items
|
||||
),
|
||||
'application_context' => $this->application_context_repository
|
||||
->current_context( $shipping_preferences )->to_array(),
|
||||
->current_context( $shipping_preference )->to_array(),
|
||||
);
|
||||
if ( $payer ) {
|
||||
$data['payer'] = $payer->to_array();
|
||||
|
@ -591,7 +597,7 @@ class OrderEndpoint {
|
|||
/**
|
||||
* Checks if there is at least one item without shipping.
|
||||
*
|
||||
* @param array $items The items.
|
||||
* @param PurchaseUnit[] $items The items.
|
||||
* @return bool Whether items contains shipping or not.
|
||||
*/
|
||||
private function has_items_without_shipping( array $items ): bool {
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
@ -36,13 +35,6 @@ class PartnerReferrals {
|
|||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The PartnerReferralsData.
|
||||
*
|
||||
* @var PartnerReferralsData
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -53,32 +45,29 @@ class PartnerReferrals {
|
|||
/**
|
||||
* PartnerReferrals constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param PartnerReferralsData $data The partner referrals data.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
PartnerReferralsData $data,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->data = $data;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the signup link.
|
||||
*
|
||||
* @param array $data The partner referrals data.
|
||||
* @return string
|
||||
* @throws RuntimeException If the request fails.
|
||||
*/
|
||||
public function signup_link(): string {
|
||||
$data = $this->data->data();
|
||||
public function signup_link( array $data ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
|
|
|
@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenEndpoint
|
||||
|
@ -50,12 +51,13 @@ class PaymentTokenEndpoint {
|
|||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
* The customer repository.
|
||||
*
|
||||
* @var string
|
||||
* @var CustomerRepository
|
||||
*/
|
||||
private $prefix;
|
||||
protected $customer_repository;
|
||||
|
||||
/**
|
||||
* PaymentTokenEndpoint constructor.
|
||||
|
@ -64,21 +66,21 @@ class PaymentTokenEndpoint {
|
|||
* @param Bearer $bearer The bearer.
|
||||
* @param PaymentTokenFactory $factory The payment token factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $prefix The prefix.
|
||||
* @param CustomerRepository $customer_repository The customer repository.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
PaymentTokenFactory $factory,
|
||||
LoggerInterface $logger,
|
||||
string $prefix
|
||||
CustomerRepository $customer_repository
|
||||
) {
|
||||
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->factory = $factory;
|
||||
$this->logger = $logger;
|
||||
$this->prefix = $prefix;
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->factory = $factory;
|
||||
$this->logger = $logger;
|
||||
$this->customer_repository = $customer_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,10 +93,8 @@ class PaymentTokenEndpoint {
|
|||
*/
|
||||
public function for_user( int $id ): array {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$customer_id = $this->prefix . $id;
|
||||
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens/?customer_id=' . $customer_id;
|
||||
$args = array(
|
||||
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens/?customer_id=' . $this->customer_repository->customer_id_for_user( $id );
|
||||
$args = array(
|
||||
'method' => 'GET',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
@ -146,22 +147,32 @@ class PaymentsEndpoint {
|
|||
/**
|
||||
* Capture an authorization by a given ID.
|
||||
*
|
||||
* @param string $authorization_id The id.
|
||||
* @param string $authorization_id The id.
|
||||
* @param Money|null $amount The amount to capture. If not specified, the whole authorized amount is captured.
|
||||
*
|
||||
* @return Capture
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function capture( string $authorization_id ): Capture {
|
||||
public function capture( string $authorization_id, ?Money $amount = null ): Capture {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
|
||||
$args = array(
|
||||
|
||||
$data = array(
|
||||
'final_capture' => true,
|
||||
);
|
||||
if ( $amount ) {
|
||||
$data['amount'] = $amount->to_array();
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
'body' => wp_json_encode( $data, JSON_FORCE_OBJECT ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
|
|
@ -16,6 +16,13 @@ use WP_Error;
|
|||
*/
|
||||
trait RequestTrait {
|
||||
|
||||
/**
|
||||
* Whether to log the detailed request/response info.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_request_logging_enabled = true;
|
||||
|
||||
/**
|
||||
* Performs a request
|
||||
*
|
||||
|
@ -39,7 +46,9 @@ trait RequestTrait {
|
|||
}
|
||||
|
||||
$response = wp_remote_get( $url, $args );
|
||||
$this->logger->debug( $this->request_response_string( $url, $args, $response ) );
|
||||
if ( $this->is_request_logging_enabled ) {
|
||||
$this->logger->debug( $this->request_response_string( $url, $args, $response ) );
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
|
|
@ -143,13 +143,15 @@ class Address {
|
|||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'country_code' => $this->country_code(),
|
||||
'address_line_1' => $this->address_line_1(),
|
||||
'address_line_2' => $this->address_line_2(),
|
||||
'admin_area_1' => $this->admin_area_1(),
|
||||
'admin_area_2' => $this->admin_area_2(),
|
||||
'postal_code' => $this->postal_code(),
|
||||
return array_filter(
|
||||
array(
|
||||
'country_code' => $this->country_code(),
|
||||
'address_line_1' => $this->address_line_1(),
|
||||
'address_line_2' => $this->address_line_2(),
|
||||
'admin_area_1' => $this->admin_area_1(),
|
||||
'admin_area_2' => $this->admin_area_2(),
|
||||
'postal_code' => $this->postal_code(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,13 @@ class Capture {
|
|||
*/
|
||||
private $seller_protection;
|
||||
|
||||
/**
|
||||
* The detailed breakdown of the capture activity (fees, ...).
|
||||
*
|
||||
* @var SellerReceivableBreakdown|null
|
||||
*/
|
||||
private $seller_receivable_breakdown;
|
||||
|
||||
/**
|
||||
* The invoice id.
|
||||
*
|
||||
|
@ -68,13 +75,14 @@ class Capture {
|
|||
/**
|
||||
* Capture constructor.
|
||||
*
|
||||
* @param string $id The ID.
|
||||
* @param CaptureStatus $status The status.
|
||||
* @param Amount $amount The amount.
|
||||
* @param bool $final_capture The final capture.
|
||||
* @param string $seller_protection The seller protection.
|
||||
* @param string $invoice_id The invoice id.
|
||||
* @param string $custom_id The custom id.
|
||||
* @param string $id The ID.
|
||||
* @param CaptureStatus $status The status.
|
||||
* @param Amount $amount The amount.
|
||||
* @param bool $final_capture The final capture.
|
||||
* @param string $seller_protection The seller protection.
|
||||
* @param string $invoice_id The invoice id.
|
||||
* @param string $custom_id The custom id.
|
||||
* @param SellerReceivableBreakdown|null $seller_receivable_breakdown The detailed breakdown of the capture activity (fees, ...).
|
||||
*/
|
||||
public function __construct(
|
||||
string $id,
|
||||
|
@ -83,16 +91,18 @@ class Capture {
|
|||
bool $final_capture,
|
||||
string $seller_protection,
|
||||
string $invoice_id,
|
||||
string $custom_id
|
||||
string $custom_id,
|
||||
?SellerReceivableBreakdown $seller_receivable_breakdown
|
||||
) {
|
||||
|
||||
$this->id = $id;
|
||||
$this->status = $status;
|
||||
$this->amount = $amount;
|
||||
$this->final_capture = $final_capture;
|
||||
$this->seller_protection = $seller_protection;
|
||||
$this->invoice_id = $invoice_id;
|
||||
$this->custom_id = $custom_id;
|
||||
$this->id = $id;
|
||||
$this->status = $status;
|
||||
$this->amount = $amount;
|
||||
$this->final_capture = $final_capture;
|
||||
$this->seller_protection = $seller_protection;
|
||||
$this->invoice_id = $invoice_id;
|
||||
$this->custom_id = $custom_id;
|
||||
$this->seller_receivable_breakdown = $seller_receivable_breakdown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,6 +168,15 @@ class Capture {
|
|||
return $this->custom_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the detailed breakdown of the capture activity (fees, ...).
|
||||
*
|
||||
* @return SellerReceivableBreakdown|null
|
||||
*/
|
||||
public function seller_receivable_breakdown() : ?SellerReceivableBreakdown {
|
||||
return $this->seller_receivable_breakdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity as array.
|
||||
*
|
||||
|
@ -177,6 +196,9 @@ class Capture {
|
|||
if ( $details ) {
|
||||
$data['status_details'] = array( 'reason' => $details->reason() );
|
||||
}
|
||||
if ( $this->seller_receivable_breakdown ) {
|
||||
$data['seller_receivable_breakdown'] = $this->seller_receivable_breakdown->to_array();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
90
modules/ppcp-api-client/src/Entity/ExchangeRate.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* The exchange rate object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class ExchangeRate.
|
||||
*/
|
||||
class ExchangeRate {
|
||||
|
||||
/**
|
||||
* The source currency from which to convert an amount.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $source_currency;
|
||||
|
||||
/**
|
||||
* The target currency to which to convert an amount.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $target_currency;
|
||||
|
||||
/**
|
||||
* The target currency amount. Equivalent to one unit of the source currency.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* ExchangeRate constructor.
|
||||
*
|
||||
* @param string $source_currency The source currency from which to convert an amount.
|
||||
* @param string $target_currency The target currency to which to convert an amount.
|
||||
* @param string $value The target currency amount. Equivalent to one unit of the source currency.
|
||||
*/
|
||||
public function __construct( string $source_currency, string $target_currency, string $value ) {
|
||||
$this->source_currency = $source_currency;
|
||||
$this->target_currency = $target_currency;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source currency from which to convert an amount.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function source_currency(): string {
|
||||
return $this->source_currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* The target currency to which to convert an amount.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function target_currency(): string {
|
||||
return $this->target_currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* The target currency amount. Equivalent to one unit of the source currency.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function value(): string {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'source_currency' => $this->source_currency,
|
||||
'target_currency' => $this->target_currency,
|
||||
'value' => $this->value,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -105,6 +105,9 @@ class PaymentToken {
|
|||
* @return array
|
||||
*/
|
||||
public static function get_valid_types() {
|
||||
/**
|
||||
* Returns a list of valid payment token types.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_valid_payment_token_types',
|
||||
array(
|
||||
|
|
74
modules/ppcp-api-client/src/Entity/PlatformFee.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* The platform fee object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class PlatformFee.
|
||||
*/
|
||||
class PlatformFee {
|
||||
|
||||
/**
|
||||
* The fee.
|
||||
*
|
||||
* @var Money
|
||||
*/
|
||||
private $amount;
|
||||
|
||||
/**
|
||||
* The recipient of the fee.
|
||||
*
|
||||
* @var Payee|null
|
||||
*/
|
||||
private $payee;
|
||||
|
||||
/**
|
||||
* PlatformFee constructor.
|
||||
*
|
||||
* @param Money $amount The fee.
|
||||
* @param Payee|null $payee The recipient of the fee.
|
||||
*/
|
||||
public function __construct( Money $amount, ?Payee $payee ) {
|
||||
$this->amount = $amount;
|
||||
$this->payee = $payee;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fee.
|
||||
*
|
||||
* @return Money
|
||||
*/
|
||||
public function amount(): Money {
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The recipient of the fee.
|
||||
*
|
||||
* @return Payee|null
|
||||
*/
|
||||
public function payee(): ?Payee {
|
||||
return $this->payee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
$data = array(
|
||||
'amount' => $this->amount->to_array(),
|
||||
);
|
||||
if ( $this->payee ) {
|
||||
$data['payee'] = $this->payee->to_array();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
211
modules/ppcp-api-client/src/Entity/SellerReceivableBreakdown.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
/**
|
||||
* The info about fees and amount that will be received by the seller.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class SellerReceivableBreakdown
|
||||
*/
|
||||
class SellerReceivableBreakdown {
|
||||
|
||||
/**
|
||||
* The amount for this captured payment in the currency of the transaction.
|
||||
*
|
||||
* @var Money
|
||||
*/
|
||||
private $gross_amount;
|
||||
|
||||
/**
|
||||
* The applicable fee for this captured payment in the currency of the transaction.
|
||||
*
|
||||
* @var Money|null
|
||||
*/
|
||||
private $paypal_fee;
|
||||
|
||||
/**
|
||||
* The applicable fee for this captured payment in the receivable currency.
|
||||
*
|
||||
* Present only in cases the fee is charged in the receivable currency.
|
||||
*
|
||||
* @var Money|null
|
||||
*/
|
||||
private $paypal_fee_in_receivable_currency;
|
||||
|
||||
/**
|
||||
* The net amount that the payee receives for this captured payment in their PayPal account.
|
||||
*
|
||||
* Computed as gross_amount minus the paypal_fee minus the platform_fees.
|
||||
*
|
||||
* @var Money|null
|
||||
*/
|
||||
private $net_amount;
|
||||
|
||||
/**
|
||||
* The net amount that is credited to the payee's PayPal account.
|
||||
*
|
||||
* Present only when the currency of the captured payment is different from the currency
|
||||
* of the PayPal account where the payee wants to credit the funds. Computed as net_amount times exchange_rate.
|
||||
*
|
||||
* @var Money|null
|
||||
*/
|
||||
private $receivable_amount;
|
||||
|
||||
/**
|
||||
* The exchange rate that determines the amount that is credited to the payee's PayPal account.
|
||||
*
|
||||
* Present when the currency of the captured payment is different from the currency of the PayPal account where the payee wants to credit the funds.
|
||||
*
|
||||
* @var ExchangeRate|null
|
||||
*/
|
||||
private $exchange_rate;
|
||||
|
||||
/**
|
||||
* An array of platform or partner fees, commissions, or brokerage fees that associated with the captured payment.
|
||||
*
|
||||
* @var PlatformFee[]
|
||||
*/
|
||||
private $platform_fees;
|
||||
|
||||
/**
|
||||
* SellerReceivableBreakdown constructor.
|
||||
*
|
||||
* @param Money $gross_amount The amount for this captured payment in the currency of the transaction.
|
||||
* @param Money|null $paypal_fee The applicable fee for this captured payment in the currency of the transaction.
|
||||
* @param Money|null $paypal_fee_in_receivable_currency The applicable fee for this captured payment in the receivable currency.
|
||||
* @param Money|null $net_amount The net amount that the payee receives for this captured payment in their PayPal account.
|
||||
* @param Money|null $receivable_amount The net amount that is credited to the payee's PayPal account.
|
||||
* @param ExchangeRate|null $exchange_rate The exchange rate that determines the amount that is credited to the payee's PayPal account.
|
||||
* @param PlatformFee[] $platform_fees An array of platform or partner fees, commissions, or brokerage fees that associated with the captured payment.
|
||||
*/
|
||||
public function __construct(
|
||||
Money $gross_amount,
|
||||
?Money $paypal_fee,
|
||||
?Money $paypal_fee_in_receivable_currency,
|
||||
?Money $net_amount,
|
||||
?Money $receivable_amount,
|
||||
?ExchangeRate $exchange_rate,
|
||||
array $platform_fees
|
||||
) {
|
||||
$this->gross_amount = $gross_amount;
|
||||
$this->paypal_fee = $paypal_fee;
|
||||
$this->paypal_fee_in_receivable_currency = $paypal_fee_in_receivable_currency;
|
||||
$this->net_amount = $net_amount;
|
||||
$this->receivable_amount = $receivable_amount;
|
||||
$this->exchange_rate = $exchange_rate;
|
||||
$this->platform_fees = $platform_fees;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount for this captured payment in the currency of the transaction.
|
||||
*
|
||||
* @return Money
|
||||
*/
|
||||
public function gross_amount(): ?Money {
|
||||
return $this->gross_amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The applicable fee for this captured payment in the currency of the transaction.
|
||||
*
|
||||
* @return Money|null
|
||||
*/
|
||||
public function paypal_fee(): ?Money {
|
||||
return $this->paypal_fee;
|
||||
}
|
||||
|
||||
/**
|
||||
* The applicable fee for this captured payment in the receivable currency.
|
||||
*
|
||||
* Present only in cases the fee is charged in the receivable currency.
|
||||
*
|
||||
* @return Money|null
|
||||
*/
|
||||
public function paypal_fee_in_receivable_currency(): ?Money {
|
||||
return $this->paypal_fee_in_receivable_currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* The net amount that the payee receives for this captured payment in their PayPal account.
|
||||
*
|
||||
* Computed as gross_amount minus the paypal_fee minus the platform_fees.
|
||||
*
|
||||
* @return Money|null
|
||||
*/
|
||||
public function net_amount(): ?Money {
|
||||
return $this->net_amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The net amount that is credited to the payee's PayPal account.
|
||||
*
|
||||
* Present only when the currency of the captured payment is different from the currency
|
||||
* of the PayPal account where the payee wants to credit the funds. Computed as net_amount times exchange_rate.
|
||||
*
|
||||
* @return Money|null
|
||||
*/
|
||||
public function receivable_amount(): ?Money {
|
||||
return $this->receivable_amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The exchange rate that determines the amount that is credited to the payee's PayPal account.
|
||||
*
|
||||
* Present when the currency of the captured payment is different from the currency of the PayPal account where the payee wants to credit the funds.
|
||||
*
|
||||
* @return ExchangeRate|null
|
||||
*/
|
||||
public function exchange_rate(): ?ExchangeRate {
|
||||
return $this->exchange_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of platform or partner fees, commissions, or brokerage fees that associated with the captured payment.
|
||||
*
|
||||
* @return PlatformFee[]
|
||||
*/
|
||||
public function platform_fees(): array {
|
||||
return $this->platform_fees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
$data = array(
|
||||
'gross_amount' => $this->gross_amount->to_array(),
|
||||
);
|
||||
if ( $this->paypal_fee ) {
|
||||
$data['paypal_fee'] = $this->paypal_fee->to_array();
|
||||
}
|
||||
if ( $this->paypal_fee_in_receivable_currency ) {
|
||||
$data['paypal_fee_in_receivable_currency'] = $this->paypal_fee_in_receivable_currency->to_array();
|
||||
}
|
||||
if ( $this->net_amount ) {
|
||||
$data['net_amount'] = $this->net_amount->to_array();
|
||||
}
|
||||
if ( $this->receivable_amount ) {
|
||||
$data['receivable_amount'] = $this->receivable_amount->to_array();
|
||||
}
|
||||
if ( $this->exchange_rate ) {
|
||||
$data['exchange_rate'] = $this->exchange_rate->to_array();
|
||||
}
|
||||
if ( $this->platform_fees ) {
|
||||
$data['platform_fees'] = array_map(
|
||||
function ( PlatformFee $fee ) {
|
||||
return $fee->to_array();
|
||||
},
|
||||
$this->platform_fees
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -110,4 +110,13 @@ class PayPalApiException extends RuntimeException {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns response issues.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function issues(): array {
|
||||
return $this->response->issues ?? array();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,13 @@ class AmountFactory {
|
|||
*/
|
||||
private $item_factory;
|
||||
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @var MoneyFactory
|
||||
*/
|
||||
private $money_factory;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
|
@ -38,12 +45,14 @@ class AmountFactory {
|
|||
/**
|
||||
* AmountFactory constructor.
|
||||
*
|
||||
* @param ItemFactory $item_factory The Item factory.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param ItemFactory $item_factory The Item factory.
|
||||
* @param MoneyFactory $money_factory The Money factory.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
*/
|
||||
public function __construct( ItemFactory $item_factory, string $currency ) {
|
||||
$this->item_factory = $item_factory;
|
||||
$this->currency = $currency;
|
||||
public function __construct( ItemFactory $item_factory, MoneyFactory $money_factory, string $currency ) {
|
||||
$this->item_factory = $item_factory;
|
||||
$this->money_factory = $money_factory;
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,16 +178,7 @@ class AmountFactory {
|
|||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( \stdClass $data ): Amount {
|
||||
if ( ! isset( $data->value ) || ! is_numeric( $data->value ) ) {
|
||||
throw new RuntimeException( __( 'No value given', 'woocommerce-paypal-payments' ) );
|
||||
}
|
||||
if ( ! isset( $data->currency_code ) ) {
|
||||
throw new RuntimeException(
|
||||
__( 'No currency given', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
$money = new Money( (float) $data->value, $data->currency_code );
|
||||
$money = $this->money_factory->from_paypal_response( $data );
|
||||
$breakdown = ( isset( $data->breakdown ) ) ? $this->break_down( $data->breakdown ) : null;
|
||||
return new Amount( $money, $breakdown );
|
||||
}
|
||||
|
|
|
@ -25,14 +25,26 @@ class CaptureFactory {
|
|||
*/
|
||||
private $amount_factory;
|
||||
|
||||
/**
|
||||
* The SellerReceivableBreakdown factory.
|
||||
*
|
||||
* @var SellerReceivableBreakdownFactory
|
||||
*/
|
||||
private $seller_receivable_breakdown_factory;
|
||||
|
||||
/**
|
||||
* CaptureFactory constructor.
|
||||
*
|
||||
* @param AmountFactory $amount_factory The amount factory.
|
||||
* @param AmountFactory $amount_factory The amount factory.
|
||||
* @param SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory The SellerReceivableBreakdown factory.
|
||||
*/
|
||||
public function __construct( AmountFactory $amount_factory ) {
|
||||
public function __construct(
|
||||
AmountFactory $amount_factory,
|
||||
SellerReceivableBreakdownFactory $seller_receivable_breakdown_factory
|
||||
) {
|
||||
|
||||
$this->amount_factory = $amount_factory;
|
||||
$this->amount_factory = $amount_factory;
|
||||
$this->seller_receivable_breakdown_factory = $seller_receivable_breakdown_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +56,10 @@ class CaptureFactory {
|
|||
*/
|
||||
public function from_paypal_response( \stdClass $data ) : Capture {
|
||||
|
||||
$reason = $data->status_details->reason ?? null;
|
||||
$reason = $data->status_details->reason ?? null;
|
||||
$seller_receivable_breakdown = isset( $data->seller_receivable_breakdown ) ?
|
||||
$this->seller_receivable_breakdown_factory->from_paypal_response( $data->seller_receivable_breakdown )
|
||||
: null;
|
||||
|
||||
return new Capture(
|
||||
(string) $data->id,
|
||||
|
@ -56,7 +71,8 @@ class CaptureFactory {
|
|||
(bool) $data->final_capture,
|
||||
(string) $data->seller_protection->status,
|
||||
(string) $data->invoice_id,
|
||||
(string) $data->custom_id
|
||||
(string) $data->custom_id,
|
||||
$seller_receivable_breakdown
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
41
modules/ppcp-api-client/src/Factory/ExchangeRateFactory.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* The ExchangeRateFactory Factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExchangeRate;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class ExchangeRateFactory
|
||||
*/
|
||||
class ExchangeRateFactory {
|
||||
/**
|
||||
* Returns an ExchangeRate object based off a PayPal Response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return ExchangeRate|null
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): ?ExchangeRate {
|
||||
// Looks like all fields in this object are optional, according to the docs,
|
||||
// and sometimes we get an empty object.
|
||||
$source_currency = $data->source_currency ?? '';
|
||||
$target_currency = $data->target_currency ?? '';
|
||||
$value = $data->value ?? '';
|
||||
if ( ! $source_currency && ! $target_currency && ! $value ) {
|
||||
// Do not return empty object.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ExchangeRate( $source_currency, $target_currency, $value );
|
||||
}
|
||||
}
|
39
modules/ppcp-api-client/src/Factory/MoneyFactory.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class MoneyFactory
|
||||
*/
|
||||
class MoneyFactory {
|
||||
|
||||
/**
|
||||
* Returns a Money object based off a PayPal Response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return Money
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): Money {
|
||||
if ( ! isset( $data->value ) || ! is_numeric( $data->value ) ) {
|
||||
throw new RuntimeException( 'No money value given' );
|
||||
}
|
||||
if ( ! isset( $data->currency_code ) ) {
|
||||
throw new RuntimeException( 'No currency given' );
|
||||
}
|
||||
|
||||
return new Money( (float) $data->value, $data->currency_code );
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Address;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PayerName;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PayerTaxInfo;
|
||||
|
@ -54,10 +55,12 @@ class PayerFactory {
|
|||
$national_number = preg_replace( '/[^0-9]/', '', $national_number );
|
||||
$national_number = substr( $national_number, 0, 14 );
|
||||
|
||||
$phone = new PhoneWithType(
|
||||
'HOME',
|
||||
new Phone( $national_number )
|
||||
);
|
||||
if ( $national_number ) {
|
||||
$phone = new PhoneWithType(
|
||||
'HOME',
|
||||
new Phone( $national_number )
|
||||
);
|
||||
}
|
||||
}
|
||||
return new Payer(
|
||||
new PayerName(
|
||||
|
@ -90,10 +93,12 @@ class PayerFactory {
|
|||
$national_number = preg_replace( '/[^0-9]/', '', $national_number );
|
||||
$national_number = substr( $national_number, 0, 14 );
|
||||
|
||||
$phone = new PhoneWithType(
|
||||
'HOME',
|
||||
new Phone( $national_number )
|
||||
);
|
||||
if ( $national_number ) {
|
||||
$phone = new PhoneWithType(
|
||||
'HOME',
|
||||
new Phone( $national_number )
|
||||
);
|
||||
}
|
||||
}
|
||||
return new Payer(
|
||||
new PayerName(
|
||||
|
@ -147,4 +152,57 @@ class PayerFactory {
|
|||
$tax_info
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Payer object based off the given checkout form fields.
|
||||
*
|
||||
* @param array $form_fields The checkout form fields.
|
||||
* @return Payer
|
||||
*/
|
||||
public function from_checkout_form( array $form_fields ): Payer {
|
||||
|
||||
$first_name = $form_fields['billing_first_name'] ?? '';
|
||||
$last_name = $form_fields['billing_last_name'] ?? '';
|
||||
$billing_email = $form_fields['billing_email'] ?? '';
|
||||
$billing_country = $form_fields['billing_country'] ?? '';
|
||||
$billing_address_1 = $form_fields['billing_address_1'] ?? '';
|
||||
$billing_address_2 = $form_fields['billing_address_2'] ?? '';
|
||||
$admin_area_1 = $form_fields['billing_state'] ?? '';
|
||||
$admin_area_2 = $form_fields['billing_city'] ?? '';
|
||||
$billing_postcode = $form_fields['billing_postcode'] ?? '';
|
||||
|
||||
$phone = null;
|
||||
if ( isset( $form_fields['billing_phone'] ) && '' !== $form_fields['billing_phone'] ) {
|
||||
// make sure the phone number contains only numbers and is max 14. chars long.
|
||||
$national_number = $form_fields['billing_phone'];
|
||||
$national_number = preg_replace( '/[^0-9]/', '', $national_number );
|
||||
|
||||
if ( null !== $national_number ) {
|
||||
$national_number = substr( $national_number, 0, 14 );
|
||||
|
||||
if ( $national_number ) {
|
||||
$phone = new PhoneWithType(
|
||||
'HOME',
|
||||
new Phone( $national_number )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Payer(
|
||||
new PayerName( $first_name, $last_name ),
|
||||
$billing_email,
|
||||
'',
|
||||
new Address(
|
||||
$billing_country,
|
||||
$billing_address_1,
|
||||
$billing_address_2,
|
||||
$admin_area_1,
|
||||
$admin_area_2,
|
||||
$billing_postcode
|
||||
),
|
||||
null,
|
||||
$phone
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
64
modules/ppcp-api-client/src/Factory/PlatformFeeFactory.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* The PlatformFee Factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PlatformFee;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PayeeFactory
|
||||
*/
|
||||
class PlatformFeeFactory {
|
||||
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @var MoneyFactory
|
||||
*/
|
||||
private $money_factory;
|
||||
|
||||
/**
|
||||
* The Payee factory.
|
||||
*
|
||||
* @var PayeeFactory
|
||||
*/
|
||||
private $payee_factory;
|
||||
|
||||
/**
|
||||
* PlatformFeeFactory constructor.
|
||||
*
|
||||
* @param MoneyFactory $money_factory The Money factory.
|
||||
* @param PayeeFactory $payee_factory The Payee factory.
|
||||
*/
|
||||
public function __construct( MoneyFactory $money_factory, PayeeFactory $payee_factory ) {
|
||||
|
||||
$this->money_factory = $money_factory;
|
||||
$this->payee_factory = $payee_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Payee object based off a PayPal Response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return PlatformFee
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): PlatformFee {
|
||||
if ( ! isset( $data->amount ) ) {
|
||||
throw new RuntimeException( 'Platform fee amount not found' );
|
||||
}
|
||||
|
||||
$amount = $this->money_factory->from_paypal_response( $data->amount );
|
||||
$payee = ( isset( $data->payee ) ) ? $this->payee_factory->from_paypal_response( $data->payee ) : null;
|
||||
return new PlatformFee( $amount, $payee );
|
||||
}
|
||||
}
|
|
@ -112,7 +112,7 @@ class PurchaseUnitFactory {
|
|||
if (
|
||||
! $this->shipping_needed( ... array_values( $items ) ) ||
|
||||
empty( $shipping->address()->country_code() ) ||
|
||||
( $shipping->address()->country_code() && ! $shipping->address()->postal_code() )
|
||||
( ! $shipping->address()->postal_code() && ! $this->country_without_postal_code( $shipping->address()->country_code() ) )
|
||||
) {
|
||||
$shipping = null;
|
||||
}
|
||||
|
@ -121,7 +121,6 @@ class PurchaseUnitFactory {
|
|||
$payee = $this->payee_repository->payee();
|
||||
$custom_id = (string) $order->get_id();
|
||||
$invoice_id = $this->prefix . $order->get_order_number();
|
||||
$retry = $order->get_meta( 'ppcp-retry' ) ? '-' . $order->get_meta( 'ppcp-retry' ) : '';
|
||||
$soft_descriptor = '';
|
||||
|
||||
$purchase_unit = new PurchaseUnit(
|
||||
|
@ -135,6 +134,9 @@ class PurchaseUnitFactory {
|
|||
$invoice_id . $retry,
|
||||
$soft_descriptor
|
||||
);
|
||||
/**
|
||||
* Returns PurchaseUnit for the WC order.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_purchase_unit_from_wc_order',
|
||||
$purchase_unit,
|
||||
|
@ -158,9 +160,8 @@ class PurchaseUnitFactory {
|
|||
if ( $this->shipping_needed( ... array_values( $items ) ) && is_a( $customer, \WC_Customer::class ) ) {
|
||||
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer );
|
||||
if (
|
||||
2 !== strlen( $shipping->address()->country_code() )
|
||||
|| ( ! $shipping->address()->postal_code() )
|
||||
|| $this->country_without_postal_code( $shipping->address()->country_code() )
|
||||
2 !== strlen( $shipping->address()->country_code() ) ||
|
||||
( ! $shipping->address()->postal_code() && ! $this->country_without_postal_code( $shipping->address()->country_code() ) )
|
||||
) {
|
||||
$shipping = null;
|
||||
}
|
||||
|
@ -276,9 +277,6 @@ class PurchaseUnitFactory {
|
|||
*/
|
||||
private function country_without_postal_code( string $country_code ): bool {
|
||||
$countries = array( 'AE', 'AF', 'AG', 'AI', 'AL', 'AN', 'AO', 'AW', 'BB', 'BF', 'BH', 'BI', 'BJ', 'BM', 'BO', 'BS', 'BT', 'BW', 'BZ', 'CD', 'CF', 'CG', 'CI', 'CK', 'CL', 'CM', 'CO', 'CR', 'CV', 'DJ', 'DM', 'DO', 'EC', 'EG', 'ER', 'ET', 'FJ', 'FK', 'GA', 'GD', 'GH', 'GI', 'GM', 'GN', 'GQ', 'GT', 'GW', 'GY', 'HK', 'HN', 'HT', 'IE', 'IQ', 'IR', 'JM', 'JO', 'KE', 'KH', 'KI', 'KM', 'KN', 'KP', 'KW', 'KY', 'LA', 'LB', 'LC', 'LK', 'LR', 'LS', 'LY', 'ML', 'MM', 'MO', 'MR', 'MS', 'MT', 'MU', 'MW', 'MZ', 'NA', 'NE', 'NG', 'NI', 'NP', 'NR', 'NU', 'OM', 'PA', 'PE', 'PF', 'PY', 'QA', 'RW', 'SA', 'SB', 'SC', 'SD', 'SL', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SY', 'TC', 'TD', 'TG', 'TL', 'TO', 'TT', 'TV', 'TZ', 'UG', 'UY', 'VC', 'VE', 'VG', 'VN', 'VU', 'WS', 'XA', 'XB', 'XC', 'XE', 'XL', 'XM', 'XN', 'XS', 'YE', 'ZM', 'ZW' );
|
||||
if ( in_array( $country_code, $countries, true ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return in_array( $country_code, $countries, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
/**
|
||||
* The SellerReceivableBreakdown Factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PlatformFee;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerReceivableBreakdown;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class SellerReceivableBreakdownFactory
|
||||
*/
|
||||
class SellerReceivableBreakdownFactory {
|
||||
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @var MoneyFactory
|
||||
*/
|
||||
private $money_factory;
|
||||
|
||||
/**
|
||||
* The ExchangeRate factory.
|
||||
*
|
||||
* @var ExchangeRateFactory
|
||||
*/
|
||||
private $exchange_rate_factory;
|
||||
|
||||
/**
|
||||
* The PlatformFee factory.
|
||||
*
|
||||
* @var PlatformFeeFactory
|
||||
*/
|
||||
private $platform_fee_factory;
|
||||
|
||||
/**
|
||||
* SellerReceivableBreakdownFactory constructor.
|
||||
*
|
||||
* @param MoneyFactory $money_factory The Money factory.
|
||||
* @param ExchangeRateFactory $exchange_rate_factory The ExchangeRate factory.
|
||||
* @param PlatformFeeFactory $platform_fee_factory The PlatformFee factory.
|
||||
*/
|
||||
public function __construct(
|
||||
MoneyFactory $money_factory,
|
||||
ExchangeRateFactory $exchange_rate_factory,
|
||||
PlatformFeeFactory $platform_fee_factory
|
||||
) {
|
||||
|
||||
$this->money_factory = $money_factory;
|
||||
$this->exchange_rate_factory = $exchange_rate_factory;
|
||||
$this->platform_fee_factory = $platform_fee_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SellerReceivableBreakdown object based off a PayPal Response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return SellerReceivableBreakdown
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): SellerReceivableBreakdown {
|
||||
if ( ! isset( $data->gross_amount ) ) {
|
||||
throw new RuntimeException( 'Seller breakdown gross amount not found' );
|
||||
}
|
||||
|
||||
$gross_amount = $this->money_factory->from_paypal_response( $data->gross_amount );
|
||||
$paypal_fee = ( isset( $data->paypal_fee ) ) ? $this->money_factory->from_paypal_response( $data->paypal_fee ) : null;
|
||||
$paypal_fee_in_receivable_currency = ( isset( $data->paypal_fee_in_receivable_currency ) ) ? $this->money_factory->from_paypal_response( $data->paypal_fee_in_receivable_currency ) : null;
|
||||
$net_amount = ( isset( $data->net_amount ) ) ? $this->money_factory->from_paypal_response( $data->net_amount ) : null;
|
||||
$receivable_amount = ( isset( $data->receivable_amount ) ) ? $this->money_factory->from_paypal_response( $data->receivable_amount ) : null;
|
||||
$exchange_rate = ( isset( $data->exchange_rate ) ) ? $this->exchange_rate_factory->from_paypal_response( $data->exchange_rate ) : null;
|
||||
$platform_fees = ( isset( $data->platform_fees ) ) ? array_map(
|
||||
function ( stdClass $fee_data ): PlatformFee {
|
||||
return $this->platform_fee_factory->from_paypal_response( $fee_data );
|
||||
},
|
||||
$data->platform_fees
|
||||
) : array();
|
||||
|
||||
return new SellerReceivableBreakdown(
|
||||
$gross_amount,
|
||||
$paypal_fee,
|
||||
$paypal_fee_in_receivable_currency,
|
||||
$net_amount,
|
||||
$receivable_amount,
|
||||
$exchange_rate,
|
||||
$platform_fees
|
||||
);
|
||||
}
|
||||
}
|
|
@ -74,7 +74,10 @@ class ApplicationContextRepository {
|
|||
|
||||
$parts = explode( '-', $locale );
|
||||
if ( count( $parts ) === 3 ) {
|
||||
return substr( $locale, 0, strrpos( $locale, '-' ) );
|
||||
$ret = substr( $locale, 0, strrpos( $locale, '-' ) );
|
||||
if ( false !== $ret ) {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 'en';
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
/**
|
||||
* The customer repository.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
||||
|
||||
/**
|
||||
* Class CustomerRepository
|
||||
*/
|
||||
class CustomerRepository {
|
||||
const CLIENT_ID_MAX_LENGTH = 22;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* CustomerRepository constructor.
|
||||
*
|
||||
* @param string $prefix The prefix.
|
||||
*/
|
||||
public function __construct( string $prefix ) {
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the customer ID for the given user ID.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @return string
|
||||
*/
|
||||
public function customer_id_for_user( int $user_id ): string {
|
||||
if ( 0 === $user_id ) {
|
||||
$guest_customer_id = WC()->session->get( 'ppcp_guest_customer_id' );
|
||||
if ( is_string( $guest_customer_id ) && $guest_customer_id ) {
|
||||
return $guest_customer_id;
|
||||
}
|
||||
|
||||
$unique_id = substr( $this->prefix . strrev( uniqid() ), 0, self::CLIENT_ID_MAX_LENGTH );
|
||||
assert( is_string( $unique_id ) );
|
||||
|
||||
WC()->session->set( 'ppcp_guest_customer_id', $unique_id );
|
||||
|
||||
return $unique_id;
|
||||
}
|
||||
|
||||
$guest_customer_id = get_user_meta( $user_id, 'ppcp_guest_customer_id', true );
|
||||
if ( $guest_customer_id ) {
|
||||
return $guest_customer_id;
|
||||
}
|
||||
|
||||
return $this->prefix . (string) $user_id;
|
||||
}
|
||||
}
|
|
@ -15,14 +15,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
|||
* Class PartnerReferralsData
|
||||
*/
|
||||
class PartnerReferralsData {
|
||||
|
||||
/**
|
||||
* The merchant email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $merchant_email;
|
||||
|
||||
/**
|
||||
* The DCC Applies Helper object.
|
||||
*
|
||||
|
@ -30,19 +22,39 @@ class PartnerReferralsData {
|
|||
*/
|
||||
private $dcc_applies;
|
||||
|
||||
/**
|
||||
* The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $products;
|
||||
|
||||
/**
|
||||
* PartnerReferralsData constructor.
|
||||
*
|
||||
* @param string $merchant_email The email of the merchant.
|
||||
* @param DccApplies $dcc_applies The DCC Applies helper.
|
||||
*/
|
||||
public function __construct(
|
||||
string $merchant_email,
|
||||
DccApplies $dcc_applies
|
||||
) {
|
||||
$this->dcc_applies = $dcc_applies;
|
||||
$this->products = array(
|
||||
$this->dcc_applies->for_country_currency() ? 'PPCP' : 'EXPRESS_CHECKOUT',
|
||||
);
|
||||
}
|
||||
|
||||
$this->merchant_email = $merchant_email;
|
||||
$this->dcc_applies = $dcc_applies;
|
||||
/**
|
||||
* Returns a new copy of this object with the given value set.
|
||||
*
|
||||
* @param string[] $products The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
||||
* @return static
|
||||
*/
|
||||
public function with_products( array $products ): self {
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->products = $products;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,33 +72,26 @@ class PartnerReferralsData {
|
|||
* @return array
|
||||
*/
|
||||
public function data(): array {
|
||||
$data = $this->default_data();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function default_data(): array {
|
||||
|
||||
return array(
|
||||
'partner_config_override' => array(
|
||||
'partner_logo_url' => 'https://connect.woocommerce.com/images/woocommerce_logo.png',
|
||||
/**
|
||||
* Returns the URL which will be opened at the end of onboarding.
|
||||
*/
|
||||
'return_url' => apply_filters(
|
||||
'woocommerce_paypal_payments_partner_config_override_return_url',
|
||||
admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' )
|
||||
),
|
||||
/**
|
||||
* Returns the description of the URL which will be opened at the end of onboarding.
|
||||
*/
|
||||
'return_url_description' => apply_filters(
|
||||
'woocommerce_paypal_payments_partner_config_override_return_url_description',
|
||||
__( 'Return to your shop.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'show_add_credit_card' => true,
|
||||
),
|
||||
'products' => array(
|
||||
$this->dcc_applies->for_country_currency() ? 'PPCP' : 'EXPRESS_CHECKOUT',
|
||||
),
|
||||
'products' => $this->products,
|
||||
'legal_consents' => array(
|
||||
array(
|
||||
'type' => 'SHARE_DATA_CONSENT',
|
||||
|
|
|
@ -82,8 +82,16 @@ document.addEventListener(
|
|||
console.error('PayPal button could not be configured.');
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
|
||||
if (
|
||||
PayPalCommerceGateway.context !== 'checkout'
|
||||
&& PayPalCommerceGateway.data_client_id.user === 0
|
||||
&& PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.addEventListener('load', (event) => {
|
||||
bootstrap();
|
||||
});
|
||||
|
|
|
@ -48,7 +48,12 @@ class CheckoutActionHandler {
|
|||
.querySelector('ul')
|
||||
);
|
||||
} else {
|
||||
errorHandler.message(data.data.message, true);
|
||||
errorHandler.clear();
|
||||
if (data.data.details.length > 0) {
|
||||
errorHandler.message(data.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
|
||||
} else {
|
||||
errorHandler.message(data.data.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -22,9 +22,12 @@ class MiniCartBootstap {
|
|||
}
|
||||
|
||||
shouldRender() {
|
||||
return document.querySelector(this.gateway.button.mini_cart_wrapper) !==
|
||||
null || document.querySelector(this.gateway.hosted_fields.mini_cart_wrapper) !==
|
||||
null;
|
||||
if (!this.gateway.can_save_vault_token) {
|
||||
return;
|
||||
}
|
||||
|
||||
return document.querySelector(this.gateway.button.mini_cart_wrapper) !== null
|
||||
|| document.querySelector(this.gateway.hosted_fields.mini_cart_wrapper) !== null;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -40,4 +43,4 @@ class MiniCartBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default MiniCartBootstap;
|
||||
export default MiniCartBootstap;
|
||||
|
|
|
@ -9,7 +9,6 @@ class CreditCardRenderer {
|
|||
this.cardValid = false;
|
||||
this.formValid = false;
|
||||
this.currentHostedFieldsInstance = null;
|
||||
this.formSubmissionSubscribed = false;
|
||||
}
|
||||
|
||||
render(wrapper, contextConfig) {
|
||||
|
@ -122,7 +121,7 @@ class CreditCardRenderer {
|
|||
|
||||
});
|
||||
|
||||
if (!this.formSubmissionSubscribed) {
|
||||
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
||||
document.querySelector(wrapper + ' button').addEventListener(
|
||||
'click',
|
||||
event => {
|
||||
|
@ -130,7 +129,8 @@ class CreditCardRenderer {
|
|||
this._submit(contextConfig);
|
||||
}
|
||||
);
|
||||
this.formSubmissionSubscribed = true;
|
||||
|
||||
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -143,7 +143,7 @@ class CreditCardRenderer {
|
|||
}
|
||||
|
||||
disableFields() {
|
||||
if( this.currentHostedFieldsInstance) {
|
||||
if (this.currentHostedFieldsInstance) {
|
||||
this.currentHostedFieldsInstance.setAttribute({
|
||||
field: 'number',
|
||||
attribute: 'disabled'
|
||||
|
@ -160,7 +160,7 @@ class CreditCardRenderer {
|
|||
}
|
||||
|
||||
enableFields() {
|
||||
if( this.currentHostedFieldsInstance) {
|
||||
if (this.currentHostedFieldsInstance) {
|
||||
this.currentHostedFieldsInstance.removeAttribute({
|
||||
field: 'number',
|
||||
attribute: 'disabled'
|
||||
|
@ -181,7 +181,7 @@ class CreditCardRenderer {
|
|||
this.errorHandler.clear();
|
||||
|
||||
if (this.formValid && this.cardValid) {
|
||||
const save_card = this.defaultConfig.save_card ? true : false;
|
||||
const save_card = this.defaultConfig.can_save_vault_token ? true : false;
|
||||
let vault = document.getElementById('ppcp-credit-card-vault') ?
|
||||
document.getElementById('ppcp-credit-card-vault').checked : save_card;
|
||||
if (this.defaultConfig.enforce_vault) {
|
||||
|
@ -216,8 +216,11 @@ class CreditCardRenderer {
|
|||
this.spinner.unblock();
|
||||
return contextConfig.onApprove(payload);
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.spinner.unblock();
|
||||
this.errorHandler.clear();
|
||||
if (err.details.length > 0) {
|
||||
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.spinner.unblock();
|
||||
|
|
|
@ -52,7 +52,7 @@ return array(
|
|||
*
|
||||
* @var State $state
|
||||
*/
|
||||
if ( $state->current_state() <= State::STATE_PROGRESSIVE ) {
|
||||
if ( $state->current_state() !== State::STATE_ONBOARDED ) {
|
||||
return new DisabledSmartButton();
|
||||
}
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
@ -73,6 +73,7 @@ return array(
|
|||
$currency = $container->get( 'api.shop.currency' );
|
||||
return new SmartButton(
|
||||
$container->get( 'button.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$settings,
|
||||
$payer_factory,
|
||||
|
@ -90,7 +91,7 @@ return array(
|
|||
'button.url' => static function ( ContainerInterface $container ): string {
|
||||
return plugins_url(
|
||||
'/modules/ppcp-button/',
|
||||
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'button.request-data' => static function ( ContainerInterface $container ): RequestData {
|
||||
|
|
|
@ -44,6 +44,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The Session Handler.
|
||||
*
|
||||
|
@ -125,6 +132,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
* 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 Settings $settings The Settings.
|
||||
* @param PayerFactory $payer_factory The Payer factory.
|
||||
|
@ -140,6 +148,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
SessionHandler $session_handler,
|
||||
Settings $settings,
|
||||
PayerFactory $payer_factory,
|
||||
|
@ -155,6 +164,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
) {
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->payer_factory = $payer_factory;
|
||||
|
@ -215,11 +225,12 @@ class SmartButton implements SmartButtonInterface {
|
|||
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 ( ! $subscription_helper->cart_contains_subscription() ) {
|
||||
$default_fields['card-vault'] = sprintf(
|
||||
'<p class="form-row form-row-wide"><label for="vault"><input class="ppcp-credit-card-vault" type="checkbox" id="ppcp-credit-card-vault" name="vault">%s</label></p>',
|
||||
esc_html__( 'Save your Credit Card', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
$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>',
|
||||
esc_html__( 'Save your Credit Card', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
if ( $subscription_helper->cart_contains_subscription() || $subscription_helper->order_pay_contains_subscription() ) {
|
||||
$default_fields['card-vault'] = '';
|
||||
}
|
||||
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( get_current_user_id() );
|
||||
|
@ -391,9 +402,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
if ( ! is_checkout() && ! $buttons_enabled ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! $this->can_save_vault_token() && $this->has_subscriptions() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$load_script = false;
|
||||
if ( is_checkout() && $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) ) {
|
||||
|
@ -406,17 +414,17 @@ class SmartButton implements SmartButtonInterface {
|
|||
if ( in_array( $this->context(), array( 'pay-now', 'checkout' ), true ) && $this->can_render_dcc() ) {
|
||||
wp_enqueue_style(
|
||||
'ppcp-hosted-fields',
|
||||
$this->module_url . '/assets/css/hosted-fields.css',
|
||||
untrailingslashit( $this->module_url ) . '/assets/css/hosted-fields.css',
|
||||
array(),
|
||||
1
|
||||
$this->version
|
||||
);
|
||||
}
|
||||
if ( $load_script ) {
|
||||
wp_enqueue_script(
|
||||
'ppcp-smart-button',
|
||||
$this->module_url . '/assets/js/button.js',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/button.js',
|
||||
array( 'jquery' ),
|
||||
'1.3.2',
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
|
@ -444,6 +452,12 @@ class SmartButton implements SmartButtonInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
|
||||
if ( ! isset( $available_gateways['ppcp-gateway'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div id="ppc-button"></div>';
|
||||
}
|
||||
|
||||
|
@ -570,7 +584,8 @@ class SmartButton implements SmartButtonInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$label = 'checkout' === $this->context() ? __( 'Place order', 'woocommerce-paypal-payments' ) : __( 'Pay for order', 'woocommerce-paypal-payments' );
|
||||
// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
|
||||
$label = 'checkout' === $this->context() ? apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ) : __( 'Pay for order', 'woocommerce' );
|
||||
|
||||
printf(
|
||||
'<div id="%1$s" style="display:none;">
|
||||
|
@ -597,7 +612,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
return false;
|
||||
}
|
||||
|
||||
return is_user_logged_in();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -612,6 +627,10 @@ class SmartButton implements SmartButtonInterface {
|
|||
if ( is_product() ) {
|
||||
return $this->subscription_helper->current_product_is_subscription();
|
||||
}
|
||||
if ( is_wc_endpoint_url( 'order-pay' ) ) {
|
||||
return $this->subscription_helper->order_pay_contains_subscription();
|
||||
}
|
||||
|
||||
return $this->subscription_helper->cart_contains_subscription();
|
||||
}
|
||||
|
||||
|
@ -644,10 +663,11 @@ class SmartButton implements SmartButtonInterface {
|
|||
$localize = array(
|
||||
'script_attributes' => $this->attributes(),
|
||||
'data_client_id' => array(
|
||||
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ) ),
|
||||
'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ),
|
||||
'user' => get_current_user_id(),
|
||||
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ) ),
|
||||
'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ),
|
||||
'user' => get_current_user_id(),
|
||||
'has_subscriptions' => $this->has_subscriptions(),
|
||||
),
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
'context' => $this->context(),
|
||||
|
@ -666,7 +686,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
),
|
||||
),
|
||||
'enforce_vault' => $this->has_subscriptions(),
|
||||
'save_card' => $this->can_save_vault_token(),
|
||||
'can_save_vault_token' => $this->can_save_vault_token(),
|
||||
'bn_codes' => $this->bn_codes(),
|
||||
'payer' => $this->payerData(),
|
||||
'button' => array(
|
||||
|
@ -1008,38 +1028,50 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal buttons will be rendered at on checkout page.
|
||||
* Returns the action name that PayPal button will use for rendering on the checkout page.
|
||||
*
|
||||
* @return string Action name.
|
||||
*/
|
||||
private function checkout_button_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal button will use for rendering on the checkout page.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_paypal_payments_checkout_button_renderer_hook', 'woocommerce_review_order_after_payment' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal DCC button will be rendered at on checkout page.
|
||||
* Returns the action name that PayPal DCC button will use for rendering on the checkout page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function checkout_dcc_button_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal DCC button will use for rendering on the checkout page.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_paypal_payments_checkout_dcc_renderer_hook', 'woocommerce_review_order_after_submit' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal button and Pay Later message will be rendered at on pay-order page.
|
||||
* Returns the action name that PayPal button and Pay Later message will use for rendering on the pay-order page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function pay_order_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal button and Pay Later message will use for rendering on the pay-order page.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_paypal_payments_pay_order_dcc_renderer_hook', 'woocommerce_pay_order_after_submit' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal button will be rendered next to Proceed to checkout button (normally displayed in cart).
|
||||
* Returns action name that PayPal button will use for rendering next to Proceed to checkout button (normally displayed in cart).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function proceed_to_checkout_button_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal button will use for rendering next to Proceed to checkout button (normally displayed in cart).
|
||||
*/
|
||||
return (string) apply_filters(
|
||||
'woocommerce_paypal_payments_proceed_to_checkout_button_renderer_hook',
|
||||
'woocommerce_proceed_to_checkout'
|
||||
|
@ -1047,11 +1079,14 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal button will be rendered in the WC mini cart.
|
||||
* Returns the action name that PayPal button will use for rendering in the WC mini cart.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function mini_cart_button_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal button will use for rendering in the WC mini cart.
|
||||
*/
|
||||
return (string) apply_filters(
|
||||
'woocommerce_paypal_payments_mini_cart_button_renderer_hook',
|
||||
'woocommerce_widget_shopping_cart_after_buttons'
|
||||
|
@ -1059,11 +1094,14 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return action name PayPal button and Pay Later message will be rendered at on the single product page.
|
||||
* Returns the action name that PayPal button and Pay Later message will use for rendering on the single product page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function single_product_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal button and Pay Later message will use for rendering on the single product page.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_paypal_payments_single_product_renderer_hook', 'woocommerce_single_product_summary' );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
|||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Address;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PayerName;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentMethod;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
|
@ -337,6 +339,15 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
|
||||
$payer = $this->payer_factory->from_paypal_response( json_decode( wp_json_encode( $data['payer'] ) ) );
|
||||
}
|
||||
|
||||
if ( ! $payer && isset( $data['form'] ) ) {
|
||||
parse_str( $data['form'], $form_fields );
|
||||
|
||||
if ( isset( $form_fields['billing_email'] ) && '' !== $form_fields['billing_email'] ) {
|
||||
return $this->payer_factory->from_checkout_form( $form_fields );
|
||||
}
|
||||
}
|
||||
|
||||
return $payer;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ class DataClientIdEndpoint implements EndpointInterface {
|
|||
try {
|
||||
$this->request_data->read_request( $this->nonce() );
|
||||
$user_id = get_current_user_id();
|
||||
$token = $this->identity_token->generate_for_customer( $user_id );
|
||||
$token = $this->identity_token->generate_for_user( $user_id );
|
||||
wp_send_json(
|
||||
array(
|
||||
'token' => $token->token(),
|
||||
|
|
|
@ -89,6 +89,9 @@ class PPECHelper {
|
|||
* @return bool
|
||||
*/
|
||||
public static function use_ppec_compat_layer_for_subscriptions() {
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -2,41 +2,28 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
#field-merchant_email_production,
|
||||
#field-ppcp_disconnect_sandbox,
|
||||
#field-ppcp_disconnect_production,
|
||||
#field-merchant_id_production,
|
||||
#field-client_id_production,
|
||||
#field-client_secret_production,
|
||||
#field-merchant_email_sandbox,
|
||||
#field-merchant_id_sandbox,
|
||||
#field-client_id_sandbox,
|
||||
#field-client_secret_sandbox{
|
||||
.ppcp-onboarded .ppcp-onboarding-element:not(.ppcp-always-shown-element) {
|
||||
display: none;
|
||||
}
|
||||
#field-merchant_email_production.show,
|
||||
#field-ppcp_disconnect_sandbox.show,
|
||||
#field-ppcp_disconnect_production.show,
|
||||
#field-merchant_id_production.show,
|
||||
#field-client_id_production.show,
|
||||
#field-client_secret_production.show,
|
||||
#field-merchant_email_sandbox.show,
|
||||
#field-merchant_id_sandbox.show,
|
||||
#field-client_id_sandbox.show,
|
||||
#field-client_secret_sandbox.show {
|
||||
|
||||
.ppcp-onboarding .ppcp-settings-field:not(.ppcp-onboarding-element):not(.ppcp-always-shown-element) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ppcp-settings-field.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ppcp-settings-field.show {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
#field-toggle_manual_input span.hide,
|
||||
#field-toggle_manual_input.show span.show{
|
||||
display: none;
|
||||
}
|
||||
#field-toggle_manual_input.show span.hide {
|
||||
display: unset;
|
||||
label.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#field-production_toggle_manual_input button,
|
||||
#field-sandbox_toggle_manual_input button {
|
||||
#field-toggle_manual_input button {
|
||||
color: #0073aa;
|
||||
transition-property: border, background, color;
|
||||
transition-duration: .05s;
|
||||
|
@ -49,39 +36,8 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
#field-sandbox_toggle_manual_input.onboarded,
|
||||
#field-production_toggle_manual_input.onboarded {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#field-ppcp_disconnect_sandbox.onboarded,
|
||||
#field-ppcp_disconnect_production.onboarded,
|
||||
#field-merchant_email_sandbox.onboarded,
|
||||
#field-merchant_id_sandbox.onboarded,
|
||||
#field-client_id_sandbox.onboarded,
|
||||
#field-client_secret_sandbox.onboarded,
|
||||
#field-merchant_email_production.onboarded,
|
||||
#field-merchant_id_production.onboarded,
|
||||
#field-client_id_production.onboarded,
|
||||
#field-client_secret_production.onboarded {
|
||||
display:table-row;
|
||||
}
|
||||
#field-ppcp_disconnect_sandbox.onboarded.hide,
|
||||
#field-ppcp_disconnect_production.onboarded.hide,
|
||||
#field-merchant_email_sandbox.onboarded.hide,
|
||||
#field-merchant_id_sandbox.onboarded.hide,
|
||||
#field-client_id_sandbox.onboarded.hide,
|
||||
#field-client_secret_sandbox.onboarded.hide,
|
||||
#field-merchant_email_production.onboarded.hide,
|
||||
#field-merchant_id_production.onboarded.hide,
|
||||
#field-client_id_production.onboarded.hide,
|
||||
#field-client_secret_production.onboarded.hide {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/* Probably not the best location for this but will do until there's a general purpose settings CSS file. */
|
||||
.ppcp-settings-field-heading td, .ppcp-settings-field-heading th {
|
||||
.ppcp-settings-field-heading td, .ppcp-settings-field-heading th, .ppcp-settings-no-title-col td {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
|
@ -92,3 +48,123 @@
|
|||
.input-text[pattern]:invalid {
|
||||
border: red solid 2px;
|
||||
}
|
||||
|
||||
ul.ppcp-onboarding-options, ul.ppcp-onboarding-options-sublist {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.ppcp-onboarding-options-sublist {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.ppcp-muted-text {
|
||||
opacity: 0.6;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
#field-ppcp_onboarading_header > td, #field-ppcp_onboarading_options > td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header, .ppcp-onboarding-cards-options {
|
||||
display: flex;
|
||||
width: 1200px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-left, .ppcp-onboarding-header-right {
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-left img {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-cards img, .ppcp-onboarding-header-paypal-logos img {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-cards img {
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-paypal-logos img {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table {
|
||||
margin-left: 35px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table th, .ppcp-onboarding-cards-options table td {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table th {
|
||||
width: 350px;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table tr th, .ppcp-onboarding-cards-options table tr td {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-options table tr {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-screen {
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-screen img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-help-tip.ppcp-table-row-tooltip {
|
||||
margin-left: -20px !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.ppcp-onboarding-header, .ppcp-onboarding-cards-options {
|
||||
width: unset;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-right {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-cards-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-left img {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-cards img, .ppcp-onboarding-header-paypal-logos img {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-cards img {
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.ppcp-onboarding-header-paypal-logos img {
|
||||
height: 23px;
|
||||
}
|
||||
}
|
||||
|
|
BIN
modules/ppcp-onboarding/assets/images/cards-screen-acdc.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
modules/ppcp-onboarding/assets/images/cards-screen-basic.png
Normal file
After Width: | Height: | Size: 89 KiB |
|
@ -1,9 +1,11 @@
|
|||
// Onboarding.
|
||||
const ppcp_onboarding = {
|
||||
BUTTON_SELECTOR: '[data-paypal-onboard-button]',
|
||||
PAYPAL_JS_ID: 'ppcp-onboarding-paypal-js',
|
||||
_timeout: false,
|
||||
|
||||
STATE_START: 'start',
|
||||
STATE_ONBOARDED: 'onboarded',
|
||||
|
||||
init: function() {
|
||||
document.addEventListener('DOMContentLoaded', this.reload);
|
||||
},
|
||||
|
@ -15,7 +17,7 @@ const ppcp_onboarding = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Add event listeners to buttons.
|
||||
// Add event listeners to buttons preventing link clicking if PayPal init failed.
|
||||
buttons.forEach(
|
||||
(element) => {
|
||||
if (element.hasAttribute('data-ppcp-button-initialized')) {
|
||||
|
@ -83,14 +85,13 @@ const ppcp_onboarding = {
|
|||
authCode: authCode,
|
||||
sharedId: sharedId,
|
||||
nonce: PayPalCommerceGatewayOnboarding.nonce,
|
||||
env: env
|
||||
env: env,
|
||||
acceptCards: document.querySelector('#ppcp-onboarding-accept-cards').checked,
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
function ppcp_onboarding_sandboxCallback(...args) {
|
||||
|
@ -101,150 +102,151 @@ function ppcp_onboarding_productionCallback(...args) {
|
|||
return ppcp_onboarding.loginSeller('production', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the PayPal modal will redirect the user a dirty form
|
||||
* provokes an alert if the user wants to leave the page. Since the user
|
||||
* needs to toggle the sandbox switch, we disable this dirty state with the
|
||||
* following workaround for checkboxes.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
const checkBoxOnClick = (event) => {
|
||||
const value = event.target.checked;
|
||||
if (event.target.getAttribute('id') === 'ppcp-sandbox_on') {
|
||||
toggleSandboxProduction(! value);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setTimeout( () => {
|
||||
event.target.checked = value;
|
||||
},1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the credential input fields.
|
||||
*
|
||||
* @param forProduction
|
||||
*/
|
||||
const credentialToggle = (forProduction) => {
|
||||
|
||||
const sandboxClassSelectors = [
|
||||
'#field-ppcp_disconnect_sandbox',
|
||||
'#field-merchant_email_sandbox',
|
||||
'#field-merchant_id_sandbox',
|
||||
'#field-client_id_sandbox',
|
||||
'#field-client_secret_sandbox',
|
||||
];
|
||||
const productionClassSelectors = [
|
||||
'#field-ppcp_disconnect_production',
|
||||
'#field-merchant_email_production',
|
||||
'#field-merchant_id_production',
|
||||
'#field-client_id_production',
|
||||
'#field-client_secret_production',
|
||||
];
|
||||
|
||||
const selectors = forProduction ? productionClassSelectors : sandboxClassSelectors;
|
||||
document.querySelectorAll(selectors.join()).forEach(
|
||||
(element) => {element.classList.toggle('show')}
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the sandbox/production input fields.
|
||||
*
|
||||
* @param showProduction
|
||||
*/
|
||||
const toggleSandboxProduction = (showProduction) => {
|
||||
const productionDisplaySelectors = [
|
||||
'#field-credentials_production_heading',
|
||||
'#field-production_toggle_manual_input',
|
||||
'#field-ppcp_onboarding_production',
|
||||
];
|
||||
const productionClassSelectors = [
|
||||
|
||||
'#field-ppcp_disconnect_production',
|
||||
'#field-merchant_email_production',
|
||||
'#field-merchant_id_production',
|
||||
'#field-client_id_production',
|
||||
'#field-client_secret_production',
|
||||
];
|
||||
const sandboxDisplaySelectors = [
|
||||
'#field-credentials_sandbox_heading',
|
||||
'#field-sandbox_toggle_manual_input',
|
||||
'#field-ppcp_onboarding_sandbox',
|
||||
];
|
||||
const sandboxClassSelectors = [
|
||||
'#field-ppcp_disconnect_sandbox',
|
||||
'#field-merchant_email_sandbox',
|
||||
'#field-merchant_id_sandbox',
|
||||
'#field-client_id_sandbox',
|
||||
'#field-client_secret_sandbox',
|
||||
];
|
||||
|
||||
if (showProduction) {
|
||||
document.querySelectorAll(productionDisplaySelectors.join()).forEach(
|
||||
(element) => {element.style.display = ''}
|
||||
);
|
||||
document.querySelectorAll(sandboxDisplaySelectors.join()).forEach(
|
||||
(element) => {element.style.display = 'none'}
|
||||
);
|
||||
document.querySelectorAll(productionClassSelectors.join()).forEach(
|
||||
(element) => {element.classList.remove('hide')}
|
||||
);
|
||||
document.querySelectorAll(sandboxClassSelectors.join()).forEach(
|
||||
(element) => {
|
||||
element.classList.remove('show');
|
||||
element.classList.add('hide');
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
document.querySelectorAll(productionDisplaySelectors.join()).forEach(
|
||||
(element) => {element.style.display = 'none'}
|
||||
);
|
||||
document.querySelectorAll(sandboxDisplaySelectors.join()).forEach(
|
||||
(element) => {element.style.display = ''}
|
||||
);
|
||||
|
||||
document.querySelectorAll(sandboxClassSelectors.join()).forEach(
|
||||
(element) => {element.classList.remove('hide')}
|
||||
);
|
||||
document.querySelectorAll(productionClassSelectors.join()).forEach(
|
||||
(element) => {
|
||||
element.classList.remove('show');
|
||||
element.classList.add('hide');
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
const disconnect = (event) => {
|
||||
event.preventDefault();
|
||||
const fields = event.target.classList.contains('production') ? [
|
||||
'#field-merchant_email_production input',
|
||||
'#field-merchant_id_production input',
|
||||
'#field-client_id_production input',
|
||||
'#field-client_secret_production input',
|
||||
] : [
|
||||
'#field-merchant_email_sandbox input',
|
||||
'#field-merchant_id_sandbox input',
|
||||
'#field-client_id_sandbox input',
|
||||
'#field-client_secret_sandbox input',
|
||||
];
|
||||
|
||||
document.querySelectorAll(fields.join()).forEach(
|
||||
(element) => {
|
||||
element.value = '';
|
||||
}
|
||||
);
|
||||
document.querySelector('.woocommerce-save-button').click();
|
||||
};
|
||||
|
||||
(() => {
|
||||
const sandboxSwitchElement = document.querySelector('#ppcp-sandbox_on');
|
||||
if (sandboxSwitchElement) {
|
||||
toggleSandboxProduction(! sandboxSwitchElement.checked);
|
||||
}
|
||||
const productionCredentialElementsSelectors = [
|
||||
'#field-merchant_email_production',
|
||||
'#field-merchant_id_production',
|
||||
'#field-client_id_production',
|
||||
'#field-client_secret_production',
|
||||
];
|
||||
const sandboxCredentialElementsSelectors = [
|
||||
'#field-merchant_email_sandbox',
|
||||
'#field-merchant_id_sandbox',
|
||||
'#field-client_id_sandbox',
|
||||
'#field-client_secret_sandbox',
|
||||
];
|
||||
|
||||
const updateOptionsState = () => {
|
||||
const cardsChk = document.querySelector('#ppcp-onboarding-accept-cards');
|
||||
if (!cardsChk) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll('#ppcp-onboarding-dcc-options input').forEach(input => {
|
||||
input.disabled = !cardsChk.checked;
|
||||
});
|
||||
|
||||
const basicRb = document.querySelector('#ppcp-onboarding-dcc-basic');
|
||||
|
||||
const isExpress = !cardsChk.checked || basicRb.checked;
|
||||
|
||||
const expressButtonSelectors = [
|
||||
'#field-ppcp_onboarding_production_express',
|
||||
'#field-ppcp_onboarding_sandbox_express',
|
||||
];
|
||||
const ppcpButtonSelectors = [
|
||||
'#field-ppcp_onboarding_production_ppcp',
|
||||
'#field-ppcp_onboarding_sandbox_ppcp',
|
||||
];
|
||||
|
||||
document.querySelectorAll(expressButtonSelectors.join()).forEach(
|
||||
element => element.style.display = isExpress ? '' : 'none'
|
||||
);
|
||||
document.querySelectorAll(ppcpButtonSelectors.join()).forEach(
|
||||
element => element.style.display = !isExpress ? '' : 'none'
|
||||
);
|
||||
|
||||
const screemImg = document.querySelector('#ppcp-onboarding-cards-screen-img');
|
||||
if (screemImg) {
|
||||
const currentRb = Array.from(document.querySelectorAll('#ppcp-onboarding-dcc-options input[type="radio"]'))
|
||||
.filter(rb => rb.checked)[0] ?? null;
|
||||
|
||||
const imgUrl = currentRb.getAttribute('data-screen-url');
|
||||
screemImg.src = imgUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const updateManualInputControls = (shown, isSandbox, isAnyEnvOnboarded) => {
|
||||
const productionElementsSelectors = productionCredentialElementsSelectors;
|
||||
const sandboxElementsSelectors = sandboxCredentialElementsSelectors;
|
||||
const otherElementsSelectors = [
|
||||
'.woocommerce-save-button',
|
||||
];
|
||||
if (!isAnyEnvOnboarded) {
|
||||
otherElementsSelectors.push('#field-sandbox_on');
|
||||
}
|
||||
|
||||
document.querySelectorAll(productionElementsSelectors.join()).forEach(
|
||||
element => {
|
||||
element.classList.remove('hide', 'show');
|
||||
element.classList.add((shown && !isSandbox) ? 'show' : 'hide');
|
||||
}
|
||||
);
|
||||
document.querySelectorAll(sandboxElementsSelectors.join()).forEach(
|
||||
element => {
|
||||
element.classList.remove('hide', 'show');
|
||||
element.classList.add((shown && isSandbox) ? 'show' : 'hide');
|
||||
}
|
||||
);
|
||||
document.querySelectorAll(otherElementsSelectors.join()).forEach(
|
||||
element => element.style.display = shown ? '' : 'none'
|
||||
);
|
||||
};
|
||||
|
||||
const updateEnvironmentControls = (isSandbox) => {
|
||||
const productionElementsSelectors = [
|
||||
'#field-ppcp_disconnect_production',
|
||||
'#field-credentials_production_heading',
|
||||
];
|
||||
const sandboxElementsSelectors = [
|
||||
'#field-ppcp_disconnect_sandbox',
|
||||
'#field-credentials_sandbox_heading',
|
||||
];
|
||||
|
||||
document.querySelectorAll(productionElementsSelectors.join()).forEach(
|
||||
element => element.style.display = !isSandbox ? '' : 'none'
|
||||
);
|
||||
document.querySelectorAll(sandboxElementsSelectors.join()).forEach(
|
||||
element => element.style.display = isSandbox ? '' : 'none'
|
||||
);
|
||||
};
|
||||
|
||||
let isDisconnecting = false;
|
||||
|
||||
const disconnect = (event) => {
|
||||
event.preventDefault();
|
||||
const fields = event.target.classList.contains('production') ? productionCredentialElementsSelectors : sandboxCredentialElementsSelectors;
|
||||
|
||||
document.querySelectorAll(fields.map(f => f + ' input').join()).forEach(
|
||||
(element) => {
|
||||
element.value = '';
|
||||
}
|
||||
);
|
||||
|
||||
isDisconnecting = true;
|
||||
|
||||
document.querySelector('.woocommerce-save-button').click();
|
||||
};
|
||||
|
||||
// Prevent the message about unsaved checkbox/radiobutton when reloading the page.
|
||||
// (WC listens for changes on all inputs and sets dirty flag until form submission)
|
||||
const preventDirtyCheckboxPropagation = event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const value = event.target.checked;
|
||||
setTimeout( () => {
|
||||
event.target.checked = value;
|
||||
}, 1
|
||||
);
|
||||
};
|
||||
|
||||
const sandboxSwitchElement = document.querySelector('#ppcp-sandbox_on');
|
||||
|
||||
const validate = () => {
|
||||
const selectors = sandboxSwitchElement.checked ? sandboxCredentialElementsSelectors : productionCredentialElementsSelectors;
|
||||
const values = selectors.map(s => document.querySelector(s + ' input')).map(el => el.value);
|
||||
|
||||
const errors = [];
|
||||
if (values.some(v => !v)) {
|
||||
errors.push(PayPalCommerceGatewayOnboarding.error_messages.no_credentials);
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
const isAnyEnvOnboarded = PayPalCommerceGatewayOnboarding.sandbox_state === ppcp_onboarding.STATE_ONBOARDED ||
|
||||
PayPalCommerceGatewayOnboarding.production_state === ppcp_onboarding.STATE_ONBOARDED;
|
||||
|
||||
document.querySelectorAll('.ppcp-disconnect').forEach(
|
||||
(button) => {
|
||||
|
@ -255,43 +257,89 @@ const disconnect = (event) => {
|
|||
}
|
||||
);
|
||||
|
||||
// Prevent a possibly dirty form arising from this particular checkbox.
|
||||
if (sandboxSwitchElement) {
|
||||
sandboxSwitchElement.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
const value = event.target.checked;
|
||||
document.querySelectorAll('.ppcp-onboarding-options input').forEach(
|
||||
(element) => {
|
||||
element.addEventListener('click', event => {
|
||||
updateOptionsState();
|
||||
|
||||
toggleSandboxProduction( ! value );
|
||||
preventDirtyCheckboxPropagation(event);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setTimeout( () => {
|
||||
event.target.checked = value;
|
||||
}, 1
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
const isSandboxInBackend = PayPalCommerceGatewayOnboarding.current_env === 'sandbox';
|
||||
if (sandboxSwitchElement.checked !== isSandboxInBackend) {
|
||||
sandboxSwitchElement.checked = isSandboxInBackend;
|
||||
}
|
||||
|
||||
// document.querySelectorAll('#mainform input[type="checkbox"]').forEach(
|
||||
// (checkbox) => {
|
||||
// checkbox.addEventListener('click', checkBoxOnClick);
|
||||
// }
|
||||
// );
|
||||
updateOptionsState();
|
||||
|
||||
document.querySelectorAll('#field-sandbox_toggle_manual_input button, #field-production_toggle_manual_input button').forEach(
|
||||
(button) => {
|
||||
button.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
const isProduction = event.target.classList.contains('production-toggle');
|
||||
credentialToggle(isProduction);
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
const settingsContainer = document.querySelector('#mainform .form-table');
|
||||
|
||||
const markCurrentOnboardingState = (isOnboarded) => {
|
||||
settingsContainer.classList.remove('ppcp-onboarded', 'ppcp-onboarding');
|
||||
settingsContainer.classList.add(isOnboarded ? 'ppcp-onboarded' : 'ppcp-onboarding');
|
||||
}
|
||||
|
||||
markCurrentOnboardingState(PayPalCommerceGatewayOnboarding.current_state === ppcp_onboarding.STATE_ONBOARDED);
|
||||
|
||||
const manualInputToggleButton = document.querySelector('#field-toggle_manual_input button');
|
||||
let isManualInputShown = PayPalCommerceGatewayOnboarding.current_state === ppcp_onboarding.STATE_ONBOARDED;
|
||||
|
||||
manualInputToggleButton.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
isManualInputShown = !isManualInputShown;
|
||||
|
||||
updateManualInputControls(isManualInputShown, sandboxSwitchElement.checked, isAnyEnvOnboarded);
|
||||
}
|
||||
);
|
||||
|
||||
sandboxSwitchElement.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
const isSandbox = sandboxSwitchElement.checked;
|
||||
|
||||
if (isAnyEnvOnboarded) {
|
||||
const onboardingState = isSandbox ? PayPalCommerceGatewayOnboarding.sandbox_state : PayPalCommerceGatewayOnboarding.production_state;
|
||||
const isOnboarded = onboardingState === ppcp_onboarding.STATE_ONBOARDED;
|
||||
|
||||
markCurrentOnboardingState(isOnboarded);
|
||||
isManualInputShown = isOnboarded;
|
||||
}
|
||||
|
||||
updateManualInputControls(isManualInputShown, isSandbox, isAnyEnvOnboarded);
|
||||
|
||||
updateEnvironmentControls(isSandbox);
|
||||
|
||||
preventDirtyCheckboxPropagation(event);
|
||||
}
|
||||
);
|
||||
|
||||
updateManualInputControls(isManualInputShown, sandboxSwitchElement.checked, isAnyEnvOnboarded);
|
||||
|
||||
updateEnvironmentControls(sandboxSwitchElement.checked);
|
||||
|
||||
document.querySelector('#mainform').addEventListener('submit', e => {
|
||||
if (isDisconnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const errors = validate();
|
||||
if (errors.length) {
|
||||
e.preventDefault();
|
||||
|
||||
const errorLabel = document.querySelector('#ppcp-form-errors-label');
|
||||
errorLabel.parentElement.parentElement.classList.remove('hide');
|
||||
|
||||
errorLabel.innerHTML = errors.join('<br/>');
|
||||
|
||||
errorLabel.scrollIntoView();
|
||||
window.scrollBy(0, -120); // WP + WC floating header
|
||||
}
|
||||
});
|
||||
|
||||
// Onboarding buttons.
|
||||
ppcp_onboarding.init();
|
||||
|
|
|
@ -23,7 +23,7 @@ document.addEventListener(
|
|||
}
|
||||
|
||||
group.forEach( (elementToShow) => {
|
||||
document.querySelector(elementToShow).style.display = 'table-row';
|
||||
document.querySelector(elementToShow).style.display = '';
|
||||
})
|
||||
|
||||
if('ppcp-message_enabled' === event.target.getAttribute('id')){
|
||||
|
@ -56,7 +56,7 @@ document.addEventListener(
|
|||
return;
|
||||
}
|
||||
if (value === elementToToggle.value && domElement.style.display !== 'none') {
|
||||
domElement.style.display = 'table-row';
|
||||
domElement.style.display = '';
|
||||
return;
|
||||
}
|
||||
domElement.style.display = 'none';
|
||||
|
@ -69,7 +69,7 @@ document.addEventListener(
|
|||
const value = event.target.value;
|
||||
group.forEach( (elementToToggle) => {
|
||||
if (value === elementToToggle.value) {
|
||||
document.querySelector(elementToToggle.selector).style.display = 'table-row';
|
||||
document.querySelector(elementToToggle.selector).style.display = '';
|
||||
return;
|
||||
}
|
||||
document.querySelector(elementToToggle.selector).style.display = 'none';
|
||||
|
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Assets\OnboardingAssets;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\OnboardingRESTController;
|
||||
|
||||
|
@ -117,9 +118,8 @@ return array(
|
|||
);
|
||||
},
|
||||
'onboarding.state' => function( ContainerInterface $container ) : State {
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return new State( $environment, $settings );
|
||||
return new State( $settings );
|
||||
},
|
||||
'onboarding.environment' => function( ContainerInterface $container ) : Environment {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
@ -131,15 +131,18 @@ return array(
|
|||
$login_seller_endpoint = $container->get( 'onboarding.endpoint.login-seller' );
|
||||
return new OnboardingAssets(
|
||||
$container->get( 'onboarding.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$state,
|
||||
$login_seller_endpoint
|
||||
$container->get( 'onboarding.environment' ),
|
||||
$login_seller_endpoint,
|
||||
$container->get( 'wcgateway.current-ppcp-settings-page-id' )
|
||||
);
|
||||
},
|
||||
|
||||
'onboarding.url' => static function ( ContainerInterface $container ): string {
|
||||
return plugins_url(
|
||||
'/modules/ppcp-onboarding/',
|
||||
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -188,7 +191,6 @@ return array(
|
|||
return new PartnerReferrals(
|
||||
CONNECT_WOO_SANDBOX_URL,
|
||||
new ConnectBearer(),
|
||||
$container->get( 'api.repository.partner-referrals-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
@ -197,7 +199,6 @@ return array(
|
|||
return new PartnerReferrals(
|
||||
CONNECT_WOO_URL,
|
||||
new ConnectBearer(),
|
||||
$container->get( 'api.repository.partner-referrals-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
@ -205,11 +206,19 @@ return array(
|
|||
|
||||
$partner_referrals = $container->get( 'api.endpoint.partner-referrals-production' );
|
||||
$partner_referrals_sandbox = $container->get( 'api.endpoint.partner-referrals-sandbox' );
|
||||
$partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return new OnboardingRenderer(
|
||||
$settings,
|
||||
$partner_referrals,
|
||||
$partner_referrals_sandbox
|
||||
$partner_referrals_sandbox,
|
||||
$partner_referrals_data
|
||||
);
|
||||
},
|
||||
'onboarding.render-options' => static function ( ContainerInterface $container ) : OnboardingOptionsRenderer {
|
||||
return new OnboardingOptionsRenderer(
|
||||
$container->get( 'onboarding.url' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
'onboarding.rest' => static function( $container ) : OnboardingRESTController {
|
||||
|
|
|
@ -10,7 +10,9 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Onboarding\Assets;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class OnboardingAssets
|
||||
|
@ -24,6 +26,13 @@ class OnboardingAssets {
|
|||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The State.
|
||||
*
|
||||
|
@ -31,6 +40,13 @@ class OnboardingAssets {
|
|||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* The Environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
private $environment;
|
||||
|
||||
/**
|
||||
* The LoginSeller Endpoint.
|
||||
*
|
||||
|
@ -38,22 +54,38 @@ class OnboardingAssets {
|
|||
*/
|
||||
private $login_seller_endpoint;
|
||||
|
||||
/**
|
||||
* ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $page_id;
|
||||
|
||||
/**
|
||||
* OnboardingAssets constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
* @param State $state The State object.
|
||||
* @param Environment $environment The Environment.
|
||||
* @param LoginSellerEndpoint $login_seller_endpoint The LoginSeller endpoint.
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
State $state,
|
||||
LoginSellerEndpoint $login_seller_endpoint
|
||||
Environment $environment,
|
||||
LoginSellerEndpoint $login_seller_endpoint,
|
||||
string $page_id
|
||||
) {
|
||||
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
$this->state = $state;
|
||||
$this->environment = $environment;
|
||||
$this->login_seller_endpoint = $login_seller_endpoint;
|
||||
$this->page_id = $page_id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,28 +95,28 @@ class OnboardingAssets {
|
|||
*/
|
||||
public function register(): bool {
|
||||
|
||||
$url = $this->module_url . '/assets/css/onboarding.css';
|
||||
$url = untrailingslashit( $this->module_url ) . '/assets/css/onboarding.css';
|
||||
wp_register_style(
|
||||
'ppcp-onboarding',
|
||||
$url,
|
||||
array(),
|
||||
1
|
||||
$this->version
|
||||
);
|
||||
$url = $this->module_url . '/assets/js/settings.js';
|
||||
$url = untrailingslashit( $this->module_url ) . '/assets/js/settings.js';
|
||||
wp_register_script(
|
||||
'ppcp-settings',
|
||||
$url,
|
||||
array(),
|
||||
1,
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
$url = $this->module_url . '/assets/js/onboarding.js';
|
||||
$url = untrailingslashit( $this->module_url ) . '/assets/js/onboarding.js';
|
||||
wp_register_script(
|
||||
'ppcp-onboarding',
|
||||
$url,
|
||||
array( 'jquery' ),
|
||||
1,
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
wp_localize_script(
|
||||
|
@ -103,9 +135,16 @@ class OnboardingAssets {
|
|||
*/
|
||||
public function get_script_data() {
|
||||
return array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( LoginSellerEndpoint::ENDPOINT ) ),
|
||||
'nonce' => wp_create_nonce( $this->login_seller_endpoint::nonce() ),
|
||||
'paypal_js_url' => 'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js',
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( LoginSellerEndpoint::ENDPOINT ) ),
|
||||
'nonce' => wp_create_nonce( $this->login_seller_endpoint::nonce() ),
|
||||
'paypal_js_url' => 'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js',
|
||||
'sandbox_state' => State::get_state_name( $this->state->sandbox_state() ),
|
||||
'production_state' => State::get_state_name( $this->state->production_state() ),
|
||||
'current_state' => State::get_state_name( $this->state->current_state() ),
|
||||
'current_env' => $this->environment->current_environment(),
|
||||
'error_messages' => array(
|
||||
'no_credentials' => __( 'API credentials must be entered to save the settings.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -131,7 +170,6 @@ class OnboardingAssets {
|
|||
* @return bool
|
||||
*/
|
||||
private function should_render_onboarding_script(): bool {
|
||||
global $current_section;
|
||||
return 'ppcp-gateway' === $current_section;
|
||||
return PayPalGateway::ID === $this->page_id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
$this->settings->set( 'sandbox_on', $is_sandbox );
|
||||
$this->settings->set( 'products_dcc_enabled', null );
|
||||
$this->settings->persist();
|
||||
|
||||
$endpoint = $is_sandbox ? $this->login_seller_sandbox : $this->login_seller_production;
|
||||
$credentials = $endpoint->credentials_for(
|
||||
$data['sharedId'],
|
||||
|
@ -143,10 +144,30 @@ class LoginSellerEndpoint implements EndpointInterface {
|
|||
}
|
||||
$this->settings->set( 'client_secret', $credentials->client_secret );
|
||||
$this->settings->set( 'client_id', $credentials->client_id );
|
||||
|
||||
$accept_cards = (bool) ( $data['acceptCards'] ?? true );
|
||||
$funding_sources = array();
|
||||
if ( $this->settings->has( 'disable_funding' ) ) {
|
||||
$funding_sources = $this->settings->get( 'disable_funding' );
|
||||
if ( ! is_array( $funding_sources ) ) {
|
||||
$funding_sources = array();
|
||||
}
|
||||
}
|
||||
if ( $accept_cards ) {
|
||||
$funding_sources = array_diff( $funding_sources, array( 'card' ) );
|
||||
} else {
|
||||
if ( ! in_array( 'card', $funding_sources, true ) ) {
|
||||
$funding_sources[] = 'card';
|
||||
}
|
||||
}
|
||||
$this->settings->set( 'disable_funding', $funding_sources );
|
||||
|
||||
$this->settings->persist();
|
||||
|
||||
if ( $this->cache->has( PayPalBearer::CACHE_KEY ) ) {
|
||||
$this->cache->delete( PayPalBearer::CACHE_KEY );
|
||||
}
|
||||
|
||||
wp_schedule_single_event(
|
||||
time() + 5,
|
||||
WebhookRegistrar::EVENT_HOOK
|
||||
|
|
|
@ -65,16 +65,15 @@ class OnboardingModule implements ModuleInterface {
|
|||
if ( 'ppcp_onboarding' !== $config['type'] ) {
|
||||
return $field;
|
||||
}
|
||||
$renderer = $c->get( 'onboarding.render' );
|
||||
$is_production = 'production' === $config['env'];
|
||||
|
||||
/**
|
||||
* The OnboardingRenderer.
|
||||
*
|
||||
* @var OnboardingRenderer $renderer
|
||||
*/
|
||||
$renderer = $c->get( 'onboarding.render' );
|
||||
assert( $renderer instanceof OnboardingRenderer );
|
||||
|
||||
$is_production = 'production' === $config['env'];
|
||||
$products = $config['products'];
|
||||
|
||||
ob_start();
|
||||
$renderer->render( $is_production );
|
||||
$renderer->render( $is_production, $products );
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $content;
|
||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Onboarding;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
|
@ -138,13 +137,13 @@ class OnboardingRESTController {
|
|||
return array(
|
||||
'environment' => $environment->current_environment(),
|
||||
'onboarded' => ( $state->current_state() >= State::STATE_ONBOARDED ),
|
||||
'state' => $this->get_onboarding_state_name( $state->current_state() ),
|
||||
'state' => State::get_state_name( $state->current_state() ),
|
||||
'sandbox' => array(
|
||||
'state' => $this->get_onboarding_state_name( $state->sandbox_state() ),
|
||||
'state' => State::get_state_name( $state->sandbox_state() ),
|
||||
'onboarded' => ( $state->sandbox_state() >= State::STATE_ONBOARDED ),
|
||||
),
|
||||
'production' => array(
|
||||
'state' => $this->get_onboarding_state_name( $state->production_state() ),
|
||||
'state' => State::get_state_name( $state->production_state() ),
|
||||
'onboarded' => ( $state->production_state() >= State::STATE_ONBOARDED ),
|
||||
),
|
||||
);
|
||||
|
@ -265,34 +264,6 @@ class OnboardingRESTController {
|
|||
return add_query_arg( $this->return_url_args, $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates an onboarding state to a string.
|
||||
*
|
||||
* @param int $state An onboarding state to translate as returned by {@link State} methods.
|
||||
* @return string A string representing the state: "start", "progressive" or "onboarded".
|
||||
* @see State::current_state(), State::sandbox_state(), State::production_state().
|
||||
*/
|
||||
public function get_onboarding_state_name( $state ) {
|
||||
$name = 'unknown';
|
||||
|
||||
switch ( absint( $state ) ) {
|
||||
case State::STATE_START:
|
||||
$name = 'start';
|
||||
break;
|
||||
case State::STATE_PROGRESSIVE:
|
||||
$name = 'progressive';
|
||||
break;
|
||||
case State::STATE_ONBOARDED:
|
||||
$name = 'onboarded';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signup link for onboarding for a given environment and optionally adding certain URL arguments
|
||||
* to the URL users are redirected after completing the onboarding flow.
|
||||
|
|
224
modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php
Normal file
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
/**
|
||||
* Renders the onboarding options.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Onboarding\Render
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Onboarding\Render;
|
||||
|
||||
/**
|
||||
* Class OnboardingRenderer
|
||||
*/
|
||||
class OnboardingOptionsRenderer {
|
||||
/**
|
||||
* The module url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* OnboardingOptionsRenderer constructor.
|
||||
*
|
||||
* @param string $module_url The module url (for assets).
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct( string $module_url, string $country ) {
|
||||
$this->module_url = $module_url;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the onboarding options.
|
||||
*
|
||||
* @param bool $is_shop_supports_dcc Whether the shop can use DCC (country, currency).
|
||||
*/
|
||||
public function render( bool $is_shop_supports_dcc ): string {
|
||||
return '
|
||||
<ul class="ppcp-onboarding-options">
|
||||
<li>
|
||||
<label><input type="checkbox" disabled checked> ' .
|
||||
__( 'Enable PayPal Payments — includes PayPal, Venmo, Pay Later — with fraud protection', 'woocommerce-paypal-payments' ) . '
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label><input type="checkbox" id="ppcp-onboarding-accept-cards" checked> ' .
|
||||
__( 'Securely accept all major credit & debit cards on the strength of the PayPal network', 'woocommerce-paypal-payments' ) . '
|
||||
</label>
|
||||
</li>
|
||||
<li>' . $this->render_dcc( $is_shop_supports_dcc ) . '</li>
|
||||
</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the onboarding DCC options.
|
||||
*
|
||||
* @param bool $is_shop_supports_dcc Whether the shop can use DCC (country, currency).
|
||||
*/
|
||||
private function render_dcc( bool $is_shop_supports_dcc ): string {
|
||||
$items = array();
|
||||
|
||||
$is_us_shop = 'US' === $this->country;
|
||||
|
||||
if ( $is_shop_supports_dcc ) {
|
||||
$dcc_table_rows = array(
|
||||
$this->render_table_row(
|
||||
__( 'Credit & Debit Card form fields', 'woocommerce-paypal-payments' ),
|
||||
__( 'Customizable user experience', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
! $is_us_shop ? '' : $this->render_table_row(
|
||||
__( 'Credit & Debit Card pricing', 'woocommerce-paypal-payments' ),
|
||||
__( '2.59% + $0.49', 'woocommerce-paypal-payments' ),
|
||||
'',
|
||||
__( 'for US domestic transactions', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Seller Protection', 'woocommerce-paypal-payments' ),
|
||||
__( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
__( 'No matter what you sell, Seller Protection can help you avoid chargebacks, reversals, and fees on eligible PayPal payment transactions — even when a customer has filed a dispute.', 'woocommerce-paypal-payments' ),
|
||||
__( 'for eligible PayPal transactions', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Fraud Protection', 'woocommerce-paypal-payments' ),
|
||||
__( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
__( 'Included with Advanced Checkout at no extra cost, Fraud Protection gives you the insight and control you need to better balance chargebacks and declines.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
! $is_us_shop ? '' : $this->render_table_row(
|
||||
__( 'Chargeback Protection', 'woocommerce-paypal-payments' ),
|
||||
__( 'Optional', 'woocommerce-paypal-payments' ),
|
||||
__( 'If you choose this optional, fee-based alternative to Fraud Protection, PayPal will manage chargebacks for eligible credit and debit card transactions — so you won’t have to worry about unexpected costs.', 'woocommerce-paypal-payments' ),
|
||||
__( 'extra 0.4% per transaction', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Additional Vetting and Underwriting Required', 'woocommerce-paypal-payments' ),
|
||||
__( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
__( 'Business Ownership and other business information will be required during the application for Advanced Card Processing.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Seller Account Type', 'woocommerce-paypal-payments' ),
|
||||
__( 'Business', 'woocommerce-paypal-payments' ),
|
||||
__( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
);
|
||||
$items[] = '
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" id="ppcp-onboarding-dcc-acdc" name="ppcp_onboarding_dcc" value="acdc" checked ' .
|
||||
'data-screen-url="' . $this->get_screen_url( 'acdc' ) . '"> ' .
|
||||
__( 'Advanced Card Processing', 'woocommerce-paypal-payments' ) . '
|
||||
</label>
|
||||
' . $this->render_tooltip( __( 'PayPal acts as the payment processor for card transactions. You can add optional features like Chargeback Protection for more security.', 'woocommerce-paypal-payments' ) ) . '
|
||||
<table>
|
||||
' . implode( '', $dcc_table_rows ) . '
|
||||
</table>
|
||||
</li>';
|
||||
}
|
||||
|
||||
$basic_table_rows = array(
|
||||
$this->render_table_row(
|
||||
__( 'Credit & Debit Card form fields', 'woocommerce-paypal-payments' ),
|
||||
__( 'Prebuilt user experience', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
! $is_us_shop ? '' : $this->render_table_row(
|
||||
__( 'Credit & Debit Card pricing', 'woocommerce-paypal-payments' ),
|
||||
__( '3.49% + $0.49', 'woocommerce-paypal-payments' ),
|
||||
'',
|
||||
__( 'for US domestic transactions', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Seller Protection', 'woocommerce-paypal-payments' ),
|
||||
__( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
__( 'No matter what you sell, Seller Protection can help you avoid chargebacks, reversals, and fees on eligible PayPal payment transactions — even when a customer has filed a dispute.', 'woocommerce-paypal-payments' ),
|
||||
__( 'for eligible PayPal transactions', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
$this->render_table_row(
|
||||
__( 'Seller Account Type', 'woocommerce-paypal-payments' ),
|
||||
__( 'Business or Casual', 'woocommerce-paypal-payments' ),
|
||||
__( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
);
|
||||
$items[] = '
|
||||
<li ' . ( ! $is_shop_supports_dcc ? 'style="display: none;"' : '' ) . '>
|
||||
<label>
|
||||
<input type="radio" id="ppcp-onboarding-dcc-basic" name="ppcp_onboarding_dcc" value="basic" ' .
|
||||
( ! $is_shop_supports_dcc ? 'checked' : '' ) .
|
||||
' data-screen-url="' . $this->get_screen_url( 'basic' ) . '"' .
|
||||
'> ' .
|
||||
__( 'Standard Card Processing', 'woocommerce-paypal-payments' ) . '
|
||||
</label>
|
||||
' . $this->render_tooltip( __( 'Card transactions are managed by PayPal, which simplifies compliance requirements for you.', 'woocommerce-paypal-payments' ) ) . '
|
||||
<table>
|
||||
' . implode( $basic_table_rows ) . '
|
||||
</table>
|
||||
</li>';
|
||||
|
||||
return '
|
||||
<div class="ppcp-onboarding-cards-options">
|
||||
<ul id="ppcp-onboarding-dcc-options" class="ppcp-onboarding-options-sublist">' .
|
||||
implode( '', $items ) .
|
||||
'
|
||||
</ul>
|
||||
<div class="ppcp-onboarding-cards-screen">' .
|
||||
( $is_shop_supports_dcc ? '<img id="ppcp-onboarding-cards-screen-img" />' : '' ) . '
|
||||
</div>
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML of a row for the cards options tables.
|
||||
*
|
||||
* @param string $header The text in the first cell.
|
||||
* @param string $value The text in the second cell.
|
||||
* @param string $tooltip The text shown on hover.
|
||||
* @param string $note The additional description text, such as about conditions.
|
||||
* @return string
|
||||
*/
|
||||
private function render_table_row( string $header, string $value, string $tooltip = '', string $note = '' ): string {
|
||||
$value_html = $value;
|
||||
if ( $note ) {
|
||||
$value_html .= '<br/><span class="ppcp-muted-text">' . $note . '</span>';
|
||||
}
|
||||
|
||||
$tooltip_html = '';
|
||||
if ( $tooltip ) {
|
||||
$tooltip_html = $this->render_tooltip( $tooltip, array( 'ppcp-table-row-tooltip' ) );
|
||||
}
|
||||
|
||||
return "
|
||||
<tr>
|
||||
<th>$tooltip_html $header</th>
|
||||
<td>$value_html</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML of a tooltip (question mark icon).
|
||||
*
|
||||
* @param string $tooltip The text shown on hover.
|
||||
* @param string[] $classes Additional CSS classes.
|
||||
* @return string
|
||||
*/
|
||||
private function render_tooltip( string $tooltip, array $classes = array() ): string {
|
||||
return '<span class="woocommerce-help-tip ' . implode( ' ', $classes ) . '" data-tip="' . esc_attr( $tooltip ) . '"></span> ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the screen image URL.
|
||||
*
|
||||
* @param string $key The image suffix, 'acdc' or 'basic'.
|
||||
* @return string
|
||||
*/
|
||||
private function get_screen_url( string $key ): string {
|
||||
return untrailingslashit( $this->module_url ) . "/assets/images/cards-screen-$key.png";
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Onboarding\Render;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
|
@ -39,31 +40,50 @@ class OnboardingRenderer {
|
|||
*/
|
||||
private $sandbox_partner_referrals;
|
||||
|
||||
/**
|
||||
* The default partner referrals data.
|
||||
*
|
||||
* @var PartnerReferralsData
|
||||
*/
|
||||
private $partner_referrals_data;
|
||||
|
||||
/**
|
||||
* OnboardingRenderer constructor.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
* @param PartnerReferrals $production_partner_referrals The PartnerReferrals for production.
|
||||
* @param PartnerReferrals $sandbox_partner_referrals The PartnerReferrals for sandbox.
|
||||
* @param Settings $settings The settings.
|
||||
* @param PartnerReferrals $production_partner_referrals The PartnerReferrals for production.
|
||||
* @param PartnerReferrals $sandbox_partner_referrals The PartnerReferrals for sandbox.
|
||||
* @param PartnerReferralsData $partner_referrals_data The default partner referrals data.
|
||||
*/
|
||||
public function __construct( Settings $settings, PartnerReferrals $production_partner_referrals, PartnerReferrals $sandbox_partner_referrals ) {
|
||||
public function __construct(
|
||||
Settings $settings,
|
||||
PartnerReferrals $production_partner_referrals,
|
||||
PartnerReferrals $sandbox_partner_referrals,
|
||||
PartnerReferralsData $partner_referrals_data
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->production_partner_referrals = $production_partner_referrals;
|
||||
$this->sandbox_partner_referrals = $sandbox_partner_referrals;
|
||||
$this->partner_referrals_data = $partner_referrals_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action URL for the onboarding button/link.
|
||||
*
|
||||
* @param boolean $is_production Whether the production or sandbox button should be rendered.
|
||||
* @param boolean $is_production Whether the production or sandbox button should be rendered.
|
||||
* @param string[] $products The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
||||
* @return string URL.
|
||||
*/
|
||||
public function get_signup_link( bool $is_production ) {
|
||||
public function get_signup_link( bool $is_production, array $products ) {
|
||||
$args = array(
|
||||
'displayMode' => 'minibrowser',
|
||||
);
|
||||
|
||||
$url = $is_production ? $this->production_partner_referrals->signup_link() : $this->sandbox_partner_referrals->signup_link();
|
||||
$data = $this->partner_referrals_data
|
||||
->with_products( $products )
|
||||
->data();
|
||||
|
||||
$url = $is_production ? $this->production_partner_referrals->signup_link( $data ) : $this->sandbox_partner_referrals->signup_link( $data );
|
||||
$url = add_query_arg( $args, $url );
|
||||
|
||||
return $url;
|
||||
|
@ -72,14 +92,18 @@ class OnboardingRenderer {
|
|||
/**
|
||||
* Renders the "Connect to PayPal" button.
|
||||
*
|
||||
* @param bool $is_production Whether the production or sandbox button should be rendered.
|
||||
* @param bool $is_production Whether the production or sandbox button should be rendered.
|
||||
* @param string[] $products The list of products ('PPCP', 'EXPRESS_CHECKOUT').
|
||||
*/
|
||||
public function render( bool $is_production ) {
|
||||
public function render( bool $is_production, array $products ) {
|
||||
try {
|
||||
$id = 'connect-to' . ( $is_production ? 'production' : 'sandbox' ) . strtolower( implode( '-', $products ) );
|
||||
|
||||
$this->render_button(
|
||||
$this->get_signup_link( $is_production ),
|
||||
$is_production ? 'connect-to-production' : 'connect-to-sandbox',
|
||||
$is_production ? __( 'Connect to PayPal', 'woocommerce-paypal-payments' ) : __( 'Connect to PayPal Sandbox', 'woocommerce-paypal-payments' ),
|
||||
$this->get_signup_link( $is_production, $products ),
|
||||
$id,
|
||||
$is_production ? __( 'Activate PayPal', 'woocommerce-paypal-payments' ) : __( 'Test payments with PayPal sandbox', 'woocommerce-paypal-payments' ),
|
||||
$is_production ? 'primary' : 'secondary',
|
||||
$is_production ? 'production' : 'sandbox'
|
||||
);
|
||||
} catch ( RuntimeException $exception ) {
|
||||
|
@ -96,13 +120,14 @@ class OnboardingRenderer {
|
|||
* @param string $url The url of the button.
|
||||
* @param string $id The ID of the button.
|
||||
* @param string $label The button text.
|
||||
* @param string $class The CSS class for button ('primary', 'secondary').
|
||||
* @param string $env The environment ('production' or 'sandbox').
|
||||
*/
|
||||
private function render_button( string $url, string $id, string $label, string $env ) {
|
||||
private function render_button( string $url, string $id, string $label, string $class, string $env ) {
|
||||
?>
|
||||
<a
|
||||
target="_blank"
|
||||
class="button-primary"
|
||||
class="button-<?php echo esc_attr( $class ); ?>"
|
||||
id="<?php echo esc_attr( $id ); ?>"
|
||||
data-paypal-onboard-complete="ppcp_onboarding_<?php echo esc_attr( $env ); ?>Callback"
|
||||
data-paypal-onboard-button="true"
|
||||
|
|
|
@ -16,16 +16,8 @@ use Psr\Container\ContainerInterface;
|
|||
*/
|
||||
class State {
|
||||
|
||||
const STATE_START = 0;
|
||||
const STATE_PROGRESSIVE = 4;
|
||||
const STATE_ONBOARDED = 8;
|
||||
|
||||
/**
|
||||
* The Environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
private $environment;
|
||||
const STATE_START = 0;
|
||||
const STATE_ONBOARDED = 8;
|
||||
|
||||
/**
|
||||
* The Settings.
|
||||
|
@ -37,16 +29,29 @@ class State {
|
|||
/**
|
||||
* State constructor.
|
||||
*
|
||||
* @param Environment $environment The Environment.
|
||||
* @param ContainerInterface $settings The Settings.
|
||||
*/
|
||||
public function __construct(
|
||||
Environment $environment,
|
||||
ContainerInterface $settings
|
||||
) {
|
||||
|
||||
$this->environment = $environment;
|
||||
$this->settings = $settings;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the specified environment (or the active environment if null).
|
||||
*
|
||||
* @param string|null $environment 'sandbox', 'production'.
|
||||
* @return int
|
||||
*/
|
||||
public function environment_state( ?string $environment = null ): int {
|
||||
switch ( $environment ) {
|
||||
case Environment::PRODUCTION:
|
||||
return $this->production_state();
|
||||
case Environment::SANDBOX:
|
||||
return $this->sandbox_state();
|
||||
}
|
||||
return $this->current_state();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,9 +62,6 @@ class State {
|
|||
public function current_state(): int {
|
||||
|
||||
return $this->state_by_keys(
|
||||
array(
|
||||
'merchant_email',
|
||||
),
|
||||
array(
|
||||
'merchant_email',
|
||||
'merchant_id',
|
||||
|
@ -77,9 +79,6 @@ class State {
|
|||
public function sandbox_state() : int {
|
||||
|
||||
return $this->state_by_keys(
|
||||
array(
|
||||
'merchant_email_sandbox',
|
||||
),
|
||||
array(
|
||||
'merchant_email_sandbox',
|
||||
'merchant_id_sandbox',
|
||||
|
@ -97,9 +96,6 @@ class State {
|
|||
public function production_state() : int {
|
||||
|
||||
return $this->state_by_keys(
|
||||
array(
|
||||
'merchant_email_production',
|
||||
),
|
||||
array(
|
||||
'merchant_email_production',
|
||||
'merchant_id_production',
|
||||
|
@ -110,36 +106,36 @@ class State {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the state based on progressive and onboarded values being looked up in the settings.
|
||||
* Translates an onboarding state to a string.
|
||||
*
|
||||
* @param int $state An onboarding state to translate.
|
||||
* @return string A string representing the state: "start" or "onboarded".
|
||||
*/
|
||||
public static function get_state_name( int $state ) : string {
|
||||
switch ( $state ) {
|
||||
case self::STATE_START:
|
||||
return 'start';
|
||||
case self::STATE_ONBOARDED:
|
||||
return 'onboarded';
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state based on onboarding settings values.
|
||||
*
|
||||
* @param array $progressive_keys The keys which need to be present to be at least in progressive state.
|
||||
* @param array $onboarded_keys The keys which need to be present to be in onboarded state.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function state_by_keys( array $progressive_keys, array $onboarded_keys ) : int {
|
||||
$state = self::STATE_START;
|
||||
$is_progressive = true;
|
||||
foreach ( $progressive_keys as $key ) {
|
||||
if ( ! $this->settings->has( $key ) || ! $this->settings->get( $key ) ) {
|
||||
$is_progressive = false;
|
||||
}
|
||||
}
|
||||
if ( $is_progressive ) {
|
||||
$state = self::STATE_PROGRESSIVE;
|
||||
}
|
||||
|
||||
$is_onboarded = true;
|
||||
private function state_by_keys( array $onboarded_keys ) : int {
|
||||
foreach ( $onboarded_keys as $key ) {
|
||||
if ( ! $this->settings->has( $key ) || ! $this->settings->get( $key ) ) {
|
||||
$is_onboarded = false;
|
||||
return self::STATE_START;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $is_onboarded ) {
|
||||
$state = self::STATE_ONBOARDED;
|
||||
}
|
||||
|
||||
return $state;
|
||||
return self::STATE_ONBOARDED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,14 @@ return array(
|
|||
$endpoint = $container->get( 'api.endpoint.order' );
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
return new RenewalHandler(
|
||||
$logger,
|
||||
$repository,
|
||||
$endpoint,
|
||||
$purchase_unit_factory,
|
||||
$payer_factory
|
||||
$payer_factory,
|
||||
$environment
|
||||
);
|
||||
},
|
||||
'subscription.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
|
||||
|
|
|
@ -55,6 +55,42 @@ class SubscriptionHelper {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether pay for order contains subscriptions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function order_pay_contains_subscription(): bool {
|
||||
if ( ! $this->plugin_is_active() || ! is_wc_endpoint_url( 'order-pay' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $wp;
|
||||
$order_id = (int) $wp->query_vars['order-pay'];
|
||||
if ( 0 === $order_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( is_a( $order, \WC_Order::class ) ) {
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( is_a( $item, \WC_Order_Item_Product::class ) ) {
|
||||
$product = wc_get_product( $item->get_product_id() );
|
||||
/**
|
||||
* Class already exist in subscriptions plugin.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
if ( is_a( $product, \WC_Product_Subscription::class ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether only automatic payment gateways are accepted.
|
||||
*
|
||||
|
|
|
@ -10,21 +10,26 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* Class RenewalHandler
|
||||
*/
|
||||
class RenewalHandler {
|
||||
|
||||
use OrderMetaTrait;
|
||||
use TransactionIdHandlingTrait;
|
||||
use PaymentsStatusHandlingTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -60,6 +65,13 @@ class RenewalHandler {
|
|||
*/
|
||||
private $payer_factory;
|
||||
|
||||
/**
|
||||
* The environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
protected $environment;
|
||||
|
||||
/**
|
||||
* RenewalHandler constructor.
|
||||
*
|
||||
|
@ -68,13 +80,15 @@ class RenewalHandler {
|
|||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param PayerFactory $payer_factory The payer factory.
|
||||
* @param Environment $environment The environment.
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
PaymentTokenRepository $repository,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
PayerFactory $payer_factory
|
||||
PayerFactory $payer_factory,
|
||||
Environment $environment
|
||||
) {
|
||||
|
||||
$this->logger = $logger;
|
||||
|
@ -82,6 +96,7 @@ class RenewalHandler {
|
|||
$this->order_endpoint = $order_endpoint;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,52 +105,23 @@ class RenewalHandler {
|
|||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
*/
|
||||
public function renew( \WC_Order $wc_order ) {
|
||||
|
||||
$this->logger->log(
|
||||
'info',
|
||||
sprintf(
|
||||
// translators: %d is the id of the order.
|
||||
__( 'Start moneytransfer for order %d', 'woocommerce-paypal-payments' ),
|
||||
(int) $wc_order->get_id()
|
||||
),
|
||||
array(
|
||||
'order' => $wc_order,
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$this->process_order( $wc_order );
|
||||
} catch ( \Exception $error ) {
|
||||
$this->logger->log(
|
||||
'error',
|
||||
$this->logger->error(
|
||||
sprintf(
|
||||
// translators: %1$d is the order number, %2$s the error message.
|
||||
__(
|
||||
'An error occured while trying to renew the subscription for order %1$d: %2$s',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(int) $wc_order->get_id(),
|
||||
'An error occurred while trying to renew the subscription for order %1$d: %2$s',
|
||||
$wc_order->get_id(),
|
||||
$error->getMessage()
|
||||
),
|
||||
array(
|
||||
'order' => $wc_order,
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->logger->log(
|
||||
'info',
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
// translators: %d is the order number.
|
||||
__(
|
||||
'Moneytransfer for order %d is completed.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(int) $wc_order->get_id()
|
||||
),
|
||||
array(
|
||||
'order' => $wc_order,
|
||||
'Renewal for order %d is completed.',
|
||||
$wc_order->get_id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -164,7 +150,19 @@ class RenewalHandler {
|
|||
$token
|
||||
);
|
||||
|
||||
$this->capture_order( $order, $wc_order );
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
if ( $order->intent() === 'AUTHORIZE' ) {
|
||||
$order = $this->order_endpoint->authorize( $order );
|
||||
$wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' );
|
||||
}
|
||||
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
if ( $transaction_id ) {
|
||||
$this->update_transaction_id( $transaction_id, $wc_order );
|
||||
}
|
||||
|
||||
$this->handle_new_order_status( $order, $wc_order );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,6 +174,9 @@ class RenewalHandler {
|
|||
* @return PaymentToken|null
|
||||
*/
|
||||
private function get_token_for_customer( \WC_Customer $customer, \WC_Order $wc_order ) {
|
||||
/**
|
||||
* Returns a payment token for a customer, or null.
|
||||
*/
|
||||
$token = apply_filters( 'woocommerce_paypal_payments_subscriptions_get_token_for_customer', null, $customer, $wc_order );
|
||||
if ( null !== $token ) {
|
||||
return $token;
|
||||
|
@ -185,12 +186,8 @@ class RenewalHandler {
|
|||
if ( ! $tokens ) {
|
||||
|
||||
$error_message = sprintf(
|
||||
// translators: %d is the customer id.
|
||||
__(
|
||||
'Payment failed. No payment tokens found for customer %d.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
(int) $customer->get_id()
|
||||
'Payment failed. No payment tokens found for customer %d.',
|
||||
$customer->get_id()
|
||||
);
|
||||
|
||||
$wc_order->update_status(
|
||||
|
@ -198,14 +195,7 @@ class RenewalHandler {
|
|||
$error_message
|
||||
);
|
||||
|
||||
$this->logger->log(
|
||||
'error',
|
||||
$error_message,
|
||||
array(
|
||||
'customer' => $customer,
|
||||
'order' => $wc_order,
|
||||
)
|
||||
);
|
||||
$this->logger->error( $error_message );
|
||||
}
|
||||
|
||||
$subscription = function_exists( 'wcs_get_subscription' ) ? wcs_get_subscription( $wc_order->get_meta( '_subscription_renewal' ) ) : null;
|
||||
|
@ -223,25 +213,4 @@ class RenewalHandler {
|
|||
|
||||
return current( $tokens );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the PayPal order is captured/authorized the WooCommerce order gets updated accordingly.
|
||||
*
|
||||
* @param Order $order The PayPal order.
|
||||
* @param \WC_Order $wc_order The related WooCommerce order.
|
||||
*/
|
||||
private function capture_order( Order $order, \WC_Order $wc_order ) {
|
||||
|
||||
if ( $order->intent() === 'CAPTURE' && $order->status()->is( OrderStatus::COMPLETED ) ) {
|
||||
$wc_order->update_status(
|
||||
'processing',
|
||||
__( 'Payment received.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $order->intent() === 'AUTHORIZE' ) {
|
||||
$this->order_endpoint->authorize( $order );
|
||||
$wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,13 @@ return array(
|
|||
'vaulting.module-url' => static function ( ContainerInterface $container ): string {
|
||||
return plugins_url(
|
||||
'/modules/ppcp-vaulting/',
|
||||
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'vaulting.assets.myaccount-payments' => function( ContainerInterface $container ) : MyAccountPaymentsAssets {
|
||||
return new MyAccountPaymentsAssets(
|
||||
$container->get( 'vaulting.module-url' )
|
||||
$container->get( 'vaulting.module-url' ),
|
||||
$container->get( 'ppcp.asset-version' )
|
||||
);
|
||||
},
|
||||
'vaulting.payment-tokens-renderer' => static function (): PaymentTokensRenderer {
|
||||
|
|
|
@ -23,15 +23,25 @@ class MyAccountPaymentsAssets {
|
|||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* MyAccountPaymentsAssets constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url
|
||||
string $module_url,
|
||||
string $version
|
||||
) {
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,9 +52,9 @@ class MyAccountPaymentsAssets {
|
|||
public function enqueue(): void {
|
||||
wp_enqueue_script(
|
||||
'ppcp-vaulting-myaccount-payments',
|
||||
$this->module_url . '/assets/js/myaccount-payments.js',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/myaccount-payments.js',
|
||||
array( 'jquery' ),
|
||||
'1',
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
@ -98,6 +98,17 @@ class VaultingModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
add_action(
|
||||
'woocommerce_created_customer',
|
||||
function( int $customer_id ) use ( $subscription_helper ) {
|
||||
$guest_customer_id = WC()->session->get( 'ppcp_guest_customer_id' );
|
||||
if ( $guest_customer_id && $subscription_helper->cart_contains_subscription() ) {
|
||||
update_user_meta( $customer_id, 'ppcp_guest_customer_id', $guest_customer_id );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$asset_loader = $container->get( 'vaulting.assets.myaccount-payments' );
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50px" height="50px" viewBox="0 0 50 50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Amex_acceptancemark_50x50</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Amex_acceptancemark_50x50">
|
||||
<polygon id="Shape" fill="#FFFFFF" fill-rule="nonzero" points="50 0 0 0 0 50 50 50"></polygon>
|
||||
<path d="M25.0930414,18.1130368 L21.2948396,18.1130368 L23.1939405,13.5354022 L25.0930414,18.1130368 Z M27.5916246,24.0501324 L31.8892892,24.0501324 L25.7128236,10.0571358 L20.7951652,10.0571358 L14.6182767,24.0501324 L18.8161334,24.0501324 L19.9755555,21.2514908 L26.412114,21.2514908 L27.5916246,24.0501324 Z M46.2218867,24.0501324 L50,24.0501324 L50,10.0571358 L44.1229584,10.0571358 L40.9845044,18.7725731 L37.8661389,10.0571358 L31.8892892,10.0571358 L31.8892892,24.0501324 L35.6671911,24.0501324 L35.6671911,14.255204 L39.2655654,24.0501324 L42.6237239,24.0501324 L46.2218867,14.234904 L46.2218867,24.0501324 Z M23.7946916,36.8350715 L23.7946916,34.6562122 L31.6905191,34.6562122 L31.6905191,31.4978812 L23.7946916,31.4978812 L23.7946916,29.3190219 L31.8903465,29.3190219 L31.8903465,26.0807599 L19.9766128,26.0807599 L19.9766128,40.0733335 L31.8903465,40.0733335 L31.8903465,36.8350715 L23.7946916,36.8350715 Z M46.1576036,33.0427906 L50,37.1306893 L50,28.989148 L46.1576036,33.0427906 Z M44.9840138,40.0733335 L50,40.0733335 L43.3646714,33.0370812 L50,26.0807599 L45.0637333,26.0807599 L40.9661076,30.558375 L36.9078129,26.0807599 L31.8907695,26.0807599 L38.4871899,33.0770467 L31.8907695,40.0733335 L36.7682509,40.0733335 L40.8861766,35.5557529 L44.9840138,40.0733335 Z M50,50 L50,42.0773174 L43.9679599,42.0773174 L40.8622819,38.6432456 L37.7411674,42.0773174 L17.8527325,42.0773174 L17.8527325,26.072513 L11.4335135,26.072513 L19.3957387,8.05336344 L27.0746107,8.05336344 L29.8157358,14.2264457 L29.8157358,8.05336344 L39.3205444,8.05336344 L40.9709711,12.7052196 L42.6319707,8.05336344 L50,8.05336344 L50,0 L0,0 L0,50 L50,50 L50,50 Z" id="Fill-18" fill="#216EA9"></path>
|
||||
<svg width="134px" height="85px" viewBox="0 0 134 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>AmEx Card Icon</title>
|
||||
<g id="RD-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="LP---venmo---social---primary" transform="translate(-824.000000, -1587.000000)">
|
||||
<g id="AmEx-Card-Icon" transform="translate(824.988647, 1587.578995)">
|
||||
<rect id="Rectangle" fill="#216EA9" x="0" y="0" width="132.39159" height="83.7921454" rx="4.581738"></rect>
|
||||
<g id="Amex" transform="translate(11.042625, 26.813487)" fill-rule="nonzero">
|
||||
<polygon id="path3082" fill="#FFFFFF" points="58.1781633 28.829246 58.1781633 0.111970975 88.752584 0.111970975 92.0328881 4.36473322 95.4216632 0.111970975 111.627357 0.111970975 100.111543 14.3998649 111.627357 28.829246 95.1623158 28.829246 91.8866983 24.4852763 88.560787 28.829246"></polygon>
|
||||
<polygon id="path3080" fill="#FFFFFF" points="12.9456251 0.111970975 24.8692065 0.111970975 28.7820013 9.06638213 28.7820013 0.111970975 43.6041122 0.111970975 45.933401 6.58395398 48.1915676 0.111970975 83.0100213 0.111970975 83.0100213 28.829228 23.1027999 28.829228 21.2713726 24.3023659 16.3877429 24.3023659 14.5692593 28.829228 0.216820214 28.829228"></polygon>
|
||||
<path d="M15.3859151,3.6521988 L6.08653439,25.1547742 L12.140911,25.1547742 L13.8567623,20.8489817 L23.8318149,20.8489817 L25.5387763,25.1547742 L31.7265095,25.1547742 L22.436018,3.6521988 L15.3859151,3.6521988 Z M18.8265072,8.6564979 L21.8670326,16.1806509 L15.7770926,16.1806509 L18.8265072,8.6564979 L18.8265072,8.6564979 Z" id="path3046" fill="#216EA9"></path>
|
||||
<polygon id="path3048" fill="#216EA9" points="32.3666185 25.1511618 32.3666185 3.64856809 40.9702248 3.68034593 45.9743744 17.543851 50.8586955 3.64856809 59.3934994 3.64856809 59.3934994 25.1511618 53.9881232 25.1511618 53.9881232 9.30714548 48.2583106 25.1511618 43.5177841 25.1511618 37.7719947 9.30714548 37.7719947 25.1511618"></polygon>
|
||||
<polygon id="path3050" fill="#216EA9" points="63.091914 25.1511618 63.091914 3.64856809 80.7305103 3.64856809 80.7305103 8.45836792 68.5541888 8.45836792 68.5541888 12.1364341 80.4460167 12.1364341 80.4460167 16.6632958 68.5541888 16.6632958 68.5541888 20.482831 80.7305103 20.482831 80.7305103 25.1511618"></polygon>
|
||||
<polygon id="path3066" fill="#216EA9" points="80.1997365 25.1511618 88.7878839 14.5324773 79.9952578 3.64856809 86.80532 3.64856809 92.0417773 10.3769555 97.2960166 3.64856809 103.839367 3.64856809 95.1623158 14.3998648 103.766272 25.1511618 96.9572869 25.1511618 91.8728606 18.5288349 86.9120057 25.1511618"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.8 KiB |
22
modules/ppcp-wc-gateway/assets/images/ideal-dark.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="88px" height="55px" viewBox="0 0 88 55" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>iDEAL_acceptancemark_80x50</title>
|
||||
<g id="RD-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="LP---venmo---social---primary" transform="translate(-1290.000000, -1489.000000)">
|
||||
<g id="iDEAL_acceptancemark_80x50" transform="translate(1290.674250, 1489.000000)">
|
||||
<rect id="Rectangle" fill="#000000" x="0" y="0" width="86.55975" height="54.78165" rx="3.55725"></rect>
|
||||
<g id="iDeal" transform="translate(19.722475, 8.569431)" fill-rule="nonzero">
|
||||
<g id="Group-10">
|
||||
<path d="M1.31483165,0.4147596 L1.31483165,38.542788 L23.6150559,38.542788 C38.3257411,38.542788 44.7042759,30.3306152 44.7042759,19.4407953 C44.7042759,8.59428425 38.3257411,0.4147596 23.6150559,0.4147596 L1.31483165,0.4147596 Z" id="Fill-6" fill="#FFFFFF"></path>
|
||||
<path d="M24.4089797,0.0145181949 C43.6643857,0.0145181949 46.5394168,12.2271201 46.5394168,19.5055435 C46.5394168,32.133495 38.680567,39.0878016 24.4089797,39.0878016 L0.807144801,39.0878016 L0.807144801,0.0145181949 L24.4089797,0.0145181949 Z M2.66510307,1.8517804 L2.66510307,37.249739 L24.4089797,37.249739 C37.5392931,37.249739 44.6818631,31.2003587 44.6818631,19.5055435 C44.6818631,7.48721161 36.9395275,1.8517804 24.4089797,1.8517804 L2.66510307,1.8517804 Z" id="Fill-8" fill="#000000"></path>
|
||||
</g>
|
||||
<polygon id="Fill-11" fill="#000000" points="5.58237061 34.364139 12.5430893 34.364139 12.5430893 22.1081216 5.58237061 22.1081216"></polygon>
|
||||
<g id="Group-16" transform="translate(4.652482, 4.673437)">
|
||||
<path d="M8.73559986,11.2615791 C8.73559986,13.6248309 6.79935854,15.5409215 4.40980375,15.5409215 C2.02146263,15.5409215 0.0834007826,13.6248309 0.0834007826,11.2615791 C0.0834007826,8.89992785 2.02146263,6.98303694 4.40980375,6.98303694 C6.79935854,6.98303694 8.73559986,8.89992785 8.73559986,11.2615791" id="Fill-12" fill="#000000"></path>
|
||||
<path d="M22.3930799,13.2641209 L22.3930799,15.2470356 L17.4327257,15.2470356 L17.4327257,7.28636627 L22.233075,7.28636627 L22.233075,9.26848068 L19.4381473,9.26848068 L19.4381473,10.1914133 L22.0813635,10.1914133 L22.0813635,12.1731276 L19.4381473,12.1731276 L19.4381473,13.2641209 L22.3930799,13.2641209 Z M23.3559412,15.2496365 L25.7841318,7.28296504 L28.6367095,7.28296504 L31.0640911,15.2496365 L28.9767452,15.2496365 L28.5218135,13.708481 L25.8990278,13.708481 L25.4426801,15.2496365 L23.3559412,15.2496365 Z M26.4850382,11.7269668 L27.9356009,11.7269668 L27.2723189,9.47555532 L27.1519613,9.47555532 L26.4850382,11.7269668 Z M32.0714544,7.2831651 L34.0764715,7.2831651 L34.0764715,13.2641209 L37.0477888,13.2641209 C36.2329978,2.4118093 27.6012291,0.0671642083 19.7581572,0.0671642083 L11.3960302,0.0671642083 L11.3960302,7.28736663 L12.6337925,7.28736663 C14.8904481,7.28736663 16.2924632,8.80091216 16.2924632,11.2355897 C16.2924632,13.7476951 14.9242292,15.2470356 12.6337925,15.2470356 L11.3960302,15.2470356 L11.3960302,29.6942429 L19.7581572,29.6942429 C32.5104059,29.6942429 36.9480638,23.8371318 37.1046299,15.2470356 L32.0714544,15.2470356 L32.0714544,7.2831651 Z M11.394412,9.26988119 L11.394412,13.2641209 L12.6337925,13.2641209 C13.492681,13.2641209 14.2858279,13.0186324 14.2858279,11.2355897 C14.2858279,9.49336174 13.4030701,9.26988119 12.6337925,9.26988119 L11.394412,9.26988119 Z" id="Fill-14" fill="#CC0066"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
17
modules/ppcp-wc-gateway/assets/images/mastercard-dark.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="134px" height="84px" viewBox="0 0 134 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Mastercard_acceptancemark_80x50</title>
|
||||
<g id="RD-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="LP---venmo---social---primary" transform="translate(-967.000000, -1489.000000)">
|
||||
<g id="Mastercard_acceptancemark_80x50" transform="translate(967.005261, 1489.000000)">
|
||||
<rect id="Rectangle" fill="#000000" x="0" y="0" width="132.39159" height="83.7921454" rx="4.581738"></rect>
|
||||
<g id="mc_symbol" transform="translate(16.758429, 11.730900)" fill-rule="nonzero">
|
||||
<rect id="Rectangle" fill="#FF5F00" x="34.961091" y="6.91411461" width="29.2905829" height="48.0845243"></rect>
|
||||
<path d="M37.978038,30.9606238 C37.9706835,21.5790039 42.2555184,12.7149877 49.5979316,6.92260862 C37.1243267,-2.93038543 19.2117299,-1.49521164 8.44863276,10.2195284 C-2.31446439,21.9342684 -2.31446439,39.9954731 8.44863276,51.7102132 C19.2117299,63.4249532 37.1243267,64.8601271 49.5979316,55.0071329 C42.2531818,49.2129145 37.9680851,40.3452244 37.978038,30.9606238 Z" id="Path" fill="#EB001B"></path>
|
||||
<path d="M95.9169404,50.3974171 L95.9169404,49.0088616 L96.3394821,49.0088616 L96.3394821,48.7215742 L95.333833,48.7215742 L95.333833,49.0088616 L95.7310222,49.0088616 L95.7310222,50.3974171 L95.9169404,50.3974171 Z M97.8690825,50.3974171 L97.8690825,48.7215742 L97.5648527,48.7215742 L97.2099177,49.9186049 L96.8549827,48.7215742 L96.5507529,48.7215742 L96.5507529,50.3974171 L96.7704745,50.3974171 L96.7704745,49.1285648 L97.1000568,50.2178626 L97.3282294,50.2178626 L97.6578119,49.1285648 L97.6578119,50.3974171 L97.8690825,50.3974171 Z" id="Shape" fill="#F79E1B"></path>
|
||||
<path d="M98.8240266,30.9606238 C98.8240266,42.6682247 92.1727326,53.3479581 81.695674,58.4641002 C71.2186153,63.5802422 58.7549062,62.2345869 49.5979316,54.9986391 C56.9371412,49.2013423 61.2222985,40.3385046 61.2222985,30.9563768 C61.2222985,21.5742491 56.9371412,12.7114113 49.5979316,6.91411461 C58.7549062,-0.321833201 71.2186153,-1.66748859 81.695674,3.44865344 C92.1727326,8.56479549 98.8240266,19.2445288 98.8240266,30.9521297 L98.8240266,30.9606238 Z" id="Path" fill="#F79E1B"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
1
modules/ppcp-wc-gateway/assets/images/paylater.svg
Normal file
After Width: | Height: | Size: 16 KiB |
1
modules/ppcp-wc-gateway/assets/images/paypal-button.svg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
modules/ppcp-wc-gateway/assets/images/paypal.png
Executable file
After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 9.7 KiB |
1
modules/ppcp-wc-gateway/assets/images/venmo.svg
Normal file
After Width: | Height: | Size: 15 KiB |
14
modules/ppcp-wc-gateway/assets/images/visa-dark.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="133px" height="84px" viewBox="0 0 133 84" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Visa_acceptancemark_80x50</title>
|
||||
<g id="RD-4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="LP---venmo---social---primary" transform="translate(-824.000000, -1489.000000)">
|
||||
<g id="Visa_acceptancemark_80x50" transform="translate(824.000000, 1489.000000)">
|
||||
<rect id="Rectangle" fill="#1D1C45" x="0" y="0" width="132.39159" height="83.7921454" rx="4.581738"></rect>
|
||||
<g id="Visa" transform="translate(15.082586, 25.137644)" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<path d="M51.4455687,0.590854329 L44.5028773,32.8958387 L36.1060355,32.8958387 L43.051613,0.590854329 L51.4455687,0.590854329 Z M86.768489,21.4493529 L91.1881833,9.31960182 L93.7323456,21.4493529 L86.768489,21.4493529 Z M96.1370094,32.8958387 L103.90226,32.8958387 L97.1240809,0.590854329 L89.9581922,0.590854329 C88.3462641,0.590854329 86.9873571,1.5231018 86.3841468,2.96001738 L73.7864787,32.8958387 L82.6003729,32.8958387 L84.3517993,28.0694111 L95.1225193,28.0694111 L96.1370094,32.8958387 Z M74.222772,22.3476047 C74.2602923,13.8223523 62.3788765,13.3516799 62.4606514,9.54224952 C62.486627,8.38400266 63.5949177,7.15249753 66.021709,6.83696026 C67.2228384,6.68086746 70.5404952,6.55972795 74.2987747,8.28201565 L75.7740906,1.43212747 C73.7523257,0.701938773 71.1542883,0 67.9188872,0 C59.6211374,0 53.7795214,4.39166605 53.7328615,10.6779922 C53.6775431,15.3310897 57.9014586,17.9238582 61.0839464,19.4709005 C64.3535004,21.0538539 65.4502465,22.0694147 65.4382207,23.4852625 C65.4136884,25.6538032 62.8291196,26.6095125 60.412911,26.6468598 C56.1918816,26.7124572 53.7420011,25.5111171 51.7899855,24.6056832 L50.2699339,31.6849224 C52.2296459,32.581259 55.8508322,33.3626806 59.6062254,33.4009855 C68.4268539,33.4009855 74.1953534,29.0658194 74.222772,22.3476047 L74.222772,22.3476047 Z M39.4487059,0.590854329 L25.8456873,32.8958387 L16.9711835,32.8958387 L10.2771842,7.11419256 C9.87071473,5.52692991 9.51812048,4.94421538 8.28235702,4.2767511 C6.26444044,3.18553796 2.93187165,2.16279498 0,1.52741111 L0.197702912,0.590854329 L14.4847447,0.590854329 C16.3044774,0.590854329 17.9428621,1.79602479 18.3560659,3.88412506 L21.8916291,22.578392 L30.6300016,0.590854329 L39.4487059,0.590854329 L39.4487059,0.590854329 Z" id="Fill-1"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
86
modules/ppcp-wc-gateway/src/Admin/FeesRenderer.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
/**
|
||||
* Renders the PayPal fees in the order details.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Admin
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Admin;
|
||||
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class FeesRenderer
|
||||
*/
|
||||
class FeesRenderer {
|
||||
/**
|
||||
* Renders the PayPal fees in the order details.
|
||||
*
|
||||
* @param WC_Order $wc_order The order for which to render the fees.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render( WC_Order $wc_order ) : string {
|
||||
$breakdown = $wc_order->get_meta( PayPalGateway::FEES_META_KEY );
|
||||
if ( ! is_array( $breakdown ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
$fee = $breakdown['paypal_fee'] ?? null;
|
||||
if ( is_array( $fee ) ) {
|
||||
$html .= $this->render_money_row(
|
||||
__( 'PayPal Fee:', 'woocommerce-paypal-payments' ),
|
||||
__( 'The fee PayPal collects for the transaction.', 'woocommerce-paypal-payments' ),
|
||||
$fee['value'],
|
||||
$fee['currency_code'],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$net = $breakdown['net_amount'] ?? null;
|
||||
if ( is_array( $net ) ) {
|
||||
$html .= $this->render_money_row(
|
||||
__( 'PayPal Payout:', 'woocommerce-paypal-payments' ),
|
||||
__( 'The net total that will be credited to your PayPal account.', 'woocommerce-paypal-payments' ),
|
||||
$net['value'],
|
||||
$net['currency_code']
|
||||
);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a row in the order price breakdown table.
|
||||
*
|
||||
* @param string $title The row title.
|
||||
* @param string $tooltip The title tooltip.
|
||||
* @param string|float $value The money value.
|
||||
* @param string $currency The currency code.
|
||||
* @param bool $negative Whether to add the minus sign.
|
||||
* @return string
|
||||
*/
|
||||
private function render_money_row( string $title, string $tooltip, $value, string $currency, bool $negative = false ): string {
|
||||
/**
|
||||
* Bad type hint in WC phpdoc.
|
||||
*
|
||||
* @psalm-suppress InvalidScalarArgument
|
||||
*/
|
||||
return '
|
||||
<tr>
|
||||
<td class="label">' . wc_help_tip( $tooltip ) . ' ' . esc_html( $title ) . '
|
||||
</td>
|
||||
<td width="1%"></td>
|
||||
<td class="total">
|
||||
' .
|
||||
( $negative ? ' - ' : '' ) .
|
||||
wc_price( $value, array( 'currency' => $currency ) ) . '
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
}
|
|
@ -25,11 +25,11 @@ class SettingsPageAssets {
|
|||
private $module_url;
|
||||
|
||||
/**
|
||||
* The filesystem path to the module dir.
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_path;
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
|
@ -42,13 +42,13 @@ class SettingsPageAssets {
|
|||
* Assets constructor.
|
||||
*
|
||||
* @param string $module_url The url of this module.
|
||||
* @param string $module_path The filesystem path to this module.
|
||||
* @param string $version The assets version.
|
||||
* @param Bearer $bearer The bearer.
|
||||
*/
|
||||
public function __construct( string $module_url, string $module_path, Bearer $bearer ) {
|
||||
$this->module_url = $module_url;
|
||||
$this->module_path = $module_path;
|
||||
$this->bearer = $bearer;
|
||||
public function __construct( string $module_url, string $version, Bearer $bearer ) {
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->bearer = $bearer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,7 +59,7 @@ class SettingsPageAssets {
|
|||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
function() use ( $bearer ) {
|
||||
if ( ! is_admin() || is_ajax() ) {
|
||||
if ( ! is_admin() || wp_doing_ajax() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -102,13 +102,11 @@ class SettingsPageAssets {
|
|||
* @param Bearer $bearer The bearer.
|
||||
*/
|
||||
private function register_admin_assets( Bearer $bearer ) {
|
||||
$gateway_settings_script_path = trailingslashit( $this->module_path ) . 'assets/js/gateway-settings.js';
|
||||
|
||||
wp_enqueue_script(
|
||||
'ppcp-gateway-settings',
|
||||
trailingslashit( $this->module_url ) . 'assets/js/gateway-settings.js',
|
||||
array(),
|
||||
file_exists( $gateway_settings_script_path ) ? (string) filemtime( $gateway_settings_script_path ) : null,
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway\Checkout;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
@ -34,29 +33,19 @@ class DisableGateways {
|
|||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* The subscription helper
|
||||
*
|
||||
* @var SubscriptionHelper
|
||||
*/
|
||||
private $subscription_helper;
|
||||
|
||||
/**
|
||||
* DisableGateways constructor.
|
||||
*
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param ContainerInterface $settings The Settings.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
*/
|
||||
public function __construct(
|
||||
SessionHandler $session_handler,
|
||||
ContainerInterface $settings,
|
||||
SubscriptionHelper $subscription_helper
|
||||
ContainerInterface $settings
|
||||
) {
|
||||
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,10 +99,6 @@ class DisableGateways {
|
|||
return true;
|
||||
}
|
||||
|
||||
if ( $this->subscription_helper->cart_contains_subscription() && ! is_user_logged_in() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
const INTENT_META_KEY = '_ppcp_paypal_intent';
|
||||
const ORDER_ID_META_KEY = '_ppcp_paypal_order_id';
|
||||
const ORDER_PAYMENT_MODE_META_KEY = '_ppcp_paypal_payment_mode';
|
||||
const FEES_META_KEY = '_ppcp_paypal_fees';
|
||||
|
||||
/**
|
||||
* The Settings Renderer.
|
||||
|
|
|
@ -184,7 +184,7 @@ trait ProcessPaymentTrait {
|
|||
if ( $error->has_detail( 'INSTRUMENT_DECLINED' ) ) {
|
||||
$wc_order->update_status(
|
||||
'failed',
|
||||
__( 'Instrument declined.', 'woocommerce-paypal-payments' )
|
||||
__( 'Instrument declined. ', 'woocommerce-paypal-payments' ) . $error->details()[0]->description ?? ''
|
||||
);
|
||||
|
||||
$this->session_handler->increment_insufficient_funding_tries();
|
||||
|
@ -205,6 +205,19 @@ trait ProcessPaymentTrait {
|
|||
);
|
||||
}
|
||||
|
||||
$error_message = $error->getMessage();
|
||||
if ( $error->issues() ) {
|
||||
$error_message = implode(
|
||||
array_map(
|
||||
function( $issue ) {
|
||||
return $issue->issue . ' ' . $issue->description . '<br/>';
|
||||
},
|
||||
$error->issues()
|
||||
)
|
||||
);
|
||||
}
|
||||
wc_add_notice( $error_message, 'error' );
|
||||
|
||||
$this->session_handler->destroy_session_data();
|
||||
} catch ( RuntimeException $error ) {
|
||||
$this->handle_failure( $wc_order, $error );
|
||||
|
@ -215,9 +228,10 @@ trait ProcessPaymentTrait {
|
|||
$this->order_processor->last_error(),
|
||||
'error'
|
||||
);
|
||||
|
||||
$wc_order->update_status(
|
||||
'failed',
|
||||
__( 'Could not process order.', 'woocommerce-paypal-payments' )
|
||||
__( 'Could not process order. ', 'woocommerce-paypal-payments' ) . $this->order_processor->last_error()
|
||||
);
|
||||
|
||||
return $failure_data;
|
||||
|
@ -262,7 +276,7 @@ trait ProcessPaymentTrait {
|
|||
|
||||
$wc_order->update_status(
|
||||
'failed',
|
||||
__( 'Could not process order.', 'woocommerce-paypal-payments' )
|
||||
__( 'Could not process order. ', 'woocommerce-paypal-payments' ) . $error->getMessage()
|
||||
);
|
||||
|
||||
$this->session_handler->destroy_session_data();
|
||||
|
|
|
@ -57,7 +57,7 @@ class ConnectAdminNotice {
|
|||
$message = sprintf(
|
||||
/* translators: %1$s the gateway name. */
|
||||
__(
|
||||
'PayPal Checkout is almost ready. To get started, <a href="%1$s">connect your account</a>.',
|
||||
'PayPal Payments is almost ready. To get started, <a href="%1$s">connect your account</a>.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' )
|
||||
|
@ -71,6 +71,6 @@ class ConnectAdminNotice {
|
|||
* @return bool
|
||||
*/
|
||||
protected function should_display(): bool {
|
||||
return $this->state->current_state() < State::STATE_PROGRESSIVE;
|
||||
return $this->state->current_state() !== State::STATE_ONBOARDED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
|||
use Exception;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
@ -115,11 +117,11 @@ class AuthorizedPaymentsProcessor {
|
|||
/**
|
||||
* Process a WooCommerce order.
|
||||
*
|
||||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
*
|
||||
* @return string One of the AuthorizedPaymentsProcessor status constants.
|
||||
*/
|
||||
public function process( \WC_Order $wc_order ): string {
|
||||
public function process( WC_Order $wc_order ): string {
|
||||
$this->captures = array();
|
||||
|
||||
try {
|
||||
|
@ -142,7 +144,7 @@ class AuthorizedPaymentsProcessor {
|
|||
}
|
||||
|
||||
try {
|
||||
$this->capture_authorizations( ...$authorizations );
|
||||
$this->captures[] = $this->capture_authorization( $wc_order, ...$authorizations );
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Failed to capture authorization: ' . $exception->getMessage() );
|
||||
return self::FAILED;
|
||||
|
@ -163,11 +165,11 @@ class AuthorizedPaymentsProcessor {
|
|||
/**
|
||||
* Captures an authorized payment for an WooCommerce order.
|
||||
*
|
||||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function capture_authorized_payment( \WC_Order $wc_order ): bool {
|
||||
public function capture_authorized_payment( WC_Order $wc_order ): bool {
|
||||
$result_status = $this->process( $wc_order );
|
||||
$this->render_authorization_message_for_status( $result_status );
|
||||
|
||||
|
@ -251,11 +253,11 @@ class AuthorizedPaymentsProcessor {
|
|||
/**
|
||||
* Returns the PayPal order from a given WooCommerce order.
|
||||
*
|
||||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
*
|
||||
* @return Order
|
||||
*/
|
||||
private function paypal_order_from_wc_order( \WC_Order $wc_order ): Order {
|
||||
private function paypal_order_from_wc_order( WC_Order $wc_order ): Order {
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
return $this->order_endpoint->order( $order_id );
|
||||
}
|
||||
|
@ -279,15 +281,21 @@ class AuthorizedPaymentsProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Captures the authorizations.
|
||||
* Captures the authorization.
|
||||
*
|
||||
* @param WC_Order $order The order.
|
||||
* @param Authorization ...$authorizations All authorizations.
|
||||
* @throws Exception If capture failed.
|
||||
*/
|
||||
private function capture_authorizations( Authorization ...$authorizations ) {
|
||||
private function capture_authorization( WC_Order $order, Authorization ...$authorizations ): Capture {
|
||||
$uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations );
|
||||
foreach ( $uncaptured_authorizations as $authorization ) {
|
||||
$this->captures[] = $this->payments_endpoint->capture( $authorization->id() );
|
||||
if ( ! $uncaptured_authorizations ) {
|
||||
throw new Exception( 'No authorizations to capture.' );
|
||||
}
|
||||
|
||||
$authorization = end( $uncaptured_authorizations );
|
||||
|
||||
return $this->payments_endpoint->capture( $authorization->id(), new Money( (float) $order->get_total(), $order->get_currency() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -149,6 +149,7 @@ class OrderProcessor {
|
|||
public function process( \WC_Order $wc_order ): bool {
|
||||
$order = $this->session_handler->order();
|
||||
if ( ! $order ) {
|
||||
$this->last_error = __( 'No PayPal order found in the current WooCommerce session.', 'woocommerce-paypal-payments' );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,10 @@ trait PaymentsStatusHandlingTrait {
|
|||
switch ( $status->name() ) {
|
||||
case CaptureStatus::COMPLETED:
|
||||
$wc_order->payment_complete();
|
||||
/**
|
||||
* Fired when PayPal order is captured.
|
||||
*/
|
||||
do_action( 'woocommerce_paypal_payments_order_captured', $wc_order, $capture );
|
||||
break;
|
||||
// It is checked in the capture endpoint already, but there are other ways to capture,
|
||||
// such as when paid via saved card.
|
||||
|
|
|
@ -36,6 +36,15 @@ trait TransactionIdHandlingTrait {
|
|||
try {
|
||||
$wc_order->set_transaction_id( $transaction_id );
|
||||
$wc_order->save();
|
||||
|
||||
$wc_order->add_order_note(
|
||||
sprintf(
|
||||
/* translators: %s is the PayPal transaction ID */
|
||||
__( 'PayPal transaction ID: %s', 'woocommerce-paypal-payments' ),
|
||||
$transaction_id
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch ( Exception $exception ) {
|
||||
if ( $logger ) {
|
||||
|
|
|
@ -14,9 +14,9 @@ use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class SettingsListener
|
||||
|
@ -146,8 +146,14 @@ class SettingsListener {
|
|||
}
|
||||
$this->settings->persist();
|
||||
|
||||
/**
|
||||
* The hook fired before performing the redirect at the end of onboarding after saving the merchant ID/email.
|
||||
*/
|
||||
do_action( 'woocommerce_paypal_payments_onboarding_before_redirect' );
|
||||
|
||||
/**
|
||||
* The URL opened at the end of onboarding after saving the merchant ID/email.
|
||||
*/
|
||||
$redirect_url = apply_filters( 'woocommerce_paypal_payments_onboarding_redirect_url', admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ) );
|
||||
if ( ! $this->settings->has( 'client_id' ) || ! $this->settings->get( 'client_id' ) ) {
|
||||
$redirect_url = add_query_arg( 'ppcp-onboarding-error', '1', $redirect_url );
|
||||
|
|
|
@ -377,8 +377,18 @@ $data_rows_html
|
|||
?>
|
||||
<input type="hidden" name="ppcp-nonce" value="<?php echo esc_attr( $nonce ); ?>">
|
||||
<?php
|
||||
|
||||
// Create a hidden first row with 2 cells to avoid issues with table-layout: fixed
|
||||
// when the first visible row needs to have one cell.
|
||||
?>
|
||||
<tr style="height: 1px; padding-top: 0; padding-bottom: 0;">
|
||||
<th style="padding-top: 0; padding-bottom: 0;"></th>
|
||||
<td style="padding-top: 0; padding-bottom: 0;"></td>
|
||||
</tr>
|
||||
<?php
|
||||
|
||||
foreach ( $this->fields as $field => $config ) :
|
||||
if ( ! in_array( $this->state->current_state(), $config['screens'], true ) ) {
|
||||
if ( ! in_array( $this->state->environment_state( $config['state_from'] ?? null ), $config['screens'], true ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! $this->field_matches_page( $config, $this->page_id ) ) {
|
||||
|
@ -406,14 +416,18 @@ $data_rows_html
|
|||
$key = 'ppcp[' . $field . ']';
|
||||
$id = 'ppcp-' . $field;
|
||||
$config['id'] = $id;
|
||||
$colspan = 'ppcp-heading' !== $config['type'] ? 1 : 2;
|
||||
$colspan = ( 'ppcp-heading' !== $config['type'] && isset( $config['title'] ) ) ? 1 : 2;
|
||||
$classes = isset( $config['classes'] ) ? $config['classes'] : array();
|
||||
$classes[] = 'ppcp-settings-field';
|
||||
$classes[] = sprintf( 'ppcp-settings-field-%s', str_replace( 'ppcp-', '', $config['type'] ) );
|
||||
$description = isset( $config['description'] ) ? $config['description'] : '';
|
||||
if ( 1 !== $colspan ) {
|
||||
$classes[] = 'ppcp-settings-no-title-col';
|
||||
}
|
||||
$description = isset( $config['description'] ) ? $config['description'] : '';
|
||||
unset( $config['description'] );
|
||||
?>
|
||||
<tr valign="top" id="<?php echo esc_attr( 'field-' . $field ); ?>" class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
|
||||
<?php if ( 'ppcp-heading' !== $config['type'] ) : ?>
|
||||
<?php if ( 'ppcp-heading' !== $config['type'] && isset( $config['title'] ) ) : ?>
|
||||
<th scope="row">
|
||||
<label
|
||||
for="<?php echo esc_attr( $id ); ?>"
|
||||
|
@ -462,7 +476,14 @@ $data_rows_html
|
|||
* @param array $config The configuration array.
|
||||
*/
|
||||
private function render_text( array $config ) {
|
||||
echo wp_kses_post( $config['text'] );
|
||||
$raw = $config['raw'] ?? false;
|
||||
if ( $raw ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $config['text'];
|
||||
} else {
|
||||
echo wp_kses_post( $config['text'] );
|
||||
}
|
||||
|
||||
if ( isset( $config['hidden'] ) ) {
|
||||
$value = $this->settings->has( $config['hidden'] ) ?
|
||||
(string) $this->settings->get( $config['hidden'] )
|
||||
|
@ -538,6 +559,14 @@ $data_rows_html
|
|||
'woocommerce-paypal-payments'
|
||||
);
|
||||
?>
|
||||
<a href="https://developer.paypal.com/docs/checkout/advanced/currency-availability-advanced-cards/">
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Advanced credit and debit country and currency availability.',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
?>
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -11,9 +11,12 @@ namespace WooCommerce\PayPalCommerce\WcGateway;
|
|||
|
||||
use Dhii\Container\ServiceProvider;
|
||||
use Dhii\Modular\Module\ModuleInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderAuthorizeAction;
|
||||
|
@ -21,6 +24,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Assets\SettingsPageAssets;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
||||
|
@ -70,10 +74,39 @@ class WCGatewayModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_order_captured',
|
||||
function ( WC_Order $wc_order, Capture $capture ) {
|
||||
$breakdown = $capture->seller_receivable_breakdown();
|
||||
if ( $breakdown ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::FEES_META_KEY, $breakdown->to_array() );
|
||||
$wc_order->save_meta_data();
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
$fees_renderer = $c->get( 'wcgateway.admin.fees-renderer' );
|
||||
assert( $fees_renderer instanceof FeesRenderer );
|
||||
|
||||
add_action(
|
||||
'woocommerce_admin_order_totals_after_total',
|
||||
function ( int $order_id ) use ( $fees_renderer ) {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! $wc_order instanceof WC_Order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $fees_renderer->render( $wc_order );
|
||||
}
|
||||
);
|
||||
|
||||
if ( $c->has( 'wcgateway.url' ) ) {
|
||||
$assets = new SettingsPageAssets(
|
||||
$c->get( 'wcgateway.url' ),
|
||||
$c->get( 'wcgateway.absolute-path' ),
|
||||
$c->get( 'ppcp.asset-version' ),
|
||||
$c->get( 'api.bearer' )
|
||||
);
|
||||
$assets->register_assets();
|
||||
|
@ -132,6 +165,23 @@ class WCGatewayModule implements ModuleInterface {
|
|||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_gateway_migrate',
|
||||
static function () use ( $c ) {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
try {
|
||||
if ( $settings->get( '3d_secure_contingency' ) === '3D_SECURE' ) {
|
||||
$settings->set( '3d_secure_contingency', 'SCA_ALWAYS' );
|
||||
$settings->persist();
|
||||
}
|
||||
} catch ( NotFoundException $exception ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,7 +281,7 @@ class WCGatewayModule implements ModuleInterface {
|
|||
static function ( $order_actions ) use ( $container ): array {
|
||||
global $theorder;
|
||||
|
||||
if ( ! is_a( $theorder, \WC_Order::class ) ) {
|
||||
if ( ! is_a( $theorder, WC_Order::class ) ) {
|
||||
return $order_actions;
|
||||
}
|
||||
|
||||
|
@ -247,7 +297,7 @@ class WCGatewayModule implements ModuleInterface {
|
|||
|
||||
add_action(
|
||||
'woocommerce_order_action_ppcp_authorize_order',
|
||||
static function ( \WC_Order $wc_order ) use ( $container ) {
|
||||
static function ( WC_Order $wc_order ) use ( $container ) {
|
||||
|
||||
/**
|
||||
* The authorized payments processor.
|
||||
|
|
|
@ -19,7 +19,6 @@ return array(
|
|||
'title' => __( 'Subscribed webhooks', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-table',
|
||||
'screens' => array(
|
||||
State::STATE_PROGRESSIVE,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
|
@ -34,7 +33,6 @@ return array(
|
|||
'type' => 'ppcp-text',
|
||||
'text' => '<button type="button" class="button ppcp-webhooks-resubscribe">' . esc_html__( 'Resubscribe', 'woocommerce-paypal-payments' ) . '</button>',
|
||||
'screens' => array(
|
||||
State::STATE_PROGRESSIVE,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
|
@ -53,7 +51,6 @@ return array(
|
|||
'type' => 'ppcp-text',
|
||||
'text' => '<button type="button" class="button ppcp-webhooks-simulate">' . esc_html__( 'Simulate', 'woocommerce-paypal-payments' ) . '</button>',
|
||||
'screens' => array(
|
||||
State::STATE_PROGRESSIVE,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
|
|
|
@ -161,7 +161,8 @@ return array(
|
|||
|
||||
'webhook.status.assets' => function( ContainerInterface $container ) : WebhooksStatusPageAssets {
|
||||
return new WebhooksStatusPageAssets(
|
||||
$container->get( 'webhook.module-url' )
|
||||
$container->get( 'webhook.module-url' ),
|
||||
$container->get( 'ppcp.asset-version' )
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -202,7 +203,7 @@ return array(
|
|||
'webhook.module-url' => static function ( ContainerInterface $container ): string {
|
||||
return plugins_url(
|
||||
'/modules/ppcp-webhooks/',
|
||||
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -10,13 +10,14 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* Class PaymentCaptureRefunded
|
||||
*/
|
||||
class PaymentCaptureRefunded implements RequestHandler {
|
||||
|
||||
use PrefixTrait;
|
||||
use PrefixTrait, TransactionIdHandlingTrait;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
|
@ -150,6 +151,11 @@ class PaymentCaptureRefunded implements RequestHandler {
|
|||
'order' => $wc_order,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_array( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
|
||||
$this->update_transaction_id( $request['resource']['id'], $wc_order, $this->logger );
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
|
|
@ -26,15 +26,25 @@ class WebhooksStatusPageAssets {
|
|||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* WebhooksStatusPageAssets constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url
|
||||
string $module_url,
|
||||
string $version
|
||||
) {
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,16 +55,16 @@ class WebhooksStatusPageAssets {
|
|||
public function register(): void {
|
||||
wp_register_style(
|
||||
'ppcp-webhooks-status-page-style',
|
||||
$this->module_url . '/assets/css/status-page.css',
|
||||
untrailingslashit( $this->module_url ) . '/assets/css/status-page.css',
|
||||
array(),
|
||||
'1'
|
||||
$this->version
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ppcp-webhooks-status-page',
|
||||
$this->module_url . '/assets/js/status-page.js',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/status-page.js',
|
||||
array(),
|
||||
'1',
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
|
|