Dec.13

Open When I Die

I have an envelope which sits in a lock box in my house. It’s pretty plain, except on the front is the date when it was sealed, and the words “Open When I Die.”
Open When I Die
Pretty gruesome, I know. But it’s going to happen, sometime.

Let me give some background: I live with my wife and 3 kids. I’m fortunate enough to bring in enough income that my wife can focus on the home & family. I also take care of all of the bills. So I bring the money in, and I control the money going out. If I died, my wife would have a hard time figuring out all of our various accounts, bills, who gets what, when, and what’s coming in. Let’s complicate things with the fact that I own a company. There’s a lot to take care of, beyond what a traditional Will covers.

Note: it’s not that I’m hiding these things, but my wife has enough going on and leaves this in my hands. We tried going over things monthly, but that only lasted 1 month, if that.

Of course, we have a Will, which takes care of things such as who gets what, who takes care of the kids, when they get money from their trusts… but that doesn’t answer questions like:

  • How do you get into my computer & my various accounts?
  • Where’s the money?
  • What debts do we have?
  • What money is coming in?
  • What investments do we have?

and more.

So years ago, I created a document that started answering these questions. It’s written in Word and saved as a PDF, stored on a USB thumbdrive in another lock box, then printed out and stored in a sealed envelope in the first lock box.

I update the document twice a year (you’d be surprised how much can change in 6 months), and when I last updated it, many people wanted to know what was in it (sans details). So I tried to wipe out any personal info, and I sent out a PDF to some people. Here’s what it contains:

Who To Call

I have 3 people to call to help take care of things. One is a friend of the family who’s known me since the day I was born. He’s also an excellent accountant and has some history of my own finances, so he gets to figure out the money. (sorry)

Next is my sys admin. He’ll be needed to get into any accounts that may need to be accessed. I use LastPass for password management, and 2-factor authentication in many accounts, so not only is my LastPass password in there, which unlocks everything else, but instructions how to access my phone (the pin, or pattern, etc) is also in there.

Finally, I list someone who can help liquidate my affiliate sites and my business. If you know me at all on a personal level, it should be no surprise to find out that person is Tricia Meyer. Because of our Mastermind Group and business partnership with our Wine Club Reviews site, she’ll know how to sell things off.

Important Documents

I list where various documents and other valuables (in my eyes) are stored: life insurance, Will, Ricky Henderson rookie card (my favorite baseball player, and a childhood dream of owning this card). And this document (in paper and electronic format).

Will

A summary of the Will, where it’s located and the filing information. Same with my Power of Attorney.

Life Insurance Policies

What policies we have, with whom, for how much, policy numbers, and expiration dates. (Note to self: add insurance agent contact info)

Lawyer

Contact information about our lawyer.

Finances

Information about how to get into Microsoft Money (I need to switch to Quicken someday!), our bank (account numbers and passwords – for some reason I don’t store my banking password in LastPass), answers to my security questions (logging in from a different computer means having to answer security questions), and details on our various savings accounts.

Funeral

It’s something we talk about, but let’s be honest: at the time a funeral needs to be planned, will the requests be remembered? I know I won’t remember what Laura wants. This is a section that I’m still building out.

Personal Note

So far I sound like I’m all business, and I have been. But I left a few sentence, personal message to Laura at the end of this document.

Is this secure?

We need to balance absolute security (information only in my head) and convenience. The information itself only exists in two places: two different fire-safe lock boxes, which are bolted to different floors of our house, which itself is locked and has an alarm system, smack-dab in the middle of the Village in New York with more police officers per mile of road than any other municipality in all of New York State, surrounded by observant neighbors. I’m OK with this.

You can download the PDF for yourself. My version is 4 pages long, in outline format. I encourage you to create a document like this for yourself, especially if you have kids. And get a Will! Use a real lawyer, or check out LegalZoom.

Or you can live in denial, and have your loved ones curse you when you die.

Resources

Dec.10

Jul.14

Jun.30

Google Rank Checker API

I read the other day, It would make life so much easier if Google had a paid rank checker API. Google might not have one, but Raven Tools does

Dec-7 2012 Update

Raven Tools no longer supplies SERP rank data. Use Microsite Masters instead


It would make life so much easier if Google had a paid rank checker API
@mattuk
Matt Sawyer

Not many people know this, but Raven Tools has an API.

Resources

Jun.01

Mar.12

Oct.26

Oct.25

Using the LinkShare Merchandiser Web Service

Linkshare Web Services

Linkshare Web Services

Building a product site using the LinkShare Merchandiser Web Service is one of the easiest and most straight-forward tasks you can do. If you’re just getting started with datafeeds and product-based sites, you may want to start here.

If you don’t have an account, you first need to signup with LinkShare. Then login, and click on Web Services under Links. Next, generate or update your token. You’ll need this when making requests.

Resources

May.18

Adding CJ Products To Your Datafeed Website

I received an email last week asking how to add Commission Junction products to datafeed websites. To do this, take a look at the CJ Web Services. Particularly, the Product Catalog Search Service (REST).

What this does is allows you to specify a merchant’s ID, and get all of the product links for that merchant. In my example, I was grabbing the products from Mighty Leaf Tea.

So this PHP script will sit in your site’s root, like the other admin scripts, and when you pull it up, you’ll be presented with a form, asking for the Advertiser ID. That’s the AID when you’re pulling links, or advertiserId in the URL when you’re looking at the merchant in CJ. For Mighty Leaf Tea, it’s 2346375.

The biggest problem is that the CJ Web Service for Product Catalog Search doesn’t return the product ID! So I had to come up with a way to create an ID for a product. In this case, I based it on the SKU and ended up with:

list($nProductID, ) = explode('E', round(hexdec(md5($oCJProduct->{'sku'}))/1000000000000000000000000));
$nProductID = str_replace('.', '', $nProductID);
$nProductID = abs($nProductID);

If you don’t know what this is doing, don’t worry about it. If you’re bored, look-up those functions & let me know if you can think of a better way to make an ID from a sku. There has to be a simpler solution.

Anyway, because of this, I had to change the ProductID field to a bigint(20), since these item IDs were quite large (ex: 22229748315941). In addition, whenever the front-end scripts type-juggled the itemID into an (int), I had to remove that, as int’s are not big enough to hold this data (browse.php and item.php).

Getting back to the script, when I enter 2346375 in the form, then submit it, this script will query CJ for all product links for that merchant, insert or update them in the database, and finally remove any old products.

<?php
	include('./vars.php');
	include('./admin-password.php');
	$bNavHome = true;
	include('./header.php');

	$cTitle = 'Get CJ Datafeed';
?>
<div class="post">
<h2 class="title"><?= $cTitle ?></h2>
<div class="entry">
<?php

	if (!empty($_GET['advertiser-id'])) {
		$cDevKey = 'youridhere';
		$nWebSiteID = "yourwebsiteid";

		$nMax = 1;

		// We'll use this later to determine which products can be deleted
		mysql_query("update products set bActiveProduct=0 where MerchantID=" . (int)$_GET['advertiser-id'] . "");

		function addCJProduct($oCJProduct) {
			list($nProductID, ) = explode('E', round(hexdec(md5($oCJProduct->{'sku'}))/1000000000000000000000000));
			$nProductID = str_replace('.', '', $nProductID);
			$nProductID = abs($nProductID);

			if (mysql_num_rows(mysql_query("select * from products where ProductID=" . $nProductID . " and MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . " limit 1")) == 0) {
				// This is a new record
				$cQuery = "insert into products (ProductID, Name, MerchantID, Merchant, Link, Thumbnail, BigImage, Price, RetailPrice, Description, Lastupdated, Manufacturer, PartNumber, ISBN, UPC, SKU, bActiveProduct) values (" . $nProductID . ", '" . myres($oCJProduct->{'name'}) . "', '" . myres($oCJProduct->{'advertiser-id'}) . "', '" . myres($oCJProduct->{'advertiser-name'}) . "', '" . myres($oCJProduct->{'buy-url'}) . "', '" . myres($oCJProduct->{'image-url'}) . "', '" . myres($oCJProduct->{'image-url'}) . "', '" . myres($oCJProduct->{'price'}) . "', '" . myres($oCJProduct->{'retail-price'}) . "', '" . myres($oCJProduct->{'description'}) . "', now(), '" . myres($oCJProduct->{'manufacturer-name'}) . "', '" . myres($oCJProduct->{'sku'}) . "', '" . myres($oCJProduct->{'isbn'}) . "', '" . myres($oCJProduct->{'upc'}) . "', '" . myres($oCJProduct->{'sku'}) . "', 1)";
			} // ends <insert new record>
			else {
				// This is an existing record
				$cQuery = "update products set Name='" . myres($oCJProduct->{'name'}) . "', MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . ", Merchant='" . myres($oCJProduct->{'advertiser-name'}) . "', Link='" . myres($oCJProduct->{'buy-url'}) . "', Thumbnail='" . myres($oCJProduct->{'image-url'}) . "', BigImage='" . myres($oCJProduct->{'image-url'}) . "', Price='" . myres($oCJProduct->{'price'}) . "', RetailPrice='" . myres($oCJProduct->{'retail-price'}) . "', Description='" . myres($oCJProduct->{'description'}) . "', Lastupdated=now(), Manufacturer='" . myres($oCJProduct->{'manufacturer-name'}) . "', PartNumber='" . myres($oCJProduct->{'sku'}) . "', ISBN='" . myres($oCJProduct->{'isbn'}) . "', UPC='" . myres($oCJProduct->{'upc'}) . "', SKU='" . myres($oCJProduct->{'sku'}) . "', bActiveProduct=1 where ProductID=" . $nProductID . " and MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . " limit 1";
			} // ends else from <updating existing record>
			mysql_query($cQuery);
			if (mysql_error()) {
				echo("<p><b>MySQL Error: " . mysql_error() . "<br />\n");
				echo("$cQuery</p>");
				echo("<pre>");
				print_r($rsItem);
				echo("</pre>");
				exit();
			} // ends if (mysql_error())

		} // ends function addCJProduct($oCJProduct)

		for ($nPageNumber = 1; $nPageNumber <= $nMax; $nPageNumber++) {

			$cURL = 'https://product-search.api.cj.com/v2/product-search?';
			$cURL .= 'advertiser-ids=' . $_GET['advertiser-id'] . '&';
			$cURL .= 'records-per-page=1000&';
			$cURL .= 'page-number=' . $nPageNumber . '&';
			$cURL .= 'website-id=' . $nWebSiteID;

			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $cURL);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
						  'Authorization: ' . $cDevKey,
						  'User-Agent: "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.15) Gecko/2009101601 Firefox/3.0.15 GTB6 (.NET CLR 3.5.30729)"'
						));

			curl_setopt($ch, CURLOPT_HEADER, false);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
			curl_setopt($ch, CURLOPT_TIMEOUT, 10);

			$cHTML = curl_exec($ch);
			if (curl_error($ch)) {
				echo "Curl error: " . curl_error($ch);
			} // ends if (curl_error($ch))
			else {
				$cXML = simplexml_load_string($cHTML);

				// Update nMax
				if ($cXML->products->attributes()->{'total-matched'} > 1000) {
					$nMax = floor($cXML->products->attributes()->{'total-matched'}/$cXML->products->attributes()->{'records-returned'});
				} // ends if ($cXML->products->attributes()->{'total-matched'} > 1000)

				for ($i = 0; $i < count($cXML->products->product); $i++) {
					addCJProduct($cXML->products->product[$i]);
				} // ends

			} // ends else from if (curl_error($ch))
		} // ends for loop

		mysql_query("delete from products where bActiveProduct=0 and MerchantID=" . (int)$_GET['advertiser-id'] . "");

		echo("<p><strong>" . $cXML->products->attributes()->{'total-matched'} . "</strong> products have been loaded.</p>");

		echo("<p>There are " . mysql_num_rows(mysql_query("select * from products where MerchantID=" . (int)$_GET['advertiser-id'] . "")) . " products.</p>");
	} // ends if (!empty($_GET['advertiser-id']))
	else {
		// Show form
		?>
		<form method="get" action="<?= $_SERVER['PHP_SELF'] ?>">
			CJ Advertiser ID: <input type="text" name="advertiser-id" /><br />
			<input type="submit" name="cAction" value="Get Datafeed" />
		</form>
		<?php
	} // ends else from if (!empty($_GET['advertiser-id']))
?>

</div>
</div><!-- ends class="post" -->

<p align="center">[ <a href="<?= $_SERVER['PHP_SELF'] ?>"><?= $cTitle  ?> Home</a> | <a href="./admin.php" target="_top">Admin Home</a> ]</p>
<?php
	include('./footer.php');
?>

Because CJ products do not include categories, I had to change my browse.php to search the product title for the category (green, black, white). To create a quality site, however, I recommend you put products in their appropriate category using the custom category script.

You’ll notice I output 2 numbers at the end – the first is how many products were returned, and the second is how many were loaded into the database. For Mighty Leaf Tea, it loaded 413 products, but only 410 were in the database when I was done. Either 3 products had duplicate SKUs, or 3 products ended up with duplicate ProductIDs after my process was done to generate a ProductID from SKU. I’m not sure, but 410/413 is pretty good, so I left it alone.

This script was really hacked together – let me know if you have questions / problems.

Resources

Jun.19

Zip Code Database

I just got word that the ZipCodeGuy is giving away his Zip Code Database for free! Zip Code databases are great when you’re targeting regional offers, such as gas prices, dating, or practically any zip submit offer. With this database, you can do tons of things, like lookup nearby cities and show singles that are there (well, not really, but the user doesn’t need to know that when they’re signing up) or their photos on a zip code map of the region (using Google Maps API).

If you’re lazy, combine this with an IP database and you have some pretty powerful info on your hands. Or scrape, I mean, reference city-data.com and show all sorts of info!

Even if you have no plans to use it, download the zip code database while you can!

Resources