Skip to content

Commit

Permalink
RPP: Factor flags (#7035)
Browse files Browse the repository at this point in the history
  • Loading branch information
RadoslavGeorgiev authored Sep 1, 2023
1 parent 39a70c0 commit ce50c7a
Show file tree
Hide file tree
Showing 16 changed files with 1,102 additions and 48 deletions.
4 changes: 4 additions & 0 deletions changelog/rpp-6679-factor-flags
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: dev

Adding factor flags to control when to enter the new payment process.
29 changes: 23 additions & 6 deletions includes/class-database-cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@
* A class for caching data as an option in the database.
*/
class Database_Cache {
const ACCOUNT_KEY = 'wcpay_account_data';
const ONBOARDING_FIELDS_DATA_KEY = 'wcpay_onboarding_fields_data';
const BUSINESS_TYPES_KEY = 'wcpay_business_types_data';
const CURRENCIES_KEY = 'wcpay_multi_currency_cached_currencies';
const CUSTOMER_CURRENCIES_KEY = 'wcpay_multi_currency_customer_currencies';
const ACCOUNT_KEY = 'wcpay_account_data';
const ONBOARDING_FIELDS_DATA_KEY = 'wcpay_onboarding_fields_data';
const BUSINESS_TYPES_KEY = 'wcpay_business_types_data';
const CURRENCIES_KEY = 'wcpay_multi_currency_cached_currencies';
const CUSTOMER_CURRENCIES_KEY = 'wcpay_multi_currency_customer_currencies';
const PAYMENT_PROCESS_FACTORS_KEY = 'wcpay_payment_process_factors';

/**
* Refresh during AJAX calls is avoided, but white-listing
* a key here will allow the refresh to happen.
*
* @var string[]
*/
const AJAX_ALLOWED_KEYS = [
self::PAYMENT_PROCESS_FACTORS_KEY,
];

/**
* Payment methods cache key prefix. Used in conjunction with the customer_id to cache a customer's payment methods.
Expand Down Expand Up @@ -216,7 +227,10 @@ private function should_refresh_cache( string $key, $cache_contents, callable $v
}

// Do not refresh if doing ajax or the refresh has been disabled (running an AS job).
if ( defined( 'DOING_CRON' ) || wp_doing_ajax() || $this->refresh_disabled ) {
if (
defined( 'DOING_CRON' )
|| ( wp_doing_ajax() && ! in_array( $key, self::AJAX_ALLOWED_KEYS, true ) )
|| $this->refresh_disabled ) {
return false;
}

Expand Down Expand Up @@ -330,6 +344,9 @@ private function get_ttl( string $key, array $cache_contents ): int {
case self::CONNECT_INCENTIVE_KEY:
$ttl = $cache_contents['data']['ttl'] ?? HOUR_IN_SECONDS * 6;
break;
case self::PAYMENT_PROCESS_FACTORS_KEY:
$ttl = 2 * HOUR_IN_SECONDS;
break;
default:
// Default to 24h.
$ttl = DAY_IN_SECONDS;
Expand Down
116 changes: 111 additions & 5 deletions includes/class-wc-payment-gateway-wcpay.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
use WCPay\Session_Rate_Limiter;
use WCPay\Tracker;
use WCPay\Internal\Service\PaymentProcessingService;
use WCPay\Internal\Payment\Factor;
use WCPay\Internal\Payment\Router;

/**
* Gateway class for WooPayments
Expand Down Expand Up @@ -706,6 +708,111 @@ public function payment_fields() {
do_action( 'wc_payments_add_payment_fields' );
}

/**
* Checks whether the new payment process should be used to pay for a given order.
*
* @param WC_Order $order Order that's being paid.
* @return bool
*/
public function should_use_new_process( WC_Order $order ) {
$order_id = $order->get_id();

// The new process us under active development, and not ready for production yet.
if ( ! WC_Payments::mode()->is_dev() ) {
return false;
}

// This array will contain all factors, present during checkout.
$factors = [
/**
* The new payment process is a factor itself.
* Even if no other factors are present, this will make entering
* the new payment process possible only if this factor is allowed.
*/
Factor::NEW_PAYMENT_PROCESS(),
];

// If there is a token in the request, we're using a saved PM.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$using_saved_payment_method = ! empty( Payment_Information::get_token_from_request( $_POST ) );
if ( $using_saved_payment_method ) {
$factors[] = Factor::USE_SAVED_PM();
}

// The PM should be saved when chosen, or when it's a recurrent payment, but not if already saved.
$save_payment_method = ! $using_saved_payment_method && (
// phpcs:ignore WordPress.Security.NonceVerification.Missing
! empty( $_POST[ 'wc-' . static::GATEWAY_ID . '-new-payment-method' ] )
|| $this->is_payment_recurring( $order_id )
);
if ( $save_payment_method ) {
$factors[] = Factor::SAVE_PM();
}

// In case amount is 0 and we're not saving the payment method, we won't be using intents and can confirm the order payment.
if (
apply_filters(
'wcpay_confirm_without_payment_intent',
$order->get_total() <= 0 && ! $save_payment_method
)
) {
$factors[] = Factor::NO_PAYMENT();
}

// Subscription (both WCPay and WCSubs) if when the order contains one.
if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
$factors[] = Factor::SUBSCRIPTION_SIGNUP();
}

// WooPay might change how payment fields were loaded.
if (
$this->woopay_util->should_enable_woopay( $this )
&& $this->woopay_util->should_enable_woopay_on_cart_or_checkout()
) {
$factors[] = Factor::WOOPAY_ENABLED();
}

// WooPay payments are indicated by the platform checkout intent.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['platform-checkout-intent'] ) ) {
$factors[] = Factor::WOOPAY_PAYMENT();
}

// Check whether the customer is signining up for a WCPay subscription.
if (
function_exists( 'wcs_order_contains_subscription' )
&& wcs_order_contains_subscription( $order_id )
&& WC_Payments_Features::is_wcpay_subscriptions_enabled()
&& ! $this->is_subscriptions_plugin_active()
) {
$factors[] = Factor::WCPAY_SUBSCRIPTION_SIGNUP();
}

if ( $this instanceof UPE_Split_Payment_Gateway ) {
$factors[] = Factor::DEFERRED_INTENT_SPLIT_UPE();
}

if ( defined( 'WCPAY_PAYMENT_REQUEST_CHECKOUT' ) && WCPAY_PAYMENT_REQUEST_CHECKOUT ) {
$factors[] = Factor::PAYMENT_REQUEST();
}

$router = wcpay_get_container()->get( Router::class );
return $router->should_use_new_payment_process( $factors );
}

/**
* Checks whether the new payment process should be entered,
* and if the answer is yes, uses it and returns the result.
*
* @param WC_Order $order Order that needs payment.
* @return array|null Array if processed, null if the new process is not supported.
*/
public function new_process_payment( WC_Order $order ) {
// Important: No factors are provided here, they were meant just for `Feature`.
$service = wcpay_get_container()->get( PaymentProcessingService::class );
return $service->process_payment( $order->get_id() );
}

/**
* Process the payment for a given order.
*
Expand All @@ -716,14 +823,13 @@ public function payment_fields() {
* @throws Exception Error processing the payment.
*/
public function process_payment( $order_id ) {
$order = wc_get_order( $order_id );

if ( defined( 'WCPAY_NEW_PROCESS' ) && true === WCPAY_NEW_PROCESS ) {
$new_process = wcpay_get_container()->get( PaymentProcessingService::class );
return $new_process->process_payment( $order_id );
// Use the new payment process if allowed.
if ( $this->should_use_new_process( $order ) ) {
return $this->new_process_payment( $order );
}

$order = wc_get_order( $order_id );

try {
if ( 20 < strlen( $order->get_billing_phone() ) ) {
throw new Process_Payment_Exception(
Expand Down
4 changes: 4 additions & 0 deletions includes/class-wc-payments-payment-request-button-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,10 @@ public function ajax_create_order() {
define( 'WOOCOMMERCE_CHECKOUT', true );
}

if ( ! defined( 'WCPAY_PAYMENT_REQUEST_CHECKOUT' ) ) {
define( 'WCPAY_PAYMENT_REQUEST_CHECKOUT', true );
}

// In case the state is required, but is missing, add a more descriptive error notice.
$this->validate_state();

Expand Down
1 change: 1 addition & 0 deletions includes/class-wc-payments.php
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ public static function init() {
include_once __DIR__ . '/core/server/request/trait-use-test-mode-only-when-dev-mode.php';
include_once __DIR__ . '/core/server/request/class-generic.php';
include_once __DIR__ . '/core/server/request/class-get-intention.php';
include_once __DIR__ . '/core/server/request/class-get-payment-process-factors.php';
include_once __DIR__ . '/core/server/request/class-create-intention.php';
include_once __DIR__ . '/core/server/request/class-update-intention.php';
include_once __DIR__ . '/core/server/request/class-capture-intention.php';
Expand Down
73 changes: 37 additions & 36 deletions includes/core/server/class-request.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,43 +101,44 @@ abstract class Request {
* @var string[]
*/
private $route_list = [
WC_Payments_API_Client::ACCOUNTS_API => 'accounts',
WC_Payments_API_Client::CAPABILITIES_API => 'accounts/capabilities',
WC_Payments_API_Client::WOOPAY_ACCOUNTS_API => 'accounts/platform_checkout',
WC_Payments_API_Client::WOOPAY_COMPATIBILITY_API => 'woopay/compatibility',
WC_Payments_API_Client::APPLE_PAY_API => 'apple_pay',
WC_Payments_API_Client::CHARGES_API => 'charges',
WC_Payments_API_Client::CONN_TOKENS_API => 'terminal/connection_tokens',
WC_Payments_API_Client::TERMINAL_LOCATIONS_API => 'terminal/locations',
WC_Payments_API_Client::CUSTOMERS_API => 'customers',
WC_Payments_API_Client::CURRENCY_API => 'currency',
WC_Payments_API_Client::INTENTIONS_API => 'intentions',
WC_Payments_API_Client::REFUNDS_API => 'refunds',
WC_Payments_API_Client::DEPOSITS_API => 'deposits',
WC_Payments_API_Client::TRANSACTIONS_API => 'transactions',
WC_Payments_API_Client::DISPUTES_API => 'disputes',
WC_Payments_API_Client::FILES_API => 'files',
WC_Payments_API_Client::ONBOARDING_API => 'onboarding',
WC_Payments_API_Client::TIMELINE_API => 'timeline',
WC_Payments_API_Client::PAYMENT_METHODS_API => 'payment_methods',
WC_Payments_API_Client::SETUP_INTENTS_API => 'setup_intents',
WC_Payments_API_Client::TRACKING_API => 'tracking',
WC_Payments_API_Client::PRODUCTS_API => 'products',
WC_Payments_API_Client::PRICES_API => 'products/prices',
WC_Payments_API_Client::INVOICES_API => 'invoices',
WC_Payments_API_Client::SUBSCRIPTIONS_API => 'subscriptions',
WC_Payments_API_Client::SUBSCRIPTION_ITEMS_API => 'subscriptions/items',
WC_Payments_API_Client::READERS_CHARGE_SUMMARY => 'reader-charges/summary',
WC_Payments_API_Client::TERMINAL_READERS_API => 'terminal/readers',
WC_Payments_API_Client::ACCOUNTS_API => 'accounts',
WC_Payments_API_Client::CAPABILITIES_API => 'accounts/capabilities',
WC_Payments_API_Client::WOOPAY_ACCOUNTS_API => 'accounts/platform_checkout',
WC_Payments_API_Client::WOOPAY_COMPATIBILITY_API => 'woopay/compatibility',
WC_Payments_API_Client::APPLE_PAY_API => 'apple_pay',
WC_Payments_API_Client::CHARGES_API => 'charges',
WC_Payments_API_Client::CONN_TOKENS_API => 'terminal/connection_tokens',
WC_Payments_API_Client::TERMINAL_LOCATIONS_API => 'terminal/locations',
WC_Payments_API_Client::CUSTOMERS_API => 'customers',
WC_Payments_API_Client::CURRENCY_API => 'currency',
WC_Payments_API_Client::INTENTIONS_API => 'intentions',
WC_Payments_API_Client::REFUNDS_API => 'refunds',
WC_Payments_API_Client::DEPOSITS_API => 'deposits',
WC_Payments_API_Client::TRANSACTIONS_API => 'transactions',
WC_Payments_API_Client::DISPUTES_API => 'disputes',
WC_Payments_API_Client::FILES_API => 'files',
WC_Payments_API_Client::ONBOARDING_API => 'onboarding',
WC_Payments_API_Client::TIMELINE_API => 'timeline',
WC_Payments_API_Client::PAYMENT_METHODS_API => 'payment_methods',
WC_Payments_API_Client::SETUP_INTENTS_API => 'setup_intents',
WC_Payments_API_Client::TRACKING_API => 'tracking',
WC_Payments_API_Client::PAYMENT_PROCESS_CONFIG_API => 'payment_process_config',
WC_Payments_API_Client::PRODUCTS_API => 'products',
WC_Payments_API_Client::PRICES_API => 'products/prices',
WC_Payments_API_Client::INVOICES_API => 'invoices',
WC_Payments_API_Client::SUBSCRIPTIONS_API => 'subscriptions',
WC_Payments_API_Client::SUBSCRIPTION_ITEMS_API => 'subscriptions/items',
WC_Payments_API_Client::READERS_CHARGE_SUMMARY => 'reader-charges/summary',
WC_Payments_API_Client::TERMINAL_READERS_API => 'terminal/readers',
WC_Payments_API_Client::MINIMUM_RECURRING_AMOUNT_API => 'subscriptions/minimum_amount',
WC_Payments_API_Client::CAPITAL_API => 'capital',
WC_Payments_API_Client::WEBHOOK_FETCH_API => 'webhook/failed_events',
WC_Payments_API_Client::DOCUMENTS_API => 'documents',
WC_Payments_API_Client::VAT_API => 'vat',
WC_Payments_API_Client::LINKS_API => 'links',
WC_Payments_API_Client::AUTHORIZATIONS_API => 'authorizations',
WC_Payments_API_Client::FRAUD_OUTCOMES_API => 'fraud_outcomes',
WC_Payments_API_Client::FRAUD_RULESET_API => 'fraud_ruleset',
WC_Payments_API_Client::CAPITAL_API => 'capital',
WC_Payments_API_Client::WEBHOOK_FETCH_API => 'webhook/failed_events',
WC_Payments_API_Client::DOCUMENTS_API => 'documents',
WC_Payments_API_Client::VAT_API => 'vat',
WC_Payments_API_Client::LINKS_API => 'links',
WC_Payments_API_Client::AUTHORIZATIONS_API => 'authorizations',
WC_Payments_API_Client::FRAUD_OUTCOMES_API => 'fraud_outcomes',
WC_Payments_API_Client::FRAUD_RULESET_API => 'fraud_ruleset',
];

/**
Expand Down
32 changes: 32 additions & 0 deletions includes/core/server/request/class-get-payment-process-factors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Class file for WCPay\Core\Server\Request\Get_Payment_Process_Factors.
*
* @package WooCommerce Payments
*/

namespace WCPay\Core\Server\Request;

use WCPay\Core\Server\Request;
use WC_Payments_API_Client;

/**
* Request class for getting routing data for the new payment process.
*/
class Get_Payment_Process_Factors extends Request {
/**
* Returns the request's API.
*
* @return string
*/
public function get_api(): string {
return WC_Payments_API_Client::PAYMENT_PROCESS_CONFIG_API . '/factors';
}

/**
* Returns the request's HTTP method.
*/
public function get_method(): string {
return 'GET';
}
}
1 change: 1 addition & 0 deletions includes/wc-payment-api/class-wc-payments-api-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class WC_Payments_API_Client {
const TRACKING_API = 'tracking';
const PRODUCTS_API = 'products';
const PRICES_API = 'products/prices';
const PAYMENT_PROCESS_CONFIG_API = 'payment_process_config';
const INVOICES_API = 'invoices';
const SUBSCRIPTIONS_API = 'subscriptions';
const SUBSCRIPTION_ITEMS_API = 'subscriptions/items';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

use Automattic\WooCommerce\Utilities\PluginUtil;
use WCPay\Core\Mode;
use WCPay\Database_Cache;
use WCPay\Internal\DependencyManagement\AbstractServiceProvider;
use WCPay\Internal\Payment\Router;
use WCPay\Internal\Service\PaymentProcessingService;
use WCPay\Internal\Service\ExampleService;
use WCPay\Internal\Service\ExampleServiceWithDependencies;
Expand All @@ -25,6 +27,7 @@ class PaymentsServiceProvider extends AbstractServiceProvider {
*/
protected $provides = [
PaymentProcessingService::class,
Router::class,
ExampleService::class,
ExampleServiceWithDependencies::class,
];
Expand All @@ -37,6 +40,9 @@ public function register(): void {

$container->addShared( PaymentProcessingService::class );

$container->addShared( Router::class )
->addArgument( Database_Cache::class );

$container->addShared( ExampleService::class );
$container->addShared( ExampleServiceWithDependencies::class )
->addArgument( ExampleService::class )
Expand Down
Loading

0 comments on commit ce50c7a

Please sign in to comment.