Handling #IAP #Subscription #Cancellations

October 16, 2017 Leave a comment

Screen-Shot-2015-05-23-at-12.45.42-PMIAP Subscriptions are great, they allow you take recurring payments from your customers, so that you can make an on-going income from a customer that continues to use your service.

However, accepting a subscription, it’s also important to know when the subscription is cancelled, so that you can stop providing your service to the customer.

First thing is to define a callback with the “Subscription Status Url”, which you can find under your app in iTunes Connect, under App Information > General Information.

This URL will be sent some JSON data using HTTP POST, and the format of the requests are as follows;

First, the Intial buy, as follows;

{
“latest_receipt”: “….”,
“latest_receipt_info”: {
“original_purchase_date_pst”: “2017-10-14 18:51:38 America\/Los_Angeles”,
“quantity”: “1”,
“unique_vendor_identifier”: “025C068A-ABA3-4967-BC20-82C397CA6E1A”,
“original_purchase_date_ms”: “1508032298000”,
“expires_date_formatted”: “2017-11-15 02:51:34 Etc\/GMT”,
“purchase_date_ms”: “1508032294000”,
“expires_date_formatted_pst”: “2017-11-14 18:51:34 America\/Los_Angeles”,
“is_trial_period”: “false”,
“item_id”: “1215609502”,
“unique_identifier”: “aae666d4899834762d29b57272c10400256ba0dc”,
“original_transaction_id”: “240000386052948”,
“expires_date”: “1510714294000”,
“app_item_id”: “1214710746”,
“transaction_id”: “240000386052948”,
“bvrs”: “1.3.3”,
“web_order_line_item_id”: “240000089571027”,
“version_external_identifier”: “822490968”,
“bid”: “ie.infiniteloop.cloudansweringmachine”,
“product_id”: “CloudAnsweringMachineSubscription”,
“purchase_date”: “2017-10-15 01:51:34 Etc\/GMT”,
“purchase_date_pst”: “2017-10-14 18:51:34 America\/Los_Angeles”,
“original_purchase_date”: “2017-10-15 01:51:38 Etc\/GMT”
},
“environment”: “PROD”,
“auto_renew_status”: “true”,
“password”: “f1d192a81bc746da837639012d3917b3”,
“auto_renew_product_id”: “CloudAnsweringMachineSubscription”,
“notification_type”: “INITIAL_BUY”
}

Then if the customer cancels the subscription, then the request would be as follows;

{
“environment”: “PROD”,
“auto_renew_status”: “false”,
“web_order_line_item_id”: “240000089571027”,
“latest_expired_receipt_info”: {
“original_purchase_date_pst”: “2017-10-14 18:51:38 America\/Los_Angeles”,
“cancellation_date_ms”: “1508089204000”,
“quantity”: “1”,
“cancellation_reason”: “0”,
“unique_vendor_identifier”: “025C068A-ABA3-4967-BC20-82C397CA6E1A”,
“original_purchase_date_ms”: “1508032298000”,
“expires_date_formatted”: “2017-11-15 02:51:34 Etc\/GMT”,
“purchase_date_ms”: “1508032294000”,
“expires_date_formatted_pst”: “2017-11-14 18:51:34 America\/Los_Angeles”,
“is_trial_period”: “false”,
“item_id”: “1215609502”,
“unique_identifier”: “aae666d4899834762d29b57272c10400256ba0dc”,
“original_transaction_id”: “240000386052948”,
“expires_date”: “1510714294000”,
“app_item_id”: “1214710746”,
“transaction_id”: “240000386052948”,
“bvrs”: “1.3.3”,
“web_order_line_item_id”: “240000089571027”,
“version_external_identifier”: “822490968”,
“bid”: “ie.infiniteloop.cloudansweringmachine”,
“cancellation_date”: “2017-10-15 17:40:04 Etc\/GMT”,
“product_id”: “CloudAnsweringMachineSubscription”,
“purchase_date”: “2017-10-15 01:51:34 Etc\/GMT”,
“cancellation_date_pst”: “2017-10-15 10:40:04 America\/Los_Angeles”,
“purchase_date_pst”: “2017-10-14 18:51:34 America\/Los_Angeles”,
“original_purchase_date”: “2017-10-15 01:51:38 Etc\/GMT”
},
“cancellation_date_ms”: “1508089204000”,
“latest_expired_receipt”: “………………….”,
“cancellation_date”: “2017-10-15 17:40:04 Etc\/GMT”,
“password”: “f1d192a81bc746da837639012d3917b3”,
“cancellation_date_pst”: “2017-10-15 10:40:04 America\/Los_Angeles”,
“auto_renew_product_id”: “CloudAnsweringMachineSubscription”,
“notification_type”: “CANCEL”
}

This example is taken from the app, CloudAnsweringMachine.com

Advertisements
Categories: Uncategorized

Enabling #CORS on #ASMX .NET #Webservices

October 13, 2017 Leave a comment

240_F_130789368_w0TYy56XPpuDn0pfgXvgG3ZmfWUZenCQ

If you would like your clients / users interface with your ASMX .NET webservice directly from Javascript, then you should enable CORS, to allow them do so without using server-side code.

To do so, you add a Global.asax file and add the code;

protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Origin”, “*”);
if (HttpContext.Current.Request.HttpMethod == “OPTIONS”)
{
HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Methods”, “GET, POST”);
HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Headers”, “Content-Type, Accept”);
HttpContext.Current.Response.AddHeader(“Access-Control-Max-Age”, “1728000”);
HttpContext.Current.Response.End();
}
}

Then, it’s possible for your clients to use Javascript code (with JQuery) such as the following to interface directly with your webservice:
(This code example is for AvatarAPI.com )

$(init);
function init()
{
AvatarAPI(“your.email@gmail.com”,function(profile){
if (profile.Valid == “false”)
{
profile.Image = “https://t4.ftcdn.net/jpg/01/30/78/93/240_F_130789368_w0TYy56XPpuDn0pfgXvgG3ZmfWUZenCQ.jpg”;
profile.Name = “Anonymous”
}
$(“#profile”).attr(“src”,profile.Image);
$(“#name”).text(profile.Name);
});
}

function AvatarAPI(email,callback)
{
var username = ‘xxxxxx’;
var password = ‘xxxxxxx’;
var strUrl = “https://www.avatarapi.com/avatar.asmx/GetProfile?”;
strUrl += “email=” + email;
strUrl += “&username=” + username;
strUrl += “&password=” + password;
$.get(strUrl,function(data){
var xml = $(data);
var profile = {
Name : xml.find(“profile”).find(“Name”).text(),
Image : xml.find(“profile”).find(“Image”).text(),
Valid : xml.find(“profile”).find(“Valid”).text(),
IsDefault : xml.find(“profile”).find(“IsDefault”).text()
};
callback(profile);
});
}

Categories: Uncategorized

Understanding F5 “bobcmn” Javascript detection

October 10, 2017 Leave a comment

F5-Networks-Logo

If you’ve seen a response like this from a website;

(function(){
var securemsg;
var dosl7_common;

window[“bobcmn”] = “1111101010101020000000220000000520000000021f86dd03200000096300000000300000000300000006/TSPD/300000008TSPD_101300000005https200000000200000000”;

window.aht=!!window.aht;try{(function(){try{var __,i_,j_=1,o_=1,z_=1,s_=1,S_=1,Ji=1,li=1;for(var oi=0;oi_j;if(l)return OI(!1);l=Ij&&ij+lj<_;l=OI(l);ij=_;Ij||(Ij=!0,Jj(function(){Ij=!1},1));return l}oj();var zj=[17795081,27611931586,1558153217];
function Zj(_){_="string"===typeof _?_:_.toString(36);var l=window[_];if(!l.toString)return;var O=""+l;window[_]=function(_,O){Ij=!1;return l(_,O)};window[_].toString=function(){return O}}for(var sj=0;sj<zj.length;++sj)Zj(zj[sj]);OI(!1!==window.aht);
(function Sj(){if(!oj())return;var l=!1;function O(l){for(var z=0;l–;)z+=Z(document.documentElement,null);return z}function Z(l,z){var O="vi";z=z||new s;return O_(l,function(l){l.setAttribute("data-"+O,z.Zz());return Z(l,z)},null)}function s(){this.jo=1;this.io=0;this.Sj=this.jo;this.c=null;this.Zz=function(){this.c=this.io+this.Sj;if(!isFinite(this.c))return this.reset(),this.Zz();this.io=this.Sj;this.Sj=this.c;this.c=null;return this.Sj};this.reset=function(){this.jo++;this.io=0;this.Sj=this.jo}}
var S=!1;function z(l,z){if(!oj())return;var O=document.createElement(l);z=z||document.body;z.appendChild(O);O&&O.style&&(O.style.display="none");oj()}function J_(z,O){if(!oj())return;O=O||z;var Z="|";function s(l){l=l.split(Z);var z=[];for(var O=0;O<l.length;++O){var S="",I_=l[O].split(",");for(var J_=0;J__}
function J(_){var l=arguments.length,O=[];for(var Z=1;Z>b>>0};

})();

/TSPD/088481ad69ab2000b270b96268cc0e553e2472504e457123c9fbd2b9bab56ec1bcd49acbc8338cd8?type=7
Please enable JavaScript to view the page content.

Then you’ve been blocked by F5 Network’s Javascript detection, and unless your next requests have the correct Cookies, then you’ll be dumped back to this page. The cookies are IP and time specific.

Now, you can try and pick through the javascript, (please leave a comment, if you find anything), or you can use PhantomJS to execute the front page, and return the cookies, with a script such as;

var page = require(‘webpage’).create();
page.open(‘https://www.somewebsite.com&#8217;, function(status) {
setInterval(function(){
if (phantom.cookies.length > 0)
{
var strCookie = “”;
for(var i in phantom.cookies)
{
var cookie = phantom.cookies[i];
strCookie += cookie.name + “=” + cookie.value + “;”;
}
console.log(strCookie);
phantom.exit();
}
},1000);
});

This script will write our the required cookies, which must then be sent along with any subsequent requests. Note that the cookies are limited by time and IP, so you need to make the subsequent request from the same IP address as PhantomJS.

Categories: Uncategorized

#Nuget #Package for #WordPress.com #API

October 10, 2017 Leave a comment

6I3oEOP4

If you want to automate processes based on the WordPress API – especially in situations where you want to do so without user interaction (i.e. server side / unattended / without oAuth), and you know C# / .NET, then this nuget package is for you.

First, you install the package via Nuget package manager;

Install-Package WordPressAPI 

Then, in order to authenticate yourself against WordPress,  You will need a wordpress username, password, and blog ID.  Get your Blog ID by going to https://developer.wordpress.com/docs/api/console/ then Login and search /me/sites or /sites/hello.wordpress.com

And here is a simple code example

var strAccessToken = WordPressComAPI.Wordpress.Login(new WordPressComAPI.Wordpress.Credentials
{
	Username = xxxxxx,
	Password = xxxxxx,
	blogId = xxxxx
});
var strSite = https://public-api.wordpress.com/rest/v1.1/sites/blog.dotnetframework.org;
var jSite = WordPressComAPI.Wordpress.Get(strSite,strAccessToken);
Categories: Uncategorized

Making a #HTTP request via #TCP/IP in C#

October 8, 2017 Leave a comment

1001004002254105

Sometimes using WebClient or HttpClient hides important information on how you connect to a remote server. Recently I was getting a protocol violation error, when making a web request to a server. OK, so the remote server was misbehaving and sending me back corrupted HTTP headers, but I still wanted to see the response, but WebClient / HTTPRestponse was not showing me anything, just throwing an exception.

So, I resorted to TCPClient – Once again, I have to stress, this should be a last resort, you should never need to make HTTP requests using raw TCP/IP, anyway.

So, what I was trying to do, is get my remote IP address while going through a proxy, which should be the IP address of the proxy, as long as it is working correctly, and here’s the code;

private static void tcpconnect()
{
var tcp=new TcpClient(“**proxy IP***”,*** Proxy port ***);
var stream = tcp.GetStream();
var send = Encoding.ASCII.GetBytes(“GET http://www.icanhazip.com HTTP/1.0\r\n\r\n”);
stream.Write(send,0,send.Length);
var sr = new StreamReader(stream);
var str = sr.ReadToEnd();
Console.WriteLine(str);
tcp.Close();
stream.Close();
}

The Http 1.0 requires the server to close the TCP connection after the response is sent. HTTP 1.1 would keep the TCP connection open, so the ReadToEnd would hang. You could send Connection:close and use HTTP 1.1 if you wanted, or use a more intelligent stream reader.

If you’d like to know more about this subject, You can do worse than check out my book which is available here:  Buy at Amazon US or Buy at Amazon UK

 

Categories: Uncategorized

Remote control app for #PopcornTime app available on #iOS

October 5, 2017 Leave a comment

Check out this video showing the Popcorn time remote control app pairing via QR code with Popcorn Time.

You can download the app from iTunes here;

https://itunes.apple.com/us/app/popcorn-remote/id1290119967?ls=1&mt=8&at=1000l9tW

Categories: Uncategorized

Using the #HTTP #API in #WordPress with C#

October 4, 2017 Leave a comment

wordpress-http-api

This is NOT how you are supposed to use the WordPress API. this is a very specific use case, and unless you’ve exhausted everything else, you’re probably in the wrong place. But caveat’s aside here’s what I was trying to achieve.

I wanted to automatically search for wordpress sites with a particular search term, and auto like the posts. It’s a technique that works well on twitter, so I wanted to try the same on WordPress.

Now, this application is to run unattended, so I don’t want the OAuth prompt to appear, I just want it to log in as me. My credentials are removed from the code examples below.

First off, create a wordpress app, and get your client id and secret. The redirect url does not matter, you won’t use it. but a valid url is required. You will also need to get your blog id, you can get this from the developer console.

Here’s the login code in C#

private static string Login()
{
var http = new HTTPRequest();

var strUrl = “https://public-api.wordpress.com/oauth2/authorize?&#8221;;
strUrl += “client_id=*****”;
strUrl += “&redirect_uri=******”;
strUrl += “&response_type=code”;
strUrl += “&blog=*******”;

var strLoginPage = http.Request(strUrl);

var strUrl2 = “https://wordpress.com/wp-login.php?action=login-endpoint&#8221;;

var strPostdata = “username=*********”;
strPostdata += “&password=**********”;
strPostdata += “&remember_me=false”;
strPostdata += “&redirect_to=”;

strPostdata += HttpUtility.UrlEncode(“https://public-api.wordpress.com/oauth2/authorize?&#8221; +
“client_id=********” +
“&redirect_uri=http://*********” +
“&response_type=code” +
“&blog=*********”);

strPostdata += “&client_id=39911”;
strPostdata += “&client_secret=cOaYKdrkgXz8xY7aysv4fU6wL6sK5J8a6ojReEIAPwggsznj4Cb6mW0nffTxtYT8”;

var strResponse = http.Request(strUrl2, “POST”, strPostdata);

var strNextUrl = “https://public-api.wordpress.com/oauth2/authorize?&#8221;;
strNextUrl += “client_id=*********”;
strNextUrl += “&redirect_uri=http://*************”;
strNextUrl += “&response_type=code”;
strNextUrl += “&blog=*********”;

var strResponse2 = http.Request(strNextUrl);
var strNonceRegex = @”nonce..value..(?<Nonce>[\w]+)”;
var strNonce = Regex.Match(strResponse2, strNonceRegex).Groups[“Nonce”].Value;

var strNextUrl2 = “https://public-api.wordpress.com/oauth2/login/?&#8221;;
strNextUrl2 += “client_id=*********”;
strNextUrl2 += “&redirect_uri=http%3A%2F%2F**********”;
strNextUrl2 += “&response_type=code”;
strNextUrl2 += “&action=oauth2-login”;
strNextUrl2 += “&redirect_to=https%3A%2F%2Fpublic-api.wordpress.com%2Foauth2%2Fauthorize%2F%3Fclient_id%3D*********%26redirect_uri%3Dhttp%253A%252F%252F********%26response_type%3Dcode%26jetpack-code%26jetpack-user-id%3D0%26action%3Doauth2-login”;
strNextUrl2 += “&blog_id=***********”;
strNextUrl2 += “&_wpnonce=” + strNonce;

var strResponse3 = http.Request(strNextUrl2);

var qs = HttpUtility.ParseQueryString(http.PageUri.Query);

var strCode = qs[“Code”];

var strExchangeUrl = “https://public-api.wordpress.com/oauth2/token&#8221;;

strPostdata = “client_id=******”;
strPostdata += “&redirect_uri=http://*******”;
strPostdata += “&client_secret=KgNwl6lETO227GEarjZWFDtiCRihpIU8gMqBky4P5srG0Z4p9WnUFCmR3XidUin5”;
strPostdata += “&code=” + strCode;
strPostdata += “&grant_type=authorization_code”;

var strAuthTokenData = http.Request(strExchangeUrl,”POST”, strPostdata);

var json = JObject.Parse(strAuthTokenData);

var strAccessToken = json[“access_token”].ToString();

return strAccessToken;
}

Once you have the access_token then you can make requests to the WordPress API,  by setting  Authorization: Bearer + strAccessToken  in the HTTP headers.

There was no API for searching all WordPress sites, but I used their search page as follows:

var strSearchUrl = “https://en.search.wordpress.com/?src=organic&q=****&s=date&t=post&#8221;;
var strSearchResults = http.Request(strSearchUrl);
var strBlogRegex = @”span…a.href=.https://(?<Blog&gt;\w+.wordpress.com)”;

where q is the search term,

Now, for example, to get all posts from a blog, call;

var strPostsEndpoint = “https://public-api.wordpress.com/rest/v1/sites/{0}/posts/”;
strPostsEndpoint = string.Format(strPostsEndpoint,strBlog);
var strPostsJson = http.Request(strPostsEndpoint);
var jPosts = JObject.Parse(strPostsJson);

and to like a post, I called

var strLikeEndpoint = “https://public-api.wordpress.com/rest/v1/sites/{0}/posts/{1}/likes/new”;
strLikeEndpoint = string.Format(strLikeEndpoint, strBlog, strPostId);
var strResponse = http.Request(strLikeEndpoint, “POST”, “”);

 

Categories: Uncategorized