Merge branch 'trunk' into issue-174-vaulting-pay-later-ux
|
@ -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()
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient;
|
|||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\IdentityToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
|
@ -25,15 +26,20 @@ 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;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentsFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentSourceFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenActionLinksFactory;
|
||||
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;
|
||||
|
@ -42,37 +48,39 @@ 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\OrderRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
return array(
|
||||
'api.host' => function( ContainerInterface $container ) : string {
|
||||
'api.host' => function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_API_URL;
|
||||
},
|
||||
'api.paypal-host' => function( ContainerInterface $container ) : string {
|
||||
'api.paypal-host' => function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_API_URL;
|
||||
},
|
||||
'api.partner_merchant_id' => static function () : string {
|
||||
'api.partner_merchant_id' => static function () : string {
|
||||
return '';
|
||||
},
|
||||
'api.merchant_email' => function () : string {
|
||||
'api.merchant_email' => function () : string {
|
||||
return '';
|
||||
},
|
||||
'api.merchant_id' => function () : string {
|
||||
'api.merchant_id' => function () : string {
|
||||
return '';
|
||||
},
|
||||
'api.key' => static function (): string {
|
||||
'api.key' => static function (): string {
|
||||
return '';
|
||||
},
|
||||
'api.secret' => static function (): string {
|
||||
'api.secret' => static function (): string {
|
||||
return '';
|
||||
},
|
||||
'api.prefix' => static function (): string {
|
||||
'api.prefix' => static function (): string {
|
||||
return 'WC-';
|
||||
},
|
||||
'api.bearer' => static function ( ContainerInterface $container ): Bearer {
|
||||
'api.bearer' => static function ( ContainerInterface $container ): Bearer {
|
||||
$cache = new Cache( 'ppcp-paypal-bearer' );
|
||||
$key = $container->get( 'api.key' );
|
||||
$secret = $container->get( 'api.secret' );
|
||||
|
@ -88,7 +96,7 @@ return array(
|
|||
$settings
|
||||
);
|
||||
},
|
||||
'api.endpoint.partners' => static function ( ContainerInterface $container ) : PartnersEndpoint {
|
||||
'api.endpoint.partners' => static function ( ContainerInterface $container ) : PartnersEndpoint {
|
||||
return new PartnersEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
|
@ -98,19 +106,21 @@ return array(
|
|||
$container->get( 'api.merchant_id' )
|
||||
);
|
||||
},
|
||||
'api.factory.sellerstatus' => static function ( ContainerInterface $container ) : SellerStatusFactory {
|
||||
'api.factory.sellerstatus' => static function ( ContainerInterface $container ) : SellerStatusFactory {
|
||||
return new SellerStatusFactory();
|
||||
},
|
||||
'api.endpoint.payment-token' => static function ( ContainerInterface $container ) : PaymentTokenEndpoint {
|
||||
'api.endpoint.payment-token' => static function ( ContainerInterface $container ) : PaymentTokenEndpoint {
|
||||
return new PaymentTokenEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.payment-token' ),
|
||||
$container->get( 'api.factory.payment-token-action-links' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.prefix' )
|
||||
$container->get( 'api.repository.customer' ),
|
||||
$container->get( 'api.repository.paypal-request-id' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.webhook' => static function ( ContainerInterface $container ) : WebhookEndpoint {
|
||||
'api.endpoint.webhook' => static function ( ContainerInterface $container ) : WebhookEndpoint {
|
||||
|
||||
return new WebhookEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
|
@ -120,27 +130,27 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.partner-referrals' => static function ( ContainerInterface $container ) : PartnerReferrals {
|
||||
'api.endpoint.partner-referrals' => static function ( ContainerInterface $container ) : PartnerReferrals {
|
||||
|
||||
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 {
|
||||
|
||||
'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,
|
||||
$customer_repository
|
||||
);
|
||||
},
|
||||
'api.endpoint.payments' => static function ( ContainerInterface $container ): PaymentsEndpoint {
|
||||
'api.endpoint.payments' => static function ( ContainerInterface $container ): PaymentsEndpoint {
|
||||
$authorizations_factory = $container->get( 'api.factory.authorization' );
|
||||
$capture_factory = $container->get( 'api.factory.capture' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
|
@ -153,7 +163,7 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'api.endpoint.login-seller' => static function ( ContainerInterface $container ) : LoginSeller {
|
||||
'api.endpoint.login-seller' => static function ( ContainerInterface $container ) : LoginSeller {
|
||||
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new LoginSeller(
|
||||
|
@ -162,7 +172,7 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'api.endpoint.order' => static function ( ContainerInterface $container ): OrderEndpoint {
|
||||
'api.endpoint.order' => static function ( ContainerInterface $container ): OrderEndpoint {
|
||||
$order_factory = $container->get( 'api.factory.order' );
|
||||
$patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
|
@ -189,47 +199,68 @@ return array(
|
|||
$subscription_helper
|
||||
);
|
||||
},
|
||||
'api.repository.paypal-request-id' => static function( ContainerInterface $container ) : PayPalRequestIdRepository {
|
||||
'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint {
|
||||
return new BillingAgreementsEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.repository.paypal-request-id' => static function( ContainerInterface $container ) : PayPalRequestIdRepository {
|
||||
return new PayPalRequestIdRepository();
|
||||
},
|
||||
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
|
||||
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return new ApplicationContextRepository( $settings );
|
||||
},
|
||||
'api.repository.partner-referrals-data' => static function ( ContainerInterface $container ) : PartnerReferralsData {
|
||||
'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 {
|
||||
'api.repository.cart' => static function ( ContainerInterface $container ): CartRepository {
|
||||
$factory = $container->get( 'api.factory.purchase-unit' );
|
||||
return new CartRepository( $factory );
|
||||
},
|
||||
'api.repository.payee' => static function ( ContainerInterface $container ): PayeeRepository {
|
||||
'api.repository.payee' => static function ( ContainerInterface $container ): PayeeRepository {
|
||||
$merchant_email = $container->get( 'api.merchant_email' );
|
||||
$merchant_id = $container->get( 'api.merchant_id' );
|
||||
return new PayeeRepository( $merchant_email, $merchant_id );
|
||||
},
|
||||
'api.factory.application-context' => static function ( ContainerInterface $container ) : ApplicationContextFactory {
|
||||
'api.repository.customer' => static function( ContainerInterface $container ): CustomerRepository {
|
||||
$prefix = $container->get( 'api.prefix' );
|
||||
return new CustomerRepository( $prefix );
|
||||
},
|
||||
'api.repository.order' => static function( ContainerInterface $container ): OrderRepository {
|
||||
return new OrderRepository(
|
||||
$container->get( 'api.endpoint.order' )
|
||||
);
|
||||
},
|
||||
'api.factory.application-context' => static function ( ContainerInterface $container ) : ApplicationContextFactory {
|
||||
return new ApplicationContextFactory();
|
||||
},
|
||||
'api.factory.payment-token' => static function ( ContainerInterface $container ) : PaymentTokenFactory {
|
||||
'api.factory.payment-token' => static function ( ContainerInterface $container ) : PaymentTokenFactory {
|
||||
return new PaymentTokenFactory();
|
||||
},
|
||||
'api.factory.webhook' => static function ( ContainerInterface $container ): WebhookFactory {
|
||||
'api.factory.payment-token-action-links' => static function ( ContainerInterface $container ) : PaymentTokenActionLinksFactory {
|
||||
return new PaymentTokenActionLinksFactory();
|
||||
},
|
||||
'api.factory.webhook' => static function ( ContainerInterface $container ): WebhookFactory {
|
||||
return new WebhookFactory();
|
||||
},
|
||||
'api.factory.webhook-event' => static function ( ContainerInterface $container ): WebhookEventFactory {
|
||||
'api.factory.webhook-event' => static function ( ContainerInterface $container ): WebhookEventFactory {
|
||||
return new WebhookEventFactory();
|
||||
},
|
||||
'api.factory.capture' => static function ( ContainerInterface $container ): CaptureFactory {
|
||||
'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 {
|
||||
'api.factory.purchase-unit' => static function ( ContainerInterface $container ): PurchaseUnitFactory {
|
||||
|
||||
$amount_factory = $container->get( 'api.factory.amount' );
|
||||
$payee_repository = $container->get( 'api.repository.payee' );
|
||||
|
@ -249,34 +280,43 @@ return array(
|
|||
$prefix
|
||||
);
|
||||
},
|
||||
'api.factory.patch-collection-factory' => static function ( ContainerInterface $container ): PatchCollectionFactory {
|
||||
'api.factory.patch-collection-factory' => static function ( ContainerInterface $container ): PatchCollectionFactory {
|
||||
return new PatchCollectionFactory();
|
||||
},
|
||||
'api.factory.payee' => static function ( ContainerInterface $container ): PayeeFactory {
|
||||
'api.factory.payee' => static function ( ContainerInterface $container ): PayeeFactory {
|
||||
return new PayeeFactory();
|
||||
},
|
||||
'api.factory.item' => static function ( ContainerInterface $container ): ItemFactory {
|
||||
return new ItemFactory();
|
||||
'api.factory.item' => static function ( ContainerInterface $container ): ItemFactory {
|
||||
return new ItemFactory(
|
||||
$container->get( 'api.shop.currency' )
|
||||
);
|
||||
},
|
||||
'api.factory.shipping' => static function ( ContainerInterface $container ): ShippingFactory {
|
||||
'api.factory.shipping' => static function ( ContainerInterface $container ): ShippingFactory {
|
||||
$address_factory = $container->get( 'api.factory.address' );
|
||||
return new ShippingFactory( $address_factory );
|
||||
},
|
||||
'api.factory.amount' => static function ( ContainerInterface $container ): AmountFactory {
|
||||
'api.factory.amount' => static function ( ContainerInterface $container ): AmountFactory {
|
||||
$item_factory = $container->get( 'api.factory.item' );
|
||||
return new AmountFactory( $item_factory );
|
||||
return new AmountFactory(
|
||||
$item_factory,
|
||||
$container->get( 'api.factory.money' ),
|
||||
$container->get( 'api.shop.currency' )
|
||||
);
|
||||
},
|
||||
'api.factory.payer' => static function ( ContainerInterface $container ): PayerFactory {
|
||||
'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 );
|
||||
},
|
||||
'api.factory.address' => static function ( ContainerInterface $container ): AddressFactory {
|
||||
'api.factory.address' => static function ( ContainerInterface $container ): AddressFactory {
|
||||
return new AddressFactory();
|
||||
},
|
||||
'api.factory.payment-source' => static function ( ContainerInterface $container ): PaymentSourceFactory {
|
||||
'api.factory.payment-source' => static function ( ContainerInterface $container ): PaymentSourceFactory {
|
||||
return new PaymentSourceFactory();
|
||||
},
|
||||
'api.factory.order' => static function ( ContainerInterface $container ): OrderFactory {
|
||||
'api.factory.order' => static function ( ContainerInterface $container ): OrderFactory {
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
$application_context_repository = $container->get( 'api.repository.application-context' );
|
||||
|
@ -290,15 +330,340 @@ return array(
|
|||
$payment_source_factory
|
||||
);
|
||||
},
|
||||
'api.factory.payments' => static function ( ContainerInterface $container ): PaymentsFactory {
|
||||
'api.factory.payments' => static function ( ContainerInterface $container ): PaymentsFactory {
|
||||
$authorizations_factory = $container->get( 'api.factory.authorization' );
|
||||
$capture_factory = $container->get( 'api.factory.capture' );
|
||||
return new PaymentsFactory( $authorizations_factory, $capture_factory );
|
||||
},
|
||||
'api.factory.authorization' => static function ( ContainerInterface $container ): AuthorizationFactory {
|
||||
'api.factory.authorization' => static function ( ContainerInterface $container ): AuthorizationFactory {
|
||||
return new AuthorizationFactory();
|
||||
},
|
||||
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
||||
return new DccApplies();
|
||||
'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' ),
|
||||
$container->get( 'api.dcc-supported-country-card-matrix' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
|
||||
'api.shop.currency' => static function ( ContainerInterface $container ) : string {
|
||||
$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();
|
||||
return $location['country'];
|
||||
},
|
||||
'api.shop.is-psd2-country' => static function ( ContainerInterface $container ) : bool {
|
||||
return in_array(
|
||||
$container->get( 'api.shop.country' ),
|
||||
$container->get( 'api.psd2-countries' ),
|
||||
true
|
||||
);
|
||||
},
|
||||
'api.shop.is-currency-supported' => static function ( ContainerInterface $container ) : bool {
|
||||
return in_array(
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.supported-currencies' ),
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Currencies supported by PayPal.
|
||||
*
|
||||
* From https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/
|
||||
*/
|
||||
'api.supported-currencies' => static function ( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CNY',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MYR',
|
||||
'MXN',
|
||||
'TWD',
|
||||
'NZD',
|
||||
'NOK',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'GBP',
|
||||
'RUB',
|
||||
'SGD',
|
||||
'SEK',
|
||||
'CHF',
|
||||
'THB',
|
||||
'USD',
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for DCC.
|
||||
*/
|
||||
'api.dcc-supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* 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',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* 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' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
'api.psd2-countries' => static function ( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'AT',
|
||||
'BE',
|
||||
'BG',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DK',
|
||||
'EE',
|
||||
'FI',
|
||||
'FR',
|
||||
'DE',
|
||||
'GB',
|
||||
'GR',
|
||||
'HU',
|
||||
'IE',
|
||||
'IT',
|
||||
'LV',
|
||||
'LT',
|
||||
'LU',
|
||||
'MT',
|
||||
'NL',
|
||||
'NO',
|
||||
'PL',
|
||||
'PT',
|
||||
'RO',
|
||||
'SK',
|
||||
'SI',
|
||||
'ES',
|
||||
'SE',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
/**
|
||||
* The billing agreements endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class BillingAgreementsEndpoint
|
||||
*/
|
||||
class BillingAgreementsEndpoint {
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingAgreementsEndpoint constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a billing agreement token.
|
||||
*
|
||||
* @param string $description The description.
|
||||
* @param string $return_url The return URL.
|
||||
* @param string $cancel_url The cancel URL.
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function create_token( string $description, string $return_url, string $cancel_url ): stdClass {
|
||||
$data = array(
|
||||
'description' => $description,
|
||||
'payer' => array(
|
||||
'payment_method' => 'PAYPAL',
|
||||
),
|
||||
'plan' => array(
|
||||
'type' => 'MERCHANT_INITIATED_BILLING',
|
||||
'merchant_preferences' => array(
|
||||
'return_url' => $return_url,
|
||||
'cancel_url' => $cancel_url,
|
||||
'skip_shipping_address' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing-agreements/agreement-tokens';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create a billing agreement token.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if reference transactions are enabled in account.
|
||||
*
|
||||
* @throws RuntimeException If the request fails (no auth, no connection, etc.).
|
||||
*/
|
||||
public function reference_transaction_enabled(): bool {
|
||||
try {
|
||||
$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 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ 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;
|
||||
|
||||
/**
|
||||
* Class IdentityToken
|
||||
|
@ -44,36 +46,51 @@ class IdentityToken {
|
|||
private $logger;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
* The settings
|
||||
*
|
||||
* @var string
|
||||
* @var Settings
|
||||
*/
|
||||
private $prefix;
|
||||
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 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 ) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->prefix = $prefix;
|
||||
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';
|
||||
|
@ -84,8 +101,17 @@ class IdentityToken {
|
|||
'Content-Type' => 'application/json',
|
||||
),
|
||||
);
|
||||
if ( $customer_id && defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION ) {
|
||||
$args['body'] = wp_json_encode( array( 'customer_id' => $this->prefix . $customer_id ) );
|
||||
if (
|
||||
( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) )
|
||||
&& defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION
|
||||
) {
|
||||
$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,9 +224,9 @@ class OrderEndpoint {
|
|||
$items
|
||||
),
|
||||
'application_context' => $this->application_context_repository
|
||||
->current_context( $shipping_preferences )->to_array(),
|
||||
->current_context( $shipping_preference )->to_array(),
|
||||
);
|
||||
if ( $payer ) {
|
||||
if ( $payer && ! empty( $payer->email_address() ) && ! empty( $payer->name() ) ) {
|
||||
$data['payer'] = $payer->to_array();
|
||||
}
|
||||
if ( $payment_token ) {
|
||||
|
@ -229,6 +235,11 @@ class OrderEndpoint {
|
|||
if ( $payment_method ) {
|
||||
$data['payment_method'] = $payment_method->to_array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter can be used to modify the order creation request body data.
|
||||
*/
|
||||
$data = apply_filters( 'ppcp_create_order_request_body_data', $data );
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
|
@ -591,7 +602,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',
|
||||
|
|
|
@ -11,10 +11,14 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentTokenActionLinks;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenActionLinksFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenEndpoint
|
||||
|
@ -44,41 +48,62 @@ class PaymentTokenEndpoint {
|
|||
*/
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* The PaymentTokenActionLinks factory.
|
||||
*
|
||||
* @var PaymentTokenActionLinksFactory
|
||||
*/
|
||||
private $payment_token_action_links_factory;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
* The customer repository.
|
||||
*
|
||||
* @var string
|
||||
* @var CustomerRepository
|
||||
*/
|
||||
private $prefix;
|
||||
protected $customer_repository;
|
||||
|
||||
/**
|
||||
* The request id repository.
|
||||
*
|
||||
* @var PayPalRequestIdRepository
|
||||
*/
|
||||
private $request_id_repository;
|
||||
|
||||
/**
|
||||
* PaymentTokenEndpoint constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param PaymentTokenFactory $factory The payment token factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $prefix The prefix.
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param PaymentTokenFactory $factory The payment token factory.
|
||||
* @param PaymentTokenActionLinksFactory $payment_token_action_links_factory The PaymentTokenActionLinks factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param CustomerRepository $customer_repository The customer repository.
|
||||
* @param PayPalRequestIdRepository $request_id_repository The request id repository.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
PaymentTokenFactory $factory,
|
||||
PaymentTokenActionLinksFactory $payment_token_action_links_factory,
|
||||
LoggerInterface $logger,
|
||||
string $prefix
|
||||
CustomerRepository $customer_repository,
|
||||
PayPalRequestIdRepository $request_id_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->payment_token_action_links_factory = $payment_token_action_links_factory;
|
||||
$this->logger = $logger;
|
||||
$this->customer_repository = $customer_repository;
|
||||
$this->request_id_repository = $request_id_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,10 +116,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(),
|
||||
|
@ -183,4 +206,120 @@ class PaymentTokenEndpoint {
|
|||
|
||||
return wp_remote_retrieve_response_code( $response ) === 204;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the process of PayPal account vaulting (without payment), returns the links for further actions.
|
||||
*
|
||||
* @param int $user_id The WP user id.
|
||||
* @param string $return_url The URL to which the customer is redirected after finishing the approval.
|
||||
* @param string $cancel_url The URL to which the customer is redirected if cancelled the operation.
|
||||
*
|
||||
* @return PaymentTokenActionLinks
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function start_paypal_token_creation(
|
||||
int $user_id,
|
||||
string $return_url,
|
||||
string $cancel_url
|
||||
): PaymentTokenActionLinks {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens';
|
||||
|
||||
$customer_id = $this->customer_repository->customer_id_for_user( ( $user_id ) );
|
||||
$data = array(
|
||||
'customer_id' => $customer_id,
|
||||
'source' => array(
|
||||
'paypal' => array(
|
||||
'usage_type' => 'MERCHANT',
|
||||
),
|
||||
),
|
||||
'application_context' => array(
|
||||
'return_url' => $return_url,
|
||||
'cancel_url' => $cancel_url,
|
||||
// TODO: can use vault_on_approval to avoid /confirm-payment-token, but currently it's not working.
|
||||
),
|
||||
);
|
||||
|
||||
$request_id = uniqid( 'ppcp-vault', true );
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Request-Id' => $request_id,
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Failed to create payment token.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
$status = $json->status;
|
||||
if ( 'CUSTOMER_ACTION_REQUIRED' !== $status ) {
|
||||
throw new RuntimeException( 'Unexpected payment token creation status. ' . $status );
|
||||
}
|
||||
|
||||
$links = $this->payment_token_action_links_factory->from_paypal_response( $json );
|
||||
|
||||
$this->request_id_repository->set( "ppcp-vault-{$user_id}", $request_id );
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the process of PayPal account vaulting.
|
||||
*
|
||||
* @param string $approval_token The id of the approval token approved by the customer.
|
||||
* @param int $user_id The WP user id.
|
||||
*
|
||||
* @return string
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function create_from_approval_token( string $approval_token, int $user_id ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v2/vault/approval-tokens/' . $approval_token . '/confirm-payment-token';
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Request-Id' => $this->request_id_repository->get( "ppcp-vault-{$user_id}" ),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Failed to create payment token from approval token.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json->id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/**
|
||||
* The links from CUSTOMER_ACTION_REQUIRED v2/vault/payment-tokens response.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenActionLinks
|
||||
*/
|
||||
class PaymentTokenActionLinks {
|
||||
/**
|
||||
* The URL for customer PayPal hosted contingency flow.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $approve_link;
|
||||
|
||||
/**
|
||||
* The URL for a POST request to save an approved approval token and vault the underlying instrument.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $confirm_link;
|
||||
|
||||
/**
|
||||
* The URL for a GET request to get the state of the approval token.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $status_link;
|
||||
|
||||
/**
|
||||
* PaymentTokenActionLinks constructor.
|
||||
*
|
||||
* @param string $approve_link The URL for customer PayPal hosted contingency flow.
|
||||
* @param string $confirm_link The URL for a POST request to save an approved approval token and vault the underlying instrument.
|
||||
* @param string $status_link The URL for a GET request to get the state of the approval token.
|
||||
*/
|
||||
public function __construct( string $approve_link, string $confirm_link, string $status_link ) {
|
||||
$this->approve_link = $approve_link;
|
||||
$this->confirm_link = $confirm_link;
|
||||
$this->status_link = $status_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for customer PayPal hosted contingency flow.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function approve_link(): string {
|
||||
return $this->approve_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for a POST request to save an approved approval token and vault the underlying instrument.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function confirm_link(): string {
|
||||
return $this->confirm_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for a GET request to get the state of the approval token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function status_link(): string {
|
||||
return $this->status_link;
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
|
@ -157,6 +157,15 @@ class PurchaseUnit {
|
|||
return $this->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount.
|
||||
*
|
||||
* @param Amount $amount The value to set.
|
||||
*/
|
||||
public function set_amount( Amount $amount ): void {
|
||||
$this->amount = $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shipping.
|
||||
*
|
||||
|
|
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,15 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\AmountBreakdown;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
||||
/**
|
||||
* Class AmountFactory
|
||||
*/
|
||||
class AmountFactory {
|
||||
|
||||
use FreeTrialHandlerTrait;
|
||||
|
||||
/**
|
||||
* The item factory.
|
||||
|
@ -28,13 +31,31 @@ class AmountFactory {
|
|||
*/
|
||||
private $item_factory;
|
||||
|
||||
/**
|
||||
* The Money factory.
|
||||
*
|
||||
* @var MoneyFactory
|
||||
*/
|
||||
private $money_factory;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* AmountFactory constructor.
|
||||
*
|
||||
* @param ItemFactory $item_factory The Item factory.
|
||||
* @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 ) {
|
||||
$this->item_factory = $item_factory;
|
||||
public function __construct( ItemFactory $item_factory, MoneyFactory $money_factory, string $currency ) {
|
||||
$this->item_factory = $item_factory;
|
||||
$this->money_factory = $money_factory;
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,8 +66,7 @@ class AmountFactory {
|
|||
* @return Amount
|
||||
*/
|
||||
public function from_wc_cart( \WC_Cart $cart ): Amount {
|
||||
$currency = get_woocommerce_currency();
|
||||
$total = new Money( (float) $cart->get_total( 'numeric' ), $currency );
|
||||
$total = new Money( (float) $cart->get_total( 'numeric' ), $this->currency );
|
||||
|
||||
$total_fees_amount = 0;
|
||||
$fees = WC()->session->get( 'ppcp_fees' );
|
||||
|
@ -57,22 +77,22 @@ class AmountFactory {
|
|||
}
|
||||
|
||||
$item_total = $cart->get_cart_contents_total() + $cart->get_discount_total() + $total_fees_amount;
|
||||
$item_total = new Money( (float) $item_total, $currency );
|
||||
$item_total = new Money( (float) $item_total, $this->currency );
|
||||
$shipping = new Money(
|
||||
(float) $cart->get_shipping_total() + $cart->get_shipping_tax(),
|
||||
$currency
|
||||
$this->currency
|
||||
);
|
||||
|
||||
$taxes = new Money(
|
||||
(float) $cart->get_cart_contents_tax() + (float) $cart->get_discount_tax(),
|
||||
$currency
|
||||
$cart->get_subtotal_tax(),
|
||||
$this->currency
|
||||
);
|
||||
|
||||
$discount = null;
|
||||
if ( $cart->get_discount_total() ) {
|
||||
$discount = new Money(
|
||||
(float) $cart->get_discount_total() + $cart->get_discount_tax(),
|
||||
$currency
|
||||
$this->currency
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -100,9 +120,15 @@ class AmountFactory {
|
|||
* @return Amount
|
||||
*/
|
||||
public function from_wc_order( \WC_Order $order ): Amount {
|
||||
$currency = $order->get_currency();
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
$total = new Money( (float) $order->get_total(), $currency );
|
||||
$currency = $order->get_currency();
|
||||
$items = $this->item_factory->from_wc_order( $order );
|
||||
|
||||
$total_value = (float) $order->get_total();
|
||||
if ( CreditCardGateway::ID === $order->get_payment_method() && $this->is_free_trial_order( $order ) ) {
|
||||
$total_value = 1.0;
|
||||
}
|
||||
$total = new Money( $total_value, $currency );
|
||||
|
||||
$item_total = new Money(
|
||||
(float) array_reduce(
|
||||
$items,
|
||||
|
@ -161,16 +187,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 );
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
@ -17,7 +18,21 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|||
* Class ItemFactory
|
||||
*/
|
||||
class ItemFactory {
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* ItemFactory constructor.
|
||||
*
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
*/
|
||||
public function __construct( string $currency ) {
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates items based off a WooCommerce cart.
|
||||
|
@ -27,9 +42,8 @@ class ItemFactory {
|
|||
* @return Item[]
|
||||
*/
|
||||
public function from_wc_cart( \WC_Cart $cart ): array {
|
||||
$currency = get_woocommerce_currency();
|
||||
$items = array_map(
|
||||
static function ( array $item ) use ( $currency ): Item {
|
||||
$items = array_map(
|
||||
function ( array $item ): Item {
|
||||
$product = $item['data'];
|
||||
|
||||
/**
|
||||
|
@ -43,12 +57,12 @@ class ItemFactory {
|
|||
$price_without_tax = (float) wc_get_price_excluding_tax( $product );
|
||||
$price_without_tax_rounded = round( $price_without_tax, 2 );
|
||||
$tax = round( $price - $price_without_tax_rounded, 2 );
|
||||
$tax = new Money( $tax, $currency );
|
||||
$tax = new Money( $tax, $this->currency );
|
||||
return new Item(
|
||||
mb_substr( $product->get_name(), 0, 127 ),
|
||||
new Money( $price_without_tax_rounded, $currency ),
|
||||
new Money( $price_without_tax_rounded, $this->currency ),
|
||||
$quantity,
|
||||
mb_substr( wp_strip_all_tags( $product->get_description() ), 0, 127 ),
|
||||
substr( wp_strip_all_tags( $product->get_description() ), 0, 127 ) ?: '',
|
||||
$tax,
|
||||
$product->get_sku(),
|
||||
( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS
|
||||
|
@ -61,13 +75,13 @@ class ItemFactory {
|
|||
$fees_from_session = WC()->session->get( 'ppcp_fees' );
|
||||
if ( $fees_from_session ) {
|
||||
$fees = array_map(
|
||||
static function ( \stdClass $fee ) use ( $currency ): Item {
|
||||
function ( \stdClass $fee ): Item {
|
||||
return new Item(
|
||||
$fee->name,
|
||||
new Money( (float) $fee->amount, $currency ),
|
||||
new Money( (float) $fee->amount, $this->currency ),
|
||||
1,
|
||||
'',
|
||||
new Money( (float) $fee->tax, $currency )
|
||||
new Money( (float) $fee->tax, $this->currency )
|
||||
);
|
||||
},
|
||||
$fees_from_session
|
||||
|
@ -110,21 +124,20 @@ class ItemFactory {
|
|||
* @return Item
|
||||
*/
|
||||
private function from_wc_order_line_item( \WC_Order_Item_Product $item, \WC_Order $order ): Item {
|
||||
$currency = $order->get_currency();
|
||||
$product = $item->get_product();
|
||||
|
||||
/**
|
||||
* The WooCommerce product.
|
||||
*
|
||||
* @var \WC_Product $product
|
||||
* @var WC_Product $product
|
||||
*/
|
||||
$quantity = (int) $item->get_quantity();
|
||||
|
||||
$product = $item->get_product();
|
||||
$currency = $order->get_currency();
|
||||
$quantity = (int) $item->get_quantity();
|
||||
$price = (float) $order->get_item_subtotal( $item, true );
|
||||
$price_without_tax = (float) $order->get_item_subtotal( $item, false );
|
||||
$price_without_tax_rounded = round( $price_without_tax, 2 );
|
||||
$tax = round( $price - $price_without_tax_rounded, 2 );
|
||||
$tax = new Money( $tax, $currency );
|
||||
|
||||
return new Item(
|
||||
mb_substr( $product->get_name(), 0, 127 ),
|
||||
new Money( $price_without_tax_rounded, $currency ),
|
||||
|
|
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* The factory for links from CUSTOMER_ACTION_REQUIRED v2/vault/payment-tokens response.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentTokenActionLinks;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenActionLinksFactory
|
||||
*/
|
||||
class PaymentTokenActionLinksFactory {
|
||||
|
||||
/**
|
||||
* Returns a PaymentTokenActionLinks object based off a PayPal response.
|
||||
*
|
||||
* @param stdClass $data The JSON object.
|
||||
*
|
||||
* @return PaymentTokenActionLinks
|
||||
* @throws RuntimeException When JSON object is malformed.
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): PaymentTokenActionLinks {
|
||||
if ( ! isset( $data->links ) ) {
|
||||
throw new RuntimeException( 'Links not found.' );
|
||||
}
|
||||
|
||||
$links_map = array();
|
||||
foreach ( $data->links as $link ) {
|
||||
if ( ! isset( $link->rel ) || ! isset( $link->href ) ) {
|
||||
throw new RuntimeException( 'Invalid link data.' );
|
||||
}
|
||||
|
||||
$links_map[ $link->rel ] = $link->href;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'approve', $links_map ) ) {
|
||||
throw new RuntimeException( 'Payment token approve link not found.' );
|
||||
}
|
||||
|
||||
return new PaymentTokenActionLinks(
|
||||
$links_map['approve'],
|
||||
$links_map['confirm'] ?? '',
|
||||
$links_map['status'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
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,18 +112,18 @@ 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;
|
||||
}
|
||||
$reference_id = 'default';
|
||||
$description = '';
|
||||
$payee = $this->payee_repository->payee();
|
||||
$wc_order_id = $order->get_order_number();
|
||||
$custom_id = $this->prefix . $wc_order_id;
|
||||
$invoice_id = $this->prefix . $wc_order_id;
|
||||
$custom_id = (string) $order->get_id();
|
||||
$invoice_id = $this->prefix . $order->get_order_number();
|
||||
$soft_descriptor = '';
|
||||
$purchase_unit = new PurchaseUnit(
|
||||
|
||||
$purchase_unit = new PurchaseUnit(
|
||||
$amount,
|
||||
$items,
|
||||
$shipping,
|
||||
|
@ -134,6 +134,9 @@ class PurchaseUnitFactory {
|
|||
$invoice_id,
|
||||
$soft_descriptor
|
||||
);
|
||||
/**
|
||||
* Returns PurchaseUnit for the WC order.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_purchase_unit_from_wc_order',
|
||||
$purchase_unit,
|
||||
|
@ -157,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;
|
||||
}
|
||||
|
@ -275,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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -19,169 +19,50 @@ class DccApplies {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix = 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',
|
||||
),
|
||||
);
|
||||
private $allowed_country_currency_matrix;
|
||||
|
||||
/**
|
||||
* Which countries support which credit cards. Empty credit card arrays mean no restriction on
|
||||
* currency. Otherwise only the currencies in the array are supported.
|
||||
* currency.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $country_card_matrix = 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' ),
|
||||
),
|
||||
);
|
||||
private $country_card_matrix;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for DCC.
|
||||
* @param array $country_card_matrix Which countries support which credit cards. Empty credit card arrays mean no restriction on
|
||||
* currency.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
array $country_card_matrix,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->country_card_matrix = $country_card_matrix;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether DCC can be used in the current country and the current currency used.
|
||||
|
@ -189,12 +70,10 @@ class DccApplies {
|
|||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
$country = $this->country();
|
||||
$currency = get_woocommerce_currency();
|
||||
if ( ! in_array( $country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
}
|
||||
$applies = in_array( $currency, $this->allowed_country_currency_matrix[ $country ], true );
|
||||
$applies = in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
return $applies;
|
||||
}
|
||||
|
||||
|
@ -204,13 +83,12 @@ class DccApplies {
|
|||
* @return array
|
||||
*/
|
||||
public function valid_cards() : array {
|
||||
$country = $this->country();
|
||||
$cards = array();
|
||||
if ( ! isset( $this->country_card_matrix[ $country ] ) ) {
|
||||
$cards = array();
|
||||
if ( ! isset( $this->country_card_matrix[ $this->country ] ) ) {
|
||||
return $cards;
|
||||
}
|
||||
|
||||
$supported_currencies = $this->country_card_matrix[ $country ];
|
||||
$supported_currencies = $this->country_card_matrix[ $this->country ];
|
||||
foreach ( $supported_currencies as $card => $currencies ) {
|
||||
if ( $this->can_process_card( $card ) ) {
|
||||
$cards[] = $card;
|
||||
|
@ -233,11 +111,10 @@ class DccApplies {
|
|||
* @return bool
|
||||
*/
|
||||
public function can_process_card( string $card ) : bool {
|
||||
$country = $this->country();
|
||||
if ( ! isset( $this->country_card_matrix[ $country ] ) ) {
|
||||
if ( ! isset( $this->country_card_matrix[ $this->country ] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! isset( $this->country_card_matrix[ $country ][ $card ] ) ) {
|
||||
if ( ! isset( $this->country_card_matrix[ $this->country ][ $card ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -245,19 +122,7 @@ class DccApplies {
|
|||
* If the supported currencies array is empty, there are no
|
||||
* restrictions, which currencies are supported by a card.
|
||||
*/
|
||||
$supported_currencies = $this->country_card_matrix[ $country ][ $card ];
|
||||
$currency = get_woocommerce_currency();
|
||||
return empty( $supported_currencies ) || in_array( $currency, $supported_currencies, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the country code of the shop.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function country() : string {
|
||||
$region = wc_get_base_location();
|
||||
$country = $region['country'];
|
||||
return $country;
|
||||
$supported_currencies = $this->country_card_matrix[ $this->country ][ $card ];
|
||||
return empty( $supported_currencies ) || in_array( $this->currency, $supported_currencies, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class ApplicationContextRepository {
|
|||
$landingpage = $this->settings->has( 'landing_page' ) ?
|
||||
$this->settings->get( 'landing_page' ) : ApplicationContext::LANDING_PAGE_NO_PREFERENCE;
|
||||
$context = new ApplicationContext(
|
||||
(string) home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
|
||||
network_home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
|
||||
(string) wc_get_checkout_url(),
|
||||
(string) $brand_name,
|
||||
$locale,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
54
modules/ppcp-api-client/src/Repository/OrderRepository.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/**
|
||||
* PayPal order repository.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
|
||||
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class OrderRepository
|
||||
*/
|
||||
class OrderRepository {
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
protected $order_endpoint;
|
||||
|
||||
/**
|
||||
* OrderRepository constructor.
|
||||
*
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
*/
|
||||
public function __construct( OrderEndpoint $order_endpoint ) {
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a PayPal order for the given WooCommerce order.
|
||||
*
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @return Order The PayPal order.
|
||||
* @throws RuntimeException When there is a problem getting the PayPal order.
|
||||
*/
|
||||
public function for_wc_order( WC_Order $wc_order ): Order {
|
||||
$paypal_order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $paypal_order_id ) {
|
||||
throw new RuntimeException( 'PayPal order ID not found in meta.' );
|
||||
}
|
||||
|
||||
return $this->order_endpoint->order( $paypal_order_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',
|
||||
|
|
|
@ -26,8 +26,7 @@ class PayPalRequestIdRepository {
|
|||
* @return string
|
||||
*/
|
||||
public function get_for_order_id( string $order_id ): string {
|
||||
$all = $this->all();
|
||||
return isset( $all[ $order_id ] ) ? (string) $all[ $order_id ]['id'] : '';
|
||||
return $this->get( $order_id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,16 +49,39 @@ class PayPalRequestIdRepository {
|
|||
* @return bool
|
||||
*/
|
||||
public function set_for_order( Order $order, string $request_id ): bool {
|
||||
$all = $this->all();
|
||||
$all[ $order->id() ] = array(
|
||||
'id' => $request_id,
|
||||
'expiration' => time() + 10 * DAY_IN_SECONDS,
|
||||
);
|
||||
$all = $this->cleanup( $all );
|
||||
update_option( self::KEY, $all );
|
||||
$this->set( $order->id(), $request_id );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a request ID for the given key.
|
||||
*
|
||||
* @param string $key The key in the request ID storage.
|
||||
* @param string $request_id The ID.
|
||||
*/
|
||||
public function set( string $key, string $request_id ): void {
|
||||
$all = $this->all();
|
||||
$day_in_seconds = 86400;
|
||||
$all[ $key ] = array(
|
||||
'id' => $request_id,
|
||||
'expiration' => time() + 10 * $day_in_seconds,
|
||||
);
|
||||
$all = $this->cleanup( $all );
|
||||
update_option( self::KEY, $all );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a request ID.
|
||||
*
|
||||
* @param string $key The key in the request ID storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get( string $key ): string {
|
||||
$all = $this->all();
|
||||
return isset( $all[ $key ] ) ? (string) $all[ $key ]['id'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all IDs.
|
||||
*
|
||||
|
|
|
@ -11,3 +11,7 @@
|
|||
.ppcp-credit-card-gateway-form-field-disabled {
|
||||
opacity: .5 !important;
|
||||
}
|
||||
|
||||
.ppcp-dcc-order-button {
|
||||
float: right;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,38 @@ import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
|
|||
import dataClientIdAttributeHandler from "./modules/DataClientIdAttributeHandler";
|
||||
import MessageRenderer from "./modules/Renderer/MessageRenderer";
|
||||
import Spinner from "./modules/Helper/Spinner";
|
||||
import {
|
||||
getCurrentPaymentMethod,
|
||||
ORDER_BUTTON_SELECTOR,
|
||||
PaymentMethods
|
||||
} from "./modules/Helper/CheckoutMethodState";
|
||||
import {hide, setVisible} from "./modules/Helper/Hiding";
|
||||
import {isChangePaymentPage} from "./modules/Helper/Subscriptions";
|
||||
import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler";
|
||||
|
||||
const buttonsSpinner = new Spinner('.ppc-button-wrapper');
|
||||
const cardsSpinner = new Spinner('#ppcp-hosted-fields');
|
||||
|
||||
const bootstrap = () => {
|
||||
const errorHandler = new ErrorHandler(PayPalCommerceGateway.labels.error.generic);
|
||||
const spinner = new Spinner();
|
||||
const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway);
|
||||
|
||||
const freeTrialHandler = new FreeTrialHandler(PayPalCommerceGateway, spinner, errorHandler);
|
||||
|
||||
const onSmartButtonClick = (data, actions) => {
|
||||
window.ppcpFundingSource = data.fundingSource;
|
||||
|
||||
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
||||
if (isFreeTrial) {
|
||||
freeTrialHandler.handle();
|
||||
return actions.reject();
|
||||
}
|
||||
};
|
||||
const onSmartButtonsInit = () => {
|
||||
buttonsSpinner.unblock();
|
||||
};
|
||||
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
|
||||
const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
|
||||
const context = PayPalCommerceGateway.context;
|
||||
if (context === 'mini-cart' || context === 'product') {
|
||||
|
@ -79,9 +105,69 @@ 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;
|
||||
}
|
||||
|
||||
// Sometimes PayPal script takes long time to load,
|
||||
// so we additionally hide the standard order button here to avoid failed orders.
|
||||
// Normally it is hidden later after the script load.
|
||||
const hideOrderButtonIfPpcpGateway = () => {
|
||||
// only in checkout and pay now page, otherwise it may break things (e.g. payment via product page),
|
||||
// and also the loading spinner may look weird on other pages
|
||||
if (
|
||||
!['checkout', 'pay-now'].includes(PayPalCommerceGateway.context)
|
||||
|| isChangePaymentPage()
|
||||
|| (PayPalCommerceGateway.is_free_trial_cart && PayPalCommerceGateway.vaulted_paypal_email !== '')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPaymentMethod = getCurrentPaymentMethod();
|
||||
const isPaypal = currentPaymentMethod === PaymentMethods.PAYPAL;
|
||||
const isCards = currentPaymentMethod === PaymentMethods.CARDS;
|
||||
|
||||
setVisible(ORDER_BUTTON_SELECTOR, !isPaypal && !isCards, true);
|
||||
|
||||
if (isPaypal) {
|
||||
// stopped after the first rendering of the buttons, in onInit
|
||||
buttonsSpinner.block();
|
||||
} else {
|
||||
buttonsSpinner.unblock();
|
||||
}
|
||||
|
||||
if (isCards) {
|
||||
cardsSpinner.block();
|
||||
} else {
|
||||
cardsSpinner.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(document).on('hosted_fields_loaded', () => {
|
||||
cardsSpinner.unblock();
|
||||
});
|
||||
|
||||
let bootstrapped = false;
|
||||
|
||||
hideOrderButtonIfPpcpGateway();
|
||||
|
||||
jQuery(document.body).on('updated_checkout payment_method_selected', () => {
|
||||
if (bootstrapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideOrderButtonIfPpcpGateway();
|
||||
});
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.addEventListener('load', (event) => {
|
||||
bootstrapped = true;
|
||||
|
||||
bootstrap();
|
||||
});
|
||||
script.setAttribute('src', PayPalCommerceGateway.button.url);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import onApprove from '../OnApproveHandler/onApproveForContinue.js';
|
||||
import {payerData} from "../Helper/PayerData";
|
||||
import {PaymentMethods} from "../Helper/CheckoutMethodState";
|
||||
|
||||
class CartActionHandler {
|
||||
|
||||
|
@ -18,6 +19,7 @@ class CartActionHandler {
|
|||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_order.nonce,
|
||||
purchase_units: [],
|
||||
payment_method: PaymentMethods.PAYPAL,
|
||||
bn_code:bnCode,
|
||||
payer,
|
||||
context:this.config.context
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import onApprove from '../OnApproveHandler/onApproveForPayNow.js';
|
||||
import {payerData} from "../Helper/PayerData";
|
||||
import {getCurrentPaymentMethod} from "../Helper/CheckoutMethodState";
|
||||
|
||||
class CheckoutActionHandler {
|
||||
|
||||
|
@ -31,6 +32,7 @@ class CheckoutActionHandler {
|
|||
bn_code:bnCode,
|
||||
context:this.config.context,
|
||||
order_id:this.config.order_id,
|
||||
payment_method: getCurrentPaymentMethod(),
|
||||
form:formValues,
|
||||
createaccount: createaccount
|
||||
})
|
||||
|
@ -48,7 +50,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;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import {PaymentMethods} from "../Helper/CheckoutMethodState";
|
||||
import errorHandler from "../ErrorHandler";
|
||||
|
||||
class FreeTrialHandler {
|
||||
constructor(
|
||||
config,
|
||||
spinner,
|
||||
errorHandler
|
||||
) {
|
||||
this.config = config;
|
||||
this.spinner = spinner;
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
handle()
|
||||
{
|
||||
this.spinner.block();
|
||||
|
||||
fetch(this.config.ajax.vault_paypal.endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.vault_paypal.nonce,
|
||||
return_url: location.href
|
||||
}),
|
||||
}).then(res => {
|
||||
return res.json();
|
||||
}).then(data => {
|
||||
if (!data.success) {
|
||||
this.spinner.unblock();
|
||||
console.error(data);
|
||||
this.errorHandler.message(data.data.message);
|
||||
throw Error(data.data.message);
|
||||
}
|
||||
|
||||
location.href = data.data.approve_link;
|
||||
}).catch(error => {
|
||||
this.spinner.unblock();
|
||||
console.error(error);
|
||||
this.errorHandler.genericError();
|
||||
});
|
||||
}
|
||||
}
|
||||
export default FreeTrialHandler;
|
|
@ -2,6 +2,7 @@ import ButtonsToggleListener from '../Helper/ButtonsToggleListener';
|
|||
import Product from '../Entity/Product';
|
||||
import onApprove from '../OnApproveHandler/onApproveForContinue';
|
||||
import {payerData} from "../Helper/PayerData";
|
||||
import {PaymentMethods} from "../Helper/CheckoutMethodState";
|
||||
|
||||
class SingleProductActionHandler {
|
||||
|
||||
|
@ -84,6 +85,7 @@ class SingleProductActionHandler {
|
|||
purchase_units,
|
||||
payer,
|
||||
bn_code:bnCode,
|
||||
payment_method: PaymentMethods.PAYPAL,
|
||||
context:this.config.context
|
||||
})
|
||||
}).then(function (res) {
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import ErrorHandler from '../ErrorHandler';
|
||||
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
|
||||
import { setVisible } from '../Helper/Hiding';
|
||||
import {
|
||||
getCurrentPaymentMethod,
|
||||
isSavedCardSelected, ORDER_BUTTON_SELECTOR,
|
||||
PaymentMethods
|
||||
} from "../Helper/CheckoutMethodState";
|
||||
|
||||
class CheckoutBootstap {
|
||||
constructor(gateway, renderer, messages, spinner) {
|
||||
|
@ -7,31 +13,38 @@ class CheckoutBootstap {
|
|||
this.renderer = renderer;
|
||||
this.messages = messages;
|
||||
this.spinner = spinner;
|
||||
|
||||
this.standardOrderButtonSelector = ORDER_BUTTON_SELECTOR;
|
||||
|
||||
this.buttonChangeObserver = new MutationObserver((el) => {
|
||||
this.updateUi();
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
this.render();
|
||||
|
||||
// Unselect saved card.
|
||||
// WC saves form values, so with our current UI it would be a bit weird
|
||||
// if the user paid with saved, then after some time tries to pay again,
|
||||
// but wants to enter a new card, and to do that they have to choose “Select payment” in the list.
|
||||
jQuery('#saved-credit-card').val(jQuery('#saved-credit-card option:first').val());
|
||||
|
||||
jQuery(document.body).on('updated_checkout', () => {
|
||||
this.render()
|
||||
});
|
||||
|
||||
jQuery(document.body).
|
||||
on('updated_checkout payment_method_selected', () => {
|
||||
this.switchBetweenPayPalandOrderButton()
|
||||
this.displayPlaceOrderButtonForSavedCreditCards()
|
||||
|
||||
})
|
||||
jQuery(document.body).on('updated_checkout payment_method_selected', () => {
|
||||
this.updateUi();
|
||||
});
|
||||
|
||||
jQuery(document).on('hosted_fields_loaded', () => {
|
||||
jQuery('#saved-credit-card').on('change', () => {
|
||||
this.displayPlaceOrderButtonForSavedCreditCards()
|
||||
this.updateUi();
|
||||
})
|
||||
});
|
||||
|
||||
this.switchBetweenPayPalandOrderButton()
|
||||
this.displayPlaceOrderButtonForSavedCreditCards()
|
||||
this.updateUi();
|
||||
}
|
||||
|
||||
shouldRender() {
|
||||
|
@ -60,55 +73,38 @@ class CheckoutBootstap {
|
|||
this.gateway.hosted_fields.wrapper,
|
||||
actionHandler.configuration(),
|
||||
);
|
||||
|
||||
this.buttonChangeObserver.observe(
|
||||
document.querySelector(this.standardOrderButtonSelector),
|
||||
{attributes: true}
|
||||
);
|
||||
}
|
||||
|
||||
switchBetweenPayPalandOrderButton() {
|
||||
jQuery('#saved-credit-card').val(jQuery('#saved-credit-card option:first').val());
|
||||
updateUi() {
|
||||
const currentPaymentMethod = getCurrentPaymentMethod();
|
||||
const isPaypal = currentPaymentMethod === PaymentMethods.PAYPAL;
|
||||
const isCard = currentPaymentMethod === PaymentMethods.CARDS;
|
||||
const isSavedCard = isCard && isSavedCardSelected();
|
||||
const isNotOurGateway = !isPaypal && !isCard;
|
||||
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
||||
const hasVaultedPaypal = PayPalCommerceGateway.vaulted_paypal_email !== '';
|
||||
|
||||
const currentPaymentMethod = jQuery(
|
||||
'input[name="payment_method"]:checked').val();
|
||||
setVisible(this.standardOrderButtonSelector, (isPaypal && isFreeTrial && hasVaultedPaypal) || isNotOurGateway || isSavedCard, true);
|
||||
setVisible('.ppcp-vaulted-paypal-details', isPaypal);
|
||||
setVisible(this.gateway.button.wrapper, isPaypal && !(isFreeTrial && hasVaultedPaypal));
|
||||
setVisible(this.gateway.messages.wrapper, isPaypal && !isFreeTrial);
|
||||
setVisible(this.gateway.hosted_fields.wrapper, isCard && !isSavedCard);
|
||||
|
||||
if (currentPaymentMethod !== 'ppcp-gateway' && currentPaymentMethod !== 'ppcp-credit-card-gateway') {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
jQuery('#place_order').show();
|
||||
if (isPaypal && !isFreeTrial) {
|
||||
this.messages.render();
|
||||
}
|
||||
else {
|
||||
jQuery('#place_order').hide();
|
||||
if (currentPaymentMethod === 'ppcp-gateway') {
|
||||
this.renderer.showButtons(this.gateway.button.wrapper);
|
||||
this.renderer.showButtons(this.gateway.messages.wrapper);
|
||||
this.messages.render()
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper)
|
||||
|
||||
if (isCard) {
|
||||
if (isSavedCard) {
|
||||
this.disableCreditCardFields();
|
||||
} else {
|
||||
this.enableCreditCardFields();
|
||||
}
|
||||
if (currentPaymentMethod === 'ppcp-credit-card-gateway') {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper)
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper)
|
||||
this.renderer.showButtons(this.gateway.hosted_fields.wrapper)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
displayPlaceOrderButtonForSavedCreditCards() {
|
||||
const currentPaymentMethod = jQuery(
|
||||
'input[name="payment_method"]:checked').val();
|
||||
if (currentPaymentMethod !== 'ppcp-credit-card-gateway') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jQuery('#saved-credit-card').length && jQuery('#saved-credit-card').val() !== '') {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper)
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper)
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper)
|
||||
jQuery('#place_order').show()
|
||||
this.disableCreditCardFields()
|
||||
} else {
|
||||
jQuery('#place_order').hide()
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper)
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper)
|
||||
this.renderer.showButtons(this.gateway.hosted_fields.wrapper)
|
||||
this.enableCreditCardFields()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,8 @@ class MiniCartBootstap {
|
|||
}
|
||||
|
||||
shouldRender() {
|
||||
return document.querySelector(this.gateway.button.mini_cart_wrapper) !==
|
||||
null || document.querySelector(this.gateway.hosted_fields.mini_cart_wrapper) !==
|
||||
null;
|
||||
return document.querySelector(this.gateway.button.mini_cart_wrapper) !== null
|
||||
|| document.querySelector(this.gateway.hosted_fields.mini_cart_wrapper) !== null;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -40,4 +39,4 @@ class MiniCartBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default MiniCartBootstap;
|
||||
export default MiniCartBootstap;
|
||||
|
|
|
@ -1,86 +1,17 @@
|
|||
import ErrorHandler from '../ErrorHandler';
|
||||
import CheckoutActionHandler from '../ActionHandler/CheckoutActionHandler';
|
||||
import CheckoutBootstap from './CheckoutBootstap'
|
||||
import {isChangePaymentPage} from "../Helper/Subscriptions";
|
||||
|
||||
class PayNowBootstrap {
|
||||
class PayNowBootstrap extends CheckoutBootstap {
|
||||
constructor(gateway, renderer, messages, spinner) {
|
||||
this.gateway = gateway;
|
||||
this.renderer = renderer;
|
||||
this.messages = messages;
|
||||
this.spinner = spinner;
|
||||
super(gateway, renderer, messages, spinner)
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
this.render();
|
||||
|
||||
jQuery(document.body).on('updated_checkout', () => {
|
||||
this.render();
|
||||
});
|
||||
|
||||
jQuery(document.body).
|
||||
on('updated_checkout payment_method_selected', () => {
|
||||
this.switchBetweenPayPalandOrderButton();
|
||||
});
|
||||
this.switchBetweenPayPalandOrderButton();
|
||||
}
|
||||
|
||||
shouldRender() {
|
||||
if (document.querySelector(this.gateway.button.cancel_wrapper)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return document.querySelector(this.gateway.button.wrapper) !== null || document.querySelector(this.gateway.hosted_fields.wrapper) !== null;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.shouldRender()) {
|
||||
return;
|
||||
}
|
||||
if (document.querySelector(this.gateway.hosted_fields.wrapper + '>div')) {
|
||||
document.querySelector(this.gateway.hosted_fields.wrapper + '>div').setAttribute('style', '');
|
||||
}
|
||||
const actionHandler = new CheckoutActionHandler(
|
||||
PayPalCommerceGateway,
|
||||
new ErrorHandler(this.gateway.labels.error.generic),
|
||||
this.spinner
|
||||
);
|
||||
|
||||
this.renderer.render(
|
||||
this.gateway.button.wrapper,
|
||||
this.gateway.hosted_fields.wrapper,
|
||||
actionHandler.configuration(),
|
||||
);
|
||||
}
|
||||
|
||||
switchBetweenPayPalandOrderButton() {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.has('change_payment_method')) {
|
||||
updateUi() {
|
||||
if (isChangePaymentPage()) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentPaymentMethod = jQuery(
|
||||
'input[name="payment_method"]:checked').val();
|
||||
|
||||
if (currentPaymentMethod !== 'ppcp-gateway' && currentPaymentMethod !== 'ppcp-credit-card-gateway') {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
jQuery('#place_order').show();
|
||||
}
|
||||
else {
|
||||
jQuery('#place_order').hide();
|
||||
if (currentPaymentMethod === 'ppcp-gateway') {
|
||||
this.renderer.showButtons(this.gateway.button.wrapper);
|
||||
this.renderer.showButtons(this.gateway.messages.wrapper);
|
||||
this.messages.render();
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
}
|
||||
if (currentPaymentMethod === 'ppcp-credit-card-gateway') {
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.messages.wrapper);
|
||||
this.renderer.showButtons(this.gateway.hosted_fields.wrapper);
|
||||
}
|
||||
}
|
||||
super.updateUi();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,21 +9,51 @@ class SingleProductBootstap {
|
|||
this.messages = messages;
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
handleChange() {
|
||||
if (!this.shouldRender()) {
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
this.renderer.hideButtons(this.gateway.button.wrapper);
|
||||
return;
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
shouldRender() {
|
||||
if (document.querySelector('form.cart') === null) {
|
||||
return false;
|
||||
init() {
|
||||
|
||||
document.querySelector('form.cart').addEventListener('change', this.handleChange.bind(this))
|
||||
|
||||
if (!this.shouldRender()) {
|
||||
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
this.render();
|
||||
|
||||
}
|
||||
|
||||
shouldRender() {
|
||||
|
||||
return document.querySelector('form.cart') !== null && !this.priceAmountIsZero();
|
||||
|
||||
}
|
||||
|
||||
priceAmountIsZero() {
|
||||
|
||||
let priceText = "0";
|
||||
if (document.querySelector('form.cart ins .woocommerce-Price-amount')) {
|
||||
priceText = document.querySelector('form.cart ins .woocommerce-Price-amount').innerText;
|
||||
}
|
||||
else if (document.querySelector('form.cart .woocommerce-Price-amount')) {
|
||||
priceText = document.querySelector('form.cart .woocommerce-Price-amount').innerText;
|
||||
}
|
||||
else if (document.querySelector('.product .woocommerce-Price-amount')) {
|
||||
priceText = document.querySelector('.product .woocommerce-Price-amount').innerText;
|
||||
}
|
||||
const amount = parseFloat(priceText.replace(/([^\d,\.\s]*)/g, ''));
|
||||
return amount === 0;
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -62,4 +92,4 @@ class SingleProductBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default SingleProductBootstap;
|
||||
export default SingleProductBootstap;
|
||||
|
|
|
@ -73,11 +73,11 @@ class ErrorHandler {
|
|||
|
||||
clear()
|
||||
{
|
||||
if (! this.wrapper.classList.contains('woocommerce-error')) {
|
||||
if (this.messagesList === null) {
|
||||
return;
|
||||
}
|
||||
this.wrapper.classList.remove('woocommerce-error');
|
||||
this.wrapper.innerText = '';
|
||||
|
||||
this.messagesList.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
export const PaymentMethods = {
|
||||
PAYPAL: 'ppcp-gateway',
|
||||
CARDS: 'ppcp-credit-card-gateway',
|
||||
};
|
||||
|
||||
export const ORDER_BUTTON_SELECTOR = '#place_order';
|
||||
|
||||
export const getCurrentPaymentMethod = () => {
|
||||
const el = document.querySelector('input[name="payment_method"]:checked');
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return el.value;
|
||||
};
|
||||
|
||||
export const isSavedCardSelected = () => {
|
||||
const savedCardList = document.querySelector('#saved-credit-card');
|
||||
return savedCardList && savedCardList.value !== '';
|
||||
};
|
44
modules/ppcp-button/resources/js/modules/Helper/Hiding.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
const getElement = (selectorOrElement) => {
|
||||
if (typeof selectorOrElement === 'string') {
|
||||
return document.querySelector(selectorOrElement);
|
||||
}
|
||||
return selectorOrElement;
|
||||
}
|
||||
|
||||
export const isVisible = (element) => {
|
||||
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
||||
}
|
||||
|
||||
export const setVisible = (selectorOrElement, show, important = false) => {
|
||||
const element = getElement(selectorOrElement);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentValue = element.style.getPropertyValue('display');
|
||||
|
||||
if (!show) {
|
||||
if (currentValue === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
element.style.setProperty('display', 'none', important ? 'important' : '');
|
||||
} else {
|
||||
if (currentValue === 'none') {
|
||||
element.style.removeProperty('display');
|
||||
}
|
||||
|
||||
// still not visible (if something else added display: none in CSS)
|
||||
if (!isVisible(element)) {
|
||||
element.style.setProperty('display', 'block');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const hide = (selectorOrElement, important = false) => {
|
||||
setVisible(selectorOrElement, false, important);
|
||||
};
|
||||
|
||||
export const show = (selectorOrElement) => {
|
||||
setVisible(selectorOrElement, true);
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
class Spinner {
|
||||
|
||||
constructor() {
|
||||
this.target = 'form.woocommerce-checkout';
|
||||
constructor(target = 'form.woocommerce-checkout') {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
setTarget(target) {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export const isChangePaymentPage = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
return urlParams.has('change_payment_method');
|
||||
}
|
|
@ -4,7 +4,8 @@ const onApprove = (context, errorHandler) => {
|
|||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
nonce: context.config.ajax.approve_order.nonce,
|
||||
order_id:data.orderID
|
||||
order_id:data.orderID,
|
||||
funding_source: window.ppcpFundingSource,
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
|
@ -13,7 +14,7 @@ const onApprove = (context, errorHandler) => {
|
|||
errorHandler.genericError();
|
||||
return actions.restart().catch(err => {
|
||||
errorHandler.genericError();
|
||||
});;
|
||||
});
|
||||
}
|
||||
location.href = context.config.redirect;
|
||||
});
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
const onApprove = (context, errorHandler, spinner) => {
|
||||
return (data, actions) => {
|
||||
spinner.block();
|
||||
errorHandler.clear();
|
||||
|
||||
return fetch(context.config.ajax.approve_order.endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
nonce: context.config.ajax.approve_order.nonce,
|
||||
order_id:data.orderID
|
||||
order_id:data.orderID,
|
||||
funding_source: window.ppcpFundingSource,
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import dccInputFactory from "../Helper/DccInputFactory";
|
||||
import {show} from "../Helper/Hiding";
|
||||
|
||||
class CreditCardRenderer {
|
||||
|
||||
|
@ -9,7 +10,6 @@ class CreditCardRenderer {
|
|||
this.cardValid = false;
|
||||
this.formValid = false;
|
||||
this.currentHostedFieldsInstance = null;
|
||||
this.formSubmissionSubscribed = false;
|
||||
}
|
||||
|
||||
render(wrapper, contextConfig) {
|
||||
|
@ -33,6 +33,8 @@ class CreditCardRenderer {
|
|||
return;
|
||||
}
|
||||
|
||||
const buttonSelector = wrapper + ' button';
|
||||
|
||||
if (this.currentHostedFieldsInstance) {
|
||||
this.currentHostedFieldsInstance.teardown()
|
||||
.catch(err => console.error(`Hosted fields teardown error: ${err}`));
|
||||
|
@ -122,15 +124,18 @@ class CreditCardRenderer {
|
|||
|
||||
});
|
||||
|
||||
if (!this.formSubmissionSubscribed) {
|
||||
document.querySelector(wrapper + ' button').addEventListener(
|
||||
show(buttonSelector);
|
||||
|
||||
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
||||
document.querySelector(buttonSelector).addEventListener(
|
||||
'click',
|
||||
event => {
|
||||
event.preventDefault();
|
||||
this._submit(contextConfig);
|
||||
}
|
||||
);
|
||||
this.formSubmissionSubscribed = true;
|
||||
|
||||
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -143,7 +148,7 @@ class CreditCardRenderer {
|
|||
}
|
||||
|
||||
disableFields() {
|
||||
if( this.currentHostedFieldsInstance) {
|
||||
if (this.currentHostedFieldsInstance) {
|
||||
this.currentHostedFieldsInstance.setAttribute({
|
||||
field: 'number',
|
||||
attribute: 'disabled'
|
||||
|
@ -160,7 +165,7 @@ class CreditCardRenderer {
|
|||
}
|
||||
|
||||
enableFields() {
|
||||
if( this.currentHostedFieldsInstance) {
|
||||
if (this.currentHostedFieldsInstance) {
|
||||
this.currentHostedFieldsInstance.removeAttribute({
|
||||
field: 'number',
|
||||
attribute: 'disabled'
|
||||
|
@ -181,7 +186,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) {
|
||||
|
@ -194,13 +199,34 @@ class CreditCardRenderer {
|
|||
if (contingency !== 'NO_3D_SECURE') {
|
||||
hostedFieldsData.contingencies = [contingency];
|
||||
}
|
||||
|
||||
if (this.defaultConfig.payer) {
|
||||
hostedFieldsData.cardholderName = this.defaultConfig.payer.name.given_name + ' ' + this.defaultConfig.payer.name.surname;
|
||||
}
|
||||
if (!hostedFieldsData.cardholderName) {
|
||||
const firstName = document.getElementById('billing_first_name') ? document.getElementById('billing_first_name').value : '';
|
||||
const lastName = document.getElementById('billing_last_name') ? document.getElementById('billing_last_name').value : '';
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
this.spinner.unblock();
|
||||
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.cardholder_name_required);
|
||||
return;
|
||||
}
|
||||
|
||||
hostedFieldsData.cardholderName = firstName + ' ' + lastName;
|
||||
}
|
||||
|
||||
this.currentHostedFieldsInstance.submit(hostedFieldsData).then((payload) => {
|
||||
payload.orderID = payload.orderId;
|
||||
this.spinner.unblock();
|
||||
return contextConfig.onApprove(payload);
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.spinner.unblock();
|
||||
this.errorHandler.clear();
|
||||
|
||||
if (err.details) {
|
||||
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.spinner.unblock();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
class Renderer {
|
||||
constructor(creditCardRenderer, defaultConfig) {
|
||||
constructor(creditCardRenderer, defaultConfig, onSmartButtonClick, onSmartButtonsInit) {
|
||||
this.defaultConfig = defaultConfig;
|
||||
this.creditCardRenderer = creditCardRenderer;
|
||||
this.onSmartButtonClick = onSmartButtonClick;
|
||||
this.onSmartButtonsInit = onSmartButtonsInit;
|
||||
}
|
||||
|
||||
render(wrapper, hostedFieldsWrapper, contextConfig) {
|
||||
|
@ -19,6 +21,8 @@ class Renderer {
|
|||
paypal.Buttons({
|
||||
style,
|
||||
...contextConfig,
|
||||
onClick: this.onSmartButtonClick,
|
||||
onInit: this.onSmartButtonsInit,
|
||||
}).render(wrapper);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
|
@ -52,7 +53,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' );
|
||||
|
@ -70,8 +71,10 @@ return array(
|
|||
$environment = $container->get( 'onboarding.environment' );
|
||||
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
|
||||
$settings_status = $container->get( 'wcgateway.settings.status' );
|
||||
$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,
|
||||
|
@ -82,13 +85,16 @@ return array(
|
|||
$messages_apply,
|
||||
$environment,
|
||||
$payment_token_repository,
|
||||
$settings_status
|
||||
$settings_status,
|
||||
$currency,
|
||||
$container->get( 'wcgateway.all-funding-sources' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'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 {
|
||||
|
@ -166,12 +172,21 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'button.endpoint.vault-paypal' => static function( ContainerInterface $container ) : StartPayPalVaultingEndpoint {
|
||||
return new StartPayPalVaultingEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.payment-token' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new ThreeDSecure( $logger );
|
||||
},
|
||||
'button.helper.messages-apply' => static function ( ContainerInterface $container ): MessagesApply {
|
||||
return new MessagesApply();
|
||||
return new MessagesApply(
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
|
||||
'button.is-logged-in' => static function ( ContainerInterface $container ): bool {
|
||||
|
|
|
@ -9,6 +9,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Button\Assets;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
|
||||
|
@ -16,9 +19,11 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
@ -30,6 +35,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
*/
|
||||
class SmartButton implements SmartButtonInterface {
|
||||
|
||||
use FreeTrialHandlerTrait;
|
||||
|
||||
/**
|
||||
* The Settings status helper.
|
||||
*
|
||||
|
@ -44,6 +51,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The Session Handler.
|
||||
*
|
||||
|
@ -114,10 +128,39 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $payment_token_repository;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* All existing funding sources.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $all_funding_sources;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Cached payment tokens.
|
||||
*
|
||||
* @var PaymentToken[]|null
|
||||
*/
|
||||
private $payment_tokens = null;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -129,9 +172,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param Environment $environment The environment object.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param array $all_funding_sources All existing funding sources.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
SessionHandler $session_handler,
|
||||
Settings $settings,
|
||||
PayerFactory $payer_factory,
|
||||
|
@ -142,10 +189,14 @@ class SmartButton implements SmartButtonInterface {
|
|||
MessagesApply $messages_apply,
|
||||
Environment $environment,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
SettingsStatus $settings_status
|
||||
SettingsStatus $settings_status,
|
||||
string $currency,
|
||||
array $all_funding_sources,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->payer_factory = $payer_factory;
|
||||
|
@ -157,6 +208,9 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->environment = $environment;
|
||||
$this->payment_token_repository = $payment_token_repository;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->all_funding_sources = $all_funding_sources;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,14 +253,19 @@ class SmartButton implements SmartButtonInterface {
|
|||
11
|
||||
);
|
||||
|
||||
$subscription_helper = $this->subscription_helper;
|
||||
add_filter(
|
||||
'woocommerce_credit_card_form_fields',
|
||||
function ( $default_fields, $id ) {
|
||||
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 ) {
|
||||
|
||||
$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>',
|
||||
'<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() );
|
||||
if ( $tokens && $this->payment_token_repository->tokens_contains_card( $tokens ) ) {
|
||||
|
@ -237,6 +296,38 @@ class SmartButton implements SmartButtonInterface {
|
|||
2
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->is_free_trial_cart() ) {
|
||||
add_action(
|
||||
'woocommerce_review_order_after_submit',
|
||||
function () {
|
||||
$vaulted_email = $this->get_vaulted_paypal_email();
|
||||
if ( ! $vaulted_email ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="ppcp-vaulted-paypal-details">
|
||||
<?php
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s - email, %2$s, %3$s - HTML tags for a link.
|
||||
esc_html__(
|
||||
'Using %2$s%1$s%3$s PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
$vaulted_email,
|
||||
'<b>',
|
||||
'</b>'
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -311,27 +402,14 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private function render_button_wrapper_registrar(): bool {
|
||||
|
||||
$not_enabled_on_cart = $this->settings->has( 'button_cart_enabled' ) &&
|
||||
! $this->settings->get( 'button_cart_enabled' );
|
||||
if (
|
||||
is_cart()
|
||||
&& ! $not_enabled_on_cart
|
||||
) {
|
||||
add_action(
|
||||
$this->proceed_to_checkout_button_renderer_hook(),
|
||||
array(
|
||||
$this,
|
||||
'button_renderer',
|
||||
),
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
$not_enabled_on_product_page = $this->settings->has( 'button_single_product_enabled' ) &&
|
||||
! $this->settings->get( 'button_single_product_enabled' );
|
||||
if (
|
||||
( is_product() || wc_post_content_has_shortcode( 'product_page' ) )
|
||||
&& ! $not_enabled_on_product_page
|
||||
// TODO: it seems like there is no easy way to properly handle vaulted PayPal free trial,
|
||||
// so disable the buttons for now everywhere except checkout for free trial.
|
||||
&& ! $this->is_free_trial_product()
|
||||
) {
|
||||
add_action(
|
||||
$this->single_product_renderer_hook(),
|
||||
|
@ -343,14 +421,21 @@ class SmartButton implements SmartButtonInterface {
|
|||
);
|
||||
}
|
||||
|
||||
add_action( $this->pay_order_renderer_hook(), array( $this, 'button_renderer' ), 10 );
|
||||
|
||||
$not_enabled_on_minicart = $this->settings->has( 'button_mini_cart_enabled' ) &&
|
||||
! $this->settings->get( 'button_mini_cart_enabled' );
|
||||
if (
|
||||
! $not_enabled_on_minicart
|
||||
! $not_enabled_on_minicart
|
||||
&& ! $this->is_free_trial_cart()
|
||||
) {
|
||||
add_action(
|
||||
$this->mini_cart_button_renderer_hook(),
|
||||
static function () {
|
||||
function () {
|
||||
if ( $this->is_cart_price_total_zero() || $this->is_free_trial_cart() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<p
|
||||
id="ppc-button-minicart"
|
||||
class="woocommerce-mini-cart__buttons buttons"
|
||||
|
@ -360,8 +445,28 @@ class SmartButton implements SmartButtonInterface {
|
|||
);
|
||||
}
|
||||
|
||||
if ( $this->is_cart_price_total_zero() && ! $this->is_free_trial_cart() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$not_enabled_on_cart = $this->settings->has( 'button_cart_enabled' ) &&
|
||||
! $this->settings->get( 'button_cart_enabled' );
|
||||
if (
|
||||
is_cart()
|
||||
&& ! $not_enabled_on_cart
|
||||
&& ! $this->is_free_trial_cart()
|
||||
) {
|
||||
add_action(
|
||||
$this->proceed_to_checkout_button_renderer_hook(),
|
||||
array(
|
||||
$this,
|
||||
'button_renderer',
|
||||
),
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
add_action( $this->checkout_button_renderer_hook(), array( $this, 'button_renderer' ), 10 );
|
||||
add_action( $this->pay_order_renderer_hook(), array( $this, 'button_renderer' ), 10 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -377,9 +482,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' ) ) {
|
||||
|
@ -392,17 +494,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
|
||||
);
|
||||
|
||||
|
@ -427,10 +529,19 @@ class SmartButton implements SmartButtonInterface {
|
|||
|| ! $product->is_in_stock()
|
||||
)
|
||||
) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div id="ppc-button"></div>';
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
|
||||
if ( ! isset( $available_gateways['ppcp-gateway'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The wrapper is needed for the loading spinner,
|
||||
// otherwise jQuery block() prevents buttons rendering.
|
||||
echo '<div class="ppc-button-wrapper"><div id="ppc-button"></div></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -556,12 +667,15 @@ 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;">
|
||||
<button class="button alt">%2$s</button>
|
||||
</div><div id="payments-sdk__contingency-lightbox"></div><style id="ppcp-hide-dcc">.payment_method_ppcp-credit-card-gateway {display:none;}</style>',
|
||||
<button type="submit" class="button alt ppcp-dcc-order-button" style="display: none;">%2$s</button>
|
||||
</div>
|
||||
<div id="payments-sdk__contingency-lightbox"></div>
|
||||
<style id="ppcp-hide-dcc">.payment_method_ppcp-credit-card-gateway {display:none;}</style>',
|
||||
esc_attr( $id ),
|
||||
esc_html( $label )
|
||||
);
|
||||
|
@ -583,7 +697,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
return false;
|
||||
}
|
||||
|
||||
return is_user_logged_in();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -598,6 +712,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();
|
||||
}
|
||||
|
||||
|
@ -626,33 +744,42 @@ class SmartButton implements SmartButtonInterface {
|
|||
private function localize_script(): array {
|
||||
global $wp;
|
||||
|
||||
$is_free_trial_cart = $this->is_free_trial_cart();
|
||||
|
||||
$this->request_data->enqueue_nonce_fix();
|
||||
$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' => \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(),
|
||||
'ajax' => array(
|
||||
'change_cart' => array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ) ),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ),
|
||||
),
|
||||
'create_order' => array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ) ),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ),
|
||||
),
|
||||
'approve_order' => array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ) ),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ),
|
||||
),
|
||||
'vault_paypal' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
'enforce_vault' => $this->has_subscriptions(),
|
||||
'save_card' => $this->can_save_vault_token(),
|
||||
'can_save_vault_token' => $this->can_save_vault_token(),
|
||||
'is_free_trial_cart' => $is_free_trial_cart,
|
||||
'vaulted_paypal_email' => ( is_checkout() && $is_free_trial_cart ) ? $this->get_vaulted_paypal_email() : '',
|
||||
'bn_codes' => $this->bn_codes(),
|
||||
'payer' => $this->payerData(),
|
||||
'button' => array(
|
||||
|
@ -680,17 +807,18 @@ class SmartButton implements SmartButtonInterface {
|
|||
'wrapper' => '#ppcp-hosted-fields',
|
||||
'mini_cart_wrapper' => '#ppcp-hosted-fields-mini-cart',
|
||||
'labels' => array(
|
||||
'credit_card_number' => '',
|
||||
'cvv' => '',
|
||||
'mm_yy' => __( 'MM/YY', 'woocommerce-paypal-payments' ),
|
||||
'fields_not_valid' => __(
|
||||
'credit_card_number' => '',
|
||||
'cvv' => '',
|
||||
'mm_yy' => __( 'MM/YY', 'woocommerce-paypal-payments' ),
|
||||
'fields_not_valid' => __(
|
||||
'Unfortunately, your credit card details are not valid.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'card_not_supported' => __(
|
||||
'card_not_supported' => __(
|
||||
'Unfortunately, we do not support your credit card.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'cardholder_name_required' => __( 'Cardholder\'s first and last name are required, please fill the checkout form required fields.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'valid_cards' => $this->dcc_applies->valid_cards(),
|
||||
'contingency' => $this->get_3ds_contingency(),
|
||||
|
@ -745,7 +873,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
|
||||
$params = array(
|
||||
'client-id' => $this->client_id,
|
||||
'currency' => get_woocommerce_currency(),
|
||||
'currency' => $this->currency,
|
||||
'integration-date' => PAYPAL_INTEGRATION_DATE,
|
||||
'components' => implode( ',', $this->components() ),
|
||||
'vault' => $this->can_save_vault_token() ? 'true' : 'false',
|
||||
|
@ -777,6 +905,10 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $this->is_free_trial_cart() ) {
|
||||
$disable_funding = array_keys( $this->all_funding_sources );
|
||||
}
|
||||
|
||||
if ( count( $disable_funding ) > 0 ) {
|
||||
$params['disable-funding'] = implode( ',', array_unique( $disable_funding ) );
|
||||
}
|
||||
|
@ -785,6 +917,11 @@ class SmartButton implements SmartButtonInterface {
|
|||
if ( $this->settings_status->pay_later_messaging_is_enabled() || ! in_array( 'credit', $disable_funding, true ) ) {
|
||||
$enable_funding[] = 'paylater';
|
||||
}
|
||||
|
||||
if ( $this->is_free_trial_cart() ) {
|
||||
$enable_funding = array();
|
||||
}
|
||||
|
||||
if ( count( $enable_funding ) > 0 ) {
|
||||
$params['enable-funding'] = implode( ',', array_unique( $enable_funding ) );
|
||||
}
|
||||
|
@ -843,7 +980,10 @@ class SmartButton implements SmartButtonInterface {
|
|||
if ( $this->load_button_component() ) {
|
||||
$components[] = 'buttons';
|
||||
}
|
||||
if ( $this->messages_apply->for_country() ) {
|
||||
if (
|
||||
$this->messages_apply->for_country()
|
||||
&& ! $this->is_free_trial_cart()
|
||||
) {
|
||||
$components[] = 'messages';
|
||||
}
|
||||
if ( $this->dcc_is_enabled() ) {
|
||||
|
@ -993,38 +1133,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'
|
||||
|
@ -1032,11 +1184,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'
|
||||
|
@ -1044,11 +1199,57 @@ 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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if cart product price total is 0.
|
||||
*
|
||||
* @return bool true if is 0, otherwise false.
|
||||
*/
|
||||
protected function is_cart_price_total_zero(): bool {
|
||||
// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
||||
return WC()->cart->get_cart_contents_total() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all payment tokens for the user, via API or cached if already queried.
|
||||
*
|
||||
* @return PaymentToken[]
|
||||
*/
|
||||
private function get_payment_tokens(): array {
|
||||
if ( null === $this->payment_tokens ) {
|
||||
$this->payment_tokens = $this->payment_token_repository->all_for_user_id( get_current_user_id() );
|
||||
}
|
||||
|
||||
return $this->payment_tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vaulted PayPal email or empty string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_vaulted_paypal_email(): string {
|
||||
try {
|
||||
$tokens = $this->get_payment_tokens();
|
||||
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( isset( $token->source()->paypal ) ) {
|
||||
return $token->source()->paypal->payer->email_address;
|
||||
}
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Failed to get PayPal vaulted email. ' . $exception->getMessage() );
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\StartPayPalVaultingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use Interop\Container\ServiceProviderInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
@ -107,6 +108,15 @@ class ButtonModule implements ModuleInterface {
|
|||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
add_action(
|
||||
'wc_ajax_' . StartPayPalVaultingEndpoint::ENDPOINT,
|
||||
static function () use ( $container ) {
|
||||
$endpoint = $container->get( 'button.endpoint.vault-paypal' );
|
||||
assert( $endpoint instanceof StartPayPalVaultingEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . ChangeCartEndpoint::ENDPOINT,
|
||||
|
|
|
@ -184,6 +184,9 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
throw new RuntimeException( $message );
|
||||
}
|
||||
|
||||
$funding_source = $data['funding_source'] ?? null;
|
||||
$this->session_handler->replace_funding_source( $funding_source );
|
||||
|
||||
$this->session_handler->replace_order( $order );
|
||||
wp_send_json_success( $order );
|
||||
return true;
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
|||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentMethod;
|
||||
|
@ -23,7 +25,9 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
|
@ -31,6 +35,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
*/
|
||||
class CreateOrderEndpoint implements EndpointInterface {
|
||||
|
||||
use FreeTrialHandlerTrait;
|
||||
|
||||
const ENDPOINT = 'ppc-create-order';
|
||||
|
||||
/**
|
||||
|
@ -175,6 +181,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
$this->parsed_request_data = $data;
|
||||
$payment_method = $data['payment_method'] ?? '';
|
||||
$wc_order = null;
|
||||
if ( 'pay-now' === $data['context'] ) {
|
||||
$wc_order = wc_get_order( (int) $data['order_id'] );
|
||||
|
@ -191,6 +198,16 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$this->purchase_units = array( $this->purchase_unit_factory->from_wc_order( $wc_order ) );
|
||||
} else {
|
||||
$this->purchase_units = $this->cart_repository->all();
|
||||
|
||||
// The cart does not have any info about payment method, so we must handle free trial here.
|
||||
if ( CreditCardGateway::ID === $payment_method && $this->is_free_trial_cart() ) {
|
||||
$this->purchase_units[0]->set_amount(
|
||||
new Amount(
|
||||
new Money( 1.0, $this->purchase_units[0]->amount()->currency_code() ),
|
||||
$this->purchase_units[0]->amount()->breakdown()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->set_bn_code( $data );
|
||||
|
@ -337,6 +354,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(),
|
||||
|
|
111
modules/ppcp-button/src/Endpoint/StartPayPalVaultingEndpoint.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/**
|
||||
* The endpoint for starting vaulting of PayPal account (for free trial).
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Button\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokenEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
|
||||
/**
|
||||
* Class StartPayPalVaultingEndpoint.
|
||||
*/
|
||||
class StartPayPalVaultingEndpoint implements EndpointInterface {
|
||||
|
||||
|
||||
const ENDPOINT = 'ppc-vault-paypal';
|
||||
|
||||
/**
|
||||
* The Request Data Helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The PaymentTokenEndpoint.
|
||||
*
|
||||
* @var PaymentTokenEndpoint
|
||||
*/
|
||||
private $payment_token_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* StartPayPalVaultingEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The Request Data Helper.
|
||||
* @param PaymentTokenEndpoint $payment_token_endpoint The PaymentTokenEndpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
PaymentTokenEndpoint $payment_token_endpoint,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->payment_token_endpoint = $payment_token_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
$return_url = $data['return_url'];
|
||||
$cancel_url = add_query_arg( array( 'ppcp_vault' => 'cancel' ), $return_url );
|
||||
|
||||
$links = $this->payment_token_endpoint->start_paypal_token_creation(
|
||||
$user_id,
|
||||
$return_url,
|
||||
$cancel_url
|
||||
);
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'approve_link' => $links->approve_link(),
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch ( Exception $error ) {
|
||||
$this->logger->error( 'Failed to start PayPal vaulting: ' . $error->getMessage() );
|
||||
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'name' => is_a( $error, PayPalApiException::class ) ? $error->name() : '',
|
||||
'message' => $error->getMessage(),
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,16 +26,32 @@ class MessagesApply {
|
|||
'GB',
|
||||
'FR',
|
||||
'AU',
|
||||
'IT',
|
||||
'ES',
|
||||
);
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* MessagesApply constructor.
|
||||
*
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct( string $country ) {
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a credit messaging is enabled for the shops location country.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country(): bool {
|
||||
$region = wc_get_base_location();
|
||||
$country = $region['country'];
|
||||
return in_array( $country, $this->countries, true );
|
||||
return in_array( $this->country, $this->countries, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,31 +23,50 @@ class MessagesDisclaimers {
|
|||
*/
|
||||
private $disclaimers = array(
|
||||
'US' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/commerce-platforms/admin-panel/woocommerce/us/',
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/us/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'GB' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/commerce-platforms/admin-panel/woocommerce/uk/',
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/gb/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'DE' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/commerce-platforms/admin-panel/woocommerce/de/',
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/de/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'AU' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/commerce-platforms/admin-panel/woocommerce/au/',
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/au/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'FR' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/commerce-platforms/admin-panel/woocommerce/fr/',
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/fr/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'IT' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/it/commerce-platforms/woocommerce/',
|
||||
),
|
||||
'ES' => array(
|
||||
'link' => 'https://developer.paypal.com/docs/checkout/pay-later/es/commerce-platforms/woocommerce/',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* MessagesDisclaimers constructor.
|
||||
*
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct( string $country ) {
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a disclaimer link based on country.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function link_for_country(): string {
|
||||
$region = wc_get_base_location();
|
||||
$country = $region['country'];
|
||||
|
||||
return $this->disclaimers[ $country ]['link'] ?? '';
|
||||
return $this->disclaimers[ $this->country ]['link'] ?? '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1722,9 +1722,9 @@ mimic-fn@^2.1.0:
|
|||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
|
|
|
@ -29,6 +29,15 @@ class PPECHelper {
|
|||
const PPEC_SETTINGS_OPTION_NAME = 'woocommerce_ppec_paypal_settings';
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the PayPal Express Checkout plugin was configured previously.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_plugin_configured() {
|
||||
return is_array( get_option( self::PPEC_SETTINGS_OPTION_NAME ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the PayPal Express Checkout plugin is active.
|
||||
*
|
||||
|
@ -80,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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ class SettingsImporter {
|
|||
$value = array_values(
|
||||
array_intersect(
|
||||
array_map( 'strtolower', is_array( $option_value ) ? $option_value : array() ),
|
||||
array( 'card', 'credit', 'sepa', 'bancontact', 'eps', 'giropay', 'ideal', 'mybank', 'p24', 'sofort', 'venmo' )
|
||||
array( 'card', 'credit', 'sepa', 'bancontact', 'blik', 'eps', 'giropay', 'ideal', 'mercadopago', 'mybank', 'p24', 'sofort', 'venmo' )
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -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,42 +36,135 @@
|
|||
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;
|
||||
}
|
||||
|
||||
.woocommerce_page_wc-settings h3.ppcp-subheading {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.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';
|
||||
|
@ -171,6 +171,16 @@ document.addEventListener(
|
|||
return;
|
||||
}
|
||||
const allOptions = Array.from(document.querySelectorAll('select[name="ppcp[disable_cards][]"] option'));
|
||||
const iconVersions = {
|
||||
'visa': {
|
||||
'light': {'label': 'Visa (light)'},
|
||||
'dark' : {'label': 'Visa (dark)', 'value': 'visa-dark'}
|
||||
},
|
||||
'mastercard': {
|
||||
'light': {'label': 'Mastercard (light)'},
|
||||
'dark' : {'label': 'Mastercard (dark)', 'value': 'mastercard-dark'}
|
||||
}
|
||||
}
|
||||
const replace = () => {
|
||||
const validOptions = allOptions.filter(
|
||||
(option) => {
|
||||
|
@ -181,13 +191,35 @@ document.addEventListener(
|
|||
const selectedValidOptions = validOptions.map(
|
||||
(option) => {
|
||||
option = option.cloneNode(true);
|
||||
option.selected = target.querySelector('option[value="' + option.value + '"]') && target.querySelector('option[value="' + option.value + '"]').selected;
|
||||
let value = option.value;
|
||||
option.selected = target.querySelector('option[value="' + value + '"]') && target.querySelector('option[value="' + value + '"]').selected;
|
||||
if(value === 'visa' || value === 'mastercard') {
|
||||
let darkOption = option.cloneNode(true);
|
||||
let currentVersion = iconVersions[value];
|
||||
let darkValue = iconVersions[value].dark.value;
|
||||
|
||||
option.text = currentVersion.light.label;
|
||||
darkOption.text = currentVersion.dark.label;
|
||||
darkOption.value = darkValue;
|
||||
darkOption.selected = target.querySelector('option[value="' + darkValue + '"]') && target.querySelector('option[value="' + darkValue + '"]').selected;
|
||||
|
||||
return [option, darkOption];
|
||||
}
|
||||
return option;
|
||||
}
|
||||
);
|
||||
|
||||
target.innerHTML = '';
|
||||
selectedValidOptions.forEach(
|
||||
(option) => {
|
||||
if(Array.isArray(option)){
|
||||
option.forEach(
|
||||
(option) => {
|
||||
target.append(option);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
target.append(option);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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' => \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,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Onboarding;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
|
@ -138,13 +138,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 ),
|
||||
),
|
||||
);
|
||||
|
@ -207,7 +207,7 @@ class OnboardingRESTController {
|
|||
}
|
||||
|
||||
foreach ( WC()->payment_gateways->payment_gateways() as $gateway ) {
|
||||
if ( PayPalGateway::ID === $gateway->id ) {
|
||||
if ( PayPalGateway::ID === $gateway->id || CreditCardGateway::ID === $gateway->id ) {
|
||||
$gateway->update_option( 'enabled', 'yes' );
|
||||
break;
|
||||
}
|
||||
|
@ -265,34 +265,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,20 +92,26 @@ 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 ) {
|
||||
esc_html_e(
|
||||
'We could not properly connect to PayPal. Please reload the page to continue',
|
||||
'woocommerce-paypal-payments'
|
||||
echo esc_html(
|
||||
__(
|
||||
'We could not properly connect to PayPal. Try reloading the page.',
|
||||
'woocommerce-paypal-payments'
|
||||
) . " {$exception->getMessage()} {$exception->getFile()}:{$exception->getLine()}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -96,13 +122,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,10 @@ return array(
|
|||
return $session_handler;
|
||||
},
|
||||
'session.cancellation.view' => function ( ContainerInterface $container ) : CancelView {
|
||||
return new CancelView();
|
||||
return new CancelView(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.funding-source.renderer' )
|
||||
);
|
||||
},
|
||||
'session.cancellation.controller' => function ( ContainerInterface $container ) : CancelController {
|
||||
return new CancelController(
|
||||
|
|
|
@ -67,7 +67,7 @@ class CancelController {
|
|||
add_action(
|
||||
'woocommerce_review_order_after_submit',
|
||||
function () use ( $url ) {
|
||||
$this->view->render_session_cancellation( $url );
|
||||
$this->view->render_session_cancellation( $url, $this->session_handler->funding_source() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,31 +9,66 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Session\Cancellation;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
|
||||
/**
|
||||
* Class CancelView
|
||||
*/
|
||||
class CancelView {
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The funding source renderer.
|
||||
*
|
||||
* @var FundingSourceRenderer
|
||||
*/
|
||||
protected $funding_source_renderer;
|
||||
|
||||
/**
|
||||
* CancelView constructor.
|
||||
*
|
||||
* @param ContainerInterface $settings The settings.
|
||||
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
|
||||
*/
|
||||
public function __construct(
|
||||
ContainerInterface $settings,
|
||||
FundingSourceRenderer $funding_source_renderer
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->funding_source_renderer = $funding_source_renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the cancel link.
|
||||
*
|
||||
* @param string $url The URL.
|
||||
* @param string $url The URL.
|
||||
* @param string|null $funding_source The ID of the funding source, such as 'venmo'.
|
||||
*/
|
||||
public function render_session_cancellation( string $url ) {
|
||||
public function render_session_cancellation( string $url, ?string $funding_source ) {
|
||||
?>
|
||||
<p id="ppcp-cancel"
|
||||
class="has-text-align-center ppcp-cancel"
|
||||
>
|
||||
<?php
|
||||
$name = $funding_source ?
|
||||
$this->funding_source_renderer->render_name( $funding_source )
|
||||
: ( $this->settings->has( 'title' ) ? $this->settings->get( 'title' ) : __( 'PayPal', 'woocommerce-paypal-payments' ) );
|
||||
printf(
|
||||
// translators: the placeholders are html tags for a link.
|
||||
// translators: %3$ is funding source like "PayPal" or "Venmo", other placeholders are html tags for a link.
|
||||
esc_html__(
|
||||
'You are currently paying with PayPal. If you want to cancel
|
||||
'You are currently paying with %3$s. If you want to cancel
|
||||
this process, please click %1$shere%2$s.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a href="' . esc_url( $url ) . '">',
|
||||
'</a>'
|
||||
'</a>',
|
||||
esc_html( $name )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
|
|
@ -40,6 +40,13 @@ class SessionHandler {
|
|||
*/
|
||||
private $insufficient_funding_tries = 0;
|
||||
|
||||
/**
|
||||
* The funding source of the current checkout (venmo, ...) or null.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $funding_source = null;
|
||||
|
||||
/**
|
||||
* Returns the order.
|
||||
*
|
||||
|
@ -84,6 +91,28 @@ class SessionHandler {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the funding source of the current checkout (venmo, ...) or null.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function funding_source(): ?string {
|
||||
return $this->funding_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the funding source of the current checkout.
|
||||
*
|
||||
* @param string|null $funding_source The funding source.
|
||||
*
|
||||
* @return SessionHandler
|
||||
*/
|
||||
public function replace_funding_source( ?string $funding_source ): SessionHandler {
|
||||
$this->funding_source = $funding_source;
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many times the customer tried to use the PayPal Gateway in this session.
|
||||
*
|
||||
|
@ -113,6 +142,7 @@ class SessionHandler {
|
|||
$this->order = null;
|
||||
$this->bn_code = '';
|
||||
$this->insufficient_funding_tries = 0;
|
||||
$this->funding_source = null;
|
||||
$this->store_session();
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -37,9 +37,11 @@ class Renderer {
|
|||
foreach ( $items as $item ) {
|
||||
?>
|
||||
<tr>
|
||||
<td data-export-label="<?php echo esc_attr( $item['label'] ); ?>"><?php echo esc_attr( $item['label'] ); ?></td>
|
||||
<td data-export-label="<?php echo esc_attr( $item['exported_label'] ?? $item['label'] ); ?>">
|
||||
<?php echo esc_attr( $item['label'] ); ?>
|
||||
</td>
|
||||
<td class="help"><?php echo wc_help_tip( $item['description'] ); ?></td>
|
||||
<td><?php echo esc_attr( $item['value'] ); ?></td>
|
||||
<td><?php echo wp_kses_post( $item['value'] ); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@ use Dhii\Modular\Module\ModuleInterface;
|
|||
use Interop\Container\ServiceProviderInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Compat\PPEC\PPECHelper;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\WebhookInfoStorage;
|
||||
|
||||
/**
|
||||
* Class StatusReportModule
|
||||
|
@ -44,6 +46,8 @@ class StatusReportModule implements ModuleInterface {
|
|||
add_action(
|
||||
'woocommerce_system_status_report',
|
||||
function () use ( $c ) {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof ContainerInterface );
|
||||
|
||||
/* @var State $state The state. */
|
||||
$state = $c->get( 'onboarding.state' );
|
||||
|
@ -57,38 +61,93 @@ class StatusReportModule implements ModuleInterface {
|
|||
/* @var MessagesApply $messages_apply The messages apply. */
|
||||
$messages_apply = $c->get( 'button.helper.messages-apply' );
|
||||
|
||||
$last_webhook_storage = $c->get( 'webhook.last-webhook-storage' );
|
||||
assert( $last_webhook_storage instanceof WebhookInfoStorage );
|
||||
|
||||
$billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' );
|
||||
assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint );
|
||||
|
||||
/* @var Renderer $renderer The renderer. */
|
||||
$renderer = $c->get( 'status-report.renderer' );
|
||||
|
||||
$had_ppec_plugin = PPECHelper::is_plugin_configured();
|
||||
|
||||
$items = array(
|
||||
array(
|
||||
'label' => esc_html__( 'Onboarded', 'woocommerce-paypal-payments' ),
|
||||
'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->onboarded( $bearer, $state ),
|
||||
'label' => esc_html__( 'Onboarded', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Onboarded',
|
||||
'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$this->onboarded( $bearer, $state )
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Shop country code', 'woocommerce-paypal-payments' ),
|
||||
'description' => esc_html__( 'Country / State value on Settings / General / Store Address.', 'woocommerce-paypal-payments' ),
|
||||
'value' => wc_get_base_location()['country'],
|
||||
'label' => esc_html__( 'Shop country code', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Shop country code',
|
||||
'description' => esc_html__( 'Country / State value on Settings / General / Store Address.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $c->get( 'api.shop.country' ),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'PayPal card processing available in country', 'woocommerce-paypal-payments' ),
|
||||
'description' => esc_html__( 'Whether PayPal card processing is available in country or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $dcc_applies->for_country_currency()
|
||||
? esc_html__( 'Yes', 'woocommerce-paypal-payments' )
|
||||
: esc_html__( 'No', 'woocommerce-paypal-payments' ),
|
||||
'label' => esc_html__( 'WooCommerce currency supported', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'WooCommerce currency supported',
|
||||
'description' => esc_html__( 'Whether PayPal supports the default store currency or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$c->get( 'api.shop.is-currency-supported' )
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Pay Later messaging available in country', 'woocommerce-paypal-payments' ),
|
||||
'description' => esc_html__( 'Whether Pay Later is available in country or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $messages_apply->for_country()
|
||||
? esc_html__( 'Yes', 'woocommerce-paypal-payments' )
|
||||
: esc_html__( 'No', 'woocommerce-paypal-payments' ),
|
||||
'label' => esc_html__( 'PayPal card processing available in country', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'PayPal card processing available in country',
|
||||
'description' => esc_html__( 'Whether PayPal card processing is available in country or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$dcc_applies->for_country_currency()
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Vault enabled', 'woocommerce-paypal-payments' ),
|
||||
'description' => esc_html__( 'Whether vaulting is enabled on PayPal account or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->vault_enabled( $bearer ),
|
||||
'label' => esc_html__( 'Pay Later messaging available in country', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Pay Later messaging available in country',
|
||||
'description' => esc_html__( 'Whether Pay Later is available in country or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$messages_apply->for_country()
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Webhook status', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Webhook status',
|
||||
'description' => esc_html__( 'Whether we received webhooks successfully.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html( ! $last_webhook_storage->is_empty() ),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Vault enabled', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Vault enabled',
|
||||
'description' => esc_html__( 'Whether vaulting is enabled on PayPal account or not.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$this->vault_enabled( $bearer )
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Logging enabled', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Logging enabled',
|
||||
'description' => esc_html__( 'Whether logging of plugin events and errors is enabled.', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$settings->has( 'logging_enabled' ) && $settings->get( 'logging_enabled' )
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Reference Transactions', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Reference Transactions',
|
||||
'description' => esc_html__( 'Whether Reference Transactions are enabled for the connected account', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$this->reference_transaction_enabled( $billing_agreements_endpoint )
|
||||
),
|
||||
),
|
||||
array(
|
||||
'label' => esc_html__( 'Used PayPal Checkout plugin', 'woocommerce-paypal-payments' ),
|
||||
'exported_label' => 'Used PayPal Checkout plugin',
|
||||
'description' => esc_html__( 'Whether the PayPal Checkout Gateway plugin was configured previously or not', 'woocommerce-paypal-payments' ),
|
||||
'value' => $this->bool_to_html(
|
||||
$had_ppec_plugin
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -112,37 +171,56 @@ class StatusReportModule implements ModuleInterface {
|
|||
*
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param State $state The state.
|
||||
* @return string
|
||||
* @return bool
|
||||
*/
|
||||
private function onboarded( $bearer, $state ): string {
|
||||
private function onboarded( Bearer $bearer, State $state ): bool {
|
||||
try {
|
||||
$token = $bearer->bearer();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
return esc_html__( 'No', 'woocommerce-paypal-payments' );
|
||||
return false;
|
||||
}
|
||||
|
||||
$current_state = $state->current_state();
|
||||
if ( $token->is_valid() && $current_state === $state::STATE_ONBOARDED ) {
|
||||
return esc_html__( 'Yes', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
return esc_html__( 'No', 'woocommerce-paypal-payments' );
|
||||
return $token->is_valid() && $current_state === $state::STATE_ONBOARDED;
|
||||
}
|
||||
|
||||
/**
|
||||
* It returns whether vaulting is enabled or not.
|
||||
*
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @return string
|
||||
* @return bool
|
||||
*/
|
||||
private function vault_enabled( $bearer ) {
|
||||
private function vault_enabled( Bearer $bearer ): bool {
|
||||
try {
|
||||
$token = $bearer->bearer();
|
||||
return $token->vaulting_available()
|
||||
? esc_html__( 'Yes', 'woocommerce-paypal-payments' )
|
||||
: esc_html__( 'No', 'woocommerce-paypal-payments' );
|
||||
return $token->vaulting_available();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
return esc_html__( 'No', 'woocommerce-paypal-payments' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if reference transactions are enabled in account.
|
||||
*
|
||||
* @param BillingAgreementsEndpoint $billing_agreements_endpoint The endpoint.
|
||||
*/
|
||||
private function reference_transaction_enabled( BillingAgreementsEndpoint $billing_agreements_endpoint ): bool {
|
||||
try {
|
||||
return $billing_agreements_endpoint->reference_transaction_enabled();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the bool value to "yes" icon or dash.
|
||||
*
|
||||
* @param bool $value The value.
|
||||
* @return string
|
||||
*/
|
||||
private function bool_to_html( bool $value ): string {
|
||||
return $value
|
||||
? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>'
|
||||
: '<mark class="no">–</mark>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
94
modules/ppcp-subscription/src/FreeTrialHandlerTrait.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/**
|
||||
* Helper trait for the subscriptions handling.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use WC_Order;
|
||||
use WC_Product;
|
||||
use WC_Subscription;
|
||||
use WC_Subscriptions_Product;
|
||||
|
||||
/**
|
||||
* Class FreeTrialHandlerTrait
|
||||
*/
|
||||
trait FreeTrialHandlerTrait {
|
||||
use SubscriptionsHandlerTrait;
|
||||
|
||||
/**
|
||||
* Checks if the cart contains only free trial.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_free_trial_cart(): bool {
|
||||
if ( ! $this->is_wcs_plugin_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cart = WC()->cart;
|
||||
if ( ! $cart || $cart->is_empty() || (float) $cart->get_total( 'numeric' ) > 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $cart->get_cart() as $item ) {
|
||||
$product = $item['data'] ?? null;
|
||||
if ( ! $product instanceof WC_Product ) {
|
||||
continue;
|
||||
}
|
||||
if ( WC_Subscriptions_Product::get_trial_length( $product ) > 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current product contains free trial.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_free_trial_product(): bool {
|
||||
if ( ! $this->is_wcs_plugin_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$product = wc_get_product();
|
||||
|
||||
return $product
|
||||
&& WC_Subscriptions_Product::is_subscription( $product )
|
||||
&& WC_Subscriptions_Product::get_trial_length( $product ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given order contains only free trial.
|
||||
*
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_free_trial_order( WC_Order $wc_order ): bool {
|
||||
if ( ! $this->is_wcs_plugin_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( (float) $wc_order->get_total( 'numeric' ) > 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$subs = wcs_get_subscriptions_for_order( $wc_order );
|
||||
|
||||
return ! empty(
|
||||
array_filter(
|
||||
$subs,
|
||||
function ( WC_Subscription $sub ): bool {
|
||||
return (float) $sub->get_total_initial_payment() <= 0;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription\Helper;
|
||||
|
||||
use WC_Product;
|
||||
use WC_Subscriptions_Product;
|
||||
|
||||
/**
|
||||
* Class SubscriptionHelper
|
||||
*/
|
||||
|
@ -26,7 +29,7 @@ class SubscriptionHelper {
|
|||
return false;
|
||||
}
|
||||
$product = wc_get_product();
|
||||
return is_a( $product, \WC_Product::class ) && $product->is_type( 'subscription' );
|
||||
return $product && WC_Subscriptions_Product::is_subscription( $product );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,10 +47,10 @@ class SubscriptionHelper {
|
|||
}
|
||||
|
||||
foreach ( $cart->get_cart() as $item ) {
|
||||
if ( ! isset( $item['data'] ) || ! is_a( $item['data'], \WC_Product::class ) ) {
|
||||
if ( ! isset( $item['data'] ) || ! is_a( $item['data'], WC_Product::class ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( $item['data']->is_type( 'subscription' ) ) {
|
||||
if ( $item['data']->is_type( 'subscription' ) || $item['data']->is_type( 'subscription_variation' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +58,25 @@ 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;
|
||||
}
|
||||
|
||||
return $this->has_subscription( $order_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether only automatic payment gateways are accepted.
|
||||
*
|
||||
|
@ -65,12 +87,10 @@ class SubscriptionHelper {
|
|||
if ( ! $this->plugin_is_active() ) {
|
||||
return false;
|
||||
}
|
||||
$accept_manual_renewals = ( 'no' !== get_option(
|
||||
//phpcs:disable Inpsyde.CodeQuality.VariablesName.SnakeCaseVar
|
||||
$accept_manual_renewals = 'no' !== get_option(
|
||||
\WC_Subscriptions_Admin::$option_prefix . '_accept_manual_renewals',
|
||||
//phpcs:enable Inpsyde.CodeQuality.VariablesName.SnakeCaseVar
|
||||
'no'
|
||||
) ) ? true : false;
|
||||
);
|
||||
return ! $accept_manual_renewals;
|
||||
}
|
||||
|
||||
|
|
|
@ -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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
26
modules/ppcp-subscription/src/SubscriptionsHandlerTrait.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* Helper trait for the free trial subscriptions handling.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use WC_Subscriptions;
|
||||
|
||||
/**
|
||||
* Class SubscriptionsHandlerTrait
|
||||
*/
|
||||
trait SubscriptionsHandlerTrait {
|
||||
/**
|
||||
* Whether the subscription plugin is active or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_wcs_plugin_active(): bool {
|
||||
return class_exists( WC_Subscriptions::class );
|
||||
}
|
||||
}
|
|
@ -14,30 +14,47 @@ use WooCommerce\PayPalCommerce\Vaulting\Assets\MyAccountPaymentsAssets;
|
|||
use WooCommerce\PayPalCommerce\Vaulting\Endpoint\DeletePaymentTokenEndpoint;
|
||||
|
||||
return array(
|
||||
'vaulting.module-url' => static function ( ContainerInterface $container ): string {
|
||||
'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 {
|
||||
'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 {
|
||||
'vaulting.payment-tokens-renderer' => static function (): PaymentTokensRenderer {
|
||||
return new PaymentTokensRenderer();
|
||||
},
|
||||
'vaulting.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
|
||||
'vaulting.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
|
||||
$factory = $container->get( 'api.factory.payment-token' );
|
||||
$endpoint = $container->get( 'api.endpoint.payment-token' );
|
||||
return new PaymentTokenRepository( $factory, $endpoint );
|
||||
},
|
||||
'vaulting.endpoint.delete' => function( ContainerInterface $container ) : DeletePaymentTokenEndpoint {
|
||||
'vaulting.endpoint.delete' => function( ContainerInterface $container ) : DeletePaymentTokenEndpoint {
|
||||
return new DeletePaymentTokenEndpoint(
|
||||
$container->get( 'vaulting.repository.payment-token' ),
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'vaulting.payment-token-checker' => function( ContainerInterface $container ) : PaymentTokenChecker {
|
||||
return new PaymentTokenChecker(
|
||||
$container->get( 'vaulting.repository.payment-token' ),
|
||||
$container->get( 'api.repository.order' ),
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.processor.authorized-payments' ),
|
||||
$container->get( 'api.endpoint.payments' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'vaulting.customer-approval-listener' => function( ContainerInterface $container ) : CustomerApprovalListener {
|
||||
return new CustomerApprovalListener(
|
||||
$container->get( 'api.endpoint.payment-token' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -58,7 +68,7 @@ class MyAccountPaymentsAssets {
|
|||
'PayPalCommerceGatewayVaulting',
|
||||
array(
|
||||
'delete' => array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( DeletePaymentTokenEndpoint::ENDPOINT ) ),
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DeletePaymentTokenEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DeletePaymentTokenEndpoint::nonce() ),
|
||||
),
|
||||
)
|
||||
|
|
78
modules/ppcp-vaulting/src/CustomerApprovalListener.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
/**
|
||||
* Confirm approval token after the PayPal vaulting approval by customer (v2/vault/payment-tokens with CUSTOMER_ACTION_REQUIRED response).
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vaulting
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vaulting;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokenEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
|
||||
/**
|
||||
* Class CustomerApprovalListener
|
||||
*/
|
||||
class CustomerApprovalListener {
|
||||
|
||||
use FreeTrialHandlerTrait;
|
||||
|
||||
/**
|
||||
* The PaymentTokenEndpoint.
|
||||
*
|
||||
* @var PaymentTokenEndpoint
|
||||
*/
|
||||
private $payment_token_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* CustomerApprovalListener constructor.
|
||||
*
|
||||
* @param PaymentTokenEndpoint $payment_token_endpoint The PaymentTokenEndpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( PaymentTokenEndpoint $payment_token_endpoint, LoggerInterface $logger ) {
|
||||
$this->payment_token_endpoint = $payment_token_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for redirects after the PayPal vaulting approval by customer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function listen(): void {
|
||||
$token = filter_input( INPUT_GET, 'approval_token_id', FILTER_SANITIZE_STRING );
|
||||
if ( ! is_string( $token ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = (string) filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL );
|
||||
|
||||
try {
|
||||
$query = wp_parse_url( $url, PHP_URL_QUERY );
|
||||
if ( $query && str_contains( $query, 'ppcp_vault=cancel' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->payment_token_endpoint->create_from_approval_token( $token, get_current_user_id() );
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Failed to create payment token. ' . $exception->getMessage() );
|
||||
}
|
||||
} finally {
|
||||
wp_safe_redirect( remove_query_arg( array( 'ppcp_vault', 'approval_token_id', 'approval_session_id' ), $url ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
189
modules/ppcp-vaulting/src/PaymentTokenChecker.php
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
/**
|
||||
* Check if payment token is saved and updates order accordingly.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vaulting
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vaulting;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class PaymentTokenChecker
|
||||
*/
|
||||
class PaymentTokenChecker {
|
||||
|
||||
use FreeTrialHandlerTrait;
|
||||
|
||||
/**
|
||||
* The payment token repository.
|
||||
*
|
||||
* @var PaymentTokenRepository
|
||||
*/
|
||||
protected $payment_token_repository;
|
||||
|
||||
/**
|
||||
* The order repository.
|
||||
*
|
||||
* @var OrderRepository
|
||||
*/
|
||||
protected $order_repository;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The authorized payments processor.
|
||||
*
|
||||
* @var AuthorizedPaymentsProcessor
|
||||
*/
|
||||
protected $authorized_payments_processor;
|
||||
|
||||
/**
|
||||
* The payments endpoint.
|
||||
*
|
||||
* @var PaymentsEndpoint
|
||||
*/
|
||||
protected $payments_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* PaymentTokenChecker constructor.
|
||||
*
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param OrderRepository $order_repository The order repository.
|
||||
* @param Settings $settings The settings.
|
||||
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The authorized payments processor.
|
||||
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
OrderRepository $order_repository,
|
||||
Settings $settings,
|
||||
AuthorizedPaymentsProcessor $authorized_payments_processor,
|
||||
PaymentsEndpoint $payments_endpoint,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->payment_token_repository = $payment_token_repository;
|
||||
$this->order_repository = $order_repository;
|
||||
$this->settings = $settings;
|
||||
$this->authorized_payments_processor = $authorized_payments_processor;
|
||||
$this->payments_endpoint = $payments_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if payment token exist and updates order accordingly.
|
||||
*
|
||||
* @param int $order_id The order ID.
|
||||
* @param int $customer_id The customer ID.
|
||||
* @param string $intent The intent from settings when order was created.
|
||||
* @return void
|
||||
*/
|
||||
public function check_and_update( int $order_id, int $customer_id, string $intent ):void {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $wc_order->get_status() === 'processing' || 'capture' !== $intent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $customer_id );
|
||||
if ( $tokens ) {
|
||||
try {
|
||||
if ( $this->is_free_trial_order( $wc_order ) ) {
|
||||
if ( CreditCardGateway::ID === $wc_order->get_payment_method() ) {
|
||||
$order = $this->order_repository->for_wc_order( $wc_order );
|
||||
$this->authorized_payments_processor->void_authorizations( $order );
|
||||
$wc_order->payment_complete();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->capture_authorized_payment( $wc_order );
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( $exception->getMessage() );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->error( "Payment for subscription parent order #{$order_id} was not saved on PayPal." );
|
||||
|
||||
try {
|
||||
$order = $this->order_repository->for_wc_order( $wc_order );
|
||||
$this->authorized_payments_processor->void_authorizations( $order );
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->logger->warning( $exception->getMessage() );
|
||||
}
|
||||
|
||||
$this->update_failed_status( $wc_order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures authorized payments for the given WC order.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @throws Exception When there is a problem capturing the payment.
|
||||
*/
|
||||
private function capture_authorized_payment( WC_Order $wc_order ): void {
|
||||
if ( $this->settings->has( 'intent' ) && strtoupper( (string) $this->settings->get( 'intent' ) ) === 'CAPTURE' ) {
|
||||
if ( ! $this->authorized_payments_processor->capture_authorized_payment( $wc_order ) ) {
|
||||
throw new Exception( "Could not capture payment for order: #{$wc_order->get_id()}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates WC order and subscription status to failed and canceled respectively.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
*/
|
||||
private function update_failed_status( WC_Order $wc_order ): void {
|
||||
$error_message = __( 'Could not process order because it was not possible to save the payment on PayPal.', 'woocommerce-paypal-payments' );
|
||||
$wc_order->update_status( 'failed', $error_message );
|
||||
|
||||
/**
|
||||
* Function already exist in Subscription plugin
|
||||
*
|
||||
* @psalm-suppress UndefinedFunction
|
||||
*/
|
||||
$subscriptions = wcs_get_subscriptions_for_order( $wc_order->get_id() );
|
||||
foreach ( $subscriptions as $key => $subscription ) {
|
||||
if ( $subscription->get_parent_id() === $wc_order->get_id() ) {
|
||||
try {
|
||||
$subscription->update_status( 'cancelled' );
|
||||
break;
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( "Could not update cancelled status on subscription #{$subscription->get_id()} " . $exception->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,16 @@ class VaultingModule implements ModuleInterface {
|
|||
*/
|
||||
public function run( ContainerInterface $container ): void {
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
if ( ! $settings->has( 'vault_enabled' ) || ! $settings->get( 'vault_enabled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$listener = $container->get( 'vaulting.customer-approval-listener' );
|
||||
assert( $listener instanceof CustomerApprovalListener );
|
||||
|
||||
$listener->listen();
|
||||
|
||||
add_filter(
|
||||
'woocommerce_account_menu_items',
|
||||
function( $menu_links ) {
|
||||
|
@ -93,6 +103,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',
|
||||
|
@ -113,6 +134,16 @@ class VaultingModule implements ModuleInterface {
|
|||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_check_saved_payment',
|
||||
function ( int $order_id, int $customer_id, string $intent ) use ( $container ) {
|
||||
$payment_token_checker = $container->get( 'vaulting.payment-token-checker' );
|
||||
$payment_token_checker->check_and_update( $order_id, $customer_id, $intent );
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1722,9 +1722,9 @@ mimic-fn@^2.1.0:
|
|||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
|
|
|
@ -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 |