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);
}
// http-fetch parent certificates. In a real web application, you should avoid
// constantly re-fetching the certificate chain, and should, instead, fetch it once
// and then store it in your database or in a file on disk and only re-fetch it when
// your certificate expires or one of the intermediate certificates is revoked/replaced.
$certs = fetch_chain($leafCert);
foreach ($certs as $cert) {
    $certChain->addCertificate($cert);
}
//
// Create signature
//
$paymentRequest->setPkiType("x509+sha1");
$paymentRequest->setPkiData($certChain->serialize());
$priv_key = file_get_contents($privatekey);
$pkeyid = openssl_get_privatekey($priv_key);
$paymentRequest->setSignature("");
$dataToSign = $paymentRequest->serialize();
$signature = "";
$result = openssl_sign($dataToSign, $signature, $pkeyid, OPENSSL_ALGO_SHA1);
if ($signature === FALSE) {
    echo "ERROR: signing failed.\n";
    exit(1);
}
$paymentRequest->setSignature($signature);
$data = $paymentRequest->serialize();
//
// Done; output:
//
if ($outfile) {
    file_put_contents($outfile, $data);
} else {