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); }
} $paymentRequest->setSerializedPaymentDetails($details->serialize()); $certChain = new \payments\X509Certificates(); $leafCert = file_get_contents($certificate); // 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: