Welcome Guest, Not a member yet? Register   Sign In
serving purchased files
#1

[eluser]infinivert[/eluser]
I'd like to build a CI site where users purchase files, and then when the purchase is confirmed, they receive an e-mail with a special link to the purchased file that is available for, say, 24 or 48 hours. Any idea how to do this? I know it's done all the time, but I can't find anything on how it's done.

Thanks!
#2

[eluser]brianw1975[/eluser]
after the purchase create an encrypted hash and store it in a db table with at least the following:

id int
file_name text <-- points to your file or:
file_id <-- points to the id of a file described in another table
encryption text <-- i would recommend 32 to 64 characters for this
expiration <-- a time for the purchase to last default to 0 to make it as inactive by default

when purchase is verified update the expiration above to whatever length of time you want - probably easiest to use strftime(time() + "plus x days"). or however it goes... (going off the top of my head here)


Of course this doesn't cover copyright protection i.e. preventing many people from downloading the same file, etc, that's administrative to determine how long the link should stay active.

also, keeping track of if someone actually downloads the file successfully is kinda tough, if not impossible so don't expect to do it very easily, if at all, with javascript. I don't know of any way to determine if a user has downloaded a file successfully without the use of some download manager like MS does with silverlight (I use that as an example because I can't think of any other sites that do something like that)
#3

[eluser]infinivert[/eluser]
Thanks!

Yeah, it's keeping other users from downloading the file using the same URL that has me worried. I'm thinking there's probably a way to put the file outside the web-root, and then serve in dynamically upon verification. Any ideas how to do that?
#4

[eluser]slowgary[/eluser]
You'd probably need to have the user set up an account, capturing their email address (preferably sending them an email with a confirmation link). Then you'd use a payment gateway to collect their payment. Upon completed payment transaction you would set the file as downloadable for that user, using a date stamp for expiration.

So you'd have a few database tables:

Code:
Users
----------------------------------------------------
| user_id  |   name  |  email           | email_ok |
----------------------------------------------------
| 0        |   bob   |  [email protected]     | true     |
| 1        |   joe   |  [email protected]     | false    |
| 2        |  wendy  |  [email protected]   | false    |
----------------------------------------------------

Files
------------------------------
| file_id | file_name | cost |
------------------------------
| 0       | file.pdf  | 2.5  |
| 1       | pic.jpg   | .75  |
------------------------------

Downloadables
-------------------------------
| user_id | file_id | expiry  |
-------------------------------
| 0       | 1       | 3/24/09 |
| 0       | 0       | 3/10/09 |
-------------------------------

You'd have the user fill out a form, store the info in the 'users' table, and send them an email with a link in it back to a script on your site to confirm the email address. The link could have a few parameters like:

http://www.yoursite.com/email_confirm.ph...NLABSUFD7G

The validation string could be a hashed string, created from their user_id, username, and email address. So you'd do something like:

Code:
//get user info from database
//WARNING:  This is a terrible way to do this!!  NO SECURITY  (just an example)
$results = mysql_query("SELECT `name`, `email` FROM `users` WHERE `user_id` = '{$_GET['user_id']}'");

$results = mysql_fetch_assoc($results);

$hash = md5($results['name'].$results['email']);

if($_GET['validation'] == $hash)
{
     mysql_query("UPDATE `users` SET `email_ok` = 'true' WHERE `user_id` = '{$_GET['user_id']}'");
}

So now the user has an account with a confirmed email address. After selecting a file and paying through your gateway, you'd check which file they purchased and put an entry into the downloadables table:

Code:
mysql_query("INSERT INTO `downloadables` VALUES('{$_GET['user_id']}', '{$_GET['file_id']}', '".strtotime('+2 days')."'");

Then when a user logs in you'd have a script check the downloadables table for that user's files:

Code:
$results = mysql_query("SELECT `file_id`, `file_name` FROM `files` WHERE `file_id` IN (SELECT `file_id` FROM `downloadables` WHERE `user_id` = '$_GET['user_id']' AND `expiry` > '".time()."'"));

if(mysql_num_rows($results))
{
   echo "Select a file to download:<br/>";

  while(
     list(
          $file_id,
          $file_name
          ) = mysql_fetch_array()
     )
  {
      echo "$file_name <a href='downloader_script.php?file_id=$file_id&user;_id=$user_id'>download file</a><br/>";
  }
}
else
{
   echo "You have no files to download";
}

Then downloader_script.php would have to check the downloadables table again to make sure the user wasn't fudging the system, then it would have to check the files table and force-download the file, like so:

Code:
$result = mysql_query("SELECT `file_name` FROM `files` WHERE `file_id` IN (SELECT `file_id` FROM `downloadables` WHERE `file_id` = '$file_id' AND `user_id` = 'user_id')");

if(mysql_num_rows($results))
{
  $file_name = mysql_fetch_assoc($results);
  $file_name = $file_name['file_name'];

  $file_contents = read_file($file_name);  //some function to read file contents
  header('content-type/mime/type/blah blah:attachment');  //force download
  echo $file_contents //spew file contents
}

It would be good practice to keep the files above the web root, so that people can't just type in the url to your files. Also, the junk above is just a basic idea, don't in any way copy the code because it will make me puke for sure.

Hope that helps
#5

[eluser]infinivert[/eluser]
Nevermind, I think I figured it out:

Create a downloads (or whatever you want to call it) folder outside the web-root, and put your files in there. Then create a CI controller function that checks for whatever verification you want, then, if everything is good to go:

Code:
$this->load->helper('download');

$data = file_get_contents("../downloads/myfile.ext"); // Read the file's contents
$name = 'myfile.ext';
force_download($name, $data);

The verification process can check for a username / password or a verification code, whether or not the download is expired, etc., and keeping the actual file outside the web-root keeps the outside world from being able to get directly to the file.

There's probably more security that could / should be done, and I'm all ears if you guys have any suggestions.

Thanks again!

--Josh
#6

[eluser]slowgary[/eluser]
You could have the downloader_script.php increment a counter after it serves a file. It wouldn't be perfect, as it would still increment even if the user hit 'cancel' at the download prompt. If you're already using an expiration date then the number of times they download is moot.

To hinder users from sharing their accounts you could provide personal information immediately after logging in.
#7

[eluser]infinivert[/eluser]
Wow, thanks slowgary! Hadn't seen your reply yet, when I posted mine. Good stuff!
#8

[eluser]brianw1975[/eluser]
[quote author="infinivert" date="1237714156"]Thanks!

Yeah, it's keeping other users from downloading the file using the same URL that has me worried. I'm thinking there's probably a way to put the file outside the web-root, and then serve in dynamically upon verification. Any ideas how to do that?[/quote]

I'd probably try using the readfile() function myself.
#9

[eluser]jedd[/eluser]
Hi Josh. Another thing I've seen to discourage people sharing the link, is to imprint the file proper with some identifying information. A mob I was working for a while back bought an e-book, and the PDF we downloaded had the purchase officer's email address at the top of every flippin' page. It was good (insofar as there was no download limit per se - as their site blew the big one and it took this guy about 8 attempts and two different browsers before they had success) and bad (it's insanely annoying seeing someone's email address plastered, none too neatly, onto the top of every page of a book you're trying to read).

I offer this as one of the solutions, if not the best one, for discouraging the link-sharing thing.

Btw, nice advert in your sig - how much money do you make if we click on it?
#10

[eluser]infinivert[/eluser]
@brianw1975 - Is there a benefit to the readfile() function? The method I posted is from the CI manual for the download helper.

@jedd - I'll keep that in mind. The first site I'm building with this in mind will sell mp3s, so I'm not sure how I'd implement that, but I'll think on it. I suppose I could put the user's name into the file name, but that's easily enough changed. Maybe there would be a way to add it to the file header. Hmmm...

And honestly, I make jack squat on that link. I'll be glad to remove it if the moderators ask. Eleven2 really is a great host, and I do recommend them, but I'm not making any income to speak of for it.




Theme © iAndrew 2016 - Forum software by © MyBB