This is an article documenting how to access ZohoCRM with API v2 using PHP and cURL. The first few functions are to manage OAuth v2 and generate the refresh and access tokens. The second snippet of code below is using the functions to read data from Zoho CRM and to write data back to the system.
Why?
I've rewritten this code a few times and want to store the finalized version (following updates) making it as generic as I can in order to apply it to any client.
How?
Firstly, you will need to browse to https://accounts.zoho.eu/developerconsole and register your new app (or the one you will have completed once copying the below scripts).
Let's start with the first PHP file which I will call functions.php. Note that you will need to edit the global vars to be used by the functions located at the top of the script:
copyraw
<?php /* ------------------------------------------------------------------------------------------------ Zoho Authorization via oAuth2.0 for REST API v2 ------------------------------------------------------------------------------------------------ Zoho API v2: https://accounts.zoho.eu/developerconsole Documentation: https://www.zoho.com/crm/help/api/v2/ Available Scopes users users.all org org.all settings settings.all, settings.territories, settings.custom_views, settings.related_lists, settings.modules, settings.tab_groups, settings.fields, settings.layouts, settings.macros, settings.custom_links, settings.custom_buttons, settings.roles, settings.profiles modules modules.all, modules.approvals, modules.leads, modules.accounts, modules.contacts, modules.deals, modules.campaigns, modules.tasks, modules.cases, modules.events, modules.calls, modules.solutions, modules.products, modules.vendors, modules.pricebooks, modules.quotes, modules.salesorders, modules.purchaseorders, modules.invoices, modules.custom, modules.dashboards, modules.notes, modules.activities, modules.search Possible Module Names leads, accounts, contacts, deals, campaigns, tasks, cases, events, calls, solutions, products, vendors, pricebooks, quotes, salesorders, purchaseorders, invoices, custom, notes, approvals, dashboards, search, activities */ // Global vars for Zoho API $zoho_apis_com = "https://www.zohoapis.com"; $zoho_apis_eu = "https://www.zohoapis.eu"; $refresh_access_token_url = "https://accounts.zoho.eu/oauth/v2/token"; // Endpoint: Sandbox // disable after testing $zoho_sandbox = "https://sandbox.zohoapis.eu"; $zoho_sandbox_domain = "https://crmsandbox.zoho.eu/crm"; // Global vars to be used by below functions specific to this app $zoho_client_id = "1000.your_client_id"; $zoho_client_secret = "your_client_secret"; $zoho_redirect_uri = "your_redirect_uri"; // will be URL to start.php in this example $access_token_path = "path_to_your_access_token_not_on_www_but_accessible_by_script/access_token.dat"; $refresh_token_path = "path_to_your_access_token_not_on_www_but_accessible_by_script/refresh_token.dat"; // function to use Zoho API v2 function abZohoApi( $post_url, $post_fields, $post_header=false, $post_type='GET' ) { // setup cURL request $ch=curl_init(); // do not return header information curl_setopt($ch, CURLOPT_HEADER, 0); // submit data in header if specified if(is_array($post_header)){ curl_setopt($ch, CURLOPT_HTTPHEADER, $post_header); } // do not return status info curl_setopt($ch, CURLOPT_VERBOSE, 0); // return data curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // cancel ssl checks curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // if using GET, POST or PUT if($post_type=='POST'){ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields); } else if($post_type=='PUT'){ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields)); } else if($post_type=='DELETE'){ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); }else{ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); if($post_fields){ $post_url.='?'.http_build_query($post_fields); } } // specified endpoint curl_setopt($ch, CURLOPT_URL, $post_url); // execute cURL request $response=curl_exec($ch); // return errors if any if (curl_exec($ch) === false) { $output = curl_error($ch); } else { $output = $response; } // close cURL handle curl_close($ch); // output return $output; } // Generate the refresh token by manually entering the following in the browser // Browse to https://accounts.zoho.eu/oauth/v2/auth?scope=ZohoCRM.modules.ALL,ZohoCRM.users.READ,ZohoCRM.settings.fields.read&client_id=your_client_id&response_type=code&access_type=offline&redirect_uri=your_url_path_to_start_page/start.php&prompt=consent // function to generate refresh token from Zoho authorization code function generate_refresh_token(){ // use vars declared at beginning of script global $zoho_client_id, $zoho_client_secret, $zoho_redirect_uri, $access_token_path, $refresh_token_path; // Generate Access Token and Refresh Token - Read url GET values $zoho_grant_token = $_GET['code']; $zoho_location = $_GET['location']; $zoho_accounts_server = $_GET['accounts-server']; // Generate Access Token and Refresh Token $url_auth=urldecode($zoho_accounts_server)."/oauth/v2/token"; // Build fields to post $fields_token=array("code"=>$zoho_grant_token, "redirect_uri"=>$zoho_redirect_uri, "client_id"=>$zoho_client_id, "client_secret"=>$zoho_client_secret, "grant_type"=>"authorization_code", "prompt"=>"consent"); // Generate Access Token and Refresh Token - post via cURL $response_json = abZohoApi($url_auth, $fields_token, false, 'POST'); // Generate Access Token and Refresh Token - format output (convert JSON to Object) $refresh_token_arr = json_decode($response_json, true); // store in var $refresh_token = isset($refresh_token_arr['refresh_token']) ? $refresh_token_arr['refresh_token'] : 0; // encode value to base64 $refresh_token_base64 = base64_encode($refresh_token); // store encoded value to file file_put_contents($refresh_token_path, $refresh_token_base64); // -- do access token while we're here // store in access token $access_token = isset($refresh_token_arr['access_token']) ? $refresh_token_arr['access_token'] : 0; // encode value to base64 $access_token_base64 = base64_encode($access_token); // store encoded value to file file_put_contents($access_token_path, $access_token_base64); // return array of json objects return $refresh_token_arr; } // function to generate access token from refresh token // returns minutes remaining of valid token function generate_access_token(){ // use vars declared at beginning of script global $zoho_client_id, $zoho_client_secret, $access_token_path, $refresh_token_path, $refresh_access_token_url; // get refresh token from file $refresh_token = base64_decode( file_get_contents( $refresh_token_path ) ); // build fields to post $refresh_fields = array("refresh_token" => $refresh_token, "client_id" => $zoho_client_id, "client_secret" => $zoho_client_secret, "grant_type" => "refresh_token"); // send to Zoho API $this_access_token_json = abZohoApi($refresh_access_token_url, $refresh_fields, false, 'POST'); // convert JSON response to array $access_token_arr = json_decode($this_access_token_json, true); // store in var $returned_token = $access_token_arr['access_token']; // encode value to base64 $access_token_base64 = base64_encode($returned_token); // store encoded value to file file_put_contents($access_token_path, $access_token_base64); } // function to decode and read access token from file function read_token($file){ // get access token from file $token_base64 = file_get_contents($file); // decode value to token $token = base64_decode($token_base64); // output return $token; } // function to sort our returned data (multidimensional array) function array_sort_by_column(&$arr, $col, $dir = SORT_DESC) { $sort_col = array(); foreach ($arr as $key=> $row) { $sort_col[$key] = strtolower($row[$col]); // strtolower to make it case insensitive } array_multisort($sort_col, $dir, $arr); } // function to get minutes left on a generated file // defaults to an hour expiry time // usage: get_time_remaining( 'access.dat', 3600) function get_time_remaining($file, $expiry_in_seconds=3600){ // get file modified time $file_modified_time = filemtime($file); // add 1 hour $file_expiry_time = $file_modified_time + $expiry_in_seconds; // calculate seconds left $diff = $file_expiry_time - time(); // round to minutes $minutes = floor($diff/60); // output return $minutes; } // function to check access token and regenerate if necessary function check_access_token(){ global $access_token_path; // get time remaining on access token (1 hour max) $access_token_time_remaining = get_time_remaining($access_token_path); // if less than 5 minutes left, regenerate token if($access_token_time_remaining<=5){ // Generate Access Token from Refresh Token generate_access_token(); // update time remaining on access token (again) $access_token_time_remaining = get_time_remaining($access_token_path); } // return time remaining (in minutes) return $access_token_time_remaining; } // get data: returns PHP Array (for functional sorting: PHP & JS) // usage: Leads: get_records("Leads") // usage: Lead: get_records("Leads", 98304820934029840) // usage: User: get_records("users", 10825000000119017) // note that users has to be lowercase function get_records($zoho_category, $zoho_id=0, $fields_data=array()){ global $access_token_path, $zoho_apis_eu; // get access token $access_token = read_token($access_token_path); // endpoint $url_data=$zoho_apis_eu."/crm/v2/".$zoho_category; // if array (eg. Related Records), accept ID and related_list_apiname if(is_array($zoho_id)){ $url_data.= $zoho_id[0]>0 ? '/'.$zoho_id[0].'/'.$zoho_id[1] : ''; }else{ // add id if exists $url_data.= $zoho_id!=0 && $zoho_id!="" ? '/'.$zoho_id : ''; } // add access token to header $header_data = array("Authorization: Zoho-oauthtoken ".$access_token); // send to Zoho API $response_json = abZohoApi($url_data, $fields_data, $header_data); // convert response to PHP array (for sorting) $response_arr = json_decode($response_json, true); // output return $response_arr; } // get data: returns PHP Array (for functional sorting: PHP & JS) // usage: Leads: search_records("Leads", array('Last_Name:starts_with:G', 'Email:equals:This email address is being protected from spambots. You need JavaScript enabled to view it.')) function search_records($zoho_category, $criteria=array()){ global $access_token_path, $zoho_apis_eu; // get access token $access_token = read_token($access_token_path); // endpoint $url_data=$zoho_apis_eu."/crm/v2/".$zoho_category."/search?criteria="; // join the criteria if(count($criteria)==1){ $url_data.= '('.$criteria[0].')'; }elseif(count($criteria)>1){ $url_data.= '(('.implode($criteria, ') and (').'))'; } // add access token to header $header_data=array("Authorization: Zoho-oauthtoken ".$access_token); // send to Zoho API $response_json = abZohoApi($url_data, false, $header_data); // convert response to PHP array (for sorting) $response_arr = json_decode($response_json, true); // output return $response_arr; } // function to retrieve current user record // accepts User ID as parameter // returns array( authorized{ok,fail}, name, email, profile, role ) function authenticate_user($user_id){ global $access_token_path; // get access token $token = read_token($access_token_path); // pass parameter requesting only active users who are also confirmed $user_fields = array("type"=>"ActiveConfirmedUsers"); // authenticate user (id was stored in cookie from GET var when this app was initially loaded) $zoho_user_record = get_records("users", $user_id, $user_fields); // failed by default $user_authorized = 'fail'; $zoho_user_name=$zoho_user_email=$zoho_user_profile=$zoho_user_role=""; if(isset($zoho_user_record['users'])){ // record is readable $zoho_user_isactive = $zoho_user_record['users'][0]['status']; // if status is active if($zoho_user_isactive=='active'){ $user_authorized = 'ok'; $zoho_user_name = $zoho_user_record['users'][0]['full_name']; // user email $zoho_user_email = $zoho_user_record['users'][0]['email']; // user profile $zoho_user_profile = $zoho_user_record['users'][0]['profile']['name']; // user role $zoho_user_role = $zoho_user_record['users'][0]['role']['name']; } } // return vars return array("authorized"=>$user_authorized, "name"=>$zoho_user_name, "email"=>$zoho_user_email, "profile"=>$zoho_user_profile, "role"=>$zoho_user_role); } // function to ensure data was transferred // accepts array (JSON Response) // returns boolean function check_data_is_valid($data){ $is_valid = false; if(isset($data['data'])){ $is_valid = true; } return $is_valid; }
- <?php
- /*
- ------------------------------------------------------------------------------------------------
- Zoho Authorization via oAuth2.0 for REST API v2
- ------------------------------------------------------------------------------------------------
- Zoho API v2: https://accounts.zoho.eu/developerconsole
- Documentation: https://www.zoho.com/crm/help/api/v2/
- Available Scopes
- users users.all
- org org.all
- settings settings.all, settings.territories, settings.custom_views, settings.related_lists,
- settings.modules, settings.tab_groups, settings.fields, settings.layouts,
- settings.macros, settings.custom_links, settings.custom_buttons, settings.roles,
- settings.profiles
- modules modules.all, modules.approvals, modules.leads, modules.accounts, modules.contacts,
- modules.deals, modules.campaigns, modules.tasks, modules.cases, modules.events,
- modules.calls, modules.solutions, modules.products, modules.vendors,
- modules.pricebooks, modules.quotes, modules.salesorders, modules.purchaseorders,
- modules.invoices, modules.custom, modules.dashboards, modules.notes,
- modules.activities, modules.search
- Possible Module Names
- leads, accounts, contacts, deals, campaigns, tasks, cases, events, calls, solutions, products,
- vendors, pricebooks, quotes, salesorders, purchaseorders, invoices, custom, notes, approvals,
- dashboards, search, activities
- */
- // Global vars for Zoho API
- $zoho_apis_com = "https://www.zohoapis.com";
- $zoho_apis_eu = "https://www.zohoapis.eu";
- $refresh_access_token_url = "https://accounts.zoho.eu/oauth/v2/token";
- // Endpoint: Sandbox // disable after testing
- $zoho_sandbox = "https://sandbox.zohoapis.eu";
- $zoho_sandbox_domain = "https://crmsandbox.zoho.eu/crm";
- // Global vars to be used by below functions specific to this app
- $zoho_client_id = "1000.your_client_id";
- $zoho_client_secret = "your_client_secret";
- $zoho_redirect_uri = "your_redirect_uri";  // will be URL to start.php in this example
- $access_token_path = "path_to_your_access_token_not_on_www_but_accessible_by_script/access_token.dat";
- $refresh_token_path = "path_to_your_access_token_not_on_www_but_accessible_by_script/refresh_token.dat";
- // function to use Zoho API v2
- function abZohoApi( $post_url, $post_fields, $post_header=false, $post_type='GET' )
- {
- // setup cURL request
- $ch=curl_init();
- // do not return header information
- curl_setopt($ch, CURLOPT_HEADER, 0);
- // submit data in header if specified
- if(is_array($post_header)){
- curl_setopt($ch, CURLOPT_HTTPHEADER, $post_header);
- }
- // do not return status info
- curl_setopt($ch, CURLOPT_VERBOSE, 0);
- // return data
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- // cancel ssl checks
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- // if using GET, POST or PUT
- if($post_type=='POST'){
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
- curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
- } else if($post_type=='PUT'){
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
- curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields));
- } else if($post_type=='DELETE'){
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
- }else{
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
- if($post_fields){
- $post_url.='?'.http_build_query($post_fields);
- }
- }
- // specified endpoint
- curl_setopt($ch, CURLOPT_URL, $post_url);
- // execute cURL request
- $response=curl_exec($ch);
- // return errors if any
- if (curl_exec($ch) === false) {
- $output = curl_error($ch);
- } else {
- $output = $response;
- }
- // close cURL handle
- curl_close($ch);
- // output
- return $output;
- }
- // Generate the refresh token by manually entering the following in the browser
- // Browse to https://accounts.zoho.eu/oauth/v2/auth?scope=ZohoCRM.modules.ALL,ZohoCRM.users.READ,ZohoCRM.settings.fields.read&client_id=your_client_id&response_type=code&access_type=offline&redirect_uri=your_url_path_to_start_page/start.php&prompt=consent
- // function to generate refresh token from Zoho authorization code
- function generate_refresh_token(){
- // use vars declared at beginning of script
- global $zoho_client_id, $zoho_client_secret, $zoho_redirect_uri, $access_token_path, $refresh_token_path;
- // Generate Access Token and Refresh Token - Read url GET values
- $zoho_grant_token = $_GET['code'];
- $zoho_location = $_GET['location'];
- $zoho_accounts_server = $_GET['accounts-server'];
- // Generate Access Token and Refresh Token
- $url_auth=urldecode($zoho_accounts_server)."/oauth/v2/token";
- // Build fields to post
- $fields_token=array("code"=>$zoho_grant_token, "redirect_uri"=>$zoho_redirect_uri, "client_id"=>$zoho_client_id, "client_secret"=>$zoho_client_secret, "grant_type"=>"authorization_code", "prompt"=>"consent");
- // Generate Access Token and Refresh Token - post via cURL
- $response_json = abZohoApi($url_auth, $fields_token, false, 'POST');
- // Generate Access Token and Refresh Token - format output (convert JSON to Object)
- $refresh_token_arr = json_decode($response_json, true);
- // store in var
- $refresh_token = isset($refresh_token_arr['refresh_token']) ? $refresh_token_arr['refresh_token'] : 0;
- // encode value to base64
- $refresh_token_base64 = base64_encode($refresh_token);
- // store encoded value to file
- file_put_contents($refresh_token_path, $refresh_token_base64);
- // -- do access token while we're here
- // store in access token
- $access_token = isset($refresh_token_arr['access_token']) ? $refresh_token_arr['access_token'] : 0;
- // encode value to base64
- $access_token_base64 = base64_encode($access_token);
- // store encoded value to file
- file_put_contents($access_token_path, $access_token_base64);
- // return array of json objects
- return $refresh_token_arr;
- }
- // function to generate access token from refresh token
- // returns minutes remaining of valid token
- function generate_access_token(){
- // use vars declared at beginning of script
- global $zoho_client_id, $zoho_client_secret, $access_token_path, $refresh_token_path, $refresh_access_token_url;
- // get refresh token from file
- $refresh_token = base64_decode( file_get_contents( $refresh_token_path ) );
- // build fields to post
- $refresh_fields = array("refresh_token" => $refresh_token, "client_id" => $zoho_client_id, "client_secret" => $zoho_client_secret, "grant_type" => "refresh_token");
- // send to Zoho API
- $this_access_token_json = abZohoApi($refresh_access_token_url, $refresh_fields, false, 'POST');
- // convert JSON response to array
- $access_token_arr = json_decode($this_access_token_json, true);
- // store in var
- $returned_token = $access_token_arr['access_token'];
- // encode value to base64
- $access_token_base64 = base64_encode($returned_token);
- // store encoded value to file
- file_put_contents($access_token_path, $access_token_base64);
- }
- // function to decode and read access token from file
- function read_token($file){
- // get access token from file
- $token_base64 = file_get_contents($file);
- // decode value to token
- $token = base64_decode($token_base64);
- // output
- return $token;
- }
- // function to sort our returned data (multidimensional array)
- function array_sort_by_column(&$arr, $col, $dir = SORT_DESC) {
- $sort_col = array();
- foreach ($arr as $key=> $row) {
- $sort_col[$key] = strtolower($row[$col]); // strtolower to make it case insensitive
- }
- array_multisort($sort_col, $dir, $arr);
- }
- // function to get minutes left on a generated file
- // defaults to an hour expiry time
- // usage: get_time_remaining( 'access.dat', 3600)
- function get_time_remaining($file, $expiry_in_seconds=3600){
- // get file modified time
- $file_modified_time = filemtime($file);
- // add 1 hour
- $file_expiry_time = $file_modified_time + $expiry_in_seconds;
- // calculate seconds left
- $diff = $file_expiry_time - time();
- // round to minutes
- $minutes = floor($diff/60);
- // output
- return $minutes;
- }
- // function to check access token and regenerate if necessary
- function check_access_token(){
- global $access_token_path;
- // get time remaining on access token (1 hour max)
- $access_token_time_remaining = get_time_remaining($access_token_path);
- // if less than 5 minutes left, regenerate token
- if($access_token_time_remaining<=5){
- // Generate Access Token from Refresh Token
- generate_access_token();
- // update time remaining on access token (again)
- $access_token_time_remaining = get_time_remaining($access_token_path);
- }
- // return time remaining (in minutes)
- return $access_token_time_remaining;
- }
- // get data: returns PHP Array (for functional sorting: PHP & JS)
- // usage: Leads: get_records("Leads")
- // usage: Lead: get_records("Leads", 98304820934029840)
- // usage: User: get_records("users", 10825000000119017) // note that users has to be lowercase
- function get_records($zoho_category, $zoho_id=0, $fields_data=array()){
- global $access_token_path, $zoho_apis_eu;
- // get access token
- $access_token = read_token($access_token_path);
- // endpoint
- $url_data=$zoho_apis_eu."/crm/v2/".$zoho_category;
- // if array (eg. Related Records), accept ID and related_list_apiname
- if(is_array($zoho_id)){
- $url_data.= $zoho_id[0]>0 ? '/'.$zoho_id[0].'/'.$zoho_id[1] : '';
- }else{
- // add id if exists
- $url_data.= $zoho_id!=0 && $zoho_id!="" ? '/'.$zoho_id : '';
- }
- // add access token to header
- $header_data = array("Authorization: Zoho-oauthtoken ".$access_token);
- // send to Zoho API
- $response_json = abZohoApi($url_data, $fields_data, $header_data);
- // convert response to PHP array (for sorting)
- $response_arr = json_decode($response_json, true);
- // output
- return $response_arr;
- }
- // get data: returns PHP Array (for functional sorting: PHP & JS)
- // usage: Leads: search_records("Leads", array('Last_Name:starts_with:G', 'Email:equals:This email address is being protected from spambots. You need JavaScript enabled to view it.'))
- function search_records($zoho_category, $criteria=array()){
- global $access_token_path, $zoho_apis_eu;
- // get access token
- $access_token = read_token($access_token_path);
- // endpoint
- $url_data=$zoho_apis_eu."/crm/v2/".$zoho_category."/search?criteria=";
- // join the criteria
- if(count($criteria)==1){
- $url_data.= '('.$criteria[0].')';
- }elseif(count($criteria)>1){
- $url_data.= '(('.implode($criteria, ') and (').'))';
- }
- // add access token to header
- $header_data=array("Authorization: Zoho-oauthtoken ".$access_token);
- // send to Zoho API
- $response_json = abZohoApi($url_data, false, $header_data);
- // convert response to PHP array (for sorting)
- $response_arr = json_decode($response_json, true);
- // output
- return $response_arr;
- }
- // function to retrieve current user record
- // accepts User ID as parameter
- // returns array( authorized{ok,fail}, name, email, profile, role )
- function authenticate_user($user_id){
- global $access_token_path;
- // get access token
- $token = read_token($access_token_path);
- // pass parameter requesting only active users who are also confirmed
- $user_fields = array("type"=>"ActiveConfirmedUsers");
- // authenticate user (id was stored in cookie from GET var when this app was initially loaded)
- $zoho_user_record = get_records("users", $user_id, $user_fields);
- // failed by default
- $user_authorized = 'fail';
- $zoho_user_name=$zoho_user_email=$zoho_user_profile=$zoho_user_role="";
- if(isset($zoho_user_record['users'])){
- // record is readable
- $zoho_user_isactive = $zoho_user_record['users'][0]['status'];
- // if status is active
- if($zoho_user_isactive=='active'){
- $user_authorized = 'ok';
- $zoho_user_name = $zoho_user_record['users'][0]['full_name'];
- // user email
- $zoho_user_email = $zoho_user_record['users'][0]['email'];
- // user profile
- $zoho_user_profile = $zoho_user_record['users'][0]['profile']['name'];
- // user role
- $zoho_user_role = $zoho_user_record['users'][0]['role']['name'];
- }
- }
- // return vars
- return array("authorized"=>$user_authorized, "name"=>$zoho_user_name, "email"=>$zoho_user_email, "profile"=>$zoho_user_profile, "role"=>$zoho_user_role);
- }
- // function to ensure data was transferred
- // accepts array (JSON Response)
- // returns boolean
- function check_data_is_valid($data){
- $is_valid = false;
- if(isset($data['data'])){
- $is_valid = true;
- }
- return $is_valid;
- }
Then we need a start page that the user will browse to (or that is the endpoint for the redirect), I will call this file start.php:
copyraw
<?php // set header header('Content-Type: text/html; charset=utf-8'); // include global functions include('./functions.php'); ?> <!doctype html> <html> <head> <title>Zoho OAuth Script</title> </head> <body> <?php // check access token, regenerate if expired (1 hour) check_access_token(); // determine minutes left $access_token_time_remaining = get_time_remaining($access_token_path); // generate another token if about to expire if($access_token_time_remaining<5){ echo '<h1>Oops! Something went wrong.</h1>'; generate_access_token(); echo '<p><b><u>Access</u></b> Token has been regenerated. Please reload this page.</p><pre>'; // get access token from file $access_token = base64_decode( file_get_contents( $access_token_path ) ); // display access token echo "\t".$access_token; echo '</pre>'; }else{ echo '<h1>Yay! All went well.</h1>'; echo '<p>Stored <b><u>Access</u></b> Token is valid for another '.$access_token_time_remaining.' minute'.($access_token_time_remaining==1?'':'s').'.</p><pre>'; // get access token from file $access_token = base64_decode( file_get_contents( $access_token_path ) ); // display access token echo "\t".$access_token; echo '</pre>'; } // if refresh token is being generated if(isset($_GET['code'])){ // read get vars (code) generate refresh and access token. Store refresh token in file. $this_response_arr = generate_refresh_token(); // get refresh token from file $refresh_token = base64_decode( file_get_contents( $refresh_token_path ) ); // check refresh token exists and is of expected length if(strlen($refresh_token)==70){ echo '<h1>Yay! All went well.</h1>'; echo '<p><b>Refresh</b> Token successfully generated and stored.</p><pre>'; print_r($this_response_arr); echo '</pre>'; }else{ echo '<h1>Oops! Something went wrong.</h1>'; echo '<p><b>Refresh</b> token was not regenerated.</p><pre>'; print_r($this_response_arr); echo '</pre>'; } } ?> <br /> PHP Code to get all <b><u>Leads</u></b>:<br /> <pre> $all_lead_records = get_records("Leads"); print_r( $all_lead_records ); <?php // $all_lead_records = get_records("Leads"); // print_r( $all_lead_records['data'][0] ); ?> </pre> <br /> PHP Code to get a specific <b><u>Lead</u></b>:<br /> <pre> $this_lead_record = get_records("Leads", "78290000004647043"); print_r( $this_lead_record ); <?php // if no lead exists with this ID then this will return blank // $this_lead_record = get_records("Leads", "78290000004647043"); // print_r($this_lead_record); ?> </pre> <br /> PHP Code to update or insert a <b><u>Lead</u></b> record in ZohoCRM:<br /> <pre> // set lead name $data_array['data'][0]['First_Name'] = json_encode("John"); $data_array['data'][0]['Last_Name'] = json_encode("Smith"); // build JSON post request $data_array['data'][0]['Email'] = json_encode("This email address is being protected from spambots. You need JavaScript enabled to view it."); $data_array['data'][0]['Mobile'] = json_encode("+44 1234 567 890"); // merge to a JSON array to post $data_json = json_encode($data_array); // prepare cURL variables $access_token = base64_decode( file_get_contents( $access_token_path ) ); $zoho_target_url = $zoho_apis_eu . '/crm/v2/Leads/upsert'; $zoho_header = array('Authorization: Zoho-oauthtoken '.$access_token, 'Content-Type: application/json'); // just do it $response_json = abZohoApi($zoho_target_url, $data_json, $zoho_header, 'POST'); // output response (optional) echo $response_json; </pre> </body> </html>
- <?php
- // set header
- header('Content-Type: text/html; charset=utf-8');
- // include global functions
- include('./functions.php');
- ?>
- <!doctype html>
- <html>
- <head>
- <title>Zoho OAuth Script</title>
- </head>
- <body>
- <?php
- // check access token, regenerate if expired (1 hour)
- check_access_token();
- // determine minutes left
- $access_token_time_remaining = get_time_remaining($access_token_path);
- // generate another token if about to expire
- if($access_token_time_remaining<5){
- echo '<h1>Oops! Something went wrong.</h1>';
- generate_access_token();
- echo '<p><b><u>Access</u></b> Token has been regenerated. Please reload this page.</p><pre>';
- // get access token from file
- $access_token = base64_decode( file_get_contents( $access_token_path ) );
- // display access token
- echo "\t".$access_token;
- echo '</pre>';
- }else{
- echo '<h1>Yay! All went well.</h1>';
- echo '<p>Stored <b><u>Access</u></b> Token is valid for another '.$access_token_time_remaining.' minute'.($access_token_time_remaining==1?'':'s').'.</p><pre>';
- // get access token from file
- $access_token = base64_decode( file_get_contents( $access_token_path ) );
- // display access token
- echo "\t".$access_token;
- echo '</pre>';
- }
- // if refresh token is being generated
- if(isset($_GET['code'])){
- // read get vars (code) generate refresh and access token. Store refresh token in file.
- $this_response_arr = generate_refresh_token();
- // get refresh token from file
- $refresh_token = base64_decode( file_get_contents( $refresh_token_path ) );
- // check refresh token exists and is of expected length
- if(strlen($refresh_token)==70){
- echo '<h1>Yay! All went well.</h1>';
- echo '<p><b>Refresh</b> Token successfully generated and stored.</p><pre>';
- print_r($this_response_arr);
- echo '</pre>';
- }else{
- echo '<h1>Oops! Something went wrong.</h1>';
- echo '<p><b>Refresh</b> token was not regenerated.</p><pre>';
- print_r($this_response_arr);
- echo '</pre>';
- }
- }
- ?>
- <br />
- PHP Code to get all <b><u>Leads</u></b>:<br />
- <pre>
- $all_lead_records = get_records("Leads");
- print_r( $all_lead_records );
- <?php
- // $all_lead_records = get_records("Leads");
- // print_r( $all_lead_records['data'][0] );
- ?>
- </pre>
- <br />
- PHP Code to get a specific <b><u>Lead</u></b>:<br />
- <pre>
- $this_lead_record = get_records("Leads", "78290000004647043");
- print_r( $this_lead_record );
- <?php
- // if no lead exists with this ID then this will return blank
- // $this_lead_record = get_records("Leads", "78290000004647043");
- // print_r($this_lead_record);
- ?>
- </pre>
- <br />
- PHP Code to update or insert a <b><u>Lead</u></b> record in ZohoCRM:<br />
- <pre>
- // set lead name
- $data_array['data'][0]['First_Name'] = json_encode("John");
- $data_array['data'][0]['Last_Name'] = json_encode("Smith");
- // build JSON post request
- $data_array['data'][0]['Email'] = json_encode("This email address is being protected from spambots. You need JavaScript enabled to view it.");
- $data_array['data'][0]['Mobile'] = json_encode("+44 1234 567 890");
- // merge to a JSON array to post
- $data_json = json_encode($data_array);
- // prepare cURL variables
- $access_token = base64_decode( file_get_contents( $access_token_path ) );
- $zoho_target_url = $zoho_apis_eu . '/crm/v2/Leads/upsert';
- $zoho_header = array('Authorization: Zoho-oauthtoken '.$access_token, 'Content-Type: application/json');
- // just do it
- $response_json = abZohoApi($zoho_target_url, $data_json, $zoho_header, 'POST');
- // output response (optional)
- echo $response_json;
- </pre>
- </body>
- </html>
Additional Notes
Note that in the above, the tokens are stored in Base64 encoded strings. You could add an actual encryption method (recommended) but for simplicity I'm using the built-in base64_encode function.
Update 2020
Please read the above article to understand the basic process which is:- Register app to generate client ID/secret
- Get Grant Token (value of variable called "Code")
- Accept permissions of app with your Zoho login
- Use refresh token to generate access token
Redirect URI file
I give the web URL to this file as the redirect_uri value in a token request. This redirect simply forwards all the received data (via GET method) on to our main script which will do everything else.
copyraw
<?php header('Location: https://localhost/get_tokens.php?' . http_build_query($_GET)); ?>
- <?php
- header('Location: https://localhost/get_tokens.php?' . http_build_query($_GET));
- ?>
Get Tokens file
Not all of what this file does is necessary. I'm just posting the entire file of what I use. It doesn't have any identifying or data-sensitive info, speaking of which, you can remove the info outputs as I also use this script to explain OAuth2.0 and getting access to a ZohoCRM.
As an overview it does the following:
- Displays a HTML form for you to select your datacenter (EU / COM / COM.CN / etc)
- Displays a HTML form for you to enter the client ID, client secret, scope(s), redirect url
- Submit form and receive the CODE variable from the URL (GET method)
- You get redirected to an app permissions page where you need to login with the Zoho account that belongs to the organization and can authorize this app.
- The script will then display the refresh_token and uses this to generate an access_token value.
- Uses the access_token and gets the last 2 contact records added to your ZohoCRM.
copyraw
<?php /* --------------------------------------------------------------------------- Zoho Authorization via oAuth2.0 for REST API v2 --------------------------------------------------------------------------- Zoho API v2: https://accounts.zoho.eu/developerconsole https://accounts.zoho.com/developerconsole Documentation: https://www.zoho.com/crm/help/api/v2/ Available Scopes users users.all org org.all settings settings.all, settings.territories, settings.custom_views, settings.related_lists, settings.modules, settings.tab_groups, settings.fields, settings.layouts, settings.macros, settings.custom_links, settings.custom_buttons, settings.roles, settings.profiles modules modules.all, modules.approvals, modules.leads, modules.accounts, modules.contacts, modules.deals, modules.campaigns, modules.tasks, modules.cases, modules.events, modules.calls, modules.solutions, modules.products, modules.vendors, modules.pricebooks, modules.quotes, modules.salesorders, modules.purchaseorders, modules.invoices, modules.custom, modules.dashboards, modules.notes, modules.activities, modules.search Possible Module Names leads, accounts, contacts, deals, campaigns, tasks, cases, events, calls, solutions, products, vendors, pricebooks, quotes, salesorders, purchaseorders, invoices, custom, notes, approvals, dashboards, search,activities */ header("Content-Type: text/html"); ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); // init $b_Sandbox = false; $v_AccessTokenPath = $v_RefreshTokenPath = ""; $a_CheckFields = $a_GrantFields = $a_Header = $a_Payload = array(); $v_RootFolder = "../App_Data/ab"; // must be writeable by script (and inaccessible by public) $v_TmpFolder = $v_RootFolder . "/tmp"; $v_DelFolder = $v_TmpFolder . "/delete_me"; $v_WorkingFolder = $v_TmpFolder; $v_TmpFile = "grant_info_" . date("YmdH") . ".dat"; $v_Protocol = $_SERVER['HTTPS']=="on" ? "https" : "http"; $v_RedirectUri = $v_Protocol . "://" . $_SERVER['SERVER_NAME'] . "/zohoapi/redirect_uri.php"; $v_Separator = "<hr style='border:3px solid #eee;' />"; // used for data samples $v_CrmEndpoint = ($b_Sandbox) ? "https://sandbox.zohoapis." . $v_TLD . "/crm/v2" : "https://www.zohoapis." . $v_TLD . "/crm/v2"; $v_DataEndpoint = "https://inventory.zoho.com/api/v1/items?organization_id=012345678"; // used to get sample data // create temp folder if not exists if (!file_exists($v_TmpFolder)) { mkdir($v_TmpFolder, 0777, true); } if (!file_exists($v_DelFolder)) { mkdir($v_DelFolder, 0777, true); } // get top level domain if(isset($_GET['location'])){ switch($_GET['location']){ case "us": $v_TLD = "com"; break; case "eu": $v_TLD = "eu"; break; default: $v_TLD = "com"; } }else{ $v_TLD = isset($_GET['tld']) ? strtolower($_GET['tld']) : "none"; } // if client id in url then get and store if(isset($_GET['client_id'])){ if($_GET['client_id'] != ""){ file_put_contents($v_TmpFolder . "/" . $v_TmpFile, json_encode($_GET, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } } // begin HTML output echo "<html><head><title>JoelLipman - API v2 Tokenizer (" . strtoupper($v_TLD) . ")</title><style>"; $v_AppStyle = "body{font-family:Arial,Verdana,Sans-serif;background-color:#ccc;}select,option,input.txt{padding:5px 10px;outline:0;border:1px solid#ccc;border-radius:5px;}input.btn{padding:10px 20px;outline:0;color:#fff;background:#10bc83;border-radius:2px;border:0;-webkit-box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);-moz-box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);cursor:pointer;}input.txt{width:400px;}form{margin:20px;}h3{margin:0;}select{float:right;}td{padding:2px 5px;}label{display:inline-block;width:100px;}div.panel{background-color:#fff;border-radius:10px;margin:20px 10px;padding:20px 10px;-webkit-box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);-moz-box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);}form{margin:0;padding:0;}#copyright{bottom:0;margin:0 auto;text-align:center;width:97%;margin-bottom:10px;}#copyright a{color:#999;text-decoration:none;font-size:10pt;line-height:20px;}#menu_bottom a{color:#f00;text-decoration:none;bottom:30;font-size:10pt;line-height:20px;}.centered{text-align:center;margin:0 auto;}h5{color:#999;font-weight:100;font-size:10pt;margin-bottom:5px;margin-top:10px;}tt.source{color:#fff;background-color:#000;}tt.target{background-color:yellow;}tt.bold{color:red;font-weight:700;}ul{margin:10px 0 0 85px;font-size:75%;color:#666;padding:0;}pre{margin:0;}"; $v_AppStyleFormatted = trim(preg_replace('/\s+/', ' ', $v_AppStyle)); $a_ReplaceFrom1 = array("px ", "0 ", " a"); $a_ReplaceTo1 = array("px?", "0?", "?a"); $v_AppStyleFormatted = str_replace($a_ReplaceFrom1, $a_ReplaceTo1, $v_AppStyleFormatted); $a_ReplaceFrom2 = array(" ", "?"); $a_ReplaceTo2 = array("", " "); $v_AppStyleFormatted = str_replace($a_ReplaceFrom2, $a_ReplaceTo2, $v_AppStyleFormatted); echo $v_AppStyleFormatted . "</style></head><body>"; if((isset($_GET['tld']) || isset($_GET['location']))&&(file_exists($v_TmpFolder . "/" . $v_TmpFile))){ // // 0. initialize vars // echo "<div class='panel'>"; echo "<h3>0. INITIALIZE...</h3>"; echo "<h5>Info:</h5>"; $v_AuthEndpoint = "https://accounts.zoho." . $v_TLD . "/oauth/v2/auth"; $v_TokenEndpoint = "https://accounts.zoho." . $v_TLD . "/oauth/v2/token"; if(file_exists($v_TmpFolder . "/" . $v_TmpFile) && isset($_GET['code'])){ $m_GrantInfo = json_decode(file_get_contents($v_TmpFolder . "/" . $v_TmpFile), true); $a_CheckFields['client_id'] = $m_GrantInfo['client_id']; $a_CheckFields['client_secret'] = $m_GrantInfo['client_secret']; $a_CheckFields['scopes'] = $m_GrantInfo['scopes']; $a_CheckFields['tld'] = $m_GrantInfo['tld']; $a_CheckFields['redirect_uri'] = $m_GrantInfo['redirect_uri']; rename($v_TmpFolder . "/" . $v_TmpFile, $v_DelFolder . "/" . $v_TmpFile); }else{ $a_CheckFields['client_id'] = $_GET['client_id']; $a_CheckFields['client_secret'] = $_GET['client_secret']; $a_CheckFields['scopes'] = $_GET['scopes']; $a_CheckFields['tld'] = $_GET['tld']; $a_CheckFields['redirect_uri'] = $_GET['redirect_uri']; } $a_InitFields['tld'] = $a_CheckFields['tld']; $a_InitFields['client_id'] = $a_CheckFields['client_id']; $a_InitFields['client_secret'] = $a_CheckFields['client_secret']; $a_InitFields['scopes'] = $a_CheckFields['scopes']; $a_InitFields['redirect_uri'] = $a_CheckFields['redirect_uri']; $a_Info = array(); $a_Info['api']['endpoint'] = $v_AuthEndpoint; $a_Info['api']['header'] = false; $a_Info['api']['ssl_check'] = false; $a_Info['sandbox'] = $b_Sandbox ? "<tt class='bold'>true</tt>" : "<tt class='bold'>false</tt>"; echo "<pre>" . json_encode(array_merge($a_InitFields,$a_Info), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; // // 1. get grant token // echo "</div><div class='panel'>"; echo "<h3>1. GRANT TOKEN...</h3>"; if($a_CheckFields['client_id']!="") { $v_GrantTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/grant_token.dat"; $v_RefreshTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/refresh_token.dat"; $v_AccessTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/access_token.dat"; $v_WorkingFolder = $v_RootFolder . $a_CheckFields['client_id']; if (!file_exists($v_WorkingFolder)) { mkdir($v_WorkingFolder, 0777, true); } } echo "<h5>Info:</h5>"; $a_Info = array(); $a_Info['api']['endpoint'] = $v_AuthEndpoint; $a_Info['api']['method'] = "GET"; $a_Info['api']['header'] = false; $a_Info['api']['ssl_check'] = false; $a_Info['expires'] = "<tt class='bold'>" . "in 1 to 10 minutes" . "</tt>"; $a_Info['date_created'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filectime($v_GrantTokenPath)) : date("Y-m-d H:i:s P"); $a_Info['date_modified'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_GrantTokenPath)) : date("Y-m-d H:i:s P"); $a_Info['date_expires'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_GrantTokenPath) + 300) : date("Y-m-d H:i:s P", strtotime(time() + 300)); echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Request (GET):</h5>"; $a_Request = array(); $a_Request['client_id'] = $a_CheckFields['client_id']; $a_Request['redirect_uri'] = $a_CheckFields['redirect_uri']; $a_Request['scope'] = $a_CheckFields['scopes']; $a_Request['response_type'] = "code"; $a_Request['access_type'] = "offline"; $a_Request['prompt'] = "consent"; $a_Request['state'] = "testing"; echo "<pre>" . json_encode($a_Request, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; $v_GrantTokenUrl = $v_AuthEndpoint . "?" . http_build_query($a_Request); if(isset($_GET['code'])) { echo "<h5>Response (GET):</h5>"; $a_Response = array(); $a_Response['state'] = $_GET['state']; $a_Response['code'] = $_GET['code']; $a_Response['location'] = $_GET['location']; $a_Response['accounts-server'] = $_GET['accounts-server']; if($v_GrantTokenPath != "" && $b_Sandbox){ file_put_contents($v_GrantTokenPath, json_encode($a_Response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } foreach($a_Response as $v_Key => $v_Value){ if($v_Key == "code"){ $a_Response['code'] = "<tt class='source'>" . $v_Value . "</tt>"; } } echo "<pre>" . json_encode($a_Response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; // // 2. get refresh token // echo "</div><div class='panel'>"; echo "<h3>2. REFRESH TOKEN...</h3>"; echo "<h5>Info:</h5>"; $a_Info = array(); $a_Info['api']['endpoint'] = $v_TokenEndpoint; $a_Info['api']['method'] = "POST"; $a_Info['api']['header'] = false; $a_Info['api']['ssl_check'] = false; $a_Info['expires'] = "<tt class='bold'>" . "Unless revoked or overwritten, a refresh token NEVER expires." . "</tt>"; $a_Info['date_created'] = file_exists($v_RefreshTokenPath) ? date("Y-m-d H:i:s P", filectime($v_RefreshTokenPath)) : date("Y-m-d H:i:s P"); $a_Info['date_modified'] = file_exists($v_RefreshTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_RefreshTokenPath)) : date("Y-m-d H:i:s P"); echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Request (POST):</h5>"; $a_Payload = array(); $a_Payload['code'] = $_GET['code']; $a_Payload['client_id'] = $a_CheckFields['client_id']; $a_Payload['client_secret'] = $a_CheckFields['client_secret']; $a_Payload['redirect_uri'] = $a_CheckFields['redirect_uri']; $a_Payload['grant_type'] = "authorization_code"; $h_Curl=curl_init(); $a_CurlOptions = array( CURLOPT_URL => $v_TokenEndpoint, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => $a_Payload, CURLOPT_HEADER => 0, CURLOPT_VERBOSE => 0, CURLOPT_RETURNTRANSFER => 1, CURLOPT_SSL_VERIFYPEER => 0, CURLOPT_SSL_VERIFYHOST => 0 ); curl_setopt_array($h_Curl, $a_CurlOptions); $v_RefreshCurl = curl_exec($h_Curl); curl_close($h_Curl); foreach($a_Payload as $v_Key => $v_Value){ if($v_Key == "code"){ $a_Payload['code'] = "<tt class='target'>" . $v_Value . "</tt>"; } } echo "<pre>" . json_encode($a_Payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Response (POST):</h5>"; $a_RefreshResponse = json_decode($v_RefreshCurl, true); $v_RefreshToken = "ERROR"; if($v_RefreshTokenPath != ""){ file_put_contents($v_RefreshTokenPath, json_encode($a_RefreshResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } if(isset($a_RefreshResponse['refresh_token'])) { $v_RefreshToken = $a_RefreshResponse['refresh_token']; foreach($a_RefreshResponse as $v_Key => $v_Value){ if($v_Key == "refresh_token"){ $a_RefreshResponse['refresh_token'] = "<tt class='source'>" . $v_Value . "</tt>"; } } echo "<pre>" . json_encode($a_RefreshResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; }else{ echo "<pre>ERROR: Check your submitted variables are the same: Client ID, Client Secret, Redirect URI, Scope(s)</pre>"; } // // 3. get access token // echo "</div><div class='panel'>"; echo "<h3>3. ACCESS TOKEN...</h3>"; echo "<h5>Info:</h5>"; $a_Info = array(); $a_Info['api']['endpoint'] = $v_TokenEndpoint; $a_Info['api']['method'] = "POST"; $a_Info['api']['header'] = false; $a_Info['api']['ssl_check'] = false; $a_Info['expires'] = "<tt class='bold'>" . "in 1 hour after creation" . "</tt>"; $a_Info['date_created'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filectime($v_AccessTokenPath)) : date("Y-m-d H:i:s P"); $a_Info['date_modified'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_AccessTokenPath)) : date("Y-m-d H:i:s P"); $a_Info['date_expires'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_AccessTokenPath) + 3600) : date("Y-m-d H:i:s P", strtotime(time() + 3600)); echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Request (POST):</h5>"; $a_Payload = array(); $a_Payload['refresh_token'] = $v_RefreshToken; $a_Payload['client_id'] = $a_CheckFields['client_id']; $a_Payload['client_secret'] = $a_CheckFields['client_secret']; $a_Payload['redirect_uri'] = $a_CheckFields['redirect_uri']; $a_Payload['grant_type'] = "refresh_token"; $h_Curl=curl_init(); $a_CurlOptions = array( CURLOPT_URL => $v_TokenEndpoint, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => $a_Payload, CURLOPT_HEADER => 0, CURLOPT_VERBOSE => 0, CURLOPT_RETURNTRANSFER => 1, CURLOPT_SSL_VERIFYPEER => 0, CURLOPT_SSL_VERIFYHOST => 0 ); curl_setopt_array($h_Curl, $a_CurlOptions); $v_AccessCurl = curl_exec($h_Curl); curl_close($h_Curl); $a_AccessResponse = json_decode($v_AccessCurl, true); if($v_AccessTokenPath != ""){ file_put_contents($v_AccessTokenPath, json_encode($a_AccessResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } foreach($a_Payload as $v_Key => $v_Value){ if($v_Key == "refresh_token"){ $a_Payload['refresh_token'] = "<tt class='target'>" . $v_Value . "</tt>"; } } echo "<pre>" . json_encode($a_Payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Response (POST):</h5>"; if(isset($a_AccessResponse['access_token'])){ $v_AccessToken = $a_AccessResponse['access_token']; foreach($a_AccessResponse as $v_Key => $v_Value){ if($v_Key == "access_token"){ $a_AccessResponse['access_token'] = "<tt class='source'>" . $v_Value . "</tt>"; } } echo "<pre>" . json_encode($a_AccessResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; }else{ echo "<pre>ERROR: Check the Refresh Token</pre>"; } // // 4. get data sample (last 2 records) // echo "</div><div class='panel'>"; echo "<h3>4. DATA SAMPLE...</h3>"; echo "<h5>Info:</h5>"; $a_Info = array(); $a_Info['api']['endpoint'] = $v_DataEndpoint; $a_Info['api']['method'] = "GET"; $a_Info['api']['header'] = true; $a_Info['api']['ssl_check'] = false; $a_Info['date'] = date("Y-m-d H:i:s P"); echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Request (GET):</h5>"; $a_Header = array(); $a_Header[] = "Authorization: Zoho-oauthtoken " . $v_AccessToken; $a_Header[] = "Content-Type: application/x-www-form-urlencoded;charset=UTF-8"; $a_Payload = array(); $a_Payload['page'] = 1; $a_Payload['per_page'] = 2; $a_Payload['sort_by'] = "id"; $a_Payload['sort_order'] = "D"; // in CRM this is "asc" or "desc". in books/inventory this is either "A" or "D" $a_Request = array("header" => $a_Header, "parameters" => $a_Payload); $h_Curl=curl_init(); $a_CurlOptions = array( CURLOPT_URL => $v_DataEndpoint . "&" . http_build_query($a_Payload), CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HEADER => 1, CURLOPT_HTTPHEADER => $a_Header, CURLOPT_VERBOSE => 0, CURLOPT_RETURNTRANSFER => 1, CURLOPT_SSL_VERIFYPEER => 0, CURLOPT_SSL_VERIFYHOST => 0 ); curl_setopt_array($h_Curl, $a_CurlOptions); $v_DataCurl = curl_exec($h_Curl); foreach($a_Request as $v_Key => $v_Value){ if($v_Key == 0){ $a_Request['header'][0] = str_replace("Zoho-oauthtoken ","Zoho-oauthtoken <tt class='target'>", $a_Request['header'][0]) . "</tt>"; } } echo "<pre>" . json_encode($a_Request, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; echo "<h5>Response (GET):</h5>"; if(stripos($v_DataCurl, '"code"')>0){ $v_DataStart = stripos($v_DataCurl, '{"code"'); $v_DataStr = trim(substr($v_DataCurl, $v_DataStart)); $a_DataResponse = json_decode($v_DataStr, true); $a_WriteJson['response'] = $a_DataResponse; echo "<pre>" . json_encode($a_WriteJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; }elseif(stripos($v_DataCurl, '"data"')>0){ $v_DataStart = stripos($v_DataCurl, '{"data"'); $v_DataStr = trim(substr($v_DataCurl, $v_DataStart)); $a_DataResponse = json_decode($v_DataStr, true); $v_Index = 0; foreach($a_DataResponse['data'] as $a_Contact){ $a_WriteJson['records'][$v_Index]['id'] = $a_Contact['id']; $a_WriteJson['records'][$v_Index]['name'] = $a_Contact['Full_Name']; $a_WriteJson['records'][$v_Index]['email'] = $a_Contact['Email']; $a_WriteJson['records'][$v_Index]['created_time'] = str_replace("+", " +", str_replace("T", " ", $a_Contact['Created_Time'])); $v_Index++; } echo "<pre>" . json_encode($a_WriteJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>"; }else{ echo "<pre>" . $v_DataCurl . "</pre>"; } echo "</div>"; } else { echo "<br /><form>"; echo '<input type="button" value="Click here to open App Permissions »" onclick="javascript:top.location.href=\'' . $v_GrantTokenUrl . '\';" class="btn" style="margin-left: 70px;" />'; echo "</form> <ul> <li>Login as a Zoho User with access to the API</li> <li>Read and click on accept to allow the app access.</li> <li>There is a limit of 5 refresh tokens per minute.</li> <li>There is a limit of 19 refresh tokens per app.</li> <li>The 20th refresh token will overwrite the 1st.</li> </ul><br />"; } // END echo "</div><div class='panel'>"; echo "<h5>Script finished.</h5>"; echo "<div id='menu_bottom' style='position:normal'><a href='./get_tokens.php' target='_top'>» Start Over</a></div></div>"; echo "<div id='copyright' style='position:normal'><a href='//joellipman.com/' target='_top'>Copyright © ".date("Y")." Joel Lipman Ltd</a></div></div>"; }else{ $v_TldSelected = ""; $a_TldOptions = array('com','com.au','com.cn','eu','in'); echo " <div class='panel'><form method='get' action='get_tokens.php'> <h3>Configure App</h3> <table> <tr><td colspan='2'>Please select the domain your ZohoCRM is on:<br /><i style='font-size:75%;'>(eg. https://crm.zoho.com = COM, https://crm.zoho.eu = EU)</i> <select name='tld' id='tldSelect'>"; foreach($a_TldOptions as $v_Option){ $v_SelectedStr = (strtolower($v_TLD) == $v_Option) ? " selected='true'" : ""; echo "<option value='" . strtolower($v_Option) . "'" . $v_SelectedStr . ">" . strtoupper($v_Option) . "</option>"; } echo " </select></td></tr> <tr><td colspan='2' class='centered'><input type='button' value='Open Zoho App Registration' onclick='javascript:openAppConsole();' class='btn' /></td></tr> <tr><td colspan='2'> </td></tr> <tr><td>Client ID</td><td><input name='client_id' value='' class='txt' onclick='this.select();' /></td></tr> <tr><td>Client Secret</td><td><input name='client_secret' value='' class='txt' onclick='this.select();' /></td></tr> <tr><td>Scopes</td><td><input name='scopes' value='ZohoCRM.modules.ALL,ZohoCRM.settings.READ,ZohoCRM.users.READ' class='txt' onclick='this.select();' /></td></tr> <tr><td>Redirect URI</td><td><input name='redirect_uri' value='" . $v_RedirectUri . "' class='txt' onclick='this.select();' /></td></tr> <tr><td colspan='2' style='text-align:center;'><input type='submit' value='Get Grant Token' class='btn' /></td></tr> </table> </form></div> <script> function openAppConsole() { var e = document.getElementById('tldSelect'); var strOption = e.options[e.selectedIndex].value; window.open('https://accounts.zoho.' + strOption + '/developerconsole', '_blank', 'location=yes,height=600,width=800,scrollbars=yes,status=yes'); } </script> "; echo "<div id='copyright' style='position:fixed'><a href='//joellipman.com/' target='_top'>Copyright © ".date("Y")." Joel Lipman Ltd</a></div>"; } echo "</body></html>";
- <?php
- /*
- ---------------------------------------------------------------------------
- Zoho Authorization via oAuth2.0 for REST API v2
- ---------------------------------------------------------------------------
- Zoho API v2: https://accounts.zoho.eu/developerconsole
- https://accounts.zoho.com/developerconsole
- Documentation: https://www.zoho.com/crm/help/api/v2/
- Available Scopes
- users users.all
- org org.all
- settings settings.all, settings.territories, settings.custom_views, settings.related_lists,
- settings.modules, settings.tab_groups, settings.fields, settings.layouts,
- settings.macros, settings.custom_links, settings.custom_buttons, settings.roles,
- settings.profiles
- modules modules.all, modules.approvals, modules.leads, modules.accounts, modules.contacts,
- modules.deals, modules.campaigns, modules.tasks, modules.cases, modules.events,
- modules.calls, modules.solutions, modules.products, modules.vendors,
- modules.pricebooks, modules.quotes, modules.salesorders, modules.purchaseorders,
- modules.invoices, modules.custom, modules.dashboards, modules.notes,
- modules.activities, modules.search
- Possible Module Names
- leads, accounts, contacts, deals, campaigns, tasks, cases, events, calls, solutions, products, vendors, pricebooks, quotes, salesorders, purchaseorders, invoices, custom, notes, approvals, dashboards, search,activities
- */
- header("Content-Type: text/html");
- ini_set('display_errors', 1);
- ini_set('display_startup_errors', 1);
- error_reporting(E_ALL);
- // init
- $b_Sandbox = false;
- $v_AccessTokenPath = $v_RefreshTokenPath = "";
- $a_CheckFields = $a_GrantFields = $a_Header = $a_Payload = array();
- $v_RootFolder = "../App_Data/ab";  // must be writeable by script (and inaccessible by public)
- $v_TmpFolder = $v_RootFolder . "/tmp";
- $v_DelFolder = $v_TmpFolder . "/delete_me";
- $v_WorkingFolder = $v_TmpFolder;
- $v_TmpFile = "grant_info_" . date("YmdH") . ".dat";
- $v_Protocol = $_SERVER['HTTPS']=="on" ? "https" : "http";
- $v_RedirectUri = $v_Protocol . "://" . $_SERVER['SERVER_NAME'] . "/zohoapi/redirect_uri.php";
- $v_Separator = "<hr style='border:3px solid #eee;' />";
- // used for data samples
- $v_CrmEndpoint = ($b_Sandbox) ? "https://sandbox.zohoapis." . $v_TLD . "/crm/v2" : "https://www.zohoapis." . $v_TLD . "/crm/v2";
- $v_DataEndpoint = "https://inventory.zoho.com/api/v1/items?organization_id=012345678"; // used to get sample data
- // create temp folder if not exists
- if (!file_exists($v_TmpFolder)) {
- mkdir($v_TmpFolder, 0777, true);
- }
- if (!file_exists($v_DelFolder)) {
- mkdir($v_DelFolder, 0777, true);
- }
- // get top level domain
- if(isset($_GET['location'])){
- switch($_GET['location']){
- case "us":
- $v_TLD = "com";
- break;
- case "eu":
- $v_TLD = "eu";
- break;
- default:
- $v_TLD = "com";
- }
- }else{
- $v_TLD = isset($_GET['tld']) ? strtolower($_GET['tld']) : "none";
- }
- // if client id in url then get and store
- if(isset($_GET['client_id'])){
- if($_GET['client_id'] != ""){
- file_put_contents($v_TmpFolder . "/" . $v_TmpFile, json_encode($_GET, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- }
- }
- // begin HTML output
- echo "<html><head><title>JoelLipman - API v2 Tokenizer (" . strtoupper($v_TLD) . ")</title><style>";
- $v_AppStyle = "body{font-family:Arial,Verdana,Sans-serif;background-color:#ccc;}select,option,input.txt{padding:5px 10px;outline:0;border:1px solid#ccc;border-radius:5px;}input.btn{padding:10px 20px;outline:0;color:#fff;background:#10bc83;border-radius:2px;border:0;-webkit-box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);-moz-box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);box-shadow:2px 2px 2px 0px rgba(51,51,51,0.3);cursor:pointer;}input.txt{width:400px;}form{margin:20px;}h3{margin:0;}select{float:right;}td{padding:2px 5px;}label{display:inline-block;width:100px;}div.panel{background-color:#fff;border-radius:10px;margin:20px 10px;padding:20px 10px;-webkit-box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);-moz-box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);box-shadow:5px 5px 5px 0px rgba(51,51,51,0.3);}form{margin:0;padding:0;}#copyright{bottom:0;margin:0 auto;text-align:center;width:97%;margin-bottom:10px;}#copyright a{color:#999;text-decoration:none;font-size:10pt;line-height:20px;}#menu_bottom a{color:#f00;text-decoration:none;bottom:30;font-size:10pt;line-height:20px;}.centered{text-align:center;margin:0 auto;}h5{color:#999;font-weight:100;font-size:10pt;margin-bottom:5px;margin-top:10px;}tt.source{color:#fff;background-color:#000;}tt.target{background-color:yellow;}tt.bold{color:red;font-weight:700;}ul{margin:10px 0 0 85px;font-size:75%;color:#666;padding:0;}pre{margin:0;}";
- $v_AppStyleFormatted = trim(preg_replace('/\s+/', ' ', $v_AppStyle));
- $a_ReplaceFrom1 = array("px ", "0 ", " a");
- $a_ReplaceTo1 = array("px?", "0?", "?a");
- $v_AppStyleFormatted = str_replace($a_ReplaceFrom1, $a_ReplaceTo1, $v_AppStyleFormatted);
- $a_ReplaceFrom2 = array(" ", "?");
- $a_ReplaceTo2 = array("", " ");
- $v_AppStyleFormatted = str_replace($a_ReplaceFrom2, $a_ReplaceTo2, $v_AppStyleFormatted);
- echo $v_AppStyleFormatted . "</style></head><body>";
- if((isset($_GET['tld']) || isset($_GET['location']))&&(file_exists($v_TmpFolder . "/" . $v_TmpFile))){
- //
- // 0. initialize vars
- //
- echo "<div class='panel'>";
- echo "<h3>0. INITIALIZE...</h3>";
- echo "<h5>Info:</h5>";
- $v_AuthEndpoint = "https://accounts.zoho." . $v_TLD . "/oauth/v2/auth";
- $v_TokenEndpoint = "https://accounts.zoho." . $v_TLD . "/oauth/v2/token";
- if(file_exists($v_TmpFolder . "/" . $v_TmpFile) && isset($_GET['code'])){
- $m_GrantInfo = json_decode(file_get_contents($v_TmpFolder . "/" . $v_TmpFile), true);
- $a_CheckFields['client_id'] = $m_GrantInfo['client_id'];
- $a_CheckFields['client_secret'] = $m_GrantInfo['client_secret'];
- $a_CheckFields['scopes'] = $m_GrantInfo['scopes'];
- $a_CheckFields['tld'] = $m_GrantInfo['tld'];
- $a_CheckFields['redirect_uri'] = $m_GrantInfo['redirect_uri'];
- rename($v_TmpFolder . "/" . $v_TmpFile, $v_DelFolder . "/" . $v_TmpFile);
- }else{
- $a_CheckFields['client_id'] = $_GET['client_id'];
- $a_CheckFields['client_secret'] = $_GET['client_secret'];
- $a_CheckFields['scopes'] = $_GET['scopes'];
- $a_CheckFields['tld'] = $_GET['tld'];
- $a_CheckFields['redirect_uri'] = $_GET['redirect_uri'];
- }
- $a_InitFields['tld'] = $a_CheckFields['tld'];
- $a_InitFields['client_id'] = $a_CheckFields['client_id'];
- $a_InitFields['client_secret'] = $a_CheckFields['client_secret'];
- $a_InitFields['scopes'] = $a_CheckFields['scopes'];
- $a_InitFields['redirect_uri'] = $a_CheckFields['redirect_uri'];
- $a_Info = array();
- $a_Info['api']['endpoint'] = $v_AuthEndpoint;
- $a_Info['api']['header'] = false;
- $a_Info['api']['ssl_check'] = false;
- $a_Info['sandbox'] = $b_Sandbox ? "<tt class='bold'>true</tt>" : "<tt class='bold'>false</tt>";
- echo "<pre>" . json_encode(array_merge($a_InitFields,$a_Info), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- //
- // 1. get grant token
- //
- echo "</div><div class='panel'>";
- echo "<h3>1. GRANT TOKEN...</h3>";
- if($a_CheckFields['client_id']!="")
- {
- $v_GrantTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/grant_token.dat";
- $v_RefreshTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/refresh_token.dat";
- $v_AccessTokenPath = $v_RootFolder . $a_CheckFields['client_id'] . "/access_token.dat";
- $v_WorkingFolder = $v_RootFolder . $a_CheckFields['client_id'];
- if (!file_exists($v_WorkingFolder)) {
- mkdir($v_WorkingFolder, 0777, true);
- }
- }
- echo "<h5>Info:</h5>";
- $a_Info = array();
- $a_Info['api']['endpoint'] = $v_AuthEndpoint;
- $a_Info['api']['method'] = "GET";
- $a_Info['api']['header'] = false;
- $a_Info['api']['ssl_check'] = false;
- $a_Info['expires'] = "<tt class='bold'>" . "in 1 to 10 minutes" . "</tt>";
- $a_Info['date_created'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filectime($v_GrantTokenPath)) : date("Y-m-d H:i:s P");
- $a_Info['date_modified'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_GrantTokenPath)) : date("Y-m-d H:i:s P");
- $a_Info['date_expires'] = file_exists($v_GrantTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_GrantTokenPath) + 300) : date("Y-m-d H:i:s P", strtotime(time() + 300));
- echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Request (GET):</h5>";
- $a_Request = array();
- $a_Request['client_id'] = $a_CheckFields['client_id'];
- $a_Request['redirect_uri'] = $a_CheckFields['redirect_uri'];
- $a_Request['scope'] = $a_CheckFields['scopes'];
- $a_Request['response_type'] = "code";
- $a_Request['access_type'] = "offline";
- $a_Request['prompt'] = "consent";
- $a_Request['state'] = "testing";
- echo "<pre>" . json_encode($a_Request, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- $v_GrantTokenUrl = $v_AuthEndpoint . "?" . http_build_query($a_Request);
- if(isset($_GET['code']))
- {
- echo "<h5>Response (GET):</h5>";
- $a_Response = array();
- $a_Response['state'] = $_GET['state'];
- $a_Response['code'] = $_GET['code'];
- $a_Response['location'] = $_GET['location'];
- $a_Response['accounts-server'] = $_GET['accounts-server'];
- if($v_GrantTokenPath != "" && $b_Sandbox){
- file_put_contents($v_GrantTokenPath, json_encode($a_Response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- }
- foreach($a_Response as $v_Key => $v_Value){
- if($v_Key == "code"){
- $a_Response['code'] = "<tt class='source'>" . $v_Value . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_Response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- //
- // 2. get refresh token
- //
- echo "</div><div class='panel'>";
- echo "<h3>2. REFRESH TOKEN...</h3>";
- echo "<h5>Info:</h5>";
- $a_Info = array();
- $a_Info['api']['endpoint'] = $v_TokenEndpoint;
- $a_Info['api']['method'] = "POST";
- $a_Info['api']['header'] = false;
- $a_Info['api']['ssl_check'] = false;
- $a_Info['expires'] = "<tt class='bold'>" . "Unless revoked or overwritten, a refresh token NEVER expires." . "</tt>";
- $a_Info['date_created'] = file_exists($v_RefreshTokenPath) ? date("Y-m-d H:i:s P", filectime($v_RefreshTokenPath)) : date("Y-m-d H:i:s P");
- $a_Info['date_modified'] = file_exists($v_RefreshTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_RefreshTokenPath)) : date("Y-m-d H:i:s P");
- echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Request (POST):</h5>";
- $a_Payload = array();
- $a_Payload['code'] = $_GET['code'];
- $a_Payload['client_id'] = $a_CheckFields['client_id'];
- $a_Payload['client_secret'] = $a_CheckFields['client_secret'];
- $a_Payload['redirect_uri'] = $a_CheckFields['redirect_uri'];
- $a_Payload['grant_type'] = "authorization_code";
- $h_Curl=curl_init();
- $a_CurlOptions = array(
- CURLOPT_URL => $v_TokenEndpoint,
- CURLOPT_CUSTOMREQUEST => "POST",
- CURLOPT_POSTFIELDS => $a_Payload,
- CURLOPT_HEADER => 0,
- CURLOPT_VERBOSE => 0,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_SSL_VERIFYPEER => 0,
- CURLOPT_SSL_VERIFYHOST => 0
- );
- curl_setopt_array($h_Curl, $a_CurlOptions);
- $v_RefreshCurl = curl_exec($h_Curl);
- curl_close($h_Curl);
- foreach($a_Payload as $v_Key => $v_Value){
- if($v_Key == "code"){
- $a_Payload['code'] = "<tt class='target'>" . $v_Value . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_Payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Response (POST):</h5>";
- $a_RefreshResponse = json_decode($v_RefreshCurl, true);
- $v_RefreshToken = "ERROR";
- if($v_RefreshTokenPath != ""){
- file_put_contents($v_RefreshTokenPath, json_encode($a_RefreshResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- }
- if(isset($a_RefreshResponse['refresh_token']))
- {
- $v_RefreshToken = $a_RefreshResponse['refresh_token'];
- foreach($a_RefreshResponse as $v_Key => $v_Value){
- if($v_Key == "refresh_token"){
- $a_RefreshResponse['refresh_token'] = "<tt class='source'>" . $v_Value . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_RefreshResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- }else{
- echo "<pre>ERROR: Check your submitted variables are the same: Client ID, Client Secret, Redirect URI, Scope(s)</pre>";
- }
- //
- // 3. get access token
- //
- echo "</div><div class='panel'>";
- echo "<h3>3. ACCESS TOKEN...</h3>";
- echo "<h5>Info:</h5>";
- $a_Info = array();
- $a_Info['api']['endpoint'] = $v_TokenEndpoint;
- $a_Info['api']['method'] = "POST";
- $a_Info['api']['header'] = false;
- $a_Info['api']['ssl_check'] = false;
- $a_Info['expires'] = "<tt class='bold'>" . "in 1 hour after creation" . "</tt>";
- $a_Info['date_created'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filectime($v_AccessTokenPath)) : date("Y-m-d H:i:s P");
- $a_Info['date_modified'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_AccessTokenPath)) : date("Y-m-d H:i:s P");
- $a_Info['date_expires'] = file_exists($v_AccessTokenPath) ? date("Y-m-d H:i:s P", filemtime($v_AccessTokenPath) + 3600) : date("Y-m-d H:i:s P", strtotime(time() + 3600));
- echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Request (POST):</h5>";
- $a_Payload = array();
- $a_Payload['refresh_token'] = $v_RefreshToken;
- $a_Payload['client_id'] = $a_CheckFields['client_id'];
- $a_Payload['client_secret'] = $a_CheckFields['client_secret'];
- $a_Payload['redirect_uri'] = $a_CheckFields['redirect_uri'];
- $a_Payload['grant_type'] = "refresh_token";
- $h_Curl=curl_init();
- $a_CurlOptions = array(
- CURLOPT_URL => $v_TokenEndpoint,
- CURLOPT_CUSTOMREQUEST => "POST",
- CURLOPT_POSTFIELDS => $a_Payload,
- CURLOPT_HEADER => 0,
- CURLOPT_VERBOSE => 0,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_SSL_VERIFYPEER => 0,
- CURLOPT_SSL_VERIFYHOST => 0
- );
- curl_setopt_array($h_Curl, $a_CurlOptions);
- $v_AccessCurl = curl_exec($h_Curl);
- curl_close($h_Curl);
- $a_AccessResponse = json_decode($v_AccessCurl, true);
- if($v_AccessTokenPath != ""){
- file_put_contents($v_AccessTokenPath, json_encode($a_AccessResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- }
- foreach($a_Payload as $v_Key => $v_Value){
- if($v_Key == "refresh_token"){
- $a_Payload['refresh_token'] = "<tt class='target'>" . $v_Value . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_Payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Response (POST):</h5>";
- if(isset($a_AccessResponse['access_token'])){
- $v_AccessToken = $a_AccessResponse['access_token'];
- foreach($a_AccessResponse as $v_Key => $v_Value){
- if($v_Key == "access_token"){
- $a_AccessResponse['access_token'] = "<tt class='source'>" . $v_Value . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_AccessResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- }else{
- echo "<pre>ERROR: Check the Refresh Token</pre>";
- }
- //
- // 4. get data sample (last 2 records)
- //
- echo "</div><div class='panel'>";
- echo "<h3>4. DATA SAMPLE...</h3>";
- echo "<h5>Info:</h5>";
- $a_Info = array();
- $a_Info['api']['endpoint'] = $v_DataEndpoint;
- $a_Info['api']['method'] = "GET";
- $a_Info['api']['header'] = true;
- $a_Info['api']['ssl_check'] = false;
- $a_Info['date'] = date("Y-m-d H:i:s P");
- echo "<pre>" . json_encode($a_Info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Request (GET):</h5>";
- $a_Header = array();
- $a_Header[] = "Authorization: Zoho-oauthtoken " . $v_AccessToken;
- $a_Header[] = "Content-Type: application/x-www-form-urlencoded;charset=UTF-8";
- $a_Payload = array();
- $a_Payload['page'] = 1;
- $a_Payload['per_page'] = 2;
- $a_Payload['sort_by'] = "id";
- $a_Payload['sort_order'] = "D";  // in CRM this is "asc" or "desc". in books/inventory this is either "A" or "D"
- $a_Request = array("header" => $a_Header, "parameters" => $a_Payload);
- $h_Curl=curl_init();
- $a_CurlOptions = array(
- CURLOPT_URL => $v_DataEndpoint . "&" . http_build_query($a_Payload),
- CURLOPT_CUSTOMREQUEST => "GET",
- CURLOPT_HEADER => 1,
- CURLOPT_HTTPHEADER => $a_Header,
- CURLOPT_VERBOSE => 0,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_SSL_VERIFYPEER => 0,
- CURLOPT_SSL_VERIFYHOST => 0
- );
- curl_setopt_array($h_Curl, $a_CurlOptions);
- $v_DataCurl = curl_exec($h_Curl);
- foreach($a_Request as $v_Key => $v_Value){
- if($v_Key == 0){
- $a_Request['header'][0] = str_replace("Zoho-oauthtoken ","Zoho-oauthtoken <tt class='target'>", $a_Request['header'][0]) . "</tt>";
- }
- }
- echo "<pre>" . json_encode($a_Request, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- echo "<h5>Response (GET):</h5>";
- if(stripos($v_DataCurl, '"code"')>0){
- $v_DataStart = stripos($v_DataCurl, '{"code"');
- $v_DataStr = trim(substr($v_DataCurl, $v_DataStart));
- $a_DataResponse = json_decode($v_DataStr, true);
- $a_WriteJson['response'] = $a_DataResponse;
- echo "<pre>" . json_encode($a_WriteJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- }elseif(stripos($v_DataCurl, '"data"')>0){
- $v_DataStart = stripos($v_DataCurl, '{"data"');
- $v_DataStr = trim(substr($v_DataCurl, $v_DataStart));
- $a_DataResponse = json_decode($v_DataStr, true);
- $v_Index = 0;
- foreach($a_DataResponse['data'] as $a_Contact){
- $a_WriteJson['records'][$v_Index]['id'] = $a_Contact['id'];
- $a_WriteJson['records'][$v_Index]['name'] = $a_Contact['Full_Name'];
- $a_WriteJson['records'][$v_Index]['email'] = $a_Contact['Email'];
- $a_WriteJson['records'][$v_Index]['created_time'] = str_replace("+", " +", str_replace("T", " ", $a_Contact['Created_Time']));
- $v_Index++;
- }
- echo "<pre>" . json_encode($a_WriteJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "</pre>";
- }else{
- echo "<pre>" . $v_DataCurl . "</pre>";
- }
- echo "</div>";
- }
- else
- {
- echo "<br /><form>";
- echo '<input type="button" value="Click here to open App Permissions »" onclick="javascript:top.location.href=\'' . $v_GrantTokenUrl . '\';" class="btn" style="margin-left: 70px;" />';
- echo "</form>
- <ul>
- <li>Login as a Zoho User with access to the API</li>
- <li>Read and click on accept to allow the app access.</li>
- <li>There is a limit of 5 refresh tokens per minute.</li>
- <li>There is a limit of 19 refresh tokens per app.</li>
- <li>The 20th refresh token will overwrite the 1st.</li>
- </ul><br />";
- }
- // END
- echo "</div><div class='panel'>";
- echo "<h5>Script finished.</h5>";
- echo "<div id='menu_bottom' style='position:normal'><a href='./get_tokens.php' target='_top'>» Start Over</a></div></div>";
- echo "<div id='copyright' style='position:normal'><a href='//joellipman.com/' target='_top'>Copyright © ".date("Y")." Joel Lipman Ltd</a></div></div>";
- }else{
- $v_TldSelected = "";
- $a_TldOptions = array('com','com.au','com.cn','eu','in');
- echo "
- <div class='panel'><form method='get' action='get_tokens.php'>
- <h3>Configure App</h3>
- <table>
- <tr><td colspan='2'>Please select the domain your ZohoCRM is on:<br /><i style='font-size:75%;'>(eg. https://crm.zoho.com = COM, https://crm.zoho.eu = EU)</i> <select name='tld' id='tldSelect'>";
- foreach($a_TldOptions as $v_Option){
- $v_SelectedStr = (strtolower($v_TLD) == $v_Option) ? " selected='true'" : "";
- echo "<option value='" . strtolower($v_Option) . "'" . $v_SelectedStr . ">" . strtoupper($v_Option) . "</option>";
- }
- echo "
- </select></td></tr>
- <tr><td colspan='2' class='centered'><input type='button' value='Open Zoho App Registration' onclick='javascript:openAppConsole();' class='btn' /></td></tr>
- <tr><td colspan='2'> </td></tr>
- <tr><td>Client ID</td><td><input name='client_id' value='' class='txt' onclick='this.select();' /></td></tr>
- <tr><td>Client Secret</td><td><input name='client_secret' value='' class='txt' onclick='this.select();' /></td></tr>
- <tr><td>Scopes</td><td><input name='scopes' value='ZohoCRM.modules.ALL,ZohoCRM.settings.READ,ZohoCRM.users.READ' class='txt' onclick='this.select();' /></td></tr>
- <tr><td>Redirect URI</td><td><input name='redirect_uri' value='" . $v_RedirectUri . "' class='txt' onclick='this.select();' /></td></tr>
- <tr><td colspan='2' style='text-align:center;'><input type='submit' value='Get Grant Token' class='btn' /></td></tr>
- </table>
- </form></div>
- <script>
- function openAppConsole() {
- var e = document.getElementById('tldSelect');
- var strOption = e.options[e.selectedIndex].value;
- window.open('https://accounts.zoho.' + strOption + '/developerconsole', '_blank', 'location=yes,height=600,width=800,scrollbars=yes,status=yes');
- }
- </script>
- ";
- echo "<div id='copyright' style='position:fixed'><a href='//joellipman.com/' target='_top'>Copyright © ".date("Y")." Joel Lipman Ltd</a></div>";
- }
- echo "</body></html>";
Warning(s):
- Do not have multiple staff using this file at the same time.
- Make a note of the refresh_token as this is all you need to access data (use it to generate an access_token).
- The refresh token does NOT expire unless overwritten or revoked.
- There is a limit of 5 refresh tokens per minute.
- There is a limit of 19 refresh tokens per app.
- The 20th refresh token will overwrite the 1st.
- An access token will last 1 hour.
Minimal Upsert
I'm including the code below in this article because it is possibly clearer to understand (and sometimes I need a quick reference):
copyraw
<?php // init $v_DataEndpoint = "https://sandbox.zohoapis.com/crm/v2/CustomModuleApiName/upsert"; $v_AccessToken = "<ENTER_YOUR_ACCESS_TOKEN_VALUE_HERE>"; $a_Header = array('Authorization: Zoho-oauthtoken '.$v_AccessToken, 'Content-Type: application/json'); // set record mandatory field(s) value $a_Data['data'] = array(); $a_Data['data'][0]['Name'] = "Upsert Test via PHP/cURL Script"; // setup cURL request $h_cURL=curl_init(); curl_setopt($h_cURL, CURLOPT_HEADER, 0); curl_setopt($h_cURL, CURLOPT_HTTPHEADER, $a_Header); curl_setopt($h_cURL, CURLOPT_VERBOSE, 0); curl_setopt($h_cURL, CURLOPT_RETURNTRANSFER, 1); curl_setopt($h_cURL, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($h_cURL, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($h_cURL, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($h_cURL, CURLOPT_POSTFIELDS, json_encode($a_Data)); curl_setopt($h_cURL, CURLOPT_URL, $v_DataEndpoint); $r_cURL = curl_exec($h_cURL); if (curl_exec($h_cURL) === false) { $v_Output = curl_error($h_cURL); } else { $v_Output = $r_cURL; } curl_close($h_cURL); // output echo $v_Output;
- <?php
- // init
- $v_DataEndpoint = "https://sandbox.zohoapis.com/crm/v2/CustomModuleApiName/upsert";
- $v_AccessToken = "<ENTER_YOUR_ACCESS_TOKEN_VALUE_HERE>";
- $a_Header = array('Authorization: Zoho-oauthtoken '.$v_AccessToken, 'Content-Type: application/json');
- // set record mandatory field(s) value
- $a_Data['data'] = array();
- $a_Data['data'][0]['Name'] = "Upsert Test via PHP/cURL Script";
- // setup cURL request
- $h_cURL=curl_init();
- curl_setopt($h_cURL, CURLOPT_HEADER, 0);
- curl_setopt($h_cURL, CURLOPT_HTTPHEADER, $a_Header);
- curl_setopt($h_cURL, CURLOPT_VERBOSE, 0);
- curl_setopt($h_cURL, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($h_cURL, CURLOPT_SSL_VERIFYPEER, 0);
- curl_setopt($h_cURL, CURLOPT_SSL_VERIFYHOST, 0);
- curl_setopt($h_cURL, CURLOPT_CUSTOMREQUEST, 'POST');
- curl_setopt($h_cURL, CURLOPT_POSTFIELDS, json_encode($a_Data));
- curl_setopt($h_cURL, CURLOPT_URL, $v_DataEndpoint);
- $r_cURL = curl_exec($h_cURL);
- if (curl_exec($h_cURL) === false) {
- $v_Output = curl_error($h_cURL);
- } else {
- $v_Output = $r_cURL;
- }
- curl_close($h_cURL);
- // output
- echo $v_Output;
Category: Zoho :: Article: 664
Add comment