/** * Simple Bitcoin URI builder using known good fields. * * @param params The network parameters that determine which network the URI is for. * @param address The Bitcoin address * @param amount The amount * @param label A label * @param message A message * @return A String containing the Bitcoin URI */ public static String convertToBitcoinURI( NetworkParameters params, String address, @Nullable Coin amount, @Nullable String label, @Nullable String message) { checkNotNull(params); checkNotNull(address); if (amount != null && amount.signum() < 0) { throw new IllegalArgumentException("Coin must be positive"); } StringBuilder builder = new StringBuilder(); String scheme = params.getUriScheme(); builder.append(scheme).append(":").append(address); boolean questionMarkHasBeenOutput = false; if (amount != null) { builder.append(QUESTION_MARK_SEPARATOR).append(FIELD_AMOUNT).append("="); builder.append(amount.toPlainString()); questionMarkHasBeenOutput = true; } if (label != null && !"".equals(label)) { if (questionMarkHasBeenOutput) { builder.append(AMPERSAND_SEPARATOR); } else { builder.append(QUESTION_MARK_SEPARATOR); questionMarkHasBeenOutput = true; } builder.append(FIELD_LABEL).append("=").append(encodeURLString(label)); } if (message != null && !"".equals(message)) { if (questionMarkHasBeenOutput) { builder.append(AMPERSAND_SEPARATOR); } else { builder.append(QUESTION_MARK_SEPARATOR); } builder.append(FIELD_MESSAGE).append("=").append(encodeURLString(message)); } return builder.toString(); }
/** * Constructs a new object by trying to parse the input as a valid Bitcoin URI. * * @param params The network parameters that determine which network the URI is from, or null if * you don't have any expectation about what network the URI is for and wish to check * yourself. * @param input The raw URI data to be parsed (see class comments for accepted formats) * @throws BitcoinURIParseException If the input fails Bitcoin URI syntax and semantic checks. */ public BitcoinURI(@Nullable NetworkParameters params, String input) throws BitcoinURIParseException { checkNotNull(input); String scheme = null == params ? AbstractBitcoinNetParams.BITCOIN_SCHEME : params.getUriScheme(); // Attempt to form the URI (fail fast syntax checking to official standards). URI uri; try { uri = new URI(input); } catch (URISyntaxException e) { throw new BitcoinURIParseException("Bad URI syntax", e); } // URI is formed as bitcoin:<address>?<query parameters> // blockchain.info generates URIs of non-BIP compliant form bitcoin://address?.... // We support both until Ben fixes his code. // Remove the bitcoin scheme. // (Note: getSchemeSpecificPart() is not used as it unescapes the label and parse then fails. // For instance with : // bitcoin:129mVqKUmJ9uwPxKJBnNdABbuaaNfho4Ha?amount=0.06&label=Tom%20%26%20Jerry // the & (%26) in Tom and Jerry gets interpreted as a separator and the label then gets parsed // as 'Tom ' instead of 'Tom & Jerry') String blockchainInfoScheme = scheme + "://"; String correctScheme = scheme + ":"; String schemeSpecificPart; if (input.startsWith(blockchainInfoScheme)) { schemeSpecificPart = input.substring(blockchainInfoScheme.length()); } else if (input.startsWith(correctScheme)) { schemeSpecificPart = input.substring(correctScheme.length()); } else { throw new BitcoinURIParseException("Unsupported URI scheme: " + uri.getScheme()); } // Split off the address from the rest of the query parameters. String[] addressSplitTokens = schemeSpecificPart.split("\\?", 2); if (addressSplitTokens.length == 0) throw new BitcoinURIParseException("No data found after the bitcoin: prefix"); String addressToken = addressSplitTokens[0]; // may be empty! String[] nameValuePairTokens; if (addressSplitTokens.length == 1) { // Only an address is specified - use an empty '<name>=<value>' token array. nameValuePairTokens = new String[] {}; } else { // Split into '<name>=<value>' tokens. nameValuePairTokens = addressSplitTokens[1].split("&"); } // Attempt to parse the rest of the URI parameters. parseParameters(params, addressToken, nameValuePairTokens); if (!addressToken.isEmpty()) { // Attempt to parse the addressToken as a Bitcoin address for this network try { Address address = Address.fromBase58(params, addressToken); putWithValidation(FIELD_ADDRESS, address); } catch (final AddressFormatException e) { throw new BitcoinURIParseException("Bad address", e); } } if (addressToken.isEmpty() && getPaymentRequestUrl() == null) { throw new BitcoinURIParseException("No address and no r= parameter found"); } }