Archive for the ‘ Tutorials ’ Category

Here’s a Node.js microservice that validates iTunes receipts

If you’re an app developer and you need to validate customers’ iTunes receipts, you’ll have to hit Apple’s API. This is especially important if your app contains subscriptions, which you’ll need to periodically check to see if they are still valid.

Unfortunately, the response JSON that Apple provides is kind of weird and bloated. For example, if a customer has a monthly renewable subscription, you’ll likely get back a bunch of objects, and you’ll need to find the latest one to properly validate their subscription.

I created a Node microservice, below, that will validate receipts and can be deployed to any cloud provider. I used Now, which is pretty awesome, by the way.

const { json } = require('micro')
const request = require('request')

module.exports = async (req, res) => {
	//get json payload, which contains receipt
	//i'm using micro, but you can use many things, like body-parser
	const reqData = await json(req);
	
	//create response object
	//status = request status from Apple
	//expired = is the subcription expired
	var respObj = { status: 0, expired: true, expiration: 0 }
	
	//hit Apple api for receipt data
	let promise = new Promise((resolve, reject) => { 
		var postData = { 'receipt-data':reqData.receipt, 'password':process.env.APP_SEC };
		request({
			url: 'https://buy.itunes.apple.com/verifyReceipt',
			method: 'POST',
			json: true,
			body: postData
		}, function (error, response, body){
			if (response.statusCode == 200) {
				resolve(validateResp(body, respObj));
			} else {
				reject('Error: ' + response.statusCode);
			}
		});
	});

	respObj = await promise;
	return respObj; 
}

var validateResp = function(body, respObj) {
	//if status is not 0, the receipt is not valid
	if (body.status > 0) {
		respObj.status = body.status;
		return respObj;
	}

	//handle multiple receipts from renewable subscriptions
	var nowMs = new Date().getMilliseconds();
	var receipts = body.receipt.in_app;
	var expirations = receipts.map(function(x) { return x.expires_date_ms });
	var expMs = expirations.reduce(function(x,y){ return (x>y) ? x:y });
	respObj.expiration = expMs;
	
	//check if expiration date is greater than current time
	if (expMs > nowMs) {
		respObj.expired = false;
	}

	return respObj;
}

T-SQL: Parse single name field into first name and last name

Use this handy function to parse a single name field into two separate first name and last name fields. Accepts name formats like:

Borges, Jorge L.
Borges, Jorge
Jorge Luis Borges
Jorge Borges

Use it like:
INSERT INTO new_authors
SELECT ParseName(name,'first'), ParseName(name,'last')
FROM authors

Function definition:

create FUNCTION [dbo].[ParseName]
(
-- Add the parameters for the function here
@Name varchar(250),
@NameType varchar(5)
)
RETURNS varchar(250)
AS
BEGIN

Declare @return_name varchar(250)

select @return_name =
case
  when charindex(',', @Name) > 0 then
    case
      when @NameType = 'last'
      then substring(@Name,0,charindex(',', @Name))
      else LTRIM(substring(@Name,charindex(',', @Name) + 1,LEN(@Name)))
    end
  else
    case
      when @NameType = 'last'
      then RIGHT(@Name, CHARINDEX(' ', REVERSE(' ' + @Name)) - 1)
      else
        case
          when LEN(@Name) - LEN(REPLACE(@Name, ' ', '')) = 2
          then LEFT(@Name, CHARINDEX(' ', REVERSE(' ' + @Name)) - 1)
          else substring(@Name,0,charindex(' ', @Name))
        end
    end
end

-- Return the result of the function
RETURN @return_name

END

Easy Way to Remote into a Raspberry Pi

It’s pretty easy to setup remote desktop access to a Raspberry Pi without opening up ports on your router. You can use this approach if you want to access your pi while traveling, or just around the corner at the coffee shop.

  1. Install xrdp on your pi.
    sudo apt-get install xrdp
  2. Edit the xrdp config file at /etc/xrdp/xrdp.ini and change the setting for crypt_level to ‘high’. xrdp will now use 128 bit encryption. Then restart the service.
    sudo service xrdp restart
  3. Install ngrok. For Raspbian 8 (Jessie), I used the ARM download.
    wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip
    unzip ngrok-stable-linux-arm.zip -d /usr/bin
  4. Sign up for an account at ngrok. This is required to create a tcp tunnel.
  5. Create a tcp tunnel for port 3389, which xrdp uses for remote desktop.
    ngrok tcp 3389
  6. In terminal, ngrok will show you the forwarding URL. This URL can also be found in your ngrok account under “Status”. Once outside your home network, you can drop this URL (remove the “tcp://”) into a Remote Desktop client, and you should see the xrdp login window. After logging in, you’ll have access to your pi.

ngrok tunnels can be dropped in the case of loss of connectivity, power outage, etc. So to always have access to your pi, you can use a shell script like this one.

#!/bin/bash

output=$(curl -s http://localhost:4040/api/tunnels)
#echo "$output"
if [ -z "$output" ]
then
  echo "Tunnel down. Starting new tunnel..."
  nohup ngrok tcp 3389 &
  echo "Tunnel started."
else
  echo "Tunnel running."
fi

exit 0

How to sleep in (as a developer)

Each morning at around 7:30am, someone uses an application I wrote to load a trading system with data so that traders can make their clients money. If the app fails, then they can’t really do this. As the app was going live, I feared I might have to set my alarm every day that week. If it’s not already apparent, I am a night owl through and through.

Agatha Christie famously said, “invention, in my opinion, arises directly from idleness, possibly also from laziness.” This was very true in my quest to not have to set an alarm. I had already added robust error logging that would send me an email if the app crashed. Here’s the code for that, by the way. It’s pretty Fisher Price My First Global class, but it gets the job done.

       public static void LogError(string error, Exception exception)
        {
            //Write to error log, in case email notification fails
            string dtNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string strFilePath = 
              new Uri(Path.GetDirectoryName(Assembly.GetExecutingAssembly()
              .GetName().CodeBase) 
              + @"\" + "myapp_error_log.txt").LocalPath;
            StreamWriter sw;

            //IF file doesn't exist
            if (!File.Exists(strFilePath))
            {
                sw = new StreamWriter(File.Create(strFilePath));
            }
            else
                sw = new StreamWriter(strFilePath, true);

            sw.WriteLine("**** ERROR ****");
            sw.WriteLine("Time: " + dtNow);
            sw.WriteLine("Error: " + error);
            if (exception != null)
            {
                sw.WriteLine("Exception: " + exception.Message);
                sw.WriteLine("Stack Trace: " + exception.StackTrace);
            }
            sw.WriteLine();
            sw.Close();

            //Send email with error to support
            SendErrorEmail(error, exception);
        }

        public static void SendErrorEmail(string error, Exception exception)
        {
            MailMessage mail = new MailMessage();
            string dtNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            
            mail.From = new MailAddress(Environment.UserName + ".myapp.com");
            if (ConfigurationManager.AppSettings.Count > 1)
            {
                var to = ConfigurationManager.AppSettings["ErrorEmailTo"];
                mail.To.Add(to);
            }
            else
            {
                mail.To.Add("me@dylan.com");
            }

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Error in myapp");
            sb.AppendLine("Time: " + dtNow);
            sb.AppendLine("Machine: " + Environment.MachineName);
            sb.AppendLine("User: " + Environment.UserName);
            sb.AppendLine("Error: " + error);
            if (exception != null)
            {
                sb.AppendLine("Exception: " + exception.Message);
                sb.AppendLine("Stack Trace: " + exception.StackTrace);
            }

            SmtpClient client = new SmtpClient("mysmtp.com");
            client.Port = 25;
            client.DeliveryMethod = SmtpDeliveryMethod.Network;
            client.UseDefaultCredentials = false;
            mail.Subject = "Error in myapp";
            mail.Body = sb.ToString();
            client.Send(mail);
        }

I just needed a way for my phone to wake me up if the application generated an error email.

I first experimented with the Gmail app for iPhone. In Notifications, you can make the app play a sound when a new email is received. The problem is the sound is just a quick beep–nothing that would wake me up from deep REM.

I then tried the built-in Mail application in iOS. It looked more promising because you can configure the sound to be a legitimate phone ring. I added my Gmail account, but after running a quick test, I realized that there was a big delay between when the email was sent and when it arrived in the Mail app. In Mail settings, iOS gives the ambiguous explanation that “new data will be pushed to your iPhone from the server when possible,” and it turns out that the fastest fetch schedule you can set is every 15 minutes. I guess Google doesn’t push rapidly because they want people to use their Gmail app. Worst.

After some searching, I figured out that you can send a text to your phone using a standard email address. I think I did this way back in the mid 2000s, but totally forgot about it. Here’s an exhaustive list of the email formats for different carriers. This enabled me to receive a text on my phone by having Gmail forward the application error email to [myphone#]@txt.att.net. Here are the steps to set up.

  1. Implement error handling and emails using something like the code provided above.
  2. Send the error emails to Gmail. You could send it directly to your phone, but it’s good to have error information in a more readable format than SMS.
  3. In Gmail, go to Settings > Forwarding and POP/IMAP, and click add a forwarding address. Use your phone number in your carrier’s format (see the link above).
  4. Gmail will keep forwarding disabled after it’s added, which is good. Now go to Settings > Filters and Blocked Addresses, and create a filter for your error email. Using the code above, it would be Subject = Error in myapp.
  5. On the following page, check Forward it to, and select your cell phone’s email address, which should be available in the dropdown. This will enable forwarding for this specific filter.
  6. On your phone, choose a text sound that will wake you up.
  7. Stay up late and forget the alarm.

You can get creative with these alerts. For example, you can set up monitoring using a free service like Uptime Robot and get texts if a site goes down, or you can setup a DIY security system using motion and a Raspberry Pi (see my previous post for more info on home automation).

Cool cats: Home automation part 1

My partner recently got a new job in DC, so I’ve been taking short trips down there every couple weeks. We are also fostering a couple of cats from our local shelter, Social Tees. The heat will continue to rise this summer, so I needed to figure out a way to keep the cats cool when I’m not there. Leaving the AC on Eco mode the whole time I was gone was one option, but I didn’t really trust it, and it also seemed like a waste of electricity and money.

Instead I purchased a Rasberry Pi 3, a Wemo switch, and this little usb thermometer. These things are all you need to remotely check the temperature in your apartment and turn your AC off and on, and together they cost around $100.

The first step was setting up the Pi. By the way, if you’re a first time Pi buyer, here are the basic items you’ll need to get it up-and-running. And here are the instructions for the noobs installation.

  1. Raspberry Pi 3
  2. Case to hold it
  3. Micro USB to USB cord (or Micro USB charger)
  4. Wireless mouse
  5. Wireless keyboard
  6. MicroSD card with standard SD card adapter

Next I got the Wemo switch set up. This entailed installing the app for my smartphone. Wemo actually allows you to turn the switch off and on from anywhere by using a setting called “Remote Connection.” I turned this off since I had read about some security issues with the way Belkin handles remote connections to your home network.

Instead I used this bash script to control the Wemo. It worked right away. I then setup an alias commands for turning off, turning on, and checking the state of the Wemo switch.

The usb thermometer, though I thought it was going to be a pain, was fairly simple to install. First I plugged it into the pi. I used the usb extension cord that came with it, to help normalize the temperature reading, since I think the device gets hot if plugged in directly. I also used these handy instructions to get a temperature reading from the usb thermometer.

Finally, to gain access to my home network from the outside world, I used Weaved. There are many alternatives here, including setting up port forwarding or maybe using a tool like ngrok, but I chose Weaved because it was easy and it seemed fairly secure.

By using Weaved to ssh into the Pi, I was able to check the temperature in the apartment and power on/off the ac accordingly. Obviously there is a ton you can do here with some basic scripting, including hacking your own little Nest thermostat. More importantly for the furballs, you can set up alerts if the temperature gets too high, or if the AC stops responding. You can even use a free dev account at Tropo to text a neighbor who can come and check on them.

Easy Way to Remove an Item from a List in C#

To remove an item from a list in C#, I often found myself going the old-school route and iterating through the list backwards with a for loop, like this:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (myList[i].Id == idToDelete)
    {
        myList.RemoveAt(i);
        break;
    }
}

I’ll let this pass in JavaScript, but surely C#  must have an easier way. The obvious candidate is the List(T).Remove method. However, it is also kind of cumbersome. For example, to remove an item based on my Id property, you have to implement the IEquatable interface (as well as override the Equals method, to be safe).

The simplest approach is to use the List(T).RemoveAll method. It takes a delegate as an argument, so you can easily pass in a lambda, like this:

myList.RemoveAll(j => j.Id == idToDelete);

Obviously only use this method if you want to remove all items that match your criteria from the list. Also, both Remove and RemoveAll are O(n) operations, so if you need to scale well, probably best to use a HashSet.

Coordinating AJAX Requests with jQuery

I was working on a requirement where I had to run the same AJAX post request multiple times, in this case to close out multiple trades in a system. I then had to route to another page if successful, or display an error message if an error occurred.

I started by looping through the AJAX calls using jQuery.each, but I quickly realized that this wasn’t going to work without using a global boolean within the .ajax success callback. I then found a Stack Overflow post that highlighted that jQuery.when can be used to group AJAX calls as “deferred objects,” allowing you to coordinate multiple calls and then run a function after they are all complete.

First I wrapped my jQuery.ajax call in a function.

function postClose(closeObj, reroute)
{
    return $.ajax({
        type: "POST",
        url: "/Trades/Close",
        data: closeObj,
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            alert(errorThrown);
        },
        success: function (data) {
            if (reroute) {
                window.location.href = "/Trades";
            }
        }
    });
}

Then I added the following code to a click handler for the “Close Selected” button on my page. This builds up the defers array with deferred objects, which are essentially calls to the postClose method, above. Then it uses .when to execute the AJAX calls. Once they are all complete, the function in .done executes the final AJAX call, which routes the user to another page upon success.

    
$('#multi-close-btn').click(function () {
    var trades = closeCombinedTrades.GetSelectedItems();
    var defers = [];
    var close;
    $.each(trades, function (i, val) {
        close = new Close(val.value, closeMethod.GetValue(), closeCloseDt.GetDate(), closeTradeStatus.GetValue());
        defers.push(postClose(close,false));
    });
    $.when.apply($, defers).done(function () {
        close = new Close($('#Trade_TradeId').val(), closeMethod.GetValue(), closeCloseDt.GetDate(), closeTradeStatus.GetValue());
        postClose(close, true);
    }).fail(function () {
        alert("An issue occurred while closing the trades. Please contact the administrator.");
    });
});