function createPaymentRequest($params, &$formErrors)
{
    $memcache = new Memcache();
    $memcache->connect('localhost', 11211) or die("Could not connect to memcache");
    // $params contains:
    // merchant / address123 / amount123 / time / expires / memo
    // payment_url / ACK_message
    $codec = new \DrSlump\Protobuf\Codec\Binary();
    $details = new \payments\PaymentDetails();
    $details->setTime(time() + (int) $params['time']);
    if ($params['expires'] != "never") {
        $details->setExpires(time() + (int) $params['expires']);
    }
    setField($params, 'memo', array($details, 'setMemo'));
    $testnet = false;
    $totalAmount = 0;
    $nAddresses = 0;
    for ($i = 1; $i <= 3; $i++) {
        $field = "address" . $i;
        if (!empty($params[$field])) {
            $output = new \payments\Output();
            $r = address_to_script($params[$field]);
            if ($r === false) {
                $script = pubkeys_to_script($params[$field]);
                if ($script === false) {
                    $formErrors[$field] = "Invalid address/pubkey";
                    continue;
                }
                $r = array(true, $script);
            }
            $testnet = $r[0];
            $output->setScript($r[1]);
            $output->setAmount($params["amount" . $i] * 100000000.0);
            $totalAmount += $params["amount" . $i];
            $nAddresses += 1;
            $details->addOutputs($output);
            // Testnet only, we don't want anybody to be able to create
            // real-money payment requests
            // from bitcoincore.org/gavinandresen@gmail.com:
            if (!$testnet && $params['merchant'] != "None") {
                $formErrors[$field] = "Testnet-only addresses, please";
                return NULL;
            }
        }
    }
    if ($testnet) {
        $details->setNetwork("test");
    }
    if (isset($params['payment_url'])) {
        /* Generate a unique id for this request: */
        $id = uniqid(mySecret($memcache));
        /* ... store it in merchant data: */
        $details->setMerchantData($id);
        $ackURL = AbsoluteURL('') . "payACK.php";
        $details->setPaymentUrl($ackURL);
        if (isset($params['ACK_message'])) {
            $memcache->set($id, $params['ACK_message'], 0, 60 * 60 * 24);
        } else {
            $memcache->set($id, '', 0, 60 * 60 * 24);
        }
    }
    $paymentRequest = new \payments\PaymentRequest();
    $serialized = $details->serialize($codec);
    $paymentRequest->setSerializedPaymentDetails($serialized);
    // Signed?
    if ($params['merchant'] != "None") {
        $certK = $params['merchant'] . "certificate";
        $keyK = $params['merchant'] . "key";
        $certChain = new \payments\X509Certificates();
        $cachedCertChain = $memcache->get($certK);
        if ($cachedCertChain === FALSE) {
            $leafCert = file_get_contents("/home/gavin/.certs/" . $params['merchant'] . ".crt");
            $certs = fetch_chain($leafCert);
            foreach ($certs as $cert) {
                $certChain->addCertificate($cert);
            }
            $cachedCertChain = $certChain->serialize($codec);
            $memcache->set($certK, $cachedCertChain);
        } else {
            $certChain->parse($cachedCertChain);
        }
        $paymentRequest->setPkiType("x509+sha1");
        $paymentRequest->setPkiData($certChain->serialize($codec));
        $priv_key = file_get_contents("/home/gavin/.certs/" . $params['merchant'] . ".key");
        $pkeyid = openssl_get_privatekey($priv_key);
        $paymentRequest->setSignature("");
        $dataToSign = $paymentRequest->serialize($codec);
        $signature = "";
        $result = openssl_sign($dataToSign, $signature, $pkeyid, OPENSSL_ALGO_SHA1);
        if ($signature === FALSE) {
            return "ERROR: signing failed.\n";
        }
        $paymentRequest->setSignature($signature);
    }
    $data = $paymentRequest->serialize($codec);
    if (isset($params['produce_uri'])) {
        $urlParams = array();
        $hash = hash('ripemd128', $data);
        $memcache->set($hash, $data, FALSE, 60 * 60 * 24);
        /* cache for 24 hours */
        // f.php is fetch payment request from memcache:
        $urlParams['r'] = AbsoluteURL('') . "f.php?h=" . $hash;
        if ($nAddresses == 1 && $totalAmount > 0) {
            $urlParams['amount'] = $totalAmount;
        }
        if ($nAddresses == 1) {
            $url = AddArgsToURL("bitcoin:" . $params["address1"], $urlParams);
        } else {
            $url = AddArgsToURL("bitcoin:", $urlParams);
        }
        return MakeAnchor("CLICK TO PAY", $url);
    }
    header('Content-Type: application/bitcoin-paymentrequest');
    $filename = "r" . (string) time() . ".bitcoinpaymentrequest";
    header('Content-Disposition: inline; filename=' . $filename);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . (string) strlen($data));
    echo $data;
    exit(0);
}
 function ShowRun($runidx)
 {
     $runidx = addslashes($runidx);
     $exptidx = $this->exptidx();
     $guid = $this->guid();
     $vers = $this->vers();
     $template = $this->template();
     $query_result = DBQueryFatal("select r.* from experiment_runs as r " . "left join experiment_template_instances as i on " . "     i.exptidx=r.exptidx " . "where r.exptidx='{$exptidx}' and r.idx='{$runidx}'");
     if (!mysql_num_rows($query_result)) {
         return;
     }
     $row = mysql_fetch_array($query_result);
     $start = $row['start_time'];
     $stop = $row['stop_time'];
     $runid = $row['runid'];
     $start_tag = $row['starting_archive_tag'];
     $end_tag = $row['ending_archive_tag'];
     $description = $row['description'];
     $hidden = $row['hidden'];
     # Run descriptions are metatdata that can be changed.
     # But use a naming convention.
     $desc_metaname = "__run_description_{$exptidx}_{$runidx}";
     $desc_metadata = $template->LookupMetadataByName($desc_metaname);
     if (!isset($stop)) {
         $stop = "&nbsp";
     }
     SUBPAGESTART();
     SUBMENUSTART("Run Options");
     WRITESUBMENUBUTTON("Export Record", CreateURL("template_export", $this, "runidx", $runidx));
     WRITESUBMENUBUTTON("Revise Record", CreateURL("record_revise", $this, "runidx", $runidx));
     if ($hidden) {
         WRITESUBMENUBUTTON("UnHide Record", CreateURL("toggle", $this, "runidx", $runidx, "type", "hiderun", "value", 0));
     } else {
         WRITESUBMENUBUTTON("Hide Record", CreateURL("toggle", $this, "runidx", $runidx, "type", "hiderun", "value", 1));
     }
     if (isset($end_tag) && $end_tag != "") {
         WRITESUBMENUBUTTON("View Archive", CreateURL("archive_view", $this, "tag", $end_tag));
     }
     SUBMENUEND();
     echo "<center>\n";
     echo "<table border=0 bgcolor=#000 color=#000 class=stealth " . " cellpadding=0 cellspacing=0>\n";
     echo "<tr valign=top>";
     echo "<td class=stealth align=center>\n";
     echo "<center>\n               <h3>Run Details</h3>\n             </center>\n";
     echo "<table align=center cellpadding=2 cellspacing=2 border=1>\n";
     ShowItem("Template", MakeLink("template", "guid={$guid}&version={$vers}", "{$guid}/{$vers}"));
     ShowItem("Instance", MakeLink("instance", "instance={$exptidx}", "{$exptidx}"));
     ShowItem("ID", $runidx);
     if ($hidden) {
         ShowItem("Hidden", "<font color=red>Yes</font>");
     }
     if ($description && $description != "") {
         $onmouseover = MakeMouseOver($description);
         if (strlen($description) > 40) {
             $description = substr($description, 0, 40) . " <b>... </b>";
         }
     } else {
         $onmouseover = "";
         $description = "Click to Add";
     }
     # If no metadata, assume its for an instance prior to modifiable
     # instance descriptions.
     if ($desc_metadata) {
         $desc_anchor = MakeAnchor(CreateURL("template_metadata", $template, $desc_metadata) . "&action=modify", $description, $onmouseover);
     } else {
         $desc_anchor = MakeAnchor(CreateURL("template_metadata", $template) . "&action=add" . "&metadata_type=run_description" . "&formfields[name]={$desc_metaname}", $description, $onmouseover);
     }
     ShowItem("Description", $desc_anchor);
     ShowItem("Started", $start);
     ShowItem("Stopped", $stop);
     ShowItem("Start Tag", $start_tag);
     ShowItem("End Tag", $end_tag);
     echo "</table>\n";
     echo "</td>\n";
     $query_result = DBQueryFatal("select * from experiment_run_bindings " . "where exptidx='{$exptidx}' and runidx='{$runidx}'");
     if (mysql_num_rows($query_result)) {
         $id = "{$exptidx}_{$runidx}_bindings";
         echo "<td align=center class=stealth> &nbsp &nbsp &nbsp </td>\n";
         echo "<td align=center class=stealth>\n";
         AddSortedTable($id);
         echo "<center>\n                   <h3>Run Bindings</h3>\n                  </center> \n                  <table id='{$id}'\n                         align=center border=1 cellpadding=5 cellspacing=2>\n";
         echo "<thead class='sort'>\n";
         echo "<tr>\n                    <th>Name</th>\n                    <th>Value</th>\n                  </tr>\n";
         echo "</thead>\n";
         while ($row = mysql_fetch_array($query_result)) {
             $name = $row['name'];
             $value = $row['value'];
             if (!isset($value)) {
                 $value = "&nbsp";
             }
             echo "<tr>\n                       <td>{$name}</td>\n                       <td>{$value}</td>\n                      </tr>\n";
         }
         echo "</table>\n";
         echo "</td>\n";
     }
     echo "</tr>\n";
     echo "</table>\n";
     $this->ShowRunAnnotation($runidx);
     echo "</center>\n";
     SUBPAGEEND();
 }