oont-contents/plugins/tiktok-for-business/mapi/Tt4b_Mapi_Class.php
2025-03-31 21:42:48 +02:00

474 lines
15 KiB
PHP

<?php
/**
* Copyright (c) Bytedance, Inc. and its affiliates. All Rights Reserved
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
* @package TikTok
*/
if ( ! defined( 'ABSPATH' ) ) {
die;
}
require_once __DIR__ . '/../utils/utilities.php';
class Tt4b_Mapi_Class {
/**
* The TikTok Ads endpoint base url.
*
* @var string
*/
protected $mapi_url;
/**
* The TBP endpoint base url.
*
* @var string
*/
protected $tbp_url;
/**
* The plugin endpoint base url.
*
* @var string
*/
protected $plugin_url;
/**
* The wc_get_logger interface.
*
* @var WC_Logger_Interface
*/
protected $logger;
/**
* Constructor
*
* @return void
*/
public function __construct( Logger $logger ) {
$this->mapi_url = 'https://business-api.tiktok.com/open_api/';
$this->tbp_url = 'https://business-api.tiktok.com/tbp/';
$this->plugin_url = 'https://business-api.tiktok.com/plugin/';
$this->logger = $logger;
}
/**
* Initializes actions related to Tt4b_Mapi_Class such as eligibility information collection
*/
public function init() {
add_action( 'tt4b_trust_signal_collection', array( $this, 'retrieve_eligibility_information' ), 1, 0 );
add_action( 'tt4b_trust_signal_helper', array( $this, 'retrieve_eligibility_helper' ), 2, 1 );
}
/**
* Posts to business-api.tiktok.com
*
* @param string $endpoint The endpoint for the mapi post
* @param string $access_token The MAPI issued access token
* @param array $params Whichever params to be included with the post
* @param string $version The MAPI version used
*
* @return string
*/
public function mapi_post( $endpoint, $access_token, $params, $version ) {
$url = $this->mapi_url . $version . '/' . $endpoint;
$args = array(
'method' => 'POST',
'data_format' => 'body',
'headers' => array(
'Access-Token' => $access_token,
'Content-Type' => 'application/json',
),
'body' => json_encode( $params ),
);
$this->logger->log_request( $url, $args );
$response = wp_remote_post( $url, $args );
$this->logger->log_response( __METHOD__, $response );
$body = wp_remote_retrieve_body( $response );
return $body;
}
/**
* Get from business-api.tiktok.com
*
* @param string $endpoint The endpoint for the mapi post
* @param string $access_token The MAPI issued access token
* @param array $params Whichever params to be included with the post
* @param string $version The MAPI version used
*
* @return string
*/
public function mapi_get( $endpoint, $access_token, $params, $version ) {
$url = $this->mapi_url . $version . '/' . $endpoint . '?' . http_build_query( $params );
$args = array(
'method' => 'GET',
'headers' => array(
'Access-Token' => $access_token,
'Content-Type' => 'application/json',
),
);
$this->logger->log_request( $url, $args );
$result = wp_remote_get( $url, $args );
$this->logger->log_response( __METHOD__, $result );
$body = wp_remote_retrieve_body( $result );
return $body;
}
/**
* Get from tbp/business_profile
*
* @param string $access_token The MAPI issued access token
* @param string $external_business_id The exteneral business_id of the merchant
*
* @return string
*/
public function get_business_profile( $access_token, $external_business_id ) {
// returns a raw API response from TikTok tbp/business_profile/get/ endpoint
if ( false === $external_business_id ) {
$this->logger->log( __METHOD__, 'external_business_id not found, exiting' );
return '';
}
$url = 'tbp/business_profile/get/';
$params = array(
'business_platform' => 'WOO_COMMERCE',
'external_business_id' => $external_business_id,
'full_data' => 1,
);
$result = $this->mapi_get( $url, $access_token, $params, 'v1.2' );
return $result;
}
/**
*
* Get from api/user/location
*
* @return string
*/
public function get_user_location() {
// returns a raw API response from TikTok api/user/location endpoint
$url = 'https://ads.tiktok.com/creative_hub_server/api/user/location';
$this->logger->log_request( $url, array() );
$result = wp_remote_get( $url );
$this->logger->log_response( __METHOD__, $result );
return wp_remote_retrieve_body( $result );
}
/**
* Post to tbp
*
* @param string $external_data The external data
* @param string $endpoint The endpoint
* @param string $version The version
* @param array $params The body of the request
* @param int $tbp_api_type Which url to use based on the TBPApi abstract class
*
* @return string
*/
public function tbp_post( $external_data, $endpoint, $version, $params, $tbp_api_type ) {
// posts to TBP
switch ( $tbp_api_type ) {
case TBPApi::PLUGIN:
$domain = $this->plugin_url;
break;
default:
$domain = $this->tbp_url;
break;
}
$base_url = $domain . $version . '/' . $endpoint;
$url = $base_url . '?external_data=' . $external_data;
$args = array(
'method' => 'POST',
'data_format' => 'body',
'headers' => array(
'Content-Type' => 'application/json',
),
'body' => json_encode( $params ),
);
$this->logger->log_request( $url, $args );
$response = wp_remote_post( $url, $args );
$this->logger->log_response( __METHOD__, $response );
return wp_remote_retrieve_body( $response );
}
/**
* Update from tbp/business_profile
*
* @param string $access_token The MAPI issued access token
* @param string $external_business_id The external business_id of the merchant
* @param integer $total_gmv The merchant's total gmv
* @param integer $total_orders The merchant's total orders
* @param integer $total_orders The merchant's tenure in days
* @param string $current_tiktok_for_woocommerce_version The current tiktok-for-woocommerce version
*
* @return void
*/
public function update_business_profile( $access_token, $external_business_id, $total_gmv, $total_orders, $days_since_first_order, $current_tiktok_for_woocommerce_version ) {
// updates the business_profile. Used for updating a merchants eligibility criteria.
if ( false === $external_business_id ) {
$this->logger->log( __METHOD__, 'external_business_id not found, exiting' );
}
$url = 'tbp/business_profile/store/update/';
$net_gmv = array(
array(
'interval' => 'LIFETIME',
'max' => $total_gmv,
'min' => $total_gmv,
'unit' => 'CURRENCY',
),
);
$net_order_count = array(
array(
'interval' => 'LIFETIME',
'max' => $total_orders,
'min' => $total_orders,
'unit' => 'COUNT',
),
);
$tenure = array(
'min' => $days_since_first_order,
'max' => $days_since_first_order,
'unit' => 'DAYS',
);
$params = array(
'business_platform' => 'WOO_COMMERCE',
'external_business_id' => $external_business_id,
'net_gmv' => $net_gmv,
'net_order_count' => $net_order_count,
'tenure' => $tenure,
'extra_data' => $current_tiktok_for_woocommerce_version,
);
$this->mapi_post( $url, $access_token, $params, 'v1.2' );
}
/**
* Returns a raw API response from TikTok
* marketing_api/api/developer/app/create_auto_approve/
*
* @param string $smb_id The merchants external_business_id
* @param string $smb_name The MAPI issued access token
* @param string $redirect_uri The redirect_url (the store url)
*
* @return string|bool
*/
public function create_open_source_app( $smb_id, $smb_name, $redirect_uri ) {
$url = 'https://ads.tiktok.com/marketing_api/api/developer/app/create_auto_approve/';
$open_source_token = '244e1de7-8dad-4656-a859-8dc09eea299d';
$tries = 0;
$params = array(
'business_platform' => 'PROD',
'smb_id' => $smb_id,
'smb_name' => $smb_name,
'redirect_url' => $redirect_uri,
);
$args = array(
'method' => 'POST',
'data_format' => 'body',
'headers' => array(
'Access-Token' => $open_source_token,
'Content-Type' => 'application/json',
'Referer' => 'https://ads.tiktok.com',
),
'body' => json_encode( $params ),
);
$this->logger->log_request( $url, $args );
while ( $tries <= 3 ) {
$response = wp_remote_post( $url, $args );
++$tries;
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
$this->logger->log_response( __METHOD__, $response );
} else {
$this->logger->log_response( __METHOD__, $response );
return wp_remote_retrieve_body( $response );
}
}
return false;
}
/**
* Returns a raw API response from TikTok oauth2/access_token_v2/ endpoint
*
* @param string $app_id The MAPI app_id
* @param string $secret The MAPI secret
* @param string $auth_code The auth_code
* @param string $version The MAPI version used
*
* @return string
*/
public function get_access_token( $app_id, $secret, $auth_code, $version ) {
$endpoint = 'oauth2/access_token/';
$url = $this->mapi_url . $version . '/' . $endpoint;
$params = array(
'app_id' => $app_id,
'secret' => $secret,
'auth_code' => $auth_code,
);
$args = array(
'method' => 'POST',
'data_format' => 'body',
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => json_encode( $params ),
);
$this->logger->log_request( $url, $args );
$response = wp_remote_post( $url, $args );
$this->logger->log_response( __METHOD__, $response );
$body = wp_remote_retrieve_body( $response );
return $body;
}
/**
* Returns a cron string with randomized hour and minute values for scheduling recurring eligibility collection
*
* @return string
*/
private function generate_cron_string() {
$minute = rand( 0, 59 );
$hour = rand( 0, 23 );
return '' . $minute . ' ' . $hour . ' * * 0-6';
}
/**
* Begins first eligibility information collection process, and scheduled recurring collection if not already scheduled
*
* @return void
*/
public function fetch_eligibility() {
// only fetch if using woocommerce
if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
return;
}
$currentGroup = 'tt4b_version_' . get_option( 'tt4b_version' );
if ( false === as_has_scheduled_action( 'tt4b_trust_signal_collection', array(), $currentGroup ) ) {
// if no scheduled trust signal collection actions with $currentGroup group name, but there are scheduled actions (with no group name, or with other group names)
// that means there are scheduled actions from a previous version that should be removed and replaced with scheduled actions from the latest
if ( true === as_has_scheduled_action( 'tt4b_trust_signal_collection' ) ) {
// deletes scheduled actions from previous version
as_unschedule_all_actions( 'tt4b_trust_signal_collection' );
as_unschedule_all_actions( 'tt4b_trust_signal_helper' );
}
as_enqueue_async_action( 'tt4b_trust_signal_collection' );
$cronStr = $this->generate_cron_string();
as_schedule_cron_action( strtotime( 'tomorrow' ), $cronStr, 'tt4b_trust_signal_collection', array(), $currentGroup );
}
}
/**
* Retrieves eligibility information from merchants woocommerce store via creation of retrieve_eligibility_helper functions for batches of 20 orders
*
* @return void
*/
public function retrieve_eligibility_information() {
// only fetch if using woocommerce
if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
return;
}
$args = array(
'post_status' => 'wc-completed',
'paginate' => true,
'limit' => 100,
);
$result = wc_get_orders( $args );
$pages = $result->max_num_pages;
update_option( 'tt4b_mapi_total_gmv', 0 );
update_option( 'tt4b_mapi_total_orders', 0 );
update_option( 'tt4b_mapi_tenure', 0 );
update_option( 'tt4b_eligibility_page_total', $pages );
$oldest_orders = ( new WC_Order_Query(
array(
'limit' => 1,
'orderby' => 'date',
'order' => 'ASC',
)
) )->get_orders();
if ( count( $oldest_orders ) > 0 ) {
$oldest_order_timestamp = $oldest_orders[0]->get_date_created()->getTimestamp();
$mapi_tenure = (int) ( ( time() - $oldest_order_timestamp ) / DAY_IN_SECONDS );
update_option( 'tt4b_mapi_tenure', $mapi_tenure );
}
if ( false === as_has_scheduled_action( 'tt4b_trust_signal_helper', array( 'page' => 1 ) ) ) {
as_enqueue_async_action( 'tt4b_trust_signal_helper', array( 'page' => 1 ) );
}
}
/**
* Helper function used to calculate eligibility information in batches of 20
*
* @param integer $page The page of orders from woocommerce
*
* @return void
*/
public function retrieve_eligibility_helper( $page ) {
// only fetch if using woocommerce
if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
return;
}
$orders = wc_get_orders(
array(
'post_status' => 'wc-completed',
'limit' => 100,
'page' => $page,
)
);
foreach ( $orders as $order ) {
if ( is_null( $order ) ) {
break;
}
$order_total = $order->get_total();
if ( $order_total > 0 ) {
$mapi_total_gmv = get_option( 'tt4b_mapi_total_gmv' );
$mapi_total_gmv += intval( $order_total );
update_option( 'tt4b_mapi_total_gmv', $mapi_total_gmv );
$mapi_total_orders = get_option( 'tt4b_mapi_total_orders' );
++$mapi_total_orders;
update_option( 'tt4b_mapi_total_orders', $mapi_total_orders );
}
if ( 0 === count( $orders ) ) {
break;
}
}
$page_total = get_option( 'tt4b_eligibility_page_total' );
++$page;
if ( ( $page <= $page_total ) && ( false === as_has_scheduled_action( 'tt4b_trust_signal_helper', array( 'page' => $page ) ) ) ) {
as_enqueue_async_action( 'tt4b_trust_signal_helper', array( 'page' => $page ) );
} else {
$access_token = get_option( 'tt4b_access_token' );
$external_business_id = get_option( 'tt4b_external_business_id' );
$total_gmv = intval( get_option( 'tt4b_mapi_total_gmv' ) );
$total_orders = intval( get_option( 'tt4b_mapi_total_orders' ) );
$tenure = intval( get_option( 'tt4b_mapi_tenure' ) );
$version = get_option( 'tt4b_version' );
$this->update_business_profile( $access_token, $external_business_id, $total_gmv, $total_orders, $tenure, $version );
}
}
/**
* TTS Disconnect
*
* @param string $external_data The external data
*
* @return void
*/
public function tts_shop_disconnect( $external_data ) {
$base_url = 'https://business-api.tiktok.com/tbp/v2.0/shop/connection/disconnect';
$url = $base_url . '?external_data=' . $external_data;
$args = array(
'method' => 'POST',
'headers' => array(
'Content-Type' => 'application/json',
),
);
$this->logger->log_request( $url, $args );
$response = wp_remote_post( $url, $args );
$this->logger->log_response( __METHOD__, $response );
}
}