Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 68 additions & 32 deletions includes/class-wc-stripe-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ class WC_Stripe_API {
/**
* Stripe API Endpoint
*/
const ENDPOINT = 'https://api.stripe.com/v1/';
const STRIPE_API_VERSION = '2024-06-20';
const ENDPOINT = 'https://api.stripe.com/v1/';

/**
* Stripe API Version
*/
public const STRIPE_API_VERSION = '2024-06-20';

/**
* The invalid API key error count cache key.
Expand Down Expand Up @@ -119,19 +123,17 @@ public static function set_secret_key_for_mode( $mode = null ) {
* @version 4.0.0
*/
public static function get_user_agent() {
$app_info = [
'name' => 'WooCommerce Stripe Gateway',
'version' => WC_STRIPE_VERSION,
'url' => 'https://woocommerce.com/products/stripe/',
'partner_id' => 'pp_partner_EYuSt9peR0WTMg',
];

return [
'lang' => 'php',
'lang_version' => phpversion(),
'publisher' => 'woocommerce',
'uname' => function_exists( 'php_uname' ) ? php_uname() : PHP_OS,
'application' => $app_info,
'application' => [
'name' => 'WooCommerce Stripe Gateway',
'version' => WC_STRIPE_VERSION,
'url' => 'https://woocommerce.com/products/stripe/',
'partner_id' => 'pp_partner_EYuSt9peR0WTMg',
],
];
}

Expand All @@ -146,7 +148,7 @@ public static function get_headers() {
$app_info = $user_agent['application'];

$headers = [
'Authorization' => 'Basic ' . base64_encode( self::get_secret_key() . ':' ),
'Authorization' => 'Basic ' . base64_encode( self::get_secret_key() . ':' ),
'Stripe-Version' => self::STRIPE_API_VERSION,
];

Expand Down Expand Up @@ -235,19 +237,32 @@ public static function request( $request, $api = 'charges', $method = 'POST', $w
*/
$request = apply_filters( 'wc_stripe_request_body', $request, $api );

// Log the request after the filters have been applied.
WC_Stripe_Logger::debug( "Stripe API request: {$method} {$api}", [ 'request' => $request ] );
$is_transact_api_enabled = WC_Stripe_Transact_API::get_instance()->is_transact_api_enabled();

$response = wp_safe_remote_post(
self::ENDPOINT . $api,
// Log the request after the filters have been applied.
WC_Stripe_Logger::debug(
"Stripe API request: {$method} {$api}",
[
'method' => $method,
'headers' => $headers,
'body' => $request,
'timeout' => 70,
'transact_api_enabled' => $is_transact_api_enabled,
'request' => $request,
]
);

if ( $is_transact_api_enabled ) {
$response = WC_Stripe_Transact_API::get_instance()->send_wpcom_proxy_request( $method, $api, $headers, $request );

} else {
$response = wp_safe_remote_post(
self::ENDPOINT . $api,
[
'method' => $method,
'headers' => $headers,
'body' => $request,
'timeout' => 70,
]
);
}

$response_headers = wp_remote_retrieve_headers( $response );

if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
Expand All @@ -266,7 +281,13 @@ public static function request( $request, $api = 'charges', $method = 'POST', $w

$response_body = json_decode( $response['body'] );

WC_Stripe_Logger::debug( "Stripe API response: {$method} {$api}", [ 'response' => $response_body ] );
WC_Stripe_Logger::debug(
"Stripe API response: {$method} {$api}",
[
'transact_api_enabled' => $is_transact_api_enabled,
'response' => $response_body,
]
);

if ( $with_headers ) {
return [
Expand Down Expand Up @@ -300,24 +321,32 @@ public static function retrieve( $api ) {
return null;
}

WC_Stripe_Logger::debug( "Stripe API request: GET {$api}" );
$is_transact_api_enabled = WC_Stripe_Transact_API::get_instance()->is_transact_api_enabled();

$response = wp_safe_remote_get(
self::ENDPOINT . $api,
[
'method' => 'GET',
'headers' => self::get_headers(),
'timeout' => 70,
]
);
WC_Stripe_Logger::debug( "Stripe API request: GET {$api}", [ 'transact_api_enabled' => $is_transact_api_enabled ] );

if ( $is_transact_api_enabled ) {
$response = WC_Stripe_Transact_API::get_instance()->send_wpcom_proxy_request( 'GET', $api, self::get_headers() );

} else {
$response = wp_safe_remote_get(
self::ENDPOINT . $api,
[
'method' => 'GET',
'headers' => self::get_headers(),
'timeout' => 70,
]
);
}

// If we get a 401 error, we know the secret key is not valid.
if ( is_array( $response ) && isset( $response['response'] ) && is_array( $response['response'] ) && isset( $response['response']['code'] ) && 401 === $response['response']['code'] ) {
// Stripe redacts API keys in the response.
WC_Stripe_Logger::error(
"Stripe API error: GET {$api} returned a 401",
[
'response' => json_decode( $response['body'] ),
'transact_api_enabled' => $is_transact_api_enabled,
'response' => json_decode( $response['body'] ),
]
);

Expand Down Expand Up @@ -350,15 +379,22 @@ public static function retrieve( $api ) {
WC_Stripe_Logger::error(
"Stripe API error: GET {$api}",
[
'response' => $response,
'transact_api_enabled' => $is_transact_api_enabled,
'response' => $response,
]
);
return new WP_Error( 'stripe_error', __( 'There was a problem connecting to the Stripe API endpoint.', 'woocommerce-gateway-stripe' ) );
}

$response_body = json_decode( $response['body'] );

WC_Stripe_Logger::debug( "Stripe API response: GET {$api}", [ 'response' => $response_body ] );
WC_Stripe_Logger::debug(
"Stripe API response: GET {$api}",
[
'transact_api_enabled' => $is_transact_api_enabled,
'response' => $response_body,
]
);

return $response_body;
}
Expand Down
9 changes: 0 additions & 9 deletions includes/class-wc-stripe-transact-account-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@ public function __construct( WC_Stripe_UPE_Payment_Gateway $gateway ) {
* @return void
*/
public function do_onboarding(): void {
$stripe_connect = woocommerce_gateway_stripe()->connect;
$mode = WC_Stripe_Mode::is_test() ? 'test' : 'live';
$oauth_connected = (bool) $stripe_connect->is_connected_via_oauth( $mode );

// Check that the merchant is connected via OAuth. Only begin onboarding if this minimum requirement is met.
if ( ! $oauth_connected ) {
return;
}

// Register with Jetpack if not already connected.
$jetpack_connection_manager = $this->gateway->get_jetpack_connection_manager();
if ( ! $jetpack_connection_manager ) {
Expand Down
112 changes: 112 additions & 0 deletions includes/class-wc-stripe-transact-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* WC_Stripe_Transact_API class.
*
* Handles Transact API requests.
*/
class WC_Stripe_Transact_API {

/**
* The API version for the proxy endpoint.
*
* @var int
*/
private const WPCOM_PROXY_ENDPOINT_API_VERSION = 2;

/**
* The timeout for requests to the WPCOM proxy endpoint.
*
* @var int
*/
private const WPCOM_PROXY_REQUEST_TIMEOUT = 60;

/**
* The base for the proxy REST endpoint.
*
* @var string
*/
private const WPCOM_PROXY_REST_BASE = 'transact/stripe/proxy/v1';

/**
* Instance of WC_Stripe_Transact_API.
*
* @var WC_Stripe_Transact_API
*/
private static $instance = null;

/**
* Get instance of WC_Stripe_Transact_API.
*
* @return WC_Stripe_Transact_API
*/
public static function get_instance(): self {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}

/**
* Check if the Transact API is enabled.
*
* @return bool
*/
public function is_transact_api_enabled(): bool {

// TODO: Add a feature flag and check.

$jetpack_connection_manager = new \Automattic\Jetpack\Connection\Manager( 'woocommerce' );

if ( ! $jetpack_connection_manager->is_connected() ) {
return false;
}

$stripe_settings = WC_Stripe_Helper::get_stripe_settings();
if ( ! $stripe_settings || ! isset( $stripe_settings['transact_onboarding_complete'] ) ) {
return false;
}

return 'yes' === $stripe_settings['transact_onboarding_complete'];
}

/**
* Send a request to the Transact platform.
*
* @param string $method The HTTP method to use.
* @param string $endpoint The endpoint to request.
* @param array $headers The headers to send.
* @param array $request_body The request body.
*
* @return array|null The API response body, or null if the request fails.
*/
public function send_wpcom_proxy_request( $method, $endpoint, $headers = [], $request_body = [] ) {
$site_id = \Jetpack_Options::get_option( 'id' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to add a check for $this->is_transact_api_enabled(), just to make sure we fail quickly if those conditions are not met.

if ( ! $site_id ) {
WC_Stripe_Logger::error( sprintf( 'Site ID not found. Cannot send request to %s.', $endpoint ) );
throw new Exception( 'Site ID not found. Cannot send proxy request.' );
}

if ( isset( $headers['Authorization'] ) ) {
$headers['Stripe-Authorization'] = $headers['Authorization'];
unset( $headers['Authorization'] );
}

$response = \Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_blog(
sprintf( '/sites/%d/%s/%s', $site_id, self::WPCOM_PROXY_REST_BASE, $endpoint ),
self::WPCOM_PROXY_ENDPOINT_API_VERSION,
[
'headers' => $headers,
'method' => $method,
'timeout' => 70,
],
'GET' === $method ? null : wp_json_encode( $request_body ),
'wpcom'
);

return $response;
}
}
1 change: 1 addition & 0 deletions includes/class-wc-stripe.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public function init() {
require_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-payment-method-configurations.php';
require_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-database-cache-prefetch.php';
include_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-api.php';
include_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-transact-api.php';
include_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-mode.php';
include_once WC_STRIPE_PLUGIN_PATH . '/includes/class-wc-stripe-transact-account-manager.php';
require_once WC_STRIPE_PLUGIN_PATH . '/includes/compat/class-wc-stripe-subscriptions-helper.php';
Expand Down