/**
  * 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);
    }
  }