/** * Parse a payment request. * * @param paymentRequest payment request to parse * @return instance of {@link PaymentSession}, used as a value object * @throws PaymentProtocolException */ public static PaymentSession parsePaymentRequest(Protos.PaymentRequest paymentRequest) throws PaymentProtocolException { return new PaymentSession(paymentRequest, false, null); }
/** * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided uri. * uri is a BIP-72-style BitcoinURI object that specifies where the {@link Protos.PaymentRequest} object may * be fetched in the r= parameter. * If verifyPki is specified and the payment request object specifies a PKI method, then the system trust store will * be used to verify the signature provided by the payment request. An exception is thrown by the future if the * signature cannot be verified. */ public static ListenableFuture<PaymentSession> createFromBitcoinUri(final BitcoinURI uri, final boolean verifyPki) throws PaymentProtocolException { return createFromBitcoinUri(uri, verifyPki, null); }
/** * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. * url is an address where the {@link Protos.PaymentRequest} object may be fetched. * If verifyPki is specified and the payment request object specifies a PKI method, then the system trust store will * be used to verify the signature provided by the payment request. An exception is thrown by the future if the * signature cannot be verified. */ public static ListenableFuture<PaymentSession> createFromUrl(final String url) throws PaymentProtocolException { return createFromUrl(url, true, null); }
Protos.PaymentRequest request = Protos.PaymentRequest.parseFrom(stream); stream.close(); session = new PaymentSession(request); } else if ("http".equals(uri.getScheme())) { session = PaymentSession.createFromUrl(arg).get(); } else if ("bitcoin".equals(uri.getScheme())) { BitcoinURI bcuri = new BitcoinURI(arg); session = PaymentSession.createFromBitcoinUri(bcuri).get(); } else { System.err.println("Unknown URI scheme: " + uri.getScheme()); return; final int version = session.getPaymentRequest().getPaymentDetailsVersion(); StringBuilder output = new StringBuilder( format("Bitcoin payment request, version %d%nDate: %s%n", version, session.getDate())); PaymentProtocol.PkiVerificationData pki = PaymentProtocol.verifyPaymentRequestPki( session.getPaymentRequest(), new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore()); if (pki != null) { output.append(format("Signed by: %s%nIdentity verified by: %s%n", pki.displayName, pki.rootAuthorityName)); if (session.getPaymentDetails().hasExpires()) { output.append(format("Expires: %s%n", new Date(session.getPaymentDetails().getExpires() * 1000))); if (session.getMemo() != null) { output.append(format("Memo: %s%n", session.getMemo())); output.append(format("%n%n%s%n%s", session.getPaymentRequest(), session.getPaymentDetails())); System.out.println(output);
/** * Generates a Payment message and sends the payment to the merchant who sent the PaymentRequest. * Provide transactions built by the wallet. * NOTE: This does not broadcast the transactions to the bitcoin network, it merely sends a Payment message to the * merchant confirming the payment. * Returns an object wrapping PaymentACK once received. * If the PaymentRequest did not specify a payment_url, returns null and does nothing. * @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 ListenableFuture<PaymentProtocol.Ack> sendPayment(List<Transaction> txns, @Nullable Address refundAddr, @Nullable String memo) throws PaymentProtocolException, VerificationException, IOException { Protos.Payment payment = getPayment(txns, refundAddr, memo); if (payment == null) return null; if (isExpired()) throw new PaymentProtocolException.Expired("PaymentRequest is expired"); URL url; try { url = new URL(paymentDetails.getPaymentUrl()); } catch (MalformedURLException e) { throw new PaymentProtocolException.InvalidPaymentURL(e); } return sendPayment(url, payment); }
log.debug("Treating as BIP21 resource"); paymentSession = PaymentSession .createFromBitcoinUri(bitcoinUri, false, null) .get(); return new PaymentSessionSummary( RAGStatus.PINK, CoreMessageKey.PAYMENT_SESSION_PKI_INVALID, new String[]{paymentSession.getMemo()} ); } else if (bitcoinUri.getPaymentRequestUrls().size() == 1) { log.debug("Treating as single BIP72 resource"); paymentSession = PaymentSession .createFromBitcoinUri(bitcoinUri, checkPKI, trustStoreLoader) .get(PAYMENT_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); } else { try { paymentSession = PaymentSession .createFromUrl(r, checkPKI, trustStoreLoader) .get(PAYMENT_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); break; log.debug("Treating as remote HTTP/S resource"); paymentSession = PaymentSession .createFromUrl(paymentRequestUri.toString(), checkPKI, trustStoreLoader) .get(PAYMENT_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); byte[] paymentRequestBytes = Resources.toByteArray(paymentRequestUri.toURL());
ListenableFuture<PaymentSession> future; if (location.startsWith("http")) { future = PaymentSession.createFromUrl(location, verifyPki); } else { BitcoinURI paymentRequestURI = new BitcoinURI(location); future = PaymentSession.createFromBitcoinUri(paymentRequestURI, verifyPki); session = new PaymentSession(paymentRequest, verifyPki); } catch (PaymentProtocolException e) { System.err.println("Error creating payment session " + e.getMessage());
public Optional<PaymentProtocolResponseDto> sendPaymentSessionPayment(List<Transaction> transactions, @Nullable Address refundAddr, @Nullable String memo) throws IOException, PaymentProtocolException { if (hasPaymentSession()) { log.debug("Sending payment details to requester at URL '{}'", paymentSession.get().getPaymentUrl()); Protos.Payment payment = paymentSession.get().getPayment(transactions, refundAddr, memo); ListenableFuture<PaymentProtocol.Ack> future = paymentSession.get().sendPayment(transactions, refundAddr, memo); return Optional.of(new PaymentProtocolResponseDto(payment, future)); } else { return Optional.absent(); } }
private static void send(PaymentSession session) { try { System.out.println("Payment Request"); System.out.println("Coin: " + session.getValue().toFriendlyString()); System.out.println("Date: " + session.getDate()); System.out.println("Memo: " + session.getMemo()); if (session.pkiVerificationData != null) { System.out.println("Pki-Verified Name: " + session.pkiVerificationData.displayName); System.out.println("PKI data verified by: " + session.pkiVerificationData.rootAuthorityName); final SendRequest req = session.getSendRequest(); if (password != null) { req.aesKey = passwordToKey(true); ListenableFuture<PaymentProtocol.Ack> future = session.sendPayment(ImmutableList.of(req.tx), null, null); if (future == null) {
@Test public void testPaymentRequest() throws Exception { // Create PaymentRequest paymentRequest = PaymentProtocol.createPaymentRequest(TestNet3Params.get(), AMOUNT, TO_ADDRESS, MEMO, PAYMENT_URL, MERCHANT_DATA).build(); byte[] paymentRequestBytes = paymentRequest.toByteArray(); // Parse PaymentSession parsedPaymentRequest = PaymentProtocol.parsePaymentRequest(PaymentRequest .parseFrom(paymentRequestBytes)); final List<Output> parsedOutputs = parsedPaymentRequest.getOutputs(); assertEquals(1, parsedOutputs.size()); assertEquals(AMOUNT, parsedOutputs.get(0).amount); assertArrayEquals(ScriptBuilder.createOutputScript(TO_ADDRESS).getProgram(), parsedOutputs.get(0).scriptData); assertEquals(MEMO, parsedPaymentRequest.getMemo()); assertEquals(PAYMENT_URL, parsedPaymentRequest.getPaymentUrl()); assertArrayEquals(MERCHANT_DATA, parsedPaymentRequest.getMerchantData()); }
/** * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. * url is an address where the {@link Protos.PaymentRequest} object may be fetched. * If the payment request object specifies a PKI method, then the system trust store will * be used to verify the signature provided by the payment request. An exception is thrown by the future if the * signature cannot be verified. * If trustStoreLoader is null, the system default trust store is used. */ public static ListenableFuture<PaymentSession> createFromUrl(final String url, final boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) throws PaymentProtocolException { if (url == null) throw new PaymentProtocolException.InvalidPaymentRequestURL("null paymentRequestUrl"); try { return fetchPaymentRequest(new URI(url), verifyPki, trustStoreLoader); } catch(URISyntaxException e) { throw new PaymentProtocolException.InvalidPaymentRequestURL(e); } }
try { if (paymentRequestData.get().getPaymentRequest().isPresent()) { paymentSession = new PaymentSession(paymentRequestData.get().getPaymentRequest().get(), false); } else { log.error("No PaymentRequest in PaymentRequestData - cannot create a paymentSession"); Wallet.SendRequest sendRequest = paymentSession.getSendRequest(); log.debug("SendRequest from BIP70 paymentSession: {}", sendRequest);
/** * 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; } }
/** * @return The memo from the payment session object */ public Optional<String> getPaymentSessionMemo() { if (hasPaymentSession()) { return Optional.fromNullable(paymentSession.get().getMemo()); } else { return Optional.absent(); } }
/** * Generates a Payment message and sends the payment to the merchant who sent the PaymentRequest. * Provide transactions built by the wallet. * NOTE: This does not broadcast the transactions to the bitcoin network, it merely sends a Payment message to the * merchant confirming the payment. * Returns an object wrapping PaymentACK once received. * If the PaymentRequest did not specify a payment_url, returns null and does nothing. * @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 ListenableFuture<PaymentProtocol.Ack> sendPayment(List<Transaction> txns, @Nullable Address refundAddr, @Nullable String memo) throws PaymentProtocolException, VerificationException, IOException { Protos.Payment payment = getPayment(txns, refundAddr, memo); if (payment == null) return null; if (isExpired()) throw new PaymentProtocolException.Expired("PaymentRequest is expired"); URL url; try { url = new URL(paymentDetails.getPaymentUrl()); } catch (MalformedURLException e) { throw new PaymentProtocolException.InvalidPaymentURL(e); } return sendPayment(url, payment); }
/** * Returns a future that will be notified with a PaymentSession object after it is fetched using the provided url. * url is an address where the {@link org.bitcoin.protocols.payments.Protos.PaymentRequest} object may be fetched. * If the payment request object specifies a PKI method, then the system trust store will * be used to verify the signature provided by the payment request. An exception is thrown by the future if the * signature cannot be verified. * If trustStoreLoader is null, the system default trust store is used. */ public static ListenableFuture<PaymentSession> createFromUrl(final String url, final boolean verifyPki, @Nullable final TrustStoreLoader trustStoreLoader) throws PaymentProtocolException { if (url == null) throw new PaymentProtocolException.InvalidPaymentRequestURL("null paymentRequestUrl"); try { return fetchPaymentRequest(new URI(url), verifyPki, trustStoreLoader); } catch(URISyntaxException e) { throw new PaymentProtocolException.InvalidPaymentRequestURL(e); } }
/** * 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; } }
/** * <p>The server has returned a well-formed payment request</p> * * @param paymentSession The payment session containing meta data (cannot be null to be OK) * @param pkiVerificationData The PKI verification data containing identity information (cannot be null to be OK) * * @return A new "payment session OK" summary */ public static PaymentSessionSummary newPaymentSessionOK(PaymentSession paymentSession, PaymentProtocol.PkiVerificationData pkiVerificationData) { Preconditions.checkNotNull(paymentSession, "'paymentSession' must be present"); return new PaymentSessionSummary( Optional.of(paymentSession), Optional.fromNullable(pkiVerificationData), PaymentSessionStatus.TRUSTED, RAGStatus.GREEN, CoreMessageKey.PAYMENT_SESSION_OK, new String[]{paymentSession.getMemo()} ); }