PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 1.8.1
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v1.8.1
3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 2.3.2 2.3.3 All 194 releases
convertkit / includes / class-convertkit-api.php

class-convertkit-api.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 1.8.1, at includes/class-convertkit-api.php

564 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit API class
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 use oldmine\RelativeToAbsoluteUrl\RelativeToAbsoluteUrl;
10 use KubAT\PhpSimple\HtmlDomParser;
11
12 /**
13 * ConvertKit_API Class
14 * Establishes API connection to ConvertKit App
15 */
16 class ConvertKit_API {
17
18 /**
19 * ConvertKit API Key
20 *
21 * @var string
22 */
23 protected $api_key;
24
25 /**
26 * ConvertKit API Secret
27 *
28 * @var string
29 */
30 protected $api_secret;
31
32 /**
33 * Save debug data to log
34 *
35 * @var string
36 */
37 protected $debug;
38
39 /**
40 * Version of ConvertKit API
41 *
42 * @var string
43 */
44 protected $api_version = 'v3';
45
46 /**
47 * ConvertKit API URL
48 *
49 * @var string
50 */
51 protected $api_url_base = 'https://api.convertkit.com/';
52
53 /**
54 * API resources
55 *
56 * @var array
57 */
58 protected $resources = array();
59
60 /**
61 * Additional markup
62 *
63 * @var array
64 */
65 protected $markup = array();
66
67 /**
68 * Constructor for ConvertKitAPI instance
69 *
70 * @param string $api_key ConvertKit API Key.
71 * @param string $api_secret ConvertKit API Secret.
72 * @param string $debug Save data to log.
73 */
74 public function __construct( $api_key, $api_secret, $debug ) {
75 $this->api_key = $api_key;
76 $this->api_secret = $api_secret;
77 $this->debug = $debug;
78 }
79
80 /**
81 * Adds a tag to a subscriber
82 *
83 * @param int $tag Tag ID
84 * @param array $options Array of user data
85 * @return object
86 */
87 public function add_tag( $tag, $options ) {
88 $request = $this->api_version . sprintf( '/tags/%s/subscribe', $tag );
89
90 $args = array(
91 'api_key' => $this->api_key,
92 'email' => $options['email'],
93 );
94
95 return $this->make_request( $request, 'POST', $args );
96 }
97
98 /**
99 * Update resources in local options table.
100 *
101 * @param string $api_key
102 */
103 public function update_resources( $api_key ) {
104
105 $this->api_key = $api_key;
106
107 $forms = array();
108 $landing_pages = array();
109 $tags = array();
110
111 WP_ConvertKit::log( 'Updating resource with API key: ' . $api_key );
112 // Forms and Landing Pages
113 $api_response = $this->_get_api_response( 'forms' );
114 WP_ConvertKit::log( 'API Response: ' . print_r( $api_response, true ) );
115
116 if ( is_null( $api_response )
117 || is_wp_error( $api_response )
118 || isset( $api_response['error'] )
119 || isset( $api_response['error_message'] ) ) {
120 $error_message = isset( $api_response['error'] ) ? $api_response['error'] : 'unknown error';
121 $error_message .= is_wp_error( $api_response ) ? $api_response->get_error_message() : '';
122 WP_ConvertKit::log( 'Error contacting API: ' . $error_message );
123
124 $forms[0] = array(
125 'id' => '-2',
126 'name' => 'Error contacting API',
127 );
128
129 $update_forms = $this->maybe_update_option( 'convertkit_forms', $forms );
130 $update_landing_pages = $this->maybe_update_option( 'convertkit_landing_pages', $landing_pages );
131 $update_tags = $this->maybe_update_option( 'convertkit_tags', $tags );
132
133 } else {
134
135 $response = isset( $api_response['forms'] ) ? $api_response['forms'] : array();
136 foreach ( $response as $form ) {
137 if ( isset( $form['archived'] ) && $form['archived'] ) {
138 continue;
139 }
140
141 if ( 'hosted' === $form['type'] ) {
142 $landing_pages[ $form['id'] ] = $form;
143 } else {
144 $forms[ $form['id'] ] = $form;
145 }
146 }
147 $update_forms = $this->maybe_update_option( 'convertkit_forms', $forms );
148 $update_landing_pages = $this->maybe_update_option( 'convertkit_landing_pages', $landing_pages );
149
150 // Tags
151 $api_response = $this->_get_api_response( 'tags' );
152 $response = isset( $api_response['tags'] ) ? $api_response['tags'] : array();
153 foreach ( $response as $tag ) {
154 $tags[] = $tag;
155 }
156 $update_tags = $this->maybe_update_option( 'convertkit_tags', $tags );
157 }
158
159 return $update_forms && $update_landing_pages && $update_tags;
160 }
161
162 /**
163 * Attempt to store updated forms, tags, or landing pages retrieved from the ConvertKit API.
164 * If they match what is already stored, WordPress's built in update_option() function will return
165 * false, which is unhelpful. So, if it returns false, we check if what we tried to store matches what is
166 * already stored, and if so, we return true.
167 *
168 * This way, we know if there was a failure or not.
169 *
170 * @param string $option_name
171 * @param mixed $option_value
172 *
173 * @return bool true if option was updated or if no update was needed, false if failure
174 */
175 public function maybe_update_option( $option_name, $option_value ) {
176 $result = update_option( $option_name, $option_value );
177
178 if ( !$result ) {
179 $old = get_option( $option_name, $option_value );
180 $result = $old === $option_value ? true : false;
181 }
182
183 return $result;
184 }
185
186 /**
187 * Gets a resource index
188 *
189 * GET /{$resource}/
190 *
191 * @param string $resource Resource type.
192 * @return object API response
193 */
194 public function get_resources( $resource ) {
195
196 if ( ! array_key_exists( $resource, $this->resources ) ) {
197
198 if ( 'landing_pages' === $resource ) {
199 $api_response = $this->_get_api_response( 'forms' );
200 } else {
201 $api_response = $this->_get_api_response( $resource );
202 }
203
204 if ( is_null( $api_response ) || is_wp_error( $api_response ) || isset( $api_response['error'] ) || isset( $api_response['error_message'] ) ) {
205 $this->resources[ $resource ] = array(
206 array(
207 'id' => '-2',
208 'name' => 'Error contacting API',
209 ),
210 );
211 } else {
212 $_resource = array();
213
214 if ( 'forms' === $resource ) {
215 $response = isset( $api_response['forms'] ) ? $api_response['forms'] : array();
216 $forms = array();
217 foreach ( $response as $form ) {
218 if ( isset( $form['archived'] ) && $form['archived'] ) {
219 continue;
220 }
221 $_resource[] = $form;
222 $forms[ $form['id'] ] = $form;
223
224 }
225 update_option( 'convertkit_forms', $forms );
226 } elseif ( 'landing_pages' === $resource ) {
227
228 $response = isset( $api_response['forms'] ) ? $api_response['forms'] : array();
229 foreach ( $response as $landing_page ) {
230 if ( 'hosted' === $landing_page['type'] ) {
231 if ( isset( $landing_page['archived'] ) && $landing_page['archived'] ) {
232 continue;
233 }
234 $_resource[] = $landing_page;
235 }
236 }
237 update_option( 'convertkit_landing_pages', $_resource );
238 } elseif ( 'subscription_forms' === $resource ) {
239 foreach ( $api_response as $mapping ) {
240 if ( isset( $mapping['archived'] ) && $mapping['archived'] ) {
241 continue;
242 }
243 $_resource[ $mapping['id'] ] = $mapping['form_id'];
244 }
245 update_option( 'convertkit_subscription_forms', $_resource );
246 } elseif ( 'tags' === $resource ) {
247 $response = isset( $api_response['tags'] ) ? $api_response['tags'] : array();
248 foreach ( $response as $tag ) {
249 $_resource[] = $tag;
250 }
251 update_option( 'convertkit_tags', $_resource );
252 }
253
254 $this->resources[ $resource ] = $_resource;
255 } // End if().
256 } // End if().
257
258 return $this->resources[ $resource ];
259 }
260
261 /**
262 * Adds a subscriber to a form.
263 *
264 * @param string $form_id Form ID.
265 * @param array $options Array of user data.
266 */
267 public function form_subscribe( $form_id, $options ) {
268 $request = $this->api_version . sprintf( '/forms/%s/subscribe', $form_id );
269
270 $args = array(
271 'api_key' => $this->api_key,
272 'email' => $options['email'],
273 'name' => $options['name'],
274 );
275
276 $this->make_request( $request, 'POST', $args );
277 }
278
279 /**
280 * Remove subscription from a form
281 *
282 * @param array $options Array of user data.
283 */
284 public function form_unsubscribe( $options ) {
285 $request = $this->api_version . '/unsubscribe';
286
287 $args = array(
288 'api_secret' => $this->api_secret,
289 'email' => $options['email'],
290 );
291
292 $this->make_request( $request, 'PUT', $args );
293 }
294
295 /**
296 * Get the ConvertKit subscriber ID associated with email address if it exists.
297 * Return 0 if subscriber not found.
298 *
299 * @param $email_address
300 * @return int $subscriber_id
301 */
302 public function get_subscriber_id( $email_address ) {
303
304 $url = add_query_arg(
305 array(
306 'api_secret' => WP_ConvertKit::get_api_secret(),
307 'email_address' => $email_address,
308 'status' => 'all',
309 ),
310 'https://api.convertkit.com/v3/subscribers'
311 );
312
313 WP_ConvertKit::log( 'get_subscriber_id for: ' . $email_address );
314
315 $result = $this->get_resource( $url );
316 if ( is_wp_error( $result ) ) {
317 WP_ConvertKit::log( 'Error getting resource for: ' . $url . '. Error: ' . $result->get_error_messages() );
318 return 0;
319 }
320
321 $subs = json_decode( $result );
322
323 $subscribers = is_array( $subs->subscribers ) ? $subs->subscribers : array();
324 if ( $subscribers ) {
325 $subscriber = array_pop( $subscribers );
326 WP_ConvertKit::log( 'Found ' . count( $subscribers ) . ' subscribers' );
327 WP_ConvertKit::log( 'ID (' . $subscriber->id . ') ' . $subscriber->email_address );
328 return $subscriber->id;
329 }
330 WP_ConvertKit::log( 'Subscriber not found with email ' . $email_address );
331 // subscriber not found
332 return 0;
333
334 }
335
336 /**
337 * @param $subscriber_id
338 *
339 * @return int
340 */
341 public function get_subscriber( $subscriber_id ) {
342 $url = add_query_arg(
343 array(
344 'api_secret' => WP_ConvertKit::get_api_secret(),
345 ),
346 'https://api.convertkit.com/v3/subscribers/' . $subscriber_id
347 );
348
349 WP_ConvertKit::log( 'get_subscriber info for id: ' . $subscriber_id );
350
351 $result = $this->get_resource( $url );
352 if ( is_wp_error( $result ) ) {
353 WP_ConvertKit::log( 'Error getting resource for: ' . $url . '. Error: ' . $result->get_error_messages() );
354 return 0;
355 }
356
357 $result = json_decode( $result );
358
359 if ( isset( $result->subscriber ) ) {
360 $subscriber = $result->subscriber;
361 WP_ConvertKit::log( 'Found: (' . $subscriber->id . ') ' . $subscriber->email_address );
362 return $subscriber;
363 }
364
365 // subscriber not found
366 return 0;
367 }
368
369 /**
370 * Get a list of the tags for a subscriber.
371 *
372 * @param $subscriber_id
373 * @return array $subscriber_tags Array of tags for customer with key of tag_id
374 */
375 public function get_subscriber_tags( $subscriber_id ) {
376
377 $url = add_query_arg(
378 array(
379 'api_key' => WP_ConvertKit::get_api_key(),
380 ),
381 'https://api.convertkit.com/v3/subscribers/' . $subscriber_id . '/tags'
382 );
383
384 WP_ConvertKit::log( 'get_subscriber_tags for: ' . $subscriber_id );
385
386 $result = $this->get_resource( $url );
387 if ( is_wp_error( $result ) ) {
388 WP_ConvertKit::log( 'Error getting resource for: ' . $url . '. Error: ' . $result->get_error_messages() );
389 return array();
390 }
391 $result = json_decode( $result );
392 $tags = isset( $result->tags ) ? $result->tags : array();
393
394 if ( empty( $tags ) ) {
395 WP_ConvertKit::log( 'No tags found for customer.' );
396 } else {
397 WP_ConvertKit::log( 'Found ' . count( $tags ) . 'tags for subscriber' );
398 $tags = wp_list_pluck( $tags, 'name', 'id' );
399 }
400
401 return $tags;
402
403 }
404
405 /**
406 * Get markup from ConvertKit for the provided $url
407 *
408 * @param string $url URL of API action.
409 * @return string
410 */
411 public function get_resource( $url ) {
412 $resource = '';
413
414 if ( ! empty( $url ) && isset( $this->markup[ $url ] ) ) {
415 $resource = $this->markup[ $url ];
416 } elseif ( ! empty( $url ) ) {
417 WP_ConvertKit::log( 'API Request (get_resource): ' . $url );
418
419 $response = wp_remote_get(
420 $url,
421 array(
422 'timeout' => 10,
423 'Accept-Encoding' => 'gzip',
424 )
425 );
426
427 if ( ! is_wp_error( $response ) ) {
428
429 // Maybe inflate response body.
430 // @see https://wordpress.stackexchange.com/questions/10088/how-do-i-troubleshoot-responses-with-wp-http-api
431 $inflate = @gzinflate( $response['body'] );
432 if ( $inflate ) {
433 $response['body'] = $inflate;
434 }
435
436 $body = wp_remote_retrieve_body( $response );
437
438 /** @var \simple_html_dom\simple_html_dom $html */
439 $html = HtmlDomParser::str_get_html( $body );
440 foreach ( $html->find( 'a, link' ) as $element ) {
441 if ( isset( $element->href ) ) {
442 $element->href = RelativeToAbsoluteUrl::urlToAbsolute( $url, $element->href );
443 }
444 }
445
446 foreach ( $html->find( 'img, script' ) as $element ) {
447 if ( isset( $element->src ) ) {
448 $element->src = RelativeToAbsoluteUrl::urlToAbsolute( $url, $element->src );
449 }
450 }
451
452 foreach ( $html->find( 'form' ) as $element ) {
453 if ( isset( $element->action ) ) {
454 $element->action = RelativeToAbsoluteUrl::urlToAbsolute( $url, $element->action );
455 } else {
456 $element->action = $url;
457 }
458 }
459
460 // Check `status_code` for 200, otherwise log error.
461 if ( 200 === $response['response']['code'] ) {
462 $resource = $html->save();
463 $this->markup[ $url ] = $resource;
464 } else {
465 WP_ConvertKit::log( 'Status Code (' . $response['response']['code'] . ') for URL (' . $url . '): ' . $html->save() );
466 }
467 } else {
468 WP_ConvertKit::log( 'API Response was WP_Error (get_resource): ' .
469 'Code: ' . $response->get_error_code() . ' ' .
470 'Message: ' . $response->get_error_message()
471 );
472 } // End if().
473 } // End if().
474
475 return $resource;
476 }
477
478 /**
479 * Do a remote request.
480 *
481 * @param string $path Part of URL.
482 * @return array
483 */
484 private function _get_api_response( $path = '' ) {
485
486 $args = array(
487 'api_key' => $this->api_key,
488 );
489 $api_path = $this->api_url_base . $this->api_version;
490 $url = add_query_arg( $args, path_join( $api_path, $path ) );
491
492 $response = wp_remote_get(
493 $url,
494 array(
495 'timeout' => 10,
496 'Accept-Encoding' => 'gzip',
497 'sslverify' => false,
498 )
499 );
500
501 if ( is_wp_error( $response ) ) {
502 WP_ConvertKit::log( 'Error: ' . $response->get_error_message() );
503
504 return array(
505 'error' => $response->get_error_message(),
506 );
507 } else {
508
509 // Maybe inflate response body.
510 // @see https://wordpress.stackexchange.com/questions/10088/how-do-i-troubleshoot-responses-with-wp-http-api
511 $inflate = @gzinflate( $response['body'] );
512 if ( $inflate ) {
513 $response['body'] = $inflate;
514 }
515
516 $body = wp_remote_retrieve_body( $response );
517 $data = json_decode( $body, true );
518 }
519
520 return $data;
521 }
522
523 /**
524 * Make a request to the ConvertKit API
525 *
526 * @param string $request Request string.
527 * @param string $method HTTP Method.
528 * @param array $args Request arguments.
529 * @return object Response object
530 */
531 private function make_request( $request, $method, $args = array() ) {
532
533 WP_ConvertKit::log( 'API Request (make_request): ' . $request . ' Args: ' . wp_json_encode( $args ) );
534
535 $url = $this->api_url_base . $request;
536
537 $headers = array(
538 'Content-Type' => 'application/json; charset=utf-8',
539 );
540
541 $settings = array(
542 'headers' => $headers,
543 'method' => $method,
544 'body' => wp_json_encode( $args ),
545 );
546
547 $result = wp_remote_request( $url, $settings );
548
549 if ( is_wp_error( $result ) ) {
550 WP_ConvertKit::log( 'API Response (make_request): WPError: ' . $result->get_error_message() );
551 } elseif ( isset( $result['response']['code'] ) && '200' === $result['response']['code'] ) {
552 if ( isset( $result['body'] ) ) {
553 WP_ConvertKit::log( 'API Response (make_request): ' . $result['body'] );
554 } else {
555 WP_ConvertKit::log( 'API Response (make_request): Response code 200, but body is not set.' );
556 }
557 } else {
558 WP_ConvertKit::log( 'API Response (make_request): Result code: ' . $result['response']['code'] . ' ' . $result['response']['message'] );
559 }
560
561 }
562
563 }
564