Guidance needed with OAuth and API / PHP SDK to Update Custom Field

The following code is my current process file that takes variables from a Keap POST TO HTTP automation step , Creates a RefCode, submits the data to a DB, and sends me an email to confirm submission.

<?php
        // this is currently commented out 
        // may need to uncomment it as part of OAuth processing
        //session_start();

        require_once('dogs.php');

$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);

if (!$conn){
    echo 'Not Connected to Server';
}

if(!mysqli_select_db($conn, 'database_name'))
{
    echo 'Database Not Selected';
}

/* Vehicle Info - 53 Varialbes*/
$CustID = $_REQUEST['CustID'];
etc...;

// generates $RefCode
function generateKey() {
    global $conn;
    $keyLength = 9;
    $str = "1234567890ABCDEFGHJKLMNPQRSTUVWXYZ";
    $checkKey=true;
    while ($checkKey == true) {
        $randstr = substr(str_shuffle($str),0,$keyLength);
        
// New random key is created, verify it is unique
        $sql = "SELECT * FROM dummydb WHERE RefCode=" . $randstr;
        $result = mysqli_query($conn,$sql);
        if (mysqli_num_rows($conn, $result)==0) {
            $checkKey=true;
        } else {
            $checkKey=false;
        }
        return $randstr;
    }
}

$RefCode = generateKey();

$stmt = mysqli_prepare($conn, "INSERT INTO dummydb VALUES 
(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");

mysqli_stmt_bind_param($stmt, 'ssssssssssssssssssssssssssssssssssssssssssssssssssssss', 

$CustID, etc .....,$RefCode);

/* execute prepared statement */

mysqli_stmt_execute($stmt);

$htmlContent = file_get_contents ("RESULT.html");
$refcod = "RefCode";

//  Loop thru all fields and get the live data
foreach($_REQUEST as $fieldname=>$data) {
      
        //  Use this to replace in the email template
         $htmlContent = str_replace($fieldname, $data, $htmlContent); 
         $htmlContent = str_replace($refcod, $RefCode, $htmlContent); 

         }
            echo $htmlContent;
--------------------------------------------------------------------           
// Here is where I believe the new API PHP SDK code to 
// submit RefCode back to Keap with dupcheck would 
// go if I were to embed it in this original process code. 

// Put the following here as an example.            
/*require_once '/autoload.php';

$infusionsoft->data()->updateCustomField()


$infusionsoft = new \Infusionsoft\Infusionsoft(array(
    'clientId' => CLIENT_ID,
    'clientSecret' => CLIENT_SECRET,
    'redirectUri' => REDIRECT_URL,
));

$infusionsoft->refreshAccessToken();

$infusionsoft->data()->updateCustomField($customFieldId, $values)            
*/            
-----------------------------------------------------------
/* PHP code to send HTML via email as part of form submission*/ 
$to = 'myemail@gmail.com';
$from = 'support@company.com';
$fromName = 'Company Name';
$subject = "Details From HTTP Post";

// Set content-type header for sending HTML email
$headers = "MIME-Version: 1.0 " . "\r\n";
$headers .= "Content-type: text/html;charset=UTF8"."\r\n";

// Additional Headers
$headers .='From: '.$fromName.'<'.$from.'>'."\r\n";

//$headers .='Bcc: support@company.net'."\r\n";
//Send Email
mail($to, $subject, $htmlContent, $headers);

function valid_email($OwnEml) {
	return filter_var($OwnEml, FILTER_VALIDATE_EMAIL);	
}

function isInjected($str) {
	$injections = array('(\n+)',
	'(\r+)',
	'(\t+)',
	'(%0A+)',
	'(%0D+)',
	'(%08+)',
	'(%09+)'
	);
	$inject = join('|', $injections);
	$inject = "/$inject/i";
	if(preg_match($inject,$str)) {
		return true;
	} else {
		return false;
	}
}

/* close statement and connection */
mysqli_stmt_close($stmt);

/* close connection */
mysqli_close($conn);

?>

As stated, above is the functioning PHP code I have now.

What follows is the sample code I’ve put together using Keap GitHub and Keap API documentation.

I am looking for guidance to figure out what parts of this sample code are needed to facilitate the OAuth and then the update of a custom field in Keap labeled ~Contact._RefCod~

//The following PHP is currently in a file named refcode2keap.php

<?PHP
// I don't know if this need to have its own file because it appears
// the session_start for this file is associated with the access token
// if this is the case I can't embed this piece of code into the above
// (Original) file because that file session_start has been commented out.
// If it is the case that I can uncomment the session_start and embed
// the new API code into the original file this is where I believe that 
// new code would start and look as follows:

session_start();


require_once('keapdogs.php');
----------------------------------------------
/* this is what is in the keapdogs.php file

 * <?php
$clientId = "I have this";
$clientSecret = "and I have this";

 // this is the file name for the code where the initial process starts,
 // the name of the original code file above
$rediretUri = "DETAILPROCR2K.php";
// I understand it may need to be the full URL later.
?>
// I also understand I will need to configure properly for the
 * specific OAuth variables that will be used...
 */
-----------------------------------------------
// this is a code sample that I obtained from the Youtube OAuth Video 
// recommended by Keap Developers.

// my question is how do you eliminate the need for physical confirmation
// in this authorization process because as a coding process there
// will be no one to click to confirm physically
// I also understand this is the OAuth process and the 
// code that comes after may be redundant, but I need
// to determine the easiest options without physical confirmation 

require("OAuth2-iSDK/src/isdk.php");

$app = new iSDK();

if(!isset($_GET['code'])) {
        //variables will need to be configured properly in the keapdogs file
	$app->setClientID(CLIENT_ID);
	$app->setSecret(CLIENT_SECRET);
	$app->setRedirectURL(REDIRECT_URL . REDIRECT_FILE);
	echo "<p>Click the link below to allow access to your Infusionsoft Application.</p>";
	echo '<a href="' . $app->getAuthorizationURL() . '">Authorize My Application</a>';
} else {
	$app->setClientID(CLIENT_ID);
	$app->setSecret(CLIENT_SECRET);
	$app->setRedirectURL(REDIRECT_URL . REDIRECT_FILE);
	$auth=null;
	$auth=$app->authorize($_GET['code']);
	var_dump ($auth);
}
// the following code is sample code from the github
// at infusionsoft-php/samples/addWithDupCheck.php
// I understand this is redundant to the OAuth
// but I need to understand which of the two is the best
// option to OAuth processing for the API request purpose with
// out the physical clicking for authorization
require_once '../vendor/autoload.php';
$infusionsoft = new \Infusionsoft\Infusionsoft(array(
    // variables established in keapdogs.php
    'clientId' => $clientId,
    'clientSecret' => $clientSecret,
    'redirectUri' => $rediretUri,
));

// If the serialized token is available in the session storage, we tell the SDK
// to use that token for subsequent requests.
if (isset($_SESSION['token'])) {
    $infusionsoft->setToken(unserialize($_SESSION['token']));
}

// If we are returning from Infusionsoft we need to exchange the code for an
// access token.
if (isset($_GET['code']) and !$infusionsoft->getToken()) {
    $infusionsoft->requestAccessToken($_GET['code']);

    // Save the serialized token to the current session for subsequent requests
    $_SESSION['token'] = serialize($infusionsoft->getToken());
}

// the following was part of github code sample and I believe its what creates a contact
// and that is not what I want to do so I commented it out, 
/*function add($infusionsoft, $email)
{
    $email1 = new \stdClass;
    $email1->field = 'EMAIL1';
    $email1->email = $email;
    $contact = ['given_name' => 'John', 'family_name' => 'Doe', 'email_addresses' => [$email1]];

    return $infusionsoft->contacts()->create($contact);
}*/

//this is a piece of a JSON response to a "POSTMAN" GET request of a custom field list
//which identifies the custom field data I look to update.
 /* {
        "id": 105,
        "label": "RefCod",
        "options": [],
        "record_type": "CONTACT",
        "field_type": "Text",
        "field_name": "RefCod"
    },
 */

//Here I've create variables using the "id" from the JSON above for the 
//$CustomFieldId variale
$CustomFieldID = "105";
//And a global value variale for $value which I assume should be the data I 
//want to update.  This variale data is established in my original process php file
$value = $RefCode;

//this is code I pulled from github file under:
// infusionsoft-php/src/Infusionsoft/Api/DataService.php
function updateCustomField($customFieldId, $values)
	{
		return $this->client->request('DataService.updateCustomField', $customFieldId, $values);
	}
// It seem like I'm missing the part that is needed to identify the customer by using
// the Keap Customer ID but then I look at the following code and wonder if
// it is doing that with the rest of the code that is part of the addWithDupCheck.php
if ($infusionsoft->getToken()) {
    try {
        //$OwnEml is a varialble establish from my original process php file from above
        // is also part of my question on embed or require_once concerns
        $email = $OwnEml;
// the rest of this I assume is part of the addWtihDupCheck and refreshing the token
        try {
            $cid = $infusionsoft->contacts()->where('email', $email)->first();
        } catch (\Infusionsoft\InfusionsoftException $e) {
            $cid = add($infusionsoft, $email);
        }

    } catch (\Infusionsoft\TokenExpiredException $e) {
        // If the request fails due to an expired access token, we can refresh
        // the token and then do the request again.
        $infusionsoft->refreshAccessToken();

        $cid = add($infusionsoft);
    }

// I think 'custom_fields' here needs to be the ~Contact._RefCod~ or //something to that affects but I'm not sure...

    $contact = $infusionsoft->contacts()->with('custom_fields')->find($cid->id);

    var_dump($contact->toArray());

    // Save the serialized token to the current session for subsequent requests
    $_SESSION['token'] = serialize($infusionsoft->getToken());
} else {
//my concern here is, this is suppose to be an automated process so
// there won't be anyone to manually "Click here to authorize" and how 
// do I get around not doing this...

    echo '<a href="' . $infusionsoft->getAuthorizationUrl() . '">Click here to authorize</a>';
}

?>

I’m sure all of this isn’t in the right order, but would definitely appreciate guidance in the right direction… I have not tried to run or test this because I know in its current form it’s not going to be even close to working…

My question is, is it better to add the API code sequence to the original PHP code I currently have (embed it as part of the original .php post to the HTTP file process), or do I create a separate .php file and then “require_once” at the top of the original .php file and will it still use the variables established in the original .php file to complete the request in the “require_once” file request.

My other question is which of the two OAuth examples is the best to use for this request and how to best navigate around the physical click to authorize since this is more of an automated process than one that has customer interaction.

Lastly, I know the code, in the state it’s in, is not where it needs to be to work, I’ve put together as much as I could to get as close to what I think is needed from my rudimentary understanding of the API and Docs. I did this because I was told by developers in this community, that prior to seeking help and guidance I should try reading and using the code samples first, then come ask for help.

So again, any help or guidance would be very much appreciated, thank you in advance for your consideration.

The OAuth flow requires initial physical authorization by a user. That’s how access_token and refresh_token get created. Once that auth takes place, the refresh token (as its name suggests) can be used to request an updated access token, which also comes with an updated refresh token, so the whole process can continue to be fully automated. But you need to make sure you’ve got a way to conduct the initial user authorization, and that you’re storing the tokens and have a scheduled way (e.g. cronjob) to refresh them before the refresh token expires.

If the system really does have no user input ever, then OAuth won’t work. Keap has introduced personal access tokens and service accounts designed for this situation (more info here). It’s definitely simpler, but usage quotas are much lower than OAuth and you need to be extra sure you store those secrets safely, because they don’t expire.

As for how to organize your code: by default variables in PHP (outside of functions ) are always global in scope, so code in the required files should have access to any variables that were defined prior to require_once() . There’s no difference. (Unless you are using namespaces or classes, but doesn’t look like you are. I highly recommend learning to use both and ultimately learning a bit about good object-oriented programming patterns if you’re working with PHP regularly.)

The only technical benefit to requiring a separate file is that it would be reusable from multiple entry points. If this whole flow is single-purpose, there’s no need for that complexity — and trusting that your global variables will be defined inside another file, as you do here, is extremely, extremely brittle.

Last point: are you using Composer?. Your question about whether to use require("OAuth2-iSDK/src/isdk.php"); or require_once '../vendor/autoload.php'; makes me question if you’ve set up Composer for your project and understand how it works, how autoloading works. (If you don’t and you’re just trying to find your own way through all this, good for you! I’ve been there and know how confusing it can be. But taking a few steps back to make sure you’ve got the basics could be helpful.)

@Andron_Ocean, someone helped Maria out with this, and has been answered in this post below.

1 Like