Skip to content

Commit 0613e7d

Browse files
Checkout optimization: Avoid multiple calls for payment method details (#11001)
1 parent a76809a commit 0613e7d

5 files changed

+410
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: fix
3+
4+
Generate payment method details in WooPayments instead of Woo core, cache them for performance improvements.

includes/class-wc-payments-order-service.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ class WC_Payments_Order_Service {
161161
*/
162162
const WCPAY_MULTIBANCO_URL_META_KEY = '_wcpay_multibanco_url';
163163

164+
/**
165+
* Meta key for cached payment method details.
166+
*
167+
* @const string
168+
*/
169+
const PAYMENT_METHOD_DETAILS_META_KEY = '_wcpay_payment_method_details';
170+
164171
/**
165172
* Client for making requests to the WooCommerce Payments API
166173
*
@@ -962,8 +969,17 @@ public function attach_intent_info_to_order( WC_Order $order, $intent, $allow_up
962969
$payment_transaction_id = $payment_transaction['id'] ?? '';
963970
$outcome = $charge ? $charge->get_outcome() : null;
964971
$risk_level = $outcome ? $outcome['risk_level'] : null;
972+
965973
// next, save it in order meta.
966974
$this->attach_intent_info_to_order__legacy( $order, $intent_id, $intent_status, $payment_method, $customer_id, $charge_id, $currency, $payment_transaction_id, $risk_level );
975+
976+
// Store payment method details when available.
977+
if ( null !== $charge ) {
978+
$payment_method_details = $charge->get_payment_method_details();
979+
if ( $payment_method_details ) {
980+
$this->store_payment_method_details( $order, $payment_method_details );
981+
}
982+
}
967983
}
968984

969985
/**
@@ -2336,6 +2352,32 @@ public function get_multibanco_info_from_order( WC_Order $order ): array {
23362352
];
23372353
}
23382354

2355+
/**
2356+
* Store payment method details in the order meta.
2357+
*
2358+
* @param WC_Order $order The order.
2359+
* @param array $payment_method_details The payment method details.
2360+
* @return void
2361+
*/
2362+
public function store_payment_method_details( WC_Order $order, array $payment_method_details ): void {
2363+
$order->update_meta_data( self::PAYMENT_METHOD_DETAILS_META_KEY, wp_json_encode( $payment_method_details ) );
2364+
$order->save_meta_data();
2365+
}
2366+
2367+
/**
2368+
* Get cached payment method details from the order meta.
2369+
*
2370+
* @param WC_Order $order The order.
2371+
* @return array The payment method details.
2372+
*/
2373+
public function get_payment_method_details( WC_Order $order ): ?array {
2374+
$json = $order->get_meta( self::PAYMENT_METHOD_DETAILS_META_KEY );
2375+
if ( '' === $json ) {
2376+
return null;
2377+
}
2378+
return json_decode( $json, true );
2379+
}
2380+
23392381
/**
23402382
* Check if FROD is supported for the given country.
23412383
*
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
/**
3+
* Class WC_Payments_Payment_Method_Service
4+
*
5+
* @package WooCommerce\Payments
6+
*/
7+
8+
if ( ! defined( 'ABSPATH' ) ) {
9+
exit; // Exit if accessed directly.
10+
}
11+
12+
use WCPay\Exceptions\API_Exception;
13+
use WCPay\Logger;
14+
15+
/**
16+
* Class handling payment method-related functionality.
17+
*
18+
* Note that this is regarding Stripe-like PaymentMethod objects, not payment methods (p24, card, etc.).
19+
*/
20+
class WC_Payments_Payment_Method_Service {
21+
/**
22+
* Client for making requests to the WooCommerce Payments API
23+
*
24+
* @var WC_Payments_API_Client
25+
*/
26+
private $payments_api_client;
27+
28+
/**
29+
* Order service.
30+
*
31+
* @var WC_Payments_Order_Service
32+
*/
33+
private $order_service;
34+
35+
/**
36+
* Constructor for WC_Payments_Payment_Method_Service.
37+
*
38+
* @param WC_Payments_API_Client $payments_api_client Client for making requests to the WooCommerce Payments API.
39+
* @param WC_Payments_Order_Service $order_service Order service.
40+
*/
41+
public function __construct(
42+
WC_Payments_API_Client $payments_api_client,
43+
WC_Payments_Order_Service $order_service
44+
) {
45+
$this->payments_api_client = $payments_api_client;
46+
$this->order_service = $order_service;
47+
}
48+
49+
/**
50+
* Initializes this class's WP hooks.
51+
*
52+
* @return void
53+
*/
54+
public function init_hooks() {
55+
add_filter( 'wc_order_payment_card_info', [ $this, 'get_card_info' ], 10, 2 );
56+
}
57+
58+
59+
/**
60+
* Prepare card info for an order.
61+
*
62+
* @param array $card_info The card info.
63+
* @param WC_Order $order The order.
64+
* @return array The card info.
65+
*/
66+
public function get_card_info( $card_info, WC_Order $order ) {
67+
if ( WC_Payment_Gateway_WCPay::GATEWAY_ID !== $order->get_payment_method() ) {
68+
return $card_info;
69+
}
70+
71+
$payment_method_details = $this->order_service->get_payment_method_details( $order );
72+
if ( ! $payment_method_details ) {
73+
$payment_method_id = $order->get_meta( '_payment_method_id' );
74+
if ( ! $payment_method_id ) {
75+
return $card_info;
76+
}
77+
78+
try {
79+
$payment_method_details = $this->payments_api_client->get_payment_method( $payment_method_id );
80+
} catch ( API_Exception $ex ) {
81+
Logger::error(
82+
sprintf(
83+
'Retrieving info for payment method for order %s: %s',
84+
$order->get_id(),
85+
$ex->getMessage()
86+
)
87+
);
88+
89+
return $card_info;
90+
}
91+
92+
// Cache payment method details.
93+
$this->order_service->store_payment_method_details( $order, $payment_method_details );
94+
}
95+
96+
$card_info = [];
97+
98+
if ( isset( $payment_method_details['type'], $payment_method_details[ $payment_method_details['type'] ] ) ) {
99+
$details = $payment_method_details[ $payment_method_details['type'] ];
100+
switch ( $payment_method_details['type'] ) {
101+
case 'card':
102+
default:
103+
$card_info['brand'] = $details['brand'] ?? '';
104+
$card_info['last4'] = $details['last4'] ?? '';
105+
break;
106+
case 'card_present':
107+
case 'interac_present':
108+
$card_info['brand'] = $details['brand'] ?? '';
109+
$card_info['last4'] = $details['last4'] ?? '';
110+
$card_info['account_type'] = $details['receipt']['account_type'] ?? '';
111+
$card_info['aid'] = $details['receipt']['dedicated_file_name'] ?? '';
112+
$card_info['app_name'] = $details['receipt']['application_preferred_name'] ?? '';
113+
break;
114+
}
115+
}
116+
117+
return array_map( 'sanitize_text_field', $card_info );
118+
}
119+
}

includes/class-wc-payments.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ class WC_Payments {
317317
*/
318318
private static $currency_manager;
319319

320+
/**
321+
* Instance of WC_Payments_Payment_Method_Service, created in init function
322+
*
323+
* @var WC_Payments_Payment_Method_Service
324+
*/
325+
private static $payment_method_service;
326+
320327
/**
321328
* Entry point to the initialization logic.
322329
*/
@@ -517,6 +524,7 @@ public static function init() {
517524
include_once __DIR__ . '/compat/multi-currency/wc-payments-multi-currency.php';
518525
include_once __DIR__ . '/compat/multi-currency/class-wc-payments-currency-manager.php';
519526
include_once __DIR__ . '/class-duplicates-detection-service.php';
527+
include_once __DIR__ . '/class-wc-payments-payment-method-service.php';
520528

521529
wcpay_get_container()->get( \WCPay\Internal\LoggerContext::class )->init_hooks();
522530

@@ -646,6 +654,9 @@ public static function init() {
646654
$express_checkout_helper = new WC_Payments_Express_Checkout_Button_Helper( self::get_gateway(), self::$account );
647655
self::set_express_checkout_helper( $express_checkout_helper );
648656

657+
self::$payment_method_service = new WC_Payments_Payment_Method_Service( self::$api_client, self::$order_service );
658+
self::$payment_method_service->init_hooks();
659+
649660
// Delay registering hooks that could end up in a fatal error due to expired account cache.
650661
// The `woocommerce_payments_account_refreshed` action will result in a fatal error if it's fired before the `$wp_rewrite` is defined.
651662
// See #8942 for more details.

0 commit comments

Comments
 (0)