verifying iOS in app purchase receipt with C# - c#

I am verifying my ios in app purchase receipt on my server using C# web service
I got receipt as string by doing below in Xcode:
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
NSString* receiptString = [[NSString alloc] initWithString:transaction.payment.productIdentifier];
NSLog(#"%#",receiptString);
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSString *jsonObjectString = [receipt base64EncodedStringWithOptions:0];
}
and I am sending that string(receipt) to my C# web service as parameter.
Here is my web service method:
[WebMethod(Description = "Purchase Item Verify")]
public string PurchaseItem(string receiptData)
{
string returnmessage = "";
try
{
var json = "{ 'receipt-data': '" + receiptData + "'}";
ASCIIEncoding ascii = new ASCIIEncoding();
byte[] postBytes = Encoding.UTF8.GetBytes(json);
HttpWebRequest request;
request = WebRequest.Create("https://sandbox.itunes.apple.com/verifyReceipt") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = postBytes.Length;
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
var sendresponse = (HttpWebResponse)request.GetResponse();
string sendresponsetext = "";
using (var streamReader = new StreamReader(sendresponse.GetResponseStream()))
{
sendresponsetext = streamReader.ReadToEnd();
}
returnmessage = sendresponsetext;
}
catch (Exception ex)
{
ex.Message.ToString();
}
return returnmessage;
}
It always return {"status":21002}.
I have been searching for two days , but still can't find out the solution. Can someone help me, what am i wrong ?
**I am testing on sandbox that is why i use sandbox URL. I can verify the transaction receipt within my app.

I got solution
The final code that works for me is:
public string PurchaseItem(string receiptData)
{
string returnmessage = "";
try
{
// var json = "{ 'receipt-data': '" + receiptData + "'}";
var json = new JObject(new JProperty("receipt-data", receiptData)).ToString();
ASCIIEncoding ascii = new ASCIIEncoding();
byte[] postBytes = Encoding.UTF8.GetBytes(json);
// HttpWebRequest request;
var request = System.Net.HttpWebRequest.Create("https://sandbox.itunes.apple.com/verifyReceipt");
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = postBytes.Length;
//Stream postStream = request.GetRequestStream();
//postStream.Write(postBytes, 0, postBytes.Length);
//postStream.Close();
using (var stream = request.GetRequestStream())
{
stream.Write(postBytes, 0, postBytes.Length);
stream.Flush();
}
// var sendresponse = (HttpWebResponse)request.GetResponse();
var sendresponse = request.GetResponse();
string sendresponsetext = "";
using (var streamReader = new StreamReader(sendresponse.GetResponseStream()))
{
sendresponsetext = streamReader.ReadToEnd().Trim();
}
returnmessage = sendresponsetext;
}
catch (Exception ex)
{
ex.Message.ToString();
}
return returnmessage;
Spending two and half days just to change a method. Thanks GOD.

Here's an alternative asynchronous implementation using HTTPClient:
public static async Task<string> CheckReceiptWithAppStore()
{
string responseStr = null;
string uri = "https://sandbox.itunes.apple.com/verifyReceipt";
string receiptData = // Get your receipt from wherever you store it
var json = new JObject(new JProperty("receipt-data", receiptData),
new JProperty("password", "paste-your-shared-secret-here")).ToString();
using (var httpClient = new HttpClient())
{
if (receiptData != null)
{
HttpContent content = new StringContent(json);
try
{
Task<HttpResponseMessage> getResponse = httpClient.PostAsync(uri, content);
HttpResponseMessage response = await getResponse;
responseStr = await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
Console.WriteLine("Error verifying receipt: " + e.Message);
}
}
}
return responseStr;
}
The shared secret is not required for non-subscription based purchases.

For managing subscriptions, #Jerry Naing's answer also requires the provision of your shared secret (can be retrieved/generated from iTunes Connect). Easiest way to include this is just to add an additional property in the line defining the json var.
var json = new JObject(new JProperty("receipt-data", receiptData), new JProperty("password", "put_your_shared_secret_here")).ToString();
Failing to provide the shared secret will result in a 21004 status response.

This code example was also helpful to me and may help others: For C# developers there is a useful open-source project called APNS-Sharp which includes receipt verification code that works in ASP.NET. In particular, the Receipt.cs and ReceiptVerification.cs files in the Jdsoft.Apple.AppStore directory
Found it from this page about Xamarin: inapp purcasing ios Transactions and Verification

Related

ALM Rest API : site-session returns 'The remote server returned an error: (400) Bad Request.'

I am getting '(400) Bad Request.' when I try complete authenticate against an ALM REST API, the first part (authentication) is successful) and I get the LWSSO_COOKIE_KEY, but site-session always fails with a 400 error code.
What am I doing wrong please... very confused!
// Authentication XML : 0 = User, 1 = Password
private const string AuthenticationXML = #"<alm-authentication>" +
"<user>{0}</user><password>{1}</password></alm-authentication>";
baseRequestURL = settings.QualityCentreURL + "/qcbin/";
Authentication is done first (and is successful) :
string authRequest = baseRequestURL + "authentication-point/alm-authenticate";
HttpWebRequest myauthrequest = (HttpWebRequest)WebRequest.Create(authRequest);
string xml = String.Format(AuthenticationXML, qcSettings.Username, qcSettings.Password);
byte[] Requestbytes = Encoding.UTF8.GetBytes(xml);
myauthrequest.Method = "POST";
myauthrequest.ContentType = "application/xml";
myauthrequest.ContentLength = Requestbytes.Length;
myauthrequest.Accept = "application/xml";
Stream RequestStr = myauthrequest.GetRequestStream();
RequestStr.Write(Requestbytes, 0, Requestbytes.Length);
RequestStr.Close();
HttpWebResponse myauthres = (HttpWebResponse)myauthrequest.GetResponse();
authenticationCookie = myauthres.Headers.Get("Set-Cookie");
The Site-Session code is :
public void GetSiteSession()
{
// Creat the web request fore site-session.
string request = baseRequestURL + "rest/site-session";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(request);
string xml = String.Empty;
byte[] requestbytes = Encoding.UTF8.GetBytes(xml);
// Update the attributes before sending.
webRequest.Method = "POST";
webRequest.ContentType = "application/xml";
webRequest.Accept = "application/xml";
webRequest.Headers.Set(HttpRequestHeader.Cookie, authenticationCookie);
try
{
Stream requestStream = webRequest.GetRequestStream();
requestStream.Write(requestbytes, 0, requestbytes.Length);
requestStream.Close();
HttpWebResponse webRequestResponse = (HttpWebResponse)webRequest.GetResponse();
Stream responseStream = webRequestResponse.GetResponseStream();
XDocument doc = XDocument.Load(responseStream);
}
catch (System.Net.WebException except)
{
Console.WriteLine(except.Message);
}
}
I have tried cutting ;Path=/;HTTPOnly from LWSSO_COOKIE_KEY as per this question, but to no avail.
The API reference I found(here) seems to be a big vague or, possibly that I haven't understood it... :P
Apologies, it seems that with 12.53 I should have been using 'api/authentication/sign-in'
string requestURL = baseRequestURL + "api/authentication/sign-in";
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL);
var credentials = String.Format("{0}:{1}", qcSettings.Username, qcSettings.Password);
request.CookieContainer = authenticationCookieContainer;
request.Headers.Set(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials)));
var authResponse = request.GetResponse();
errorString = String.Empty;
}
catch (System.Net.WebException except)
{
errorString = except.Message;
return false;
}
errorString = String.Empty;
return true;
}

Check if API url is valid - POST call

I need to allow user to enter a remote API url with basic authentication/ or a static token to POST the data from my application to the URL at specified intervals.
I tried setting the "HEAD" only but it does the GET operation and throws 405 - Method Not Allowed error for all the requests.
I would like to know if there is any way to validate the url and the credentials with given POST url.
I understand I can valid the the url but my concern is to ensure that I need to validate the basic auth credentials entered by the user is also correct.
Try this-
try
{
WebRequest tRequest = WebRequest.Create("YOUR API URL");
tRequest.Method = "post";
tRequest.ContentType = "application/json";
var data = new
{
//REQUIERED PARAMETERIZED DATA
};
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(data);
Byte[] byteArray = Encoding.UTF8.GetBytes(json);
tRequest.Headers.Add(string.Format("Authorization: key={0}", ApplicationID));
tRequest.Headers.Add(string.Format("YOUR HEADER"));
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
str = sResponseFromServer;
}
}
}
}
}
catch (Exception ex)
{
str = ex.Message;
}

Set a body for WebClient when making a Post Request

So I have an api that I want to call to. The first call is an ahoy call and in the body of the request I need to send the ship_type, piratename and my piratepass. I then want to read the response which has my treasure booty that i will use for later.
I'm able to do this with web request. but i feel like there is a better way to do it with webclient.
(way I currently do it in webrequest)
//Credentials for the Pirate api
string piratename = "IvanTheTerrible";
string piratepass= "YARRRRRRRR";
string URI = "https://www.PiratesSuperSecretHQ.com/sandyShores/api/respectmyauthority";
WebRequest wr = WebRequest.Create(URI);
wr.Method = "POST";
wr.ContentType = "application/x-www-form-urlencoded";
string bodyData = "ship_type=BattleCruiser&piratename=" + piratename + "&piratepass=" + piratepass;
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] byte1 = encoder.GetBytes(bodyData);
wr.ContentLength = byte1.Length;
//writes the body to the request
Stream newStream = wr.GetRequestStream();
newStream.Write(byte1, 0, byte1.Length);
newStream.Close();
WebResponse wrep = wr.GetResponse();
string result;
using (var reader = new StreamReader(wrep.GetResponseStream()))
{
result = reader.ReadToEnd(); // do something fun...
}
Thanks in advance either way.
You can do with this simple code
Uri uri = new Uri("yourUri");
string data = "yourData";
WebClient client = new WebClient();
var result = client.UploadString(uri, data);
Remember that you can use UploadStringTaskAsync if you want to be async
You can try like below as well:
public String wcPost(){
Map<String, String> bodyMap = new HashMap();
bodyMap.put("key1","value1");
WebClient client = WebClient.builder()
.baseUrl("domainURL")
.build();
String responseSpec = client.post()
.uri("URI")
.headers(h -> h.setBearerAuth("token if any"))
.body(BodyInserters.fromValue(bodyMap))
.exchange()
.flatMap(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError()) {
clientResponse.body((clientHttpResponse, context) -> {
return clientHttpResponse.getBody();
});
return clientResponse.bodyToMono(String.class);
}
else
return clientResponse.bodyToMono(String.class);
})
.block();
return responseSpec;
}

oauth/token returns empty body

I am encountering a problem getting the access_token in client application using oauth.
The returned response has empty body though in API I can see the response is not empty.
tokenresponse = {
"access_token":"[ACCESSTOKENVALUE]",
"token_type":"bearer",
"expires_in":"1200",
"refresh_token":"[REFRESHTOKENVALUE]",
"scope":"[SCOPEVALUE]"
}
The method from API that returns the token http://api.sample.com/OAuth/Token:
public ActionResult Token()
{
OutgoingWebResponse response =
this.AuthorizationServer.HandleTokenRequest(this.Request);
string tokenresponse = string.Format("Token({0})", response!=null?response.Body:""));
return response.AsActionResult();
}
The client method that requests the token is:
public string GetAuthorizationToken(string code)
{
string Url = ServerPath + "OAuth/Token";
string redirect_uri_encode = UrlEncode(ClientPath);
string param = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",code, ClientId, ClientSecret, redirect_uri_encode);
HttpWebRequest request = HttpWebRequest.Create(Url) as HttpWebRequest;
string result = null;
request.Method = "POST";
request.KeepAlive = true;
request.ContentType = "application/x-www-form-urlencoded";
request.Timeout = 10000;
request.Headers.Remove(HttpRequestHeader.Cookie);
var bs = Encoding.UTF8.GetBytes(param);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(bs, 0, bs.Length);
}
using (WebResponse response = request.GetResponse())
{
var sr = new StreamReader(response.GetResponseStream());
result = sr.ReadToEnd();
sr.Close();
}
if (!string.IsNullOrEmpty(result))
{
TokenData tokendata = JsonConvert.DeserializeObject<TokenData>(result);
return UpdateAuthorizotionFromToken(tokendata);
}
return null;
}
The result variable is empty.
Please let me know if you have any idea what could cause this. Initially I assumed is because of the cookies so I tried to remove them from request.
Thanks in advance.
Dear just create webclient using following code and you will get json info in tokeninfo.I used it and simply its working perfect.
WebClient client = new WebClient();
string postData = "client_id=" + ""
+ "&client_secret=" + ""
+ "&grant_type=password&username=" + "" //your username
+ "&password=" + "";//your password :)
string soundCloudTokenRes = "https://api.soundcloud.com/oauth2/token";
string tokenInfo = client.UploadString(soundCloudTokenRes, postData);
You can then use substring that contains only token from tokeninfo.
To upload tracks on sound cloud.
private void TestSoundCloudupload()
{
System.Net.ServicePointManager.Expect100Continue = false;
var request = WebRequest.Create("https://api.soundcloud.com/tracks") as HttpWebRequest;
//some default headers
request.Accept = "*/*";
request.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
request.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
request.Headers.Add("Accept-Language", "en-US,en;q=0.8,ru;q=0.6");
//file array
var files = new UploadFile[] { new UploadFile(Server.MapPath("Downloads//0.mp3"), "track[asset_data]", "application/octet-stream") };
//other form data
var form = new NameValueCollection();
form.Add("track[title]", "Some title");
form.Add("track[sharing]", "public");
form.Add("oauth_token", "");
form.Add("format", "json");
form.Add("Filename", "0.mp3");
form.Add("Upload", "Submit Query");
try
{
using (var response = HttpUploadHelper.Upload(request, files, form))
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
Response.Write(reader.ReadToEnd());
}
}
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
}

problem in uploading data with HttpWebRequest

Request you to please help me to solve my problem. I am new to web-services and HTTP.
I wrote following code to update data to website. Code run; but I am not able to see my data if uploaded. Here we have facility to see what data is getting uploaded but I am not able to see my data.
// Above URL is not real as I do not want to disclose real URL as of Now
Uri targetUrl = new Uri("http://www.x86map.com/post-embed/ewspost");
HttpWebRequest request = null;
StringBuilder sb = new StringBuilder();
Stream requestStream = null;
try
{
request = (HttpWebRequest)WebRequest.Create(targetUrl);
using (StreamReader inputReader = new StreamReader("C:\\SupportXml.xml"))
{
sb.Append(inputReader.ReadToEnd());
}
String postData = sb.ToString();
byte[] postDataBytes = Encoding.UTF8.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postDataBytes.Length;
request.KeepAlive = true;
request.Accept = "*/*";
request.Headers.Add("Cache-Control", "no-cache");
request.Headers.Add("Accept-Language", "en-us");
request.Headers.Add("Accept-Encoding", "gzip,deflate");
request.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8,q=0.66,*;q=0.66");
requestStream = request.GetRequestStream();
requestStream.Write(postDataBytes, 0, postDataBytes.Length);
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
finally
{
if (null != requestStream)
requestStream.Close();
}
URL I mentioned in Code is not real. Please let me know what is the problem in my code.
Following is the Java code working perfect. I want to convert same code in C#.
// Above URL is not real as I do not want to disclose real URL as of Now
String urlString = "http://www.x86map.com/post-embed/ewspost";
StringBuffer s = new StringBuffer();
try
{
String line = null;
BufferedReader input = new BufferedReader(new FileReader("C:\\SupportXml.xml"));
while ((line = input.readLine()) != null)
{
s.append(line);
s.append(System.getProperty("line.separator"));
}
String xmlDataString = s.toString();
int length = xmlDataString.length();
System.out.println("length " + length);
URL url = new URL(urlString);
System.out.println(url.toString());
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setAllowUserInteraction(false);
connection.setUseCaches(false);
connection.setRequestProperty("Accept", "*/*");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", (String.valueOf(length)));
connection.setRequestProperty("Cache-Control", "no-cache");
connection.setRequestProperty("Accept-Language", "en-us");
connection.setRequestProperty("Accept-Encoding", "gzip,deflate");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8,q=0.66, *;q=0.66");
BufferedOutputStream bos = new BufferedOutputStream(connection.getOutputStream());
BufferedReader reader = new BufferedReader(new StringReader(xmlDataString));
System.out.println("Proxy Used :" + connection.usingProxy());
int dataRead;
bos.write("XML_string=".getBytes());
while ((dataRead = reader.read()) != -1)
{
bos.write(dataRead);
}
bos.flush();
bos.close();
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String res = null;
while ((res = br.readLine()) != null)
{
}
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
Please help me to resolve this issue.
Thanks and Regards,
map125
You may find it helps to include
requestStream.Flush();
before .Closeing it.
Stream.Flush
I do not see the code that actually gets the response. Is this want is missing?
using (var r = new StreamReader(request.GetResponse().GetResponseStream(), Encoding.UTF8))
result = r.ReadToEnd();

Categories