Here are some example values for these fields:
desc=mountain.jpg:A .jpg picture of a mountain qty=1 item=MD5:9f8f1e491f3ada143a9052c13463c857 target=MD5:e47204cb0e3ba306527717533462db80The payment program inserts an extra parameter, SOX-Payment, into the URL, which contains the payment (in hex). Optionally, the payment program may choose to remove the item, qty and target fields, since these are included in the payment itself.
The payment program also removes the /SOX/payment-request/ part from the file component of the URL, since this has served its purpose of signalling the payment program that a payment is required.
Here is an example of a pay per view HTML anchor:
<A HREF="http://www.shop.crypt/SOX/payment-request/cgi-bin/ pay-per-view.cgi?desc=mountain.jpg:A+.jpg+picture+of+a+moun tain&qty=1&item=MD5:9f8f1e491f3ada143a9052c13463c857&target =MD5:e47204cb0e3ba306527717533462db80"> A Mountain </A>>Notice that this link causes the pay-per-view.cgi script to be executed. This script will validate the payment, and then, if successful, will provide the information or services contained within the description ("desc") field.
The key elements of a CGI script will now be described:
The following two modules are used, one to obtain the URL parameters, and the other to send the payment to the shop backend for verification.
use Message::Server; use CGI;
The CGI script must create a Message::Server object in order to communicate with the shop backend. The shop backend has a name and a port number, which in this case are "shop-deposit-request" and 12345 respectively.
my $backend = new Message::Server; defined $backend || internal_error "Could not create socket ($!)"; $backend->registerPortname("shop-deposit-request", 12345);
The CGI parameters must now be extracted from the environment, using the CGI perl module:
local $::query = new CGI; my $payment = $::query->param('SOX-Payment'); defined $payment || error "Payment missing"; my $desc = $::query->param('desc'); defined $desc || error "Payment description missing";
The desc parameter should now be verified; perhaps it contains the name of a program that is to be downloaded - in this case it is important to ensure the program exists, and to determine the price.
Once the script is certain that it can complete the request, it will be necessary to send the payment to the shop backend for verification and depositing. This is done using the s_request method of the Message::Server object. Six parameters are required, five of which are named. The first paramater, which is not named, is the name of the shop backend port (in this case, shop-deposit-request). The other five (named) parameters are:
All of these parameters would have been passed to Webfunds to produce the payment used for the -payment parameter and must match these exactly. For security reasons it is advised that these are recalculated in the same way they were calculated to produce the payment.
Typical code will look like this:
my ($reply, $status); ($reply, $status) = $backend->s_request("shop-deposit-request", -account => "MD5:0f7fd046e46984ba2201c5b266592660", -item => "MD5:9f8f1e491f3ada143a9052c13463c857", -qty => 1, -payment => pack("H*", $payment), -desc => $desc ); $status && internal_error("backend not responding ($reply)"); $reply->[1] && error $reply->[0];
At this stage, if the script has not exited with errors, then the payment is valid and the information or services should be provided.
#!/usr/local/bin/perl -w use strict; use IO::File; use Message::Server; use CGI; sub error { my $msg = shift; print $::query->header( -type => 'text/html', -status => "402 $msg" ); print $::query->start_html; print "<H1>An error has occured - $msg</H1>"; print $::query->end_html; exit 0; } sub internal_error { my $msg = shift; error "Internal error [$msg]"; } # # Create agent to shop backend # my $backend = new Message::Server; defined $backend || internal_error "Could not create socket ($!)"; $backend->registerPortname("shop-deposit-request", 11100); # # Extract payment and description from CGI request # local $::query = new CGI; my $payment = $::query->param('SOX-Payment'); defined $payment || error "Payment missing"; my $desc = $::query->param('desc'); defined $desc || error "Payment description missing"; # # See if request is for valid file # my $filename = (split(':', $desc))[0]; my $file = "/home/data/images/cars/yugo/$filename"; my $fh = new IO::File $file, "r"; defined $fh || error "No such picture ($filename)"; # # Send request to backend for processing # fields are - account, currency, cost, payment, desc # my ($reply, $status); ($reply, $status) = $backend->s_request("shop-deposit-request", -account => "MD5:0f7fd046e46984ba2201c5b266592660", -item => "MD5:9f8f1e491f3ada143a9052c13463c857", -qty => 1, -payment => pack("H*", $payment), -desc => $desc ); $status && internal_error("backend not responding ($reply)"); $reply->[1] && error $reply->[0]; # # Payment succeeded - read picture and send HTTP response # input_record_separator $fh undef; my $picture = <fh>; print $::query->header( -type => 'image/jpeg', 'Content-length' => length($picture) ); print $picture;
The above example does have one potential security hole. When the payment is sent to the shop backend ( $backend->s_request(...) ) the description passed on by Webfunds is used directly and the qty hard-coded. This will be fine for a situation where all the pictures cost 1 each. If at a later point more pay-per-view items were added with a higher cost (say 5), with the intention of calling another script, problems could occur. Webfunds could be asked to make a payment for 1 with a description for an expensive picture but calling the script for the lower priced ones. Webfunds will write the payment and the shop backend will accept it as everything matches. A 5 value picture has just been viewed for 1.
To avoid this, the script sending the payment to the shop backend should check the payment by reproducing the qty and desc parameters.