Remote Ops API Issue

I’m having a serious issue with the Remote Operations API. I have a mu_plugin that I’m using to save data from submitted via a Gravity Forms form to s2Member fields in the database. The code works fine on my Dev site, but doesn’t run appear to be firing on the Production site.

The only difference in the code is the API key (and I have verified that the key on the Production site is correct).

There were several plugins running on the Production site that aren’t on the Dev site, but I deactivated them all and nothing changed.

I’ve checked for errors in the Developer Console, the WordPress debug.log and on the server. Both sites are on SiteGround (although on different accounts they are the same TYPE of account). I’m at my wit’s end here because I can’t find a logical explanation for this. I’m hoping I’ve forgotten something stupid and someone can point it out for me. The code (minus the API key) is below:

<?php 
add_action( 'gform_user_registered', 'sg_add_s2data', 10, 4 );

/*******************************************************
  Notes on the $entry Values in the following function 

 All $entry values are retrieved using the Field ID   except the Stripe Transaction ID (which is not a field entry. Ths function works for ALL forms which have a 
 User Registration and Stripe feed. BUT the Field IDs 
 must remain the same. You can delete fields but if you 
 add fields to a form based on the template form, you 
 must be sure that the field IDs of the added fields are 
 greater than 40 (to be safe).                      
 ******************************************************/

function sg_add_s2data( $user_id, $feed, $entry, $user_pass ) {    
    
    $sg_user = $user_id;
    $sg_company = rgar( $entry, '7');
    $sg_address = rgar( $entry, '8');
    $sg_address2 = rgar( $entry, '9');
    $sg_city = rgar( $entry, '10');
    $sg_state = rgar( $entry, '11');
    $sg_zip = rgar( $entry, '12');
    $sg_country = rgar( $entry, '13');
    $sg_profession = rgar( $entry, '15');
    $sg_profession_providers = rgar( $entry, '16');
    $sg_profession_other = rgar( $entry, '17');
    $sg_ibclc = rgar( $entry, '18');
    $sg_nlnum = rgar( $entry, '19');
    $sg_contactsrc = rgar( $entry, '20');
    $sg_ccaps = rgar( $entry, '21');
    $sg_eot = rgar( $entry, '23' );
    $sg_txnid = rgar( $entry, 'transaction_id');
     
    // Troubleshooting: Echo Field Values 
    // echo "<p><strong>User ID: </strong>". $user_id."</p>";
    // echo "<p><strong>Company: </strong>". $sg_company."</p>";
    // echo "<p><strong>Address: </strong>". $sg_address."</p>";
    //echo "<p><strong>Address2: </strong>". $sg_address2."</p>";
    // echo "<p><strong>City: </strong>". $sg_city."</p>";
    // echo "<p><strong>State: </strong>". $sg_state."</p>";
    // echo "<p><strong>Zip: </strong>". $sg_zip."</p>";
    // echo "<p><strong>Country: </strong>". $sg_country."</p>";
    // echo "<p><strong>Profession: </strong>". $sg_profession."</p>";
    // echo "<p><strong>Provider Profession: </strong>". $sg_profession_providers."</p>";
    // echo "<p><strong>Profession - Other: </strong>". $sg_profession_other."</p>";
    // echo "<p><strong>IBCLC: </strong>". $sg_ibclc."</p>";
    // echo "<p><strong>Nursing License: </strong>". $sg_nlnum."</p>";
    // echo "<p><strong>Where Did You Hear About Us?: </strong>". $sg_contactsrc."</p>";
    // echo "<p><strong>CCAPs: </strong>". $sg_ccaps."</p>";
    // echo "<p><strong>EOT: </strong>". $sg_eot."</p>";
    // echo "<p><strong>Transaction ID: </strong>". $sg_txnid."</p>";

    $op['op'] = 'modify_user'; // The Remote Operation.
    $op['api_key'] = 'removed'; // Check your Dashboard for this value.
// Save Data
$op['data'] = array(
    'user_id'    => $sg_user, // A WordPress® User ID.
    's2member_ccaps' => $sg_ccaps, // Optional—if updating.
    // Any Custom Capabilities you supply here will be added to any that a User already has.
    // If you want to remove all Custom Capabilities, start your list with "-all". Ex: "-all,music,videos" (removes all, then adds: music,videos).
    // If you simply want to remove all Custom Capabilities, set this to "-all" (removes all Custom Capabilities, adds none).
    's2member_subscr_gateway' => 'stripe', 
    's2member_subscr_id'      => $sg_txnid, 
    's2member_custom' => 'tmmgf.sitegeeks.com', 
    's2member_auto_eot_time' => $sg_eot, 
    'custom_fields' => array('company' => $sg_company,
                             'address' => $sg_address,
                             'address2' => $sg_address2,
                             'city' => $sg_city,
                             'state' => $sg_state,
                             'zip' => $sg_zip,
                             'country' => $sg_country,
                             'profession' => $sg_profession,
                             'profession_providers' => $sg_profession_providers,
                             'profession_other' => $sg_profession_other,
                             'ibclc' => $sg_ibclc,
                             'nurse_license_number' => $sg_nlnum,
                             'contact_source' => $sg_contactsrc,
                            ), 
    's2member_notes' => 'Modified this User via API call from Gravity Forms.', 

);
$post_data = stream_context_create(array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => 's2member_pro_remote_op='.urlencode(json_encode($op)))));
$result    = json_decode(trim(file_get_contents('https://tmmgf.sitegeeks.com/?s2member_pro_remote_op=1', false, $post_data)), true);

if ($result && empty($result['error']) && !empty($result['ID'])) {
    echo 'Success. Modified user ID: '.$result['ID'];
} elseif (!empty($result['error'])) {
    echo 'API error reads: '.$result['error']; 
}
}

There’s still a lot of troubleshooting code because I still haven’t got this working on the Production site.

Any ideas, anyone?

Thanks in advance.

Pat, this is at a level way beyond my expertise, but I notice that your conditionals at the end don’t capture all possibilities. What if there is no error but also no ID? Is that possible? If so, could that be what’s happening on your production site?

The other thing I notice, when comparing your code to what is shown here, is that they assign a variable to what in your code is ```
array(‘http’ => array(‘method’ => ‘POST’, ‘header’ => ‘Content-type: application/x-www-form-urlencoded’, ‘content’ => ‘s2member_pro_remote_op=’.urlencode(json_encode($op))))

and then they apply the `$post_data = stream_context_create()` to that variable.

I know it shouldn't make a difference, but I've sometimes seen that things like this do have a tendency to behave differently from site to site.

Well, I’ve figured out that the function is never firing on the production site. I added the following as the first lines of the function:

echo "Function sg_add_s2data() Triggered";
    usleep(5000000);

Nothing. So, for some reason, the gform_user_registered hook isn’t firing on the production site even though the user is being registered. Neither is the WP core hook user_register (I just checked.) Not an s2Member problem., but do you have any idea what would cause these hooks not to fire after I’ve virtually eliminated a plugin/theme conflict? (I’ve checked everything that is different between the 2 sites that I can find… ) :wink:

@JediShark Here are some things you might take a look at.

  • Is your hook attached early enough for it to be picked up and processed when the action occurs? If you have this code in an MU plugin, then that’s as early as you can get. So you’re good there. Just be sure that your plugin is actually being included by WordPress; i.e., an MU plugin file must end with a .php extension.

  • As seen here, there is a scenario when this hook will not fire. So just be sure the registration event itself is not failing due to a validation issue in Gravity Forms.

@jaswrks:

Thanks for taking a look, but the registration event is occurring (a User is registered) so it isn’t door #2 either. I now know that neither the Gravity Form hook (gform_user_registered) nor the WP core hook (user_register) trigger the function in the mu_plugin (on the production site, both work fine on the dev site).

I’m in the process of cloning a new dev site on the same server account as the production site so that I can swap out the theme. Although the versions are identical on the dev and production site I found some files on the production site dated later than the dev site. The client didn’t want me to swap out the themes on the production site and he’s paying for it, so… Before I swap out the theme, I’ll run another test to be absolutely sure it isn’t some server setting that’s causing the problem on the Production site… sigh

Is it works with the default WP theme on production site?

Nope. I got it down to TwentySixteen and just the plugins I need to run the form (all of which exist on the Dev Site) on a new Dev site on the production server. It still didn’t work. I’m almost convinced it is something in the server setup, but I’m going to create a truly clean WordPress install on the same server, add just the plugins I need to run the form and s2Member and see what happens then.

The Gravity Forms logs indicate that User Registration is completing successfully and those logs look identical on both (well now “all”) the servers so that hook should be firing. It just isn’t.

The good news is that if the form doesn’t work on a clean install I can go to SiteGround and unless they’ve changed since last we spoke, they will do their best to track this down.

I did test this in the cleanest WordPress installation possible: a new install,TwentySixteen, and just Gravity Forms, the User Registration AddOn, the Coupon AddOn, the Stripe AddOn, and s2Member. Still no go.

I submitted a ticket to Siteground yesterday afternoon. They reported that they had found the code was causing a mod_security filter to kick in and that they had excluded that filter from the account, but still no joy. The function still is not being triggered even though User Registration is taking place. I’ve kicked it back to Siteground. It will probably need to be elevated and the Level II/III guys usually don’t work on the weekends.

I’ll keep you guys updated.

Stupid, but possible way to debug: Install “Debug Bar” and “Debug Bar Actions and Filters Addon” (if not installed already), it’s a good “digital shovel” to dig in code :slight_smile: Check if the hook is fired, there is a list with hooks, in the order they run. Check in code, find nearest hook that is in the list. This way you will narrow the area of code, that runs wrong. Can set messages to near hooks to confirm that they fire, like you did for your function. And all this can show you some more info, that will allow you to understand the problem. Not easy. Stupid. But “brute force” way works always…

1 Like

@krumch: Thanks! I’ll try that. (Tomorrow. Today is my “Photography” day and I’ve already spent an hour on this…

@jaswrks: SiteGround said they were getting the following error when trying to run WP CLI on the problem account:

Notice: Undefined index: HTTP_HOST in /home/themilkm/public_html/clean/wp-content/plugins/s2member-pro/src/includes/syscon.inc.php on line 137

Could this have anything to do with my issue? Thanks in advance.

@jaswrks:

It appears that the function is finally being called on my clean WordPress installation. Although I am still not seeing my echo statements, I am seeing this error

Function sg_add_s2data() TriggeredAPI error reads: Invalid API key.

The trouble is the API key is correct. I’ve triple-checked it and I even checked the s2Member Options in the database. While all of those entries are serialized and thus hard to read, the correct API key is in that data.

Any ideas?

An E_NOTICE level error in PHP should not stop of the flow of events, so this should not prevent a hook from firing. However, you may want to take the site out of WP_DEBUG mode and test again, because there are times when E_NOTICE level errors can corrupt output expected from API calls; e.g., asking an API for a user ID, and getting back the ID number, but with a debug-level notice alongside it. That’s not what’s expected, so things like this can creep into an application when WP_DEBUG mode is enabled. This makes WP_DEBUG a double-edge sword at times. It can help you find bugs, but it can also introduce bugs that simply do not exist when debug mode is off.

The s2Member software itself has several E_NOTICE level errors that show up when running in WP_DEBUG mode. We have been on a mission to get rid of all of those for some time now, but there are certain areas of the codebase where the refactoring required would pose more of a risk than trying to do a quick fIx that gets rid of them. For that reason, some of these (i.e., the ones that are harmless) we have left in there for now. As the codebase undergoes additional refactoring in the future, they will be removed. For example, if you run from a CLI, the $_SERVER['HTTP_HOST'] is undefined. That’s to be expected from a CLI request. It’s harmless, but running from WP CLI without defining a host URL from the command-line is not advised. I would suggest they review the documentation for WP CLI and add the --url argument when they run commands. https://make.wordpress.org/cli/handbook/config/ ~ This is an aid for many plugins (ones that rely upon $_SERVER['HTTP_HOST'], not just for s2Member.

You can add a debug line here. Before that line, add the following:

file_put_contents(WP_CONTENT_DIR.'/s2-debug.log', 'Key should be: '.print_r(self::remote_ops_key_gen(), true)."\n".print_r($data, true)."\n\n", FILE_APPEND);

Run the test again, and then review the s2-debug.log file.

Thanks, Jason. I’ll do that and report back here.

I did exactly what you said, copying the given code and pasting it following the opening bracket at the end of the IF statement. I ran the test again. Got the API Key error, but nothing was saved to s2-debug.log. The s2-debug.log file did not exist, so I created one just in case that was the problem and ran the test again. Still nothing. I don’t know if that means the API key is actually correct or that the debug code is not working (since my echo statements are not working…).

I’m beginning to think I’m going to have to return to the drawing board and find a way to save the s2Member data that does not involve the Remote Ops API.

@jaswrks: If there’s anything you’d recommend I try to hook into (that I could feed my form fields) or any other advice would be greatly appreciated.

The API error you mentioned comes from that specific line of code in the s2Member codebase. So if you updated that file, added the debug line, ran another test, and didn’t get a log file, that would indicate to me that your addition to the code was never truly applied.

How can that be? PHP has a built-in OPcache that keeps code in memory. Changing the file on the server is not always enough by itself. See: http://php.net/manual/en/function.opcache-reset.php

While going through this issue with another developer, one not familiary with s2Member but good with WordPress, I noticed that the API key on my dev site (the one that works) had no special characters in it while the API that did not work (on the dev site on the client’s server) did have quite a few special characters. I was grasping at straws and didn’t think anything would change so I did a bad thing and did not save the API key that did not work. I replaced it with the same API key as the other dev site, both in s2Member (saved to database) and in the hack file. Guess what? The API key was recognized. There may be an issue with some special characters in the API key for remote operations. I will do some further testing and submit a GitHub issue.

However, the code is still not working on the client’s server. Now it is reporting that the User ID is invalid (again it is not). I’m going to do some further troubleshooting here as well, but hoped maybe something would jump out at you @jaswrks about any reason this User ID thing might happen.

Thanks in advance.