We are in process of migration of Google Apps Marketplace listing using the UpgradeableApp API resource but when calling PUT https://www.googleapis.com/appsmarket/v2/upgradableApp/listingID/cwsID/domain with signed request getting error:
81 {"error":{"errors":[{"domain":"global","reason":"backendError","message":"Backend Error"}],"code":500,"message":"Backend Error"}} 0
What i am doing wrong....
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OAuth.Net.Common;
using OAuth.Net.Components;
using System.IO;
using System.Net;
using System.Security.Cryptography;
namespace Google_UpgradeableApi_Console
{
class Program
{
private static readonly ISigningProvider SigningProvider = new HmacSha1SigningProvider();
static void Main(string[] args)
{
// Setup the variables necessary to create the OAuth 1.0 signature and make the request
string httpMethod = "PUT";
string listingID = "xxxx+23453809800066606066";
string cwsID = "bbmagicjcjeblpadhhnnjahfbbbbhjk";
string domain = "xyz.com";
Uri url = new Uri(String.Format("{0}/{1}/{2}/{3}", "https://www.googleapis.com/appsmarket/v2/upgradableApp", listingID, cwsID, domain));
string consumerKey = "xyz.apps.googleusercontent.com";
string secret = "gt2sj34656U687f8qj677+GK";
string body = "";
MemoryStream requestBody = null;
string signatureMethod = SigningProvider.SignatureMethod;
HttpWebResponse response = null;
// Set the Nonce and Timestamp parameters
string nonce = getNonce();
string timestamp = getTimestamp();
// Set the request body if making a POST or PUT request
if (httpMethod == "POST" || httpMethod == "PUT")
{
requestBody = new MemoryStream(Encoding.UTF8.GetBytes(body));
}
// Create the OAuth parameter name/value pair dictionary
Dictionary<string, string> oauthParams = new Dictionary<string, string>
{
{ "oauth_consumer_key", consumerKey },
{ "oauth_signature_method", signatureMethod },
{ "oauth_timestamp", timestamp },
{ "oauth_nonce", nonce },
};
// Get the OAuth 1.0 Signature
string signature = generateSignature(httpMethod, url, oauthParams, requestBody, secret);
Console.WriteLine("OAuth 1.0 Signature = " + signature + "\r\n\r\n");
// Add the oauth_signature parameter to the set of OAuth Parameters
IEnumerable<KeyValuePair<string, string>> allParams = oauthParams.Union(new[]
{
new KeyValuePair<string, string>("oauth_signature", signature)
});
// Defines a query that produces a set of: keyname="URL-encoded(value)"
IEnumerable<string> encodedParams = from param in allParams
select param.Key + "=\"" + Uri.EscapeDataString(param.Value) + "\"";
// Join all encoded parameters with a comma delimiter and convert to a string
string stringParams = String.Join(",", encodedParams);
// Build the X-Authorization request header
string xauth = String.Format("X-Authorization: OAuth realm=\"{0}\",{1}", url, stringParams);
Console.WriteLine("X-Authorization request header: \r\n" + xauth + "\r\n\r\n");
try
{
// Setup the Request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = httpMethod;
request.Headers.Add(xauth);
// Set the request body if making a POST or PUT request
if (httpMethod == "POST" || httpMethod == "PUT")
{
byte[] dataArray = Encoding.UTF8.GetBytes(body);
request.ContentLength = dataArray.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(dataArray, 0, dataArray.Length);
requestStream.Close();
}
// Send Request & Get Response
response = (HttpWebResponse)request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
// Get the response stream and write to console
string json = reader.ReadToEnd();
Console.WriteLine("Successful Response: \r\n" + json);
}
}
catch (WebException e)
{
// This exception will be raised if the server didn't return 200 - OK
// Retrieve more information about the error
if (e.Response != null)
{
using (HttpWebResponse err = (HttpWebResponse)e.Response)
{
Console.WriteLine("The server returned '{0}' with the status code '{1} ({2:d})'.",
err.StatusDescription, err.StatusCode, err.StatusCode);
}
}
}
finally
{
if (response != null) { response.Close(); }
}
Console.ReadLine();
}
#region Helper Functions
/// <summary>
/// Generates a random nonce.
/// </summary>
/// <returns>A unique identifier for the request.</returns>
private static string getNonce()
{
string rtn = Path.GetRandomFileName() + Path.GetRandomFileName() + Path.GetRandomFileName();
rtn = rtn.Replace(".", "");
if (rtn.Length > 32)
return rtn.Substring(0, 32);
else
return rtn;
}
/// <summary>
/// Generates an integer representing the number of seconds since the unix epoch using the
/// UTC date/time of the request.
/// </summary>
/// <returns>A timestamp for the request.</returns>
private static string getTimestamp()
{
return ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
}
/// <summary>
/// Generates an OAuth 1.0 signature.
/// </summary>
/// <param name="httpMethod">The HTTP method of the request.</param>
/// <param name="url">The URI of the request.</param>
/// <param name="oauthParams">The associative set of signable oauth parameters.</param>
/// <param name="requestBody">A stream containing the serialized message body.</param>
/// <param name="secret">Alphanumeric string used to validate the identity of the education partner (Private Key).</param>
/// <returns>A string containing the BASE64-encoded signature digest.</returns>
private static string generateSignature(
string httpMethod,
Uri url,
IDictionary<string, string> oauthParams,
Stream requestBody,
string secret
)
{
// Ensure the HTTP Method is upper-cased
httpMethod = httpMethod.ToUpper();
// Construct the URL-encoded OAuth parameter portion of the signature base string
string encodedParams = normalizeParams(httpMethod, url, oauthParams, requestBody);
// URL-encode the relative URL
string encodedUri = Uri.EscapeDataString(url.AbsolutePath);
// Build the signature base string to be signed with the Consumer Secret
string baseString = String.Format("{0}&{1}&{2}", httpMethod, encodedUri, encodedParams);
//return generateCmac(secret, baseString);
return generateHMAC(secret, baseString);
}
/// <summary>
/// Normalizes all oauth signable parameters and url query parameters according to OAuth 1.0.
/// </summary>
/// <param name="httpMethod">The upper-cased HTTP method.</param>
/// <param name="url">The request URL.</param>
/// <param name="oauthParams">The associative set of signable oauth parameters.</param>
/// <param name="requestBody">A stream containing the serialized message body.</param>
/// <returns>A string containing normalized and encoded OAuth parameters.</returns>
private static string normalizeParams(
string httpMethod,
Uri url,
IEnumerable<KeyValuePair<string, string>> oauthParams,
Stream requestBody
)
{
IEnumerable<KeyValuePair<string, string>> kvpParams = oauthParams;
// Place any Query String parameters into a key value pair using equals ("=") to mark
// the key/value relationship and join each paramter with an ampersand ("&")
if (!String.IsNullOrWhiteSpace(url.Query))
{
IEnumerable<KeyValuePair<string, string>> queryParams =
from p in url.Query.Substring(1).Split('&').AsEnumerable()
let key = Uri.EscapeDataString(p.Substring(0, p.IndexOf("=")))
let value = Uri.EscapeDataString(p.Substring(p.IndexOf("=") + 1))
select new KeyValuePair<string, string>(key, value);
kvpParams = kvpParams.Union(queryParams);
}
// Include the body parameter if dealing with a POST or PUT request
if (httpMethod == "POST" || httpMethod == "PUT")
{
MemoryStream ms = new MemoryStream();
requestBody.CopyTo(ms);
byte[] bodyBytes = ms.ToArray();
string body = Convert.ToBase64String(bodyBytes, Base64FormattingOptions.None);
body = Uri.EscapeDataString(body);
kvpParams = kvpParams.Union(new[]
{
new KeyValuePair<string, string>("body", Uri.EscapeDataString(body))
});
}
// Sort the parameters in lexicographical order, 1st by Key then by Value; separate with ("=")
IEnumerable<string> sortedParams =
from p in kvpParams
orderby p.Key ascending, p.Value ascending
select p.Key + "=" + p.Value;
// Add the ampersand delimiter and then URL-encode the equals ("%3D") and ampersand ("%26")
string stringParams = String.Join("&", sortedParams);
string encodedParams = Uri.EscapeDataString(stringParams);
return encodedParams;
}
private static string generateHMAC(string _key, string _msg)
{
string message;
string key;
key = _key;
message = _msg;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);
HMACSHA1 hmacsha1 = new HMACSHA1(keyByte);
byte[] messageBytes = encoding.GetBytes(message);
byte[] hashmessage = hmacsha1.ComputeHash(messageBytes);
return ByteToString(hashmessage);
}
public static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
#endregion // Helper Functions
}
}
There are many reasons behind this, but first please check the URL must be signed with OAuth1 (not OAuth2) and must have valid values for the other parameters. The error reporting may make throw the same error for a lot of reason, but make sure your setup is correct and make sure your app is published on new chrome apps marketplace. Make sure you've submitted the Google Apps Marketplace Listing Review Request form here:
https://docs.google.com/forms/d/14QOb8PbSLKDgwIp8Zv-luoAAVurPXUqtzL0Hgikp3rk/viewform
After all is finished and google approved (you will get an email) your app then make call to UpgradeableApp API with oAuth 1.0 sign request, Here is an example you can see how to do it.
http://www.codeproject.com/Tips/359144/Legged-OAuth-Authentication-in-NET-Csharp
Hope that helps