/** * Generates a Payment message based on the information in the PaymentRequest. Provide * transactions built by the wallet. If the PaymentRequest did not specify a payment_url, returns * null. * * @param txns list of transactions to be included with the Payment message. * @param refundAddr will be used by the merchant to send money back if there was a problem. * @param memo is a message to include in the payment message sent to the merchant. */ @Nullable public Protos.Payment getPayment( List<Transaction> txns, @Nullable Address refundAddr, @Nullable String memo) throws IOException, PaymentProtocolException.InvalidNetwork { if (paymentDetails.hasPaymentUrl()) { for (Transaction tx : txns) if (!tx.getParams().equals(params)) throw new PaymentProtocolException.InvalidNetwork(params.getPaymentProtocolId()); return PaymentProtocol.createPaymentMessage( txns, totalValue, refundAddr, memo, getMerchantData()); } else { return null; } }
/** * Creates a PaymentSession from the provided {@link Protos.PaymentRequest}. If verifyPki is true, * also validates the signature and throws an exception if it fails. If trustStoreLoader is null, * the system default trust store is used. */ public PaymentSession( Protos.PaymentRequest request, boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) throws PaymentProtocolException { TrustStoreLoader nonNullTrustStoreLoader = trustStoreLoader != null ? trustStoreLoader : new TrustStoreLoader.DefaultTrustStoreLoader(); parsePaymentRequest(request); if (verifyPki) { try { pkiVerificationData = PaymentProtocol.verifyPaymentRequestPki(request, nonNullTrustStoreLoader.getKeyStore()); } catch (IOException x) { throw new PaymentProtocolException(x); } catch (KeyStoreException x) { throw new PaymentProtocolException(x); } } else { pkiVerificationData = null; } }
@Override public Boolean call() { if (serverSocket.isClosed()) { log.warn("Server socket is closed. Aborting."); return false; } else { try { // Wait for a client connection log.debug("Await client connection to SSLSocket"); SSLSocket socket = (SSLSocket) serverSocket.accept(); socket.startHandshake(); log.debug("Sending PaymentACK"); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); // Read the inputStream - this is expected to be a header followed by a serialised Payment Reader reader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(reader); String line; int contentLength = -1; StringBuilder builder = new StringBuilder(); while (!"".equals((line = bufferedReader.readLine()))) { builder.append(line).append("\n"); log.debug("Read line: {}", line); if (line.startsWith("Content-Length")) { String[] tokens = line.replaceAll(" ", "").split(":"); if (tokens.length >= 2) { contentLength = Integer.parseInt(tokens[1]); } } } log.debug("Calculated contentLength: {}", contentLength); log.debug("Read the header:\n{}\n", builder.toString()); // Get the Content-Length and read those - this is expected to be the serialised Payment if (contentLength > -1) { byte buffer[] = new byte[contentLength]; for (int i = 0; i < contentLength; i++) { buffer[i] = (byte) inputStream.read(); } log.debug("Read:\n", HexUtils.toHexBytes(buffer)); try { Protos.Payment payment = Protos.Payment.parseFrom(buffer); log.debug("Successfully parsed a payment {}", payment); // Create a PaymentACK for the payment Protos.PaymentACK paymentAck = PaymentProtocol.createPaymentAck(payment, "You sent: '" + payment.getMemo() + "'"); log.debug("Sending paymentACK as a response: {}", paymentAck); // Write the HTTP header outputStream.write("HTTP/1.0 200 OK\n".getBytes(Charsets.UTF_8)); outputStream.write("Content-Type: ".getBytes(Charsets.UTF_8)); outputStream.write(contentType.getBytes(Charsets.UTF_8)); outputStream.write("\n\n".getBytes(Charsets.UTF_8)); // Write the protobuf response paymentAck.writeTo(outputStream); // ByteArrayInputStream responseInputStream = new ByteArrayInputStream(paymentAckBytes); // ByteStreams.copy(responseInputStream, outputStream); } catch (com.google.protobuf.InvalidProtocolBufferException ipbe) { log.error("Was expecting a Payment on the socket but saw something else"); ipbe.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // Release resources log.debug("Flush then close client socket..."); socket.getOutputStream().flush(); socket.close(); } } else { log.debug("Could not find a Content-Length in the header so not reading payment"); } return true; } catch (IOException e) { throw new IllegalStateException("Unexpected IOException", e); } } }
public static PaymentIntent parsePaymentRequest(@Nonnull final byte[] serializedPaymentRequest) throws PaymentProtocolException { try { if (serializedPaymentRequest.length > 50000) throw new PaymentProtocolException( "payment request too big: " + serializedPaymentRequest.length); final Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.parseFrom(serializedPaymentRequest); final String pkiName; final String pkiCaName; if (!"none".equals(paymentRequest.getPkiType())) { final KeyStore keystore = new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore(); final PkiVerificationData verificationData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest, keystore); pkiName = verificationData.displayName; pkiCaName = verificationData.rootAuthorityName; } else { pkiName = null; pkiCaName = null; } final PaymentSession paymentSession = PaymentProtocol.parsePaymentRequest(paymentRequest); if (paymentSession.isExpired()) throw new PaymentProtocolException.Expired( "payment details expired: current time " + new Date() + " after expiry time " + paymentSession.getExpires()); if (!paymentSession.getNetworkParameters().equals(Constants.NETWORK_PARAMETERS)) throw new PaymentProtocolException.InvalidNetwork( "cannot handle payment request network: " + paymentSession.getNetworkParameters()); final ArrayList<PaymentIntent.Output> outputs = new ArrayList<PaymentIntent.Output>(1); for (final PaymentProtocol.Output output : paymentSession.getOutputs()) outputs.add(PaymentIntent.Output.valueOf(output)); final String memo = paymentSession.getMemo(); final String paymentUrl = paymentSession.getPaymentUrl(); final byte[] merchantData = paymentSession.getMerchantData(); final byte[] paymentRequestHash = Hashing.sha256().hashBytes(serializedPaymentRequest).asBytes(); final PaymentIntent paymentIntent = new PaymentIntent( PaymentIntent.Standard.BIP70, pkiName, pkiCaName, outputs.toArray(new PaymentIntent.Output[0]), memo, paymentUrl, merchantData, null, paymentRequestHash); if (paymentIntent.hasPaymentUrl() && !paymentIntent.isSupportedPaymentUrl()) throw new PaymentProtocolException.InvalidPaymentURL( "cannot handle payment url: " + paymentIntent.paymentUrl); return paymentIntent; } catch (final InvalidProtocolBufferException x) { throw new PaymentProtocolException(x); } catch (final UninitializedMessageException x) { throw new PaymentProtocolException(x); } catch (final FileNotFoundException x) { throw new RuntimeException(x); } catch (final KeyStoreException x) { throw new RuntimeException(x); } }