I am trying to connect to the Google Shopping Products API to create some new product items. Before I do that more complex task, I am just trying to connect to get a list of the existing products.
I have setup a Service Account within Google API console and downloaded the json key file. I have saved the file to the server. I have then taken all of the examples from Google's documents and tried to piece them together.
VARIABLE NOTES:
$KEY_FILE_LOCATION - this is the location of the json service account key file.
$merchantid - this is the merchantid of my Google Merchant Centre
CODE:
$client->setAuthConfig($KEY_FILE_LOCATION);
$client->setApplicationName('Merchant Centre');
$client->setScopes('https://www.googleapis.com/auth/content');
echo "<br/><br/>client: ".json_encode($client);
$url = "https://www.googleapis.com/content/v2/".$merchantid."/products";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
echo "<br/><br/>data: ".$data;
What I am having trouble with, is how to connect the authentication to the actual google shopping call. The connection to the content/v2/merchantid/products call returns this response:
{ "error": { "errors": [ { "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Login Required" } }
So how do I connect the oauth 2.0 authentication service account json to the actual call. I can't find anything in the documentation or online, about how you actually connect the two things together. I have integrations with other Google API's but none of the code I have provides a clear example of implementing this.
EDIT: Following the documentation through further I have managed to work out a flow that may work. I need to use JWT to obtain a token for the call to the API - the code below is being used to access the token, but it is still failing on the last section. The response from this call is { "error": "invalid_grant", "error_description": "Invalid JWT Signature." }. The signature section of the JWT is the only section that looks different to the example Google has given - my code outputs a signature of 43 characters whereas googles is significantly longer.
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
$iat = strtotime("now");
$exp = strtotime("+1 hour");
$currenttime = date("H:i:s");
$claimset = json_encode(['iss' => 'REDACTED',
'scope' => 'https://www.googleapis.com/auth/content',
'aud' => 'https://www.googleapis.com/oauth2/v4/token',
'exp' => $exp,
'iat' => $iat]);
$base64UrlClaimSet = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claimset));
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlClaimSet, $privatekey, true);
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
$jwt = $base64UrlHeader . "." . $base64UrlClaimSet . "." . $base64UrlSignature;
$url = "https://www.googleapis.com/oauth2/v4/token";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=".$jwt);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
echo "<br/><br/>data: ".$data;
I am trying to follow the 'computing the signature' section of this https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Okay, I have managed to work through this over 2 days, and have the solution. I am posting it here to help out others in the future who may struggle with this. This is for using php with a google service account to obtain a token using JWT and then make a call to get all products from your Google Merchant Shopping account.
$merchantid = xxxxxxxxx; //this is a 9 digit code found in top left of Merchant Center
$email = '[email protected]'; //this is the email address assigned to the service account
$privatekey = "-----BEGIN PRIVATE KEY-----\nXXXXXapprox 6 lines long key from json fileXXXXXX\n-----END PRIVATE KEY-----\n"; //this is the long key that you download within the json file at the end of the service account setup
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
$iat = strtotime("now");
$exp = strtotime("+1 hour");
$currenttime = date("H:i:s");
$claimset = json_encode(['iss' => $email,
'scope' => 'https://www.googleapis.com/auth/content',
'aud' => 'https://www.googleapis.com/oauth2/v4/token',
'exp' => $exp,
'iat' => $iat]);
$base64UrlClaimSet = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claimset));
$binary_signature = "";
$algo = "SHA256";
openssl_sign($base64UrlHeader.".".$base64UrlClaimSet, $binary_signature, $privatekey, $algo);
$jwtSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($binary_signature));
$jwt = $base64UrlHeader . "." . $base64UrlClaimSet . "." . $jwtSignature;
$url = "https://www.googleapis.com/oauth2/v4/token";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=".$jwt);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
$accesstoken = substr($data, strpos($data, 'access_token') + 16);
$arr = explode('"',trim($accesstoken));
$accesstoken = $arr[0];
$url = "https://www.googleapis.com/content/v2/".$merchantid."/products";
$header = array(
'Authorization: Bearer '.$accesstoken
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
echo "<br/><br/>data: ".$data;