class-wc-payments-http.php
6.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<?php
/**
* WC_Payments_Http class.
*
* @package WooCommerce\Payments
*/
defined( 'ABSPATH' ) || exit;
use WCPay\Exceptions\API_Exception;
use WCPay\Exceptions\Connection_Exception;
use WCPay\Logger;
/**
* A wrapper around Jetpack HTTP request library. Necessary to increase
* the testability of WC_Payments_API_Client, and allow dependency
* injection.
*/
class WC_Payments_Http implements WC_Payments_Http_Interface {
/**
* Jetpack connection handler.
*
* @var Automattic\Jetpack\Connection\Manager
*/
private $connection_manager;
/**
* WC_Payments_Http constructor.
*
* @param Automattic\Jetpack\Connection\Manager $connection_manager - Jetpack connection handler.
*/
public function __construct( $connection_manager ) {
$this->connection_manager = $connection_manager;
add_filter( 'allowed_redirect_hosts', [ $this, 'allowed_redirect_hosts' ] );
}
/**
* Sends a remote request through Jetpack.
*
* @param array $args - The arguments to passed to Jetpack.
* @param string $body - The body passed on to the HTTP request.
* @param bool $is_site_specific - If true, the site ID will be included in the request url. Defaults to true.
* @param bool $use_user_token - If true, the request will be signed with the user token rather than blog token. Defaults to false.
*
* @return array HTTP response on success.
* @throws API_Exception - If not connected or request failed.
*/
public function remote_request( $args, $body = null, $is_site_specific = true, $use_user_token = false ) {
// Make sure we're not sending requests if Jetpack is not connected.
if ( ! $this->is_connected() ) {
Logger::error( 'HTTP_REQUEST_ERROR Site is not connected to WordPress.com' );
throw new API_Exception(
__( 'Site is not connected to WordPress.com', 'woocommerce-payments' ),
'wcpay_wpcom_not_connected',
409 // HTTP Conflict status code.
);
}
$args['blog_id'] = $this->get_blog_id();
if ( $use_user_token ) {
$args['user_id'] = $this->connection_manager->get_connection_owner_id();
}
if ( $is_site_specific ) {
// We expect `url` to include a `%s` placeholder which will allow us inject the blog id.
$url = explode( '?', $args['url'], 2 );
$url[0] = sprintf( $url[0], $args['blog_id'] );
$args['url'] = implode( '?', $url );
}
return self::make_request( $args, $body );
}
/**
* Queries the WordPress.com REST API with a user token.
*
* @param string $path REST API path.
* @param string $version REST API version. Default is `2`.
* @param array $args Arguments to {@see WP_Http}. Default is `array()`.
* @param string|array $body Body passed to {@see WP_Http}. Default is `null`.
* @param string $base_api_path REST API root. Default is `wpcom`.
*
* @return array|\WP_Error $response Response data, else WP_Error on failure.
*/
public function wpcom_json_api_request_as_user(
$path,
$version = '2',
$args = [],
$body = null,
$base_api_path = 'wpcom'
) {
return Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_user( $path, $version, $args, $body, $base_api_path );
}
/**
* Makes a request through Jetpack.
*
* @param array $args - The arguments passed to Jetpack.
* @param string $body - The body passed to the HTTP request.
*
* @return array HTTP response on success.
* @throws Connection_Exception - If request returns WP_Error.
*/
private static function make_request( $args, $body ) {
$response = Automattic\Jetpack\Connection\Client::remote_request( $args, $body );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
Logger::error( 'HTTP_REQUEST_ERROR ' . var_export( $response, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
$message = sprintf(
// translators: %1: original error message.
__( 'Http request failed. Reason: %1$s', 'woocommerce-payments' ),
$response->get_error_message()
);
throw new Connection_Exception( $message, 'wcpay_http_request_failed', 500 );
}
return $response;
}
/**
* Checks if Jetpack is connected.
*
* Checks if connection is authenticated in the same way as Jetpack_Client or Jetpack Connection Client does.
*
* @return bool true if Jetpack connection has access token.
*/
public function is_connected() {
return $this->connection_manager->is_plugin_enabled() && $this->connection_manager->is_active();
}
/**
* Checks if the current user is connected to WordPress.com.
*
* @return bool true if the current user is connected.
*/
public function is_user_connected() {
return $this->connection_manager->is_user_connected();
}
/**
* Get the wpcom user data of the current connected user.
*
* @return bool|array An array with the WPCOM user data on success, false otherwise.
*/
public function get_connected_user_data() {
return $this->connection_manager->get_connected_user_data();
}
/**
* Checks if the site has an admin who is also a connection owner.
*
* @return bool True if Jetpack connection has an owner.
*/
public function has_connection_owner() {
return ! empty( $this->connection_manager->get_connection_owner_id() );
}
/**
* Gets the current WP.com blog ID.
*
* @return integer Current WPCOM blog ID.
*/
public function get_blog_id() {
return Jetpack_Options::get_option( 'id' );
}
/**
* Starts the Jetpack connection process. Note that running this function will immediately redirect
* to the Jetpack flow, so any PHP code after it will never be executed.
*
* @param string $redirect - URL to redirect to after the connection process is over.
*
* @throws API_Exception - Exception thrown on failure.
*/
public function start_connection( $redirect ) {
// Mark the plugin as enabled in case it had been soft-disconnected.
$this->connection_manager->enable_plugin();
// Register the site to wp.com.
if ( ! $this->connection_manager->is_connected() ) {
$result = $this->connection_manager->register();
if ( is_wp_error( $result ) ) {
throw new API_Exception( $result->get_error_message(), 'wcpay_jetpack_register_site_failed', 500 );
}
}
// Redirect the user to the Jetpack user connection flow.
add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false' );
// Same logic as in WC-Admin.
$calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, [ 'development', 'wpcalypso', 'horizon', 'stage' ], true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production';
wp_safe_redirect(
add_query_arg(
[
'from' => 'woocommerce-payments',
'calypso_env' => $calypso_env,
],
$this->connection_manager->get_authorization_url( null, $redirect )
)
);
exit;
}
/**
* Filter function to add WP.com to the list of allowed redirect hosts
*
* @param array $hosts - array of allowed hosts.
*
* @return array allowed hosts
*/
public function allowed_redirect_hosts( $hosts ) {
$hosts[] = 'jetpack.wordpress.com';
return $hosts;
}
}